PageSourceSearch

https://shop.sqcf.org/js/site_shop/site_shop.js?1790048649

js sqcf.org collected 2026-09-24 10:12:33 UTC 659,198 bytes, 14,506 lines download raw bytes

1/**
2 * site_shop 상수 값
3 * @type {{WIDTH_MOBILE: number, SEEMORE_HEIGHT: {PC: number, MOBILE: number}, PROD_TYPE: {DIGITAL: string, SUBSCRIBE: string, NORMAL: string}, TAB_TYPE: {RETURN: string, QNA: string, REVIEW: string, DETAIL: string}}}
4 */
5const SHOP_CONST = {
6    /** shopping default */
7    WIDTH_MOBILE: 768,
8    PROD_TYPE: {
9        NORMAL: 'normal',
10        DIGITAL: 'digital',
11        SUBSCRIBE: 'subscribe'
12    },
13    TAB_TYPE: {
14        DETAIL: 'detail',
15        QNA: 'qna',
16        REVIEW: 'review',
17        RETURN: 'return'
18    },
19    SEEMORE_HEIGHT: {
20        PC: 1050,
21        MOBILE: 550
22    },
23    /** cart */
24    CART_TYPE_NORMAL: 'normal',
25    CART_TYPE_REGULARLY: 'regularly'
26};
27
28/**
29 * 사은품 MFE(magnet-shell)가 DOM에 적재되어 있는지 검사한다.
30 * 사은품 미사용 사이트는 PHP단에서 magnet-shell 자체를 렌더하지 않으므로,
31 * 이 검사가 false 이면 사은품 관련 처리를 스킵해도 안전하다.
32 * @returns {boolean}
33 */
34const isFreebieMfeMounted = () => {
35    return !!document.querySelector('magnet-shell[data-manifest-url*="fo-prod-detail-freebie"]');
36};
37
38/**
39 * 로그인 페이지로 리다이렉트 (코드 7 처리용) - 전역 함수
40 * @returns {void}
41 */
42function redirectToLoginPage() {
43    var back_url_base64 = window.location.href; // 전체 URL 사용
44
45    // 로그인 팝업 사용 여부에 따른 분기
46    if (typeof cm_data !== 'undefined' && cm_data.use_login_popup === 'Y') {
47        if (typeof window.SITE_MEMBER !== 'undefined' && typeof window.SITE_MEMBER.openLogin === 'function') {
48            window.SITE_MEMBER.openLogin(cm_data.back_url, cm_data.back_url_unit_type, 'order', cm_data.use_login_popup);
49        } else {
50            // 팝업 함수가 없으면 리다이렉트로 폴백
51            window.location.href = '/login?back_url=' + encodeURIComponent(btoa(back_url_base64));
52        }
53    } else {
54        window.location.href = '/login?back_url=' + encodeURIComponent(btoa(back_url_base64));
55    }
56}
57
58// __bs_imweb localStorage 의 landingUrl 을 imweb-landing-url 헤더 값으로 변환한다.
59// 정규식은 HTTP 헤더 인젝션 방어, @direct 는 attribution 정책상 미송신.
60function getBsLandingUrlHeaderValue() {
61    try {
62        const raw = localStorage.getItem('__bs_imweb');
63        if (!raw) return '';
64        const data = JSON.parse(raw);
65        if (!data || typeof data.landingUrl !== 'string' || !data.landingUrl) return '';
66        const stripped = data.landingUrl.replace(/[\r\n\x00-\x1F\x7F]/g, '');
67        if (!stripped || stripped.indexOf('@direct') === 0) return '';
68        return stripped;
69    } catch (e) {
70        return '';
71    }
72}
73
74var SITE_SHOP_DETAIL = function () {
75    var $body;
76    var $selected_options;
77    var $prod_detail;
78    var $prod_detail_content_mobile;
79    var $prod_detail_content_pc;
80    var $prod_detail_content_tab_mobile;
81    var $prod_detail_return;
82    var $prod_detail_return_mobile;
83    var $first_photo_review_wrap;
84    var $prod_image_list;
85    var $prod_image_list_rolling;
86    var $review_image_list;
87    var $review_image_list_rolling;
88    var $review_summary_wrap;
89    var $review_summary_wrap_mobile;
90    var $prod_goods_form;
91    var $add_cart_alarm;
92    // 장바구니 담기 후 무료 배송 안내 모달 ($_init_deliv_price_flexable_key > 0인 케이스에만 DOM에 존재)
93    var $add_cart_alarm_with_free_ship_notify;
94    var $add_cart_alarm_with_product_recommendation;
95    var $confirm_order_with_cart_alarm;
96    var $deliv_visit_wrap;
97    var $options;
98		var $prod_additional;
99		var $prod_additional_options;
100		var $prod_additional_options_default;
101    var $prod_deliv_setting;
102    var _initDetailDone = false;
103    var _pendingSalesToolPreviewMessages = [];
104    var _handleSalesToolPreviewMessage;
105    var _isFreeShipNotifySalesToolPreview = false;
106    var _isWaitingFreeShipNotifySdkForAddCartAlarm = false;
107    var _isWaitingFreeShipNotifyPreviewReadyToNotify = false;
108    var _freeShipNotifyPreviewReadyObserver = null;
109    var _freeShipNotifyPreviewReadyCallbacks = [];
110    var _hasFreeShipNotifyInlineMagnet = false;
111    // HTML sanitize: script 태그, 이벤트 핸들러 제거 (IMOPS-13023)
112    var _sanitizeHtml = function (html) {
113        if (html === null || html === undefined) return html;
114        var div = document.createElement('div');
115        div.innerHTML = html;
116        var scripts = div.querySelectorAll('script');
117        for (var i = 0; i < scripts.length; i++) {
118            scripts[i].parentNode.removeChild(scripts[i]);
119        }
120        var dangerousTags = div.querySelectorAll('svg, math, iframe, object, embed, form');
121        for (var t = 0; t < dangerousTags.length; t++) {
122            dangerousTags[t].parentNode.removeChild(dangerousTags[t]);
123        }
124        var allEls = div.querySelectorAll('*');
125        for (var j = 0; j < allEls.length; j++) {
126            var attrs = allEls[j].attributes;
127            for (var k = attrs.length - 1; k >= 0; k--) {
128                var attrName = attrs[k].name.toLowerCase();
129                var attrVal = (attrs[k].value || '').replace(/\s/g, '').toLowerCase();
130                if (attrName.indexOf('on') === 0) {
131                    allEls[j].removeAttribute(attrs[k].name);
132                } else if ((attrName === 'href' || attrName === 'src' || attrName === 'action') &&
133                           (attrVal.indexOf('javascript:') === 0 || attrVal.indexOf('data:text/html') === 0 || attrVal.indexOf('vbscript:') === 0)) {
134                    allEls[j].removeAttribute(attrs[k].name);
135                } else if (attrName === 'srcdoc' || attrName === 'xlink:href') {
136                    allEls[j].removeAttribute(attrs[k].name);
137                }
138            }
139        }
140        return div.innerHTML;
141    };
142		var $wish_buttons;
143		var selected_prod_additionals = [];
144    var selected_options = []; /** [ {options:[{option_code:, value_code:, value_name:}], price:, count:, require:} ] */
145    var selected_prod_additional_options = []; /** [ {options:[{option_code:, value_code:, value_name:}], price:, count:, require:} ] */
146    var selected_require_options = []; /** [ { value_type:SELECT(선택형)/INPUT(입력형), option_code:, value_code:, value_name:  } ] */
147    var selected_prod_additional_require_options = []; /** [ { value_type:SELECT(선택형)/INPUT(입력형), option_code:, value_code:, value_name:  } ] */
148    var free_ship_notify_prod_additional_regular_price_map = {};
149		var current_select_additional_prod_idx = 0; /** 최근 선택한 추가상품 idx(추가상품 UI 영역에 표시하기 위해 사용) **/
150    var require_option_count = 0;
151    var prod_additional_require_option_count = {};
152    var require_input_option_count = 0;
153    var prod_additional_require_input_option_count = {};
154    var current_prod_idx = 0;
155		var current_prod_code = 0;
156    var prod_stock_use = false;
157    var prod_stock = 0;
158    var prod_stock_unlimit = false;
159    var prod_price = 0;
160    var FREE_SHIP_NOTIFY_DELIV_PRICE_COST_TYPE_AFTER = 'after_sale';
161    var free_ship_notify_deliv_price_cost_type = 'before_sale';
162    var free_ship_notify_prod_regular_price = 0;
163    var order_count = 0;
164    /** 구매할 수량 (옵션이 없는 경우에만)(*/
165    /** 크리에이터 전용 혜택가 데이터 — PHP 에서 setCreatorDiscountData 로 주입. null 이면 비활성 */
166    var creator_discount_data = null;
167    var creator_discount_max_reached = false;
168    var creator_discount_max_toast_timer = null;
169    var creator_discount_max_toast_text = '최대 할인 금액에 도달했어요';
170    var isComplete = false;
171    var current_content_tab = '';
172    /** detail,review,qna*/
173    var isUseNpMobile = false;
174    /** 모바일 네이버페이 사용 유무*/
175    var $deliv_type = '';
176    var $deliv_pay_type = '';
177    /** 배송 국가 (옵션 select 커스텀 변경) */
178    var $deliv_country = '';
179    /** 선택된 배송 템플릿 코드 (OMS), 프런트 UI에서 설정 예정 */
180    var selected_shipping_template_code = '';
181
182    /* 구매 수량 체크를 위한 변수 */
183    var max_prod_quantity = 0;
184    var max_member_quantity = 0;
185    var maximum_purchase_quantity_type = 'order';
186    var optional_limit = 0;
187    var optional_limit_type = 'relative';
188    var prod_name = '';
189
190    var total_price_localize_text = '';		// 총금액 다국어 코드
191
192    var add_order_progress_check = false;
193
194    var use_lazy_load = true; //레이지 로드 사용여부
195
196    var tab_type = 'Y'; // pc 버전 탭 타입
197
198    var prod_type = SHOP_CONST.PROD_TYPE.NORMAL;
199
200    // 상품 상세페이지 내 기획전 위젯의 cart 버튼을 클릭하여 모달을 띄우는 동작에서
201    // 모달이 닫힐 시 설정 값의 초기화를 위해 변수를 설정
202    var prod_idx_org = 0;
203		var prod_code_org = 0;
204    var prod_price_org = 0;
205    var require_option_count_org = 0;
206    var use_lazy_load_org = true;
207    var tab_type_org = 'Y';
208    var is_site_page_org = true;
209    var prod_type_org = SHOP_CONST.PROD_TYPE.NORMAL;
210
211    var is_view_price = true;
212    var paging_type = 'st00';
213    var paging_default_type = 'st00';
214    var paging_active_type = 'st00';
215    var cm_data = {};
216    var section_code;
217
218    var prod_edit_time = '';
219    var prod_deliv_hash = '';
220    var shop_view_style = '';
221
222    var handle_loadDelivSetting = 0;
223    var prod_option_touching = false;
224
225    var first_tab = '';
226    var use_tab_list = [];
227
228    var window_width = 1920;
229    var is_mobile_width = false;
230
231    var default_options = {
232        prod_idx: 0,
233        prod_price: 0,
234        require_option_count: 0,
235        require_input_option_count: 0,
236		use_image_optimizer_on_product_detail_markup: true,
237        shop_use_full_load: false,
238        is_site_page: false,
239        prod_type: SHOP_CONST.PROD_TYPE.NORMAL,
240        is_prod_detail_page: false,
241        is_price_view_permission: false,
242        cm_style: '{}',
243        section_code: '',
244        exist_color_option: false,
245        is_exist_color_option_images: false,
246        first_tab: '',
247        can_add_order: true,
248        add_order_permission_message: '',
249        add_order_permission_type: '',
250        initial_is_guest: false,
251        shipping_template_code: '',
252        free_ship_notify_deliv_price_cost_type: 'before_sale',
253        free_ship_notify_prod_regular_price: 0
254    };
255    var options = {};
256    var analyticsEventName = 'imweb:shop:detail-analytics';
257
258    var dispatchShopAnalyticsEvent = function (type, payload) {
259        try {
260            if (!document || typeof document.dispatchEvent !== 'function') return;
261
262            var event = null;
263            if (typeof CustomEvent === 'function') {
264                event = new CustomEvent(analyticsEventName, {
265                    detail: {
266                        type: type,
267                        payload: payload || {}
268                    }
269                });
270            } else if (document.createEvent) {
271                event = document.createEvent('CustomEvent');
272                event.initCustomEvent(
273                    analyticsEventName,
274                    false,
275                    false,
276                    {
277                        type: type,
278                        payload: payload || {}
279                    }
280                );
281            }
282
283            if (event) {
284                document.dispatchEvent(event);
285            }
286        } catch (e) {
287            return;
288        }
289    };
290
291    var safeSetProdDetailAnalyticsTargets = function (body, pcTarget, mobileTarget, isMobileGetter) {
292        try {
293            dispatchShopAnalyticsEvent('setTargets', {
294                body: body,
295                pcTarget: pcTarget,
296                mobileTarget: mobileTarget,
297                isMobileGetter: isMobileGetter
298            });
299        } catch (e) {
300            return;
301        }
302    };
303
304    var safeScheduleProdDetailBodyExpandedHeightUpdate = function () {
305        try {
306            dispatchShopAnalyticsEvent('scheduleBodyHeightUpdate');
307        } catch (e) {
308            return;
309        }
310    };
311
312    // redirectToLoginPage 함수는 전역 스코프로 이동됨
313
314    function ensureAddOrderPermission() {
315        if (options.can_add_order !== false) {
316            return true;
317        }
318
319        var wasGuestInitially = options.initial_is_guest === true;
320        var isGuestNow = typeof window !== 'undefined' && window.IS_GUEST === true;
321        if (wasGuestInitially && !isGuestNow) {
322            return true;
323        }
324
325        var message = options.add_order_permission_message;
326        if (!message) {
327            if (typeof LOCALIZE !== 'undefined') {
328                if (options.add_order_permission_type === 'member' && typeof LOCALIZE.설명_회원만구매가능1 === 'function') {
329                    message = LOCALIZE.설명_회원만구매가능1();
330                } else if (options.add_order_permission_type === 'group' && typeof LOCALIZE.설명_그룹만구매가능1 === 'function') {
331                    message = LOCALIZE.설명_그룹만구매가능1();
332                } else if (options.add_order_permission_type === 'admin' && typeof LOCALIZE.설명_소유자만구매가능1 === 'function') {
333                    message = LOCALIZE.설명_소유자만구매가능1();
334                } else if (typeof LOCALIZE.설명_회원만구매가능1 === 'function') {
335                    message = LOCALIZE.설명_회원만구매가능1();
336                }
337            }
338
339        }
340
341        if (!message) {
342            message = 'Purchase not permitted.';
343        }
344
345        alert(message);
346        return false;
347    }
348
349    var is_init_detail = false;
350    var last_refund_data;
351    //결제 타입 (일반, 정기구독)
352    var cart_type;
353
354	var review_page = 1;
355	var qna_page = 1;
356
357	let use_cdn_optimized = false;
358	let animatedWebpSupport = null;
359
360	const isProblematicAnimatedWebpUserAgent = (userAgent) => {
361		if (!userAgent) {
362			return false;
363		}
364
365		return /(iPad|iPhone|iPod)/i.test(userAgent) || (/Macintosh/i.test(userAgent) && /Mobile/i.test(userAgent));
366	};
367
368	// animated WebP 사용 여부는 페이지당 한 번만 판별한다.
369	const canUseAnimatedWebp = () => {
370		if (animatedWebpSupport !== null) {
371			return animatedWebpSupport;
372		}
373
374		let userAgent = '';
375		try {
376			userAgent = navigator.userAgent || '';
377		} catch (e) {
378			userAgent = '';
379		}
380
381		animatedWebpSupport = !isProblematicAnimatedWebpUserAgent(userAgent);
382
383		return animatedWebpSupport;
384	};
385
386
387	const setImageWidthHeightBeforeLoad = async (node) => {
388		console.time('loadTemplate')
389		const styleFragment = window.document.createElement('style');
390		styleFragment.innerHTML = `@keyframes lazyload {0% {opacity: 0;} 50% {opacity: 0.1;} 100% {opacity: 0;}}`
391		node.prepend(styleFragment);
392
393		const ORIGINAL_WIDTH_HEADER_KEY = 'x-amz-meta-original-width';
394		const ORIGINAL_HEIGHT_HEADER_KEY = 'x-amz-meta-original-height';
395		const imgElements = node.querySelectorAll('img') || [];
396		const timeoutLimit = 1000000;
397		const checkOnceSupport = checkerlistenerOption('once');
398		const loadingImageJobs = Array.prototype.map.call(imgElements, (async (imgElement) => {
399			let abortTimer = null;
400			try {
401				const srcOriginal = new URL(imgElement.src);
402				const srcDefault = new URL(window.CDN_UPLOAD_URL);
403				if (srcOriginal.host !== srcDefault.host || !srcOriginal.pathname.startsWith(srcDefault.pathname)) {
404					throw new Error();
405				}
406				// WARN: srcResized 값이 콜백 내에서 계속 변경되므로(searchParams.set) 주의
407				const srcResized = new URL(srcOriginal);
408				srcResized.host = new URL(window.CDN_OPTIMIZED_URL).host;
409				// 본문이 아니라 x-amz-meta-original-* 헤더만 쓰는 HEAD 프로브라 가장 작은 프리셋을 요청한다.
410				// 종전에는 0 을 보냈고 imgo 가 최근접 프리셋인 100 으로 스냅해 같은 객체를 내려줬다.
411				// imgo 가 w=0 을 "원본 해상도 유지"로 바꾸므로, 스냅에 기대지 않고 100 을 명시한다.
412				srcResized.searchParams.set('w', 100);
413
414				const signal = typeof AbortSignal.timeout === 'function'
415					? AbortSignal.timeout(timeoutLimit)
416					: new AbortController()
417
418				if (typeof signal.abort === 'function') {
419					abortTimer = setTimeout(signal.abort, timeoutLimit);
420				}
421				const imgRes = await fetch(srcResized, { method: 'HEAD',  signal });
422
423				const checkResized = imgRes.headers.has(ORIGINAL_WIDTH_HEADER_KEY)
424					&& imgRes.headers.has(ORIGINAL_HEIGHT_HEADER_KEY);
425				if (
426					!imgRes.ok || !checkResized
427				) {
428					throw new Error();
429				}
430
431				const meta = {
432					originalWidth: imgRes.headers.get(ORIGINAL_WIDTH_HEADER_KEY),
433					originalHeight: imgRes.headers.get(ORIGINAL_HEIGHT_HEADER_KEY),
434				}
435
436				const imgKey = srcResized.pathname.replace(/[^a-z\d]/g, '_');
437				imgElement.dataset[imgKey] = "";
438				const imgSelector = `img[data-${imgKey}]`;
439
440				const styleFragment = window.document.createElement('style');
441				styleFragment.innerHTML = `${imgSelector} {opacity:0;transition:opacity 60ms ease-out;} ${imgSelector}.loaded {opacity:1;}`;
442				imgElement.parentNode.prepend(styleFragment);
443				srcResized.searchParams.set('w', 1920);
444
445				if (srcResized.pathname.endsWith('.gif')) {
446					const useAnimatedWebp = canUseAnimatedWebp();
447					srcResized.searchParams.set('f', useAnimatedWebp ? 'webp' : 'gif');
448				}
449				imgElement.src = srcResized.href;
450				imgElement.style.objectFit = 'cover';
451				imgElement.style.maxWidth = '100%';
452				imgElement.style.height = 'auto';
453				imgElement.width = (imgElement.width || meta.originalWidth);
454				imgElement.height = (imgElement.height || meta.originalHeight);
455				imgElement.sizes = '100vw';
456				imgElement.srcset = [1536, 1280, 1080, 828, 768, 640, 576, 368]
457					.map((w) => {
458						srcResized.searchParams.set('w', w);
459						return `${srcResized.href} ${w}w`;
460					}).join(', ');
461				imgElement.loading = 'lazy';
462
463				imgElement.addEventListener('error', () => {
464					imgElement.src = imgElement.dataset.original || srcOriginal.href;
465					imgElement.classList.add('loaded');
466					imgElement.style.removeProperty('objectFit');
467					imgElement.style.removeProperty('maxWidth');
468					imgElement.style.removeProperty('height');
469					imgElement.removeAttribute('width');
470					imgElement.removeAttribute('height');
471					imgElement.removeAttribute('sizes');
472					imgElement.removeAttribute('srcset');
473					imgElement.removeAttribute('loading');
474				});
475
476				if (checkOnceSupport) {
477					imgElement.addEventListener('load', () => imgElement.classList.add('loaded'), { once: true });
478				} else {
479					imgElement.onload = () => imgElement.classList.add('loaded');
480				}
481			} catch (e) {
482				// 비-CDN 호스트·리사이즈 헤더 없음은 throw new Error() 로 표현한 "최적화 대상 아님" 의도 분기(빈 메시지)다.
483				// 이런 정상 폴백은 debug 로 낮추고, fetch/네트워크 등 진짜 예외는 error 로 남겨 운영 가시성을 유지한다.
484				if (e instanceof Error && !e.message) {
485					console.debug(e);
486				} else {
487					console.error(e);
488				}
489				// TypeError, FetchError, AbortError, TimeoutError ... 어떤 에러든 원본 이미지로 대체
490				imgElement.src = imgElement.dataset.original || imgElement.src;
491				imgElement.classList.add('loaded');
492				imgElement.removeAttribute('height');
493			} finally {
494				if (abortTimer) {
495					clearTimeout(abortTimer);
496				}
497			}
498		}));
499
500		for ( const job of loadingImageJobs ) {
501			await job;
502		}
503		console.timeEnd('loadTemplate')
504		return node;
505	}
506
507    /*
508    option 샘플
509    initDetail({
510        prod_idx: ,
511        prod_price: ,
512        require_option_count: ,
513		use_image_optimizer_on_product_detail_markup: ,
514        shop_use_full_load: ,
515        shop_view_tab_display: ,
516        is_site_page: ,
517        prod_type: ,
518        is_prod_detail_page: ,
519        is_price_view_permission: ,
520        cm_style: ,
521        section_code:
522        exist_color_option:
523        is_empty_color_option_images:
524        is_prod_wish_by_member,
525        wish_count,
526        is_use_wish_btn,
527        is_show_wish_count,
528        is_soldout,
529        is_possible_use_gift
530    });
531    */
532    var initDetail = function (_option) {
533        options = $.extend({}, default_options, _option);
534
535	    if(typeof options.shop_view_tab_display === 'undefined') {
536		    options.shop_view_tab_display = options.shop_pc_tab_type_one_page === 'Y' ? 'Y' : 'N';
537	    }
538	    if(typeof options.shop_tab_fixed === 'undefined') {
539		    options.shop_tab_fixed = options.shop_tab_type_unfixed !== 'Y' ? 'Y' : 'N';
540	    }
541
542        var prodIdx = options.prod_idx;
543				var prodCode = options.prod_code;
544        var price = options.prod_price;
545        var requireOptionCnt = options.require_option_count;
546        var requireInputOptionCnt = options.require_input_option_count;
547        use_lazy_load = options.use_image_optimizer_on_product_detail_markup;
548        tab_type = options.shop_view_tab_display;
549        var is_site_page = options.is_site_page;
550        var _prod_type = options.prod_type;
551        var is_prod_detail_page = options.is_prod_detail_page;
552        var view_price = options.is_price_view_permission;
553        var _cm_data = options.cm_style;
554        section_code = options.section_code;
555        prod_edit_time = options.prod_edit_time;
556        prod_deliv_hash = options.prod_deliv_hash;
557        shop_view_style = options.shop_view_style;
558        first_tab = options.first_tab;
559        use_tab_list = options.use_tab_list;
560        selected_shipping_template_code = options.shipping_template_code || '';
561
562        window_width = window.innerWidth;
563        is_mobile_width = (window_width < SHOP_CONST.WIDTH_MOBILE);
564		use_cdn_optimized = options.use_cdn_optimized;
565
566        $body = $('body');
567
568        if (is_prod_detail_page) {
569            var $target_modal = $('.modal_prod_detail_from_shopping_list');
570            $prod_image_list = $target_modal.find('#prod_image_list');
571            $prod_image_list_rolling = $prod_image_list.find('div.owl-carousel');
572            $prod_goods_form = $target_modal.find('#prod_goods_form');
573            $selected_options = $target_modal.find('#prod_selected_options');
574            $options = $target_modal.find('#prod_options');
575            $prod_additional = $target_modal.find('#prod_additional');
576            $prod_additional_options = $target_modal.find('#prod_additional_options');
577            $prod_additional_options_default = $prod_additional_options; // 기본 위치 저장
578            $prod_deliv_setting = $target_modal.find('#prod_deliv_setting');
579            $prod_detail = $target_modal.find('#prod_detail');
580            $prod_detail_content_pc = $prod_detail.find('._prod_detail_detail_lazy_load');
581            $prod_detail_content_mobile = $target_modal.find('._prod_detail_detail_lazy_load_mobile');
582            $prod_detail_content_tab_mobile = $target_modal.find('.categorize-mobile .site_prod_nav_wrap');
583            $add_cart_alarm = $target_modal.find('#shop_detail_add_cart_alarm');
584            $add_cart_alarm_with_free_ship_notify = $target_modal.find('#shop_detail_add_cart_alarm_with_free_ship_notify');
585            $add_cart_alarm_with_product_recommendation = $target_modal.find('#shop_detail_add_cart_alarm_with_product_recommendation');
586            $confirm_order_with_cart_alarm = $target_modal.find('#shop_detail_confirm_order_with_cart_alarm');
587            if ($add_cart_alarm.length === 0) {
588                $add_cart_alarm = $('#shop_detail_add_cart_alarm');
589            }
590            if ($add_cart_alarm_with_free_ship_notify.length === 0) {
591                $add_cart_alarm_with_free_ship_notify = $('#shop_detail_add_cart_alarm_with_free_ship_notify');
592            }
593            if ($add_cart_alarm_with_product_recommendation.length === 0) {
594                $add_cart_alarm_with_product_recommendation = $('#shop_detail_add_cart_alarm_with_product_recommendation');
595            }
596            if ($confirm_order_with_cart_alarm.length === 0) {
597                $confirm_order_with_cart_alarm = $('#shop_detail_confirm_order_with_cart_alarm');
598            }
599            $deliv_visit_wrap = $target_modal.find('#deliv_visit_wrap');
600	        $wish_buttons = $target_modal.find('._wish_button');
601        } else {
602            $prod_image_list = $('#prod_image_list');
603            $prod_image_list_rolling = $('#prod_image_list').find('div.owl-carousel');
604            $prod_goods_form = $('#prod_goods_form');
605            $selected_options = $('#prod_selected_options');
606            $options = $('#prod_options');
607            $prod_additional = $('#prod_additional');
608	          $prod_additional_options = $('#prod_additional_options');
609            $prod_additional_options_default = $prod_additional_options; // 기본 위치 저장
610            $prod_deliv_setting = $('#prod_deliv_setting');
611            $prod_detail = $('#prod_detail');
612            $prod_detail_content_pc = $prod_detail.find('._prod_detail_detail_lazy_load');
613            $prod_detail_content_mobile = $('._prod_detail_detail_lazy_load_mobile');
614            $prod_detail_content_tab_mobile = $('.categorize-mobile .site_prod_nav_wrap');
615            $add_cart_alarm = $('#shop_detail_add_cart_alarm');
616            $add_cart_alarm_with_free_ship_notify = $('#shop_detail_add_cart_alarm_with_free_ship_notify');
617            $add_cart_alarm_with_product_recommendation = $('#shop_detail_add_cart_alarm_with_product_recommendation');
618            $confirm_order_with_cart_alarm = $('#shop_detail_confirm_order_with_cart_alarm');
619            $deliv_visit_wrap = $('#deliv_visit_wrap');
620	        $wish_buttons = $('._wish_button');
621        }
622
623        $prod_detail_return = $("#prod_detail_return_body");
624        $prod_detail_return_mobile = $("#prod_detail_return_body_mobile");
625
626        // 서버 렌더링된 초기 데이터 읽기 (data 속성)
627        var $item_detail_wrap = $prod_detail.find('._item_detail_wrap');
628        if ($item_detail_wrap.length && $item_detail_wrap.data('deliv-type')) {
629            $deliv_type = $item_detail_wrap.data('deliv-type');
630            $deliv_pay_type = $item_detail_wrap.data('deliv-pay-type') || '';
631            $deliv_country = $item_detail_wrap.data('deliv-country') || '';
632            $('._free_ship_notify_detail_magnet_wrap').toggleClass('tw-hidden', $('._deliv_country_selector').length > 0 && $deliv_country !== 'KR');
633            $('.btn-popover').popover();
634            $('.html-popover').popover({html: true});
635            // 환불 데이터 → 반품/교환 탭 렌더링
636            if ($item_detail_wrap.data('refund')) {
637                last_refund_data = $item_detail_wrap.data('refund');
638                setDetailReturnHtml();
639            }
640        }
641
642        $first_photo_review_wrap = $prod_detail.find('._first_photo_review_wrap');
643        $review_summary_wrap = $prod_detail.find('._review_summary_wrap');
644        $review_summary_wrap_mobile = $prod_detail.find('._review_summary_wrap_mobile');
645        safeSetProdDetailAnalyticsTargets(
646            $body,
647            $prod_detail_content_pc,
648            $prod_detail_content_mobile,
649            function () {
650                return is_mobile_width;
651            }
652        );
653        if (options.only_regularly) {
654            if (!$prod_detail.hasClass('shop-style-b')) selectCartType('regularly');
655        }
656
657
658        is_view_price = view_price;
659        current_prod_idx = prodIdx;
660        current_prod_code = prodCode;
661        prod_price = price;
662        free_ship_notify_deliv_price_cost_type = options.free_ship_notify_deliv_price_cost_type || 'before_sale';
663        free_ship_notify_prod_regular_price = getFreeShipNotifyRegularPrice(options.free_ship_notify_prod_regular_price, prod_price);
664        require_option_count = parseInt(requireOptionCnt);
665        require_input_option_count = parseInt(requireInputOptionCnt);
666	      selected_prod_additionals = [];
667        selected_options = [];
668	      selected_prod_additional_options = [];
669	      free_ship_notify_prod_additional_regular_price_map = {};
670        selected_require_options = [];
671	      selected_prod_additional_require_options = [];
672	      order_count = 0;
673				prod_additional_require_option_count = {};
674				prod_additional_require_input_option_count = {};
675	    prod_additional_period_discount_data = {};
676	    prod_additional_period_dc_type = {};
677	    current_select_additional_prod_idx = 0;
678        isComplete = true;
679        //isUseNpMobile = use_np_mobile;
680        cm_data = JSON.parse(_cm_data);
681
682        // 옵션 없는 상품(require_option_count == 0)은 사용자가 옵션/수량을 건드리기 전까지
683        // updateSelectedOptions 가 호출되지 않아 무료배송 안내 MFE 가 초기 가격을 받지 못한다.
684        // DOM 갱신은 필요 없으므로 SDK dispatch 만 분리 호출하여 첫 렌더부터 무료배송 기준 금액이 반영되도록 한다.
685        if (require_option_count == 0) {
686            notifyFreeShipNotifySdk(getFreeShipNotifyInitialTotal());
687        }
688
689		// 그러니까 이 값들은, 상품 상세페이지 내 기획전 위젯의 cart 버튼을 클릭하여 모달을 띄우는 동작에서
690		// 모달이 닫힐 시 설정 값의 초기화를 위해 사용하는 것이다.
691        if (!is_prod_detail_page) {
692            prod_idx_org = current_prod_idx;
693						prod_code_org = current_prod_code;
694            prod_price_org = prod_price;
695            require_option_count_org = require_option_count;
696            use_lazy_load_org = use_lazy_load;
697            tab_type_org = tab_type;
698            is_site_page_org = is_site_page;
699            prod_type_org = _prod_type;
700        }
701
702        if (options.is_use_wish_btn) {
703            toggleWishButtons($wish_buttons, options.wish_count, options.is_show_wish_count, options.is_prod_wish_by_member);
704        }
705
706        /* 하나의 페이지에 모든 탭 열람일 경우 상품 상세설명 HTML 세팅 */
707        // html설정
708        var is_shop_view_tab_display = (options.shop_view_tab_display == 'Y');
709
710        if (is_shop_view_tab_display) {
711            let detail
711_html = null;
712            let $seemore_wrap = null;
713
714			if (use_cdn_optimized) {
715				if (is_mobile_width) {
716				Promise.resolve(loadTemplate('prodDetailMobile', $prod_detail_content_mobile, use_lazy_load ? setImageWidthHeightBeforeLoad : null))
717					.then(function() {
718						safeScheduleProdDetailBodyExpandedHeightUpdate();
719					});
720			} else {
721				Promise.resolve(loadTemplate('prodDetailPC', $prod_detail_content_pc, use_lazy_load ? setImageWidthHeightBeforeLoad : null))
722					.then(function() {
723						safeScheduleProdDetailBodyExpandedHeightUpdate();
724					});
725			}
726		} else {
727			detail_html = IMWEB_TEMPLATE.loadSimple("prodDetailPC");
728			$prod_detail_content_pc.html(detail_html);
729			detail_html = IMWEB_TEMPLATE.loadSimple("prodDetailMobile");
730			$prod_detail_content_mobile.html(detail_html);
731			safeScheduleProdDetailBodyExpandedHeightUpdate();
732		}
733
734            $seemore_wrap = $body.find('._seemore_wrap');
735
736            if ($seemore_wrap.length > 0) {
737                $seemore_wrap.show();
738                $prod_detail_content_pc.toggleClass('hide_seemore', false);
739	            $prod_detail_content_mobile.toggleClass('hide_seemore', false);
740            }
741        }
742
743		if (!use_cdn_optimized) {
744
745			runLazyload();
746		}
747
748        prod_type = _prod_type;
749
750        var hash_temp = location.hash.split('!/');
751
752	    setTimeout(() => {
753		    switch(hash_temp[0]){
754			    case '#prod_detail_detail':
755				    is_init_detail = true;
756				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.DETAIL);
757				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.DETAIL);
758				    break;
759			    case '#prod_detail_review':
760				    is_init_detail = true;
761				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.REVIEW);
762				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW);
763				    if(hash_temp[1]){
764					    SITE_SHOP_DETAIL.viewReviewDetail(hash_temp[1], 1, 'N', 'Y');
765				    }
766				    break;
767			    case '#prod_detail_qna':
768				    is_init_detail = true;
769				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.QNA);
770				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.QNA);
771				    if(hash_temp[1]){
772					    SITE_SHOP_DETAIL.viewQnaDetail(hash_temp[1], 1, 'Y');
773				    }
774				    break;
775			    case '#prod_detail_return':
776				    is_init_detail = true;
777				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.RETURN);
778				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.RETURN);
779				    break;
780			    default:
781
782				    let target_tab = options.first_tab === "prod_detail"
783						    ? SHOP_CONST.TAB_TYPE.DETAIL
784						    : options.first_tab === "prod_review"
785								    ? SHOP_CONST.TAB_TYPE.REVIEW
786								    : options.first_tab === "prod_qna"
787										    ? SHOP_CONST.TAB_TYPE.QNA
788										    : options.first_tab === "prod_return"
789												    ? SHOP_CONST.TAB_TYPE.RETURN
790												    : "";
791
792				    if(options.shop_view_tab_display === "N"){
793					    if(is_mobile_width) {
794						    SITE_SHOP_DETAIL.changeContentTab(target_tab, review_page, qna_page, false, false);
795					    } else {
796						    SITE_SHOP_DETAIL.changeContentPCTab(target_tab, review_page, qna_page, false, false);
797					    }
798				    }
799
800				    checkSeemore(target_tab);
801				    is_init_detail = true;
802				    break;
803		    }
804
805		    /* 크리마 리뷰 사용 중일 시 위젯 호출 */
806		    if(document.querySelector(".crema-product-reviews")){
807			    if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
808				    crema.run();
809			    }
810		    }
811	    }, 200)
812
813        if (cm_data.shop_view_buy_item_tooltip === 'Y') {
814            $('.btn_tooltip[data-toggle="tooltip"]').tooltip({
815                delay: { show: 500, hide: 1000000 }
816            });
817            var toggle_regularly = $("input[name='add_cart_type']");
818            var $tooltip = $('.btn_tooltip[data-toggle="tooltip"]');
819            var window_height = $(window).height();
820            toggle_regularly.off('change').on('change', function () {
821                $tooltip.css({ 'opacity': '0' });
822                if ($(this).prop('checked')) {
823                    if ($tooltip.length > 0) {
824                        var window_height = $(window).height();
825                        $.each($tooltip, function (k, v) {
826                            var tooltip_y = v.getBoundingClientRect().y;
827                            if (tooltip_y >= 0 && tooltip_y < window_height) {
828                                $tooltip.css({ 'opacity': '1' });
829                                $(v).tooltip('show');
830                            }
831                        });
832                    }
833                }
834            });
835            if ($tooltip.length > 0) {
836                $.each($tooltip, function (k, v) {
837                    var tooltip_y = v.getBoundingClientRect().y;
838                    if (tooltip_y >= 0 && tooltip_y < window_height) {
839                        // 툴팁이 화면 내에 보이지 않을 때 show하면 위치가 어긋나게 됨
840                        setTimeout(function () {
841                            $(v).tooltip('show');
842                        }, 100);
843                    }
844
845                    $(window).scroll(function () {
846                        if ($(v).next('.tooltip:visible').length === 0) {
847                            var tooltip_y = v.getBoundingClientRect().y;
848                            if (tooltip_y > 0 && tooltip_y < (window_height - $(v).height())) {
849                                $(v).tooltip('show');
850                            }
851                        }
852                    });
853                });
854            }
855        }
856
857        setTimesale();
858        if (!is_prod_detail_page) {
859            setImgZoom(30, 800, section_code);
860
861            if ($first_photo_review_wrap.length > 0) {
862                getFirstPhotoReview(1);
863            }
864        }
865
866        /* resize 시 window_width 재계산 */
867        let is_resizing = false;
868        window.addEventListener('resize', function () {
869            if (!is_resizing) {
870                is_resizing = true;
871                setTimeout(function () {
872                    window_width = window.innerWidth;
873					let was_mobile_width = is_mobile_width;
874                    is_mobile_width = (window_width < SHOP_CONST.WIDTH_MOBILE);
875					if(is_mobile_width != was_mobile_width){
876						if(is_mobile_width){
877							changeContentTab(current_content_tab);
878						}else{
879							/* 동영상 전체화면 시 PC 너비로 인지되는데 탭을 새로 그리면서 동영상이 다시 불러와지는 문제가 있어 예외처리 */
880							if (!is_shop_view_tab_display && current_content_tab === SHOP_CONST.TAB_TYPE.REVIEW) {
881								changeContentPCTab(current_content_tab);
882							}
883						}
884						// 상단 조치로 인해 lazyload가 다시 실행되지 않아서 추가
885						setTabHistory(current_content_tab);
886						runLazyload();
887					}
888                    is_resizing = false;
889                }, 100);
890            }
891        });
892
893        /* debounce로 변경 */
894		// const handleResize = _.debounce(function () {
895		// 	window_width = window.innerWidth;
896		// 	let was_mobile_width = is_mobile_width;
897		// 	is_mobile_width = (window_width < SHOP_CONST.WIDTH_MOBILE /* 768w */);
898		// 	if(is_mobile_width != was_mobile_width){
899		// 		if(is_mobile_width){
900		// 			changeContentTab(current_content_tab);
901		// 		}else{
902		// 			/* 동영상 전체화면 시 PC 너비로 인지되는데 탭을 새로 그리면서 동영상이 다시 불러와지는 문제가 있어 예외처리 */
903		// 			if (!is_shop_view_tab_display && current_content_tab === SHOP_CONST.TAB_TYPE.REVIEW) {
904		// 				changeContentPCTab(current_content_tab);
905		// 			}
906		// 		}
907		// 		// 상단 조치로 인해 lazyload가 다시 실행되지 않아서 추가
908		// 		setTabHistory(current_content_tab);
909		// 		// runLazyload();
910		// 	}
911        // }, 100)
912
913        // window.addEventListener('resize', handleResize);
914
915        _hasFreeShipNotifyInlineMagnet = !!document.querySelector('.foFreeShipNotifyMagnet');
916
917        _initDetailDone = true;
918        if (isInSalesToolPreview && typeof _handleSalesToolPreviewMessage === 'function') {
919            while (_pendingSalesToolPreviewMessages.length > 0) {
920                _handleSalesToolPreviewMessage(_pendingSalesToolPreviewMessages.shift());
921            }
922        }
923        notifySalesToolPreviewReady();
924    };
925
926    var initProdStock = function (stock_use, stock, stock_un_limit) {
927        prod_stock_use = stock_use;
928        prod_stock = stock;
929        prod_stock_unlimit = stock_un_limit;
930    };
931
932    var runLazyload = function () {
933        if (!use_lazy_load)
934            return false;
935
936        var $seemore_wrap = $body.find('._seemore_wrap');
937        // [TFR-1179] 한 페이지에 전체 노출 + 순차적 로드 + 상세정보 펼쳐보기 미사용 시 순차적 로드에 의해 다른 탭 스크롤이 밀리는 현상 방지를 위해 마지막 이미지는 레이지 로드 제외
938        let is_except_last_lazy_load = options.shop_view_tab_display === 'Y' && $seemore_wrap.length == 0;
939		if(is_mobile_width){
940            let lazy_load_selector = 'img';
941            if(is_except_last_lazy_load){
942                let last_img = $prod_detail_content_mobile.find('img:last');
943                last_img.attr('src', last_img.attr('data-original')).removeAttr('height');
944                lazy_load_selector = 'img:not(:last)';
945            }
946			$prod_detail_content_mobile.find(lazy_load_selector).lazyload({		/* 상품 상세페이지 lazy load 적용, 기본 /img/no-image.png 는 한번만 불러옴 */
947				placeholder: NO_IMAGE_URL,
948				threshold: 100,
949				effect: "fadeIn",
950				effect_speed: 50, // fadeIn 시간 단축(기본 400ms→50ms)
951				load: function () {
952					$(this).removeAttr('height');
953					if ($seemore_wrap.length > 0) {
954						// 레이지로드 이후 no-image보다 로딩된 이미지가 작아 펼쳐보기 높이 제한에 걸리지 않게 되면 펼쳐보기 해제
955						setTimeout(function () {
956							if ($prod_detail_content_mobile.outerHeight() < SHOP_CONST.SEEMORE_HEIGHT.MOBILE) {
957								$prod_detail_content_mobile.toggleClass('hide_seemore', true);
958								checkSeemore(current_content_tab);
959							}
960						safeScheduleProdDetailBodyExpandedHeightUpdate();
961						}, 100);
962					}
963				}
964			});
965		}else{
966            let lazy_load_selector = 'img';
967            if(is_except_last_lazy_load){
968                let last_img = $prod_detail_content_pc.find('img:last');
969                last_img.attr('src', last_img.attr('data-original')).removeAttr('height');
970                lazy_load_selector = 'img:not(:last)';
971            }
972			$prod_detail_content_pc.find(lazy_load_selector).lazyload({		/* 상품 상세페이지 lazy load 적용, 기본 /img/no-image.png 는 한번만 불러옴 */
973				placeholder: NO_IMAGE_URL,
974				threshold: 100,
975				effect: "fadeIn",
976				effect_speed: 50, // fadeIn 시간 단축(기본 400ms→50ms)
977				load: function () {
978					$(this).removeAttr('height');
979					if ($seemore_wrap.length > 0) {
980						// 레이지로드 이후 no-image보다 로딩된 이미지가 작아 펼쳐보기 높이 제한에 걸리지 않게 되면 펼쳐보기 해제
981						setTimeout(function () {
982							if ($prod_detail_content_pc.outerHeight() < SHOP_CONST.SEEMORE_HEIGHT.PC) {
983								$prod_detail_content_pc.toggleClass('hide_seemore', true);
984								checkSeemore(current_content_tab);
985							}
986						safeScheduleProdDetailBodyExpandedHeightUpdate();
987						}, 100);
988					}
989				}
990			});
991		}
992    };
993
994    var setImgZoom = function (margin, max_width, section_code) {
995        if ($('.shop_view .xzoom').length > 0) {
996            // 메인 이미지에 object-fit 크롭이 적용된 경우(원본 경로 서빙) xzoom 전환 페이드 고스트가
997            // 크롭 없이 fill 로 복제되어 왜곡되므로, 이 경우에만 fadeTrans 를 비활성화한다.
998            // 기본값 fill(기존 사이트)에서는 fadeTrans: true 로 기존 크로스페이드 동작 유지.
999            var is_cover_fit_main = ($('.shop_view .xzoom').css('object-fit') || 'fill') !== 'fill';
1000            var shop_view_xzoom = $('.shop_view .xzoom, .shop_view .xzoom-gallery').xzoom({ Xoffset: margin, openOnSmall: false, defaultScale: 0, scroll: false, custom: true, hover: true, fadeTrans: !is_cover_fit_main });
1001            if (typeof shop_view_xzoom != 'undefined') {
1002                // 연관상품 장바구니 확인 후 닫을 시 재실행되면서 오류나는 문제 방지
1003                shop_view_xzoom.eventmove = function (element) {
1004                    var $xzoom_preview = $('.shop_view .xzoom-preview');
1005                    $xzoom_preview.addClass(section_code);
1006                    var $xzoom_preview_img = $xzoom_preview.find('img');
1007                    var $xzoom = $('.shop_view .xzoom');
1008                    var xzoom_width = $(window).width() - ($xzoom.offset().left + $xzoom.outerWidth() + margin * 2);
1009                    if (xzoom_width > max_width) {
1010                        xzoom_width = max_width;
1011                    }
1012                    var xzoom_height = xzoom_width;
1013                    var xzoom_preview_img_width = $xzoom_preview_img.outerWidth();
1014                    var xzoom_preview_img_height = $xzoom_preview_img.outerHeight();
1015                    if (xzoom_preview_img_width < xzoom_width || xzoom_preview_img_height < xzoom_height) {
1016                        shop_view_xzoom.closezoom();
1017                    } else {
1018                        $xzoom_preview.outerWidth(xzoom_width);
1019                        $xzoom_preview.outerHeight(xzoom_height);
1020                        element.bind('mousemove', shop_view_xzoom.movezoom);
1021                    }
1022                    $xzoom_preview.mouseover(shop_view_xzoom.closezoom);
1023                };
1024            }
1025        }
1026    };
1027
1028    var setTimesale = function () {
1029        var $doz_timesale_wrap = $('#prod_goods_form ._doz_timesale_wrap');
1030        if ($doz_timesale_wrap.length > 0) {
1031            $doz_timesale_wrap.each(function () {
1032                var $that = $(this);
1033                var $doz_timesale = $that.find('._doz_timesale');
1034                var start_time = ($that.find('._doz_timesale').attr('data-start-time') * 1000);
1035                var timesale_interval = setInterval(function () {
1036                    var remain_ms = ($doz_timesale.attr('data-end-time') * 1000) - start_time;
1037                    if (remain_ms > 0) {
1038                        var remain_d = Math.floor(remain_ms / 86400000);
1039                        var remain_h = Math.floor((remain_ms % 86400000) / 3600000);
1040                        var remain_m = Math.floor((remain_ms % 3600000) / 60000);
1041                        var remain_s = Math.floor((remain_ms % 60000) / 1000);
1042
1043                        var remain_hh = remain_h < 10 ? '0' + remain_h : '' + remain_h;
1044                        var remain_mm = remain_m < 10 ? '0' + remain_m : '' + remain_m;
1045                        var remain_ss = remain_s < 10 ? '0' + remain_s : '' + remain_s;
1046
1047                        if (remain_d >= 1) {
1048	                        $doz_timesale.html(getLocalizeString('설명_상세페이지타임세일종료까지n1일n2시n3분n4초남음', [remain_d, remain_hh, remain_mm, remain_ss], "<label class='text-bold text-brand'>타임세일</span> 종료까지 <strong>%1일 %1:%2:%3</strong> 남음"));
1049                        } else {
1050                            $doz_timesale.html(getLocalizeString('설명_상세페이지타임세일종료까지n1시n2분n3초남음', [remain_hh, remain_mm, remain_ss], "<label class='text-bold text-brand'>타임세일</span> 종료까지 <strong>%1:%2:%3</strong> 남음"));
1051                        }
1052                        start_time = start_time + 1000;
1053                    } else {
1054                        /* 타임세일 종료 */
1055                        clearInterval(timesale_interval);
1056                        $that.remove();
1057                    }
1058                }, 1000);
1059            });
1060        }
1061    };
1062
1063    /**
1064     * 다국어코드를 백엔드에서 호출하여 자바스크립트에 저장하는 함수
1065     * initDetail 피라미터 개수가 다른 히스토리를 모르겠어서 일단 따로 만들었음
1066     * @param code
1067     */
1068    var initLocalize = function (code) {
1069        total_price_localize_text = code;
1070    };
1071
1072    /**
1073     * 특정상품 위시리스트 추가 처리
1074     * @param prod_code
1075     */
1076    var addProdWish = function (prod_code, back_url) {
1077        var back_url = typeof back_url == 'undefined' ? '' : back_url;
1078        $.ajax({
1079            type: 'POST',
1080            data: { 'prod_code': prod_code },
1081            url: ('/shop/add_prod_wish.cm'),
1082            dataType: 'json',
1083            success: function (res) {
1084                if (res.msg == 'SUCCESS') {
1085                    if (res.res == 'add') {
1086                        if (typeof FB_PIXEL != 'undefined') FB_PIXEL.AddToWishlist();
1087                        if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.AddToWishlist();
1088	                    if (typeof NP_NDA != 'undefined') NP_NDA.AddToWishList(prod_code);
1089                        if (typeof TIKTOK_PIXEL != 'undefined') {
1090                            // TIKTOK_PIXEL.track('AddToWishlist', {
1091                            //     contents: [
1092                            //         {
1093                            //             content_id: options.prod_idx,
1094                            //             content_name: res.prod_name,
1095                            //             quantity: res.wish_cnt,
1096                            //             price: options.prod_price
1097                            //         },
1098                            //     ],
1099                            //     content_type: 'product',
1100                            //     value: options.prod_price,
1101                            //     currency: res.currency,
1102                            // });
1103                        }
1104
1105                        if ($wish_buttons) {
1106                            toggleWishButtons($wish_buttons, res.wish_cnt, options.is_show_wish_count, true);
1107                        }
1108                        if ($(".wish-icon-" + prod_code).length > 0) {
1109                            $(".wish-icon-" + prod_code).removeClass('im-ico-like');
1110                            $(".wish-icon-" + prod_code).addClass('im-ico-liked');
1111                        }
1112                    } else if (res.res == 'delete') {
1113                        if ($wish_buttons) {
1114                            toggleWishButtons($wish_buttons, res.wish_cnt, options.is_show_wish_count, false);
1115                        }
1116                        if ($(".wish-icon-" + prod_code).length > 0) {
1117                            $(".wish-icon-" + prod_code).removeClass('im-ico-liked');
1118                            $(".wish-icon-" + prod_code).addClass('im-ico-like');
1119                        }
1120                        if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.addCountUserProfileAttr('wishCount', -1);
1121                    }
1122                    if ($(".wish-text-" + prod_code).length > 0) {
1123                        $(".wish-text-" + prod_code).text(res.wish_cnt);
1124                    }
1125                } else if (res.msg === 'NON_MEMBERS') {
1126                    SITE_MEMBER.openLogin(back_url);
1127                } else {
1128                    alert(res.msg);
1129                }
1130            }
1131        });
1132    };
1133    /**
1134     * 특정상품 위시정보 가져오기 (내 위시여부,위시갯수)
1135     * @param prod_code
1136     */
1137    var getProdWish = function (prod_code) {
1138        $.ajax({
1139            type: 'POST',
1140            data: { 'prod_code': prod_code },
1141            url: ('/shop/get_prod_wish.cm'),
1142            dataType: 'json',
1143            success: function (res) {
1144                if (res.msg == 'SUCCESS') {
1145                } else
1146                    alert(res.msg);
1147            }
1148        });
1149    };
1150    /**
1151     * 두개의 옵션 데이터가 같은지 확인 (순서 무관)
1152     * @param options1 [ {option_code: option_value} ]
1153     * @param options2 [ {option_code: option_value} ]
1154     */
1155    var isSameOptionList = function (options1, options2) {
1156        if (options1.length == options2.length) {
1157            var isSame = true;
1158            var exist = false;
1159            $.each(options1, function (no, data) {
1160                exist = false;
1161                $.each(options2, function (no2, data2) {
1162                    if (data.option_code == data2.option_code) {
1163                        if (data.value_code != '') {
1164                            if (data.value_code == data2.value_code) {
1165                                exist = true;
1166                                return false;
1167                            }
1168                        } else {
1169                            if (data.value_name == data2.value_name) {
1170                                exist = true;
1171                                return false;
1172                            }
1173                        }
1174                    }
1175                });
1176                if (!exist) {
1177                    isSame = false;
1178                    return false;
1179                }
1180            });
1181            return isSame;
1182        } else {
1183            return false;
1184        }
1185    };
1186
1187    /**
1188     * 상품 이미지 롤링 시작
1189     */
1190    var startProdImageRolling = function (autoWidth) {
1191
1192        var items = $prod_image_list.find('._item');
1193        var is_dots = false;
1194        is_dots = items.length > 1 ? true : false;
1195
1196        if (is_dots) {
1197            switch (cm_data.paging_style_type) {
1198                case 'st00':
1199                    switch (cm_data.paging_default_style_type) {
1200                        case 'st00':
1201                            $prod_image_list_rolling.toggleClass('paging_type_dot paging_type_dot01', true);
1202                            break;
1203                        case 'st01':
1204                            $prod_image_list_rolling.toggleClass('paging_type_dot paging_type_dot02', true);
1205                            break;
1206                    }
1207                    break;
1208                case 'st01':
1209                    $prod_image_list_rolling.toggleClass('paging_type_big_dot', true);
1210                    break;
1211                case 'st02':
1212                    $prod_image_list_rolling.toggleClass('paging_type_line', true);
1213                    break;
1214                case 'st03':
1215                    switch (cm_data.paging_active_style_type) {
1216                        case 'st00':
1217                            $prod_image_list_rolling.toggleClass('paging_type_count paging_type_count01', true);
1218                            break;
1219                        case 'st01':
1220                            $prod_image_list_rolling.toggleClass('paging_type_count paging_type_count02', true);
1221                            break;
1222                        case 'st02':
1223                            $prod_image_list_rolling.toggleClass('paging_type_count paging_type_count03', true);
1224                            break;
1225                    }
1226                    break;
1227            }
1228        }
1229
1230        if (autoWidth) {
1231            $prod_image_list_rolling.owlCarousel({
1232                dots: is_dots,
1233                navigation: true,
1234                slideSpeed: 300,
1235                paginationSpeed: 400,
1236                singleItem: true,
1237                animateOut: 'fadeOut',
1238                items: 1,
1239                autoHeight: true,
1240                autoWidth: true,
1241                onInitialized: function () {
1242                    var owl_item_list = $prod_image_list.find('.owl-item');
1243                    var first_url = $(items[0]).find('img').attr('src');
1244                    var max_width = 750;
1245                    // TODO: is_prod_detail_page가 명확하지 않아 장바구니 임시조치
1246                    // shopping_list에서 들어오면 400으로 잡아야 함
1247                    if ($('.modal_prod_detail_from_shopping_list').length > 0) {
1248                        var max_width = 400;
1249                    } else if ($prod_detail.width() < max_width) {
1250                        max_width = $prod_detail.width();
1251                    }
1252                    $.each(owl_item_list, function (key, value) {
1253                        var $value = $(value);
1254                        var image_url = $value.find('img').attr('src');
1255                        var image = new Image();
1256                        image.onload = function () {
1257                            var that = this;
1258                            // owl initialized 이후 내부적으로 resized를 하면서 크기가 어긋나는 경우가 있어 timeout을 둠
1259                            setTimeout(function () {
1260                                var org_width = that.width;
1261                                var org_height = that.height;
1262                                var width = org_width > max_width ? max_width : org_width;
1263                                var height = width * (org_height / org_width);
1264                                const new_width = getComputedStyle($prod_image_list[0]).margin == '0px -15px' ? width + 30 : width;
1265                                const new_height = getComputedStyle($prod_image_list[0]).margin == '0px -15px' ? height - 30 : height;
1266                                $value.css({ 'width': new_width + 'px', 'height': new_height + 'px' });
1267                                if (that.src === first_url) {
1268                                    // 첫 이미지 크기 세팅
1269                                    $prod_image_list.find('.owl-carousel').css({ 'width': `${new_width}px`, 'height': `${new_height}px` });
1270                                    $prod_image_list.find('.owl-height').css({ 'height': `${new_height}px` });
1271                                }
1272                            }, 5);
1273                        };
1274                        image.src = image_url;
1275                    });
1276                },
1277                onChanged: function () {
1278                    var owl = $prod_image_list_rolling.data('owlCarousel');
1279                    var current = 0;
1280                    if (typeof owl !== "undefined") current = owl._current;
1281                    var li_list = $prod_image_list.find('li.owl-dot');
1282                    li_list.find('a').removeClass('active');
1283                    li_list.eq(current).find('a').addClass('active');
1284                    if (items.length > 1) {
1285                        $prod_goods_form.find('header').css('margin-top', 0);
1286                    }
1287                    // 현재 이미지 너비로 슬라이드 너비 수정
1288                    var current_width = $(items[current]).find('img').css('width');
1289                    $prod_image_list.find('.owl-carousel').css({ 'width': current_width, 'height': '' });
1290                }
1291            });
1292        } else {
1293            $prod_image_list_rolling.owlCarousel({
1294                dots: true,
1295                navigation: true,
1296                slideSpeed: 300,
1297                paginationSpeed: 400,
1298                singleItem: true,
1299                animateOut: 'fadeOut',
1300                items: 1,
1301                autoHeight: false,
1302                onInitialized: function () {
1303                    var owl_item_list = $prod_image_list.find('.owl-item');
1304                    var total = owl_item_list.length;
1305                    var html = '';
1306                    html += '<span class="_numbering_variable">1</span> <span class="_numbering_total">/ ' + total + '</span>';
1307                    if (total <= 1) {
1308                        $("._carousel_count_numbering").hide();
1309                    }
1310                    else {
1311                        $("._carousel_count_numbering").append(html);
1312                    }
1313                },
1314                onChanged: function () {
1315                    var owl = $prod_image_list_rolling.data('owlCarousel');
1316                    var current = 0;
1317                    if (typeof owl !== "undefined") current = owl._current;
1318                    var now = parseInt(current);
1319                    var html = '';
1320                    html += '<span class="_numbering_variable">' + (now + 1) + '</span>';
1321                    $("._numbering_variable").html(html);
1322                }
1323            });
1324        }
1325    };
1326
1327    /**
1328     * 상품 이미지 롤링 특정 위치 이동
1329     * @param no
1330     */
1331    function changeProdImageRolling(no) {
1332        var owl = $prod_image_list_rolling.data('owlCarousel');
1333        if (typeof owl !== "undefined") {
1334            owl.to(no);
1335
1336            var option_img_list = $prod_image_list.find('._color_option_img');
1337            if (option_img_list.length) {
1338                $prod_image_list.attr('data-preview-type', 'prod');
1339                option_img_list.attr('data-visible', 'false');
1340            }
1341        }
1342    }
1343
1344    const loadProductDetail = function (code, idx, menu_code, current_url) {
1345        $.ajax({
1346            type: 'POST',
1347            data: { 'idx': idx, 'menu_code': menu_code, current_url },
1348            url: ('/shop/load_product_detail.cm'),
1349            dataType: 'json',
1350            cache: false,
1351            success: function (result) {
1352                if (result.msg === 'SUCCESS') {
1353                    $('.shop_detail_wrap_csr_' + code).html(result.html);
1354
1355		                function injectHTMLWithScripts(html, target = document.body) {
1356			                const wrapper = document.createElement('div');
1357			                wrapper.innerHTML = html;
1358
1359			                // 먼저 script를 제외한 나머지 노드 삽입
1360			                const scripts = wrapper.querySelectorAll('script');
1361			                scripts.forEach(script => script.remove()); // 미리 제거
1362
1363			                // 나머지 HTML 삽입
1364			                Array.from(wrapper.childNodes).forEach(node => target.appendChild(node));
1365
1366			                // script 수동 실행
1367			                scripts.forEach(oldScript => {
1368				                const newScript = document.createElement('script');
1369
1370				                // 복사: src / type / defer / async 등
1371				                for (const attr of oldScript.attributes) {
1372					                newScript.setAttribute(attr.name, attr.value);
1373				                }
1374
1375				                // inline script일 경우 textContent 복사
1376				                if (!oldScript.src) {
1377					                newScript.textContent = oldScript.textContent;
1378				                }
1379
1380				                // 실행
1381				                document.body.appendChild(newScript);
1382			                });
1383		                }
1384
1385										if (result.script){
1386											injectHTMLWithScripts(result.script);
1387										}
1388
1389
1390	                // 최근 본 상품, refresh
1391										if (window.RECENT_PRODUCT && typeof window.RECENT_PRODUCT.refresh === 'function') {
1392											window.RECENT_PRODUCT.refresh();
1393										}
1394
1395										// 상세페이지 CSR 로드 완료
1396										document.dispatchEvent(new CustomEvent('productDetailLoaded', {detail: {idx}}));
1397								}
1398            },
1399            error: function (req, status, error) {
1400                document.write(req.responseText);
1401            }
1402        });
1403    };
1404
1405    const loadBookingDetail = function (code, idx, day, endDay, current_url) {
1406        $.ajax({
1407            type: 'POST',
1408            data: { 'idx': idx, 'day': day, 'endDay': endDay, current_url, 'widgetCode': code },
1409            url: ('/shop/load_booking_detail.cm'),
1410            dataType: 'json',
1411            cache: false,
1412            success: function (result) {
1413                if (result.msg === 'SUCCESS') {
1414                    $('.book_detail_wrap_csr_' + code).html(result.output);
1415                }
1416            }
1417        });
1418    };
1419
1420    const loadCode = function (code) {
1421        $.ajax({
1422            type: 'POST',
1423            data: { 'code': code },
1424            url: ('/shop/load_code.cm'),
1425            dataType: 'json',
1426            cache: false,
1427            success: function (result) {
1428                if (result.msg === 'SUCCESS') {
1429                    // DOM에 콘텐츠를 추가하는 함수
1430                    const appendContentToDOM = function() {
1431                        $('.code_wrap_csr_' + code).html(result.output);
1432
1433                        // script 태그를 찾아서 실행
1434                        const $container = $('.code_wrap_csr_' + code);
1435                        $container.find('script').each(function() {
1436                            const scriptContent = $(this).html();
1437                            if (scriptContent) {
1438                                try {
1439                                    // script 내용 실행
1440                                    setTimeout(function() {
1441                                        eval(scriptContent);
1442                                    }, 0);
1443                                } catch (e) {
1444                                    console.error('Script execution error:', e);
1445                                }
1446                            }
1447                        });
1448                    };
1449
1450                    // 다른 CSR 위젯이 로드되었는지 확인
1451                    const checkInterval = setInterval(function () {
1452                        const target_now = $('[class^="shop_detail_wrap_csr_"], [class^="book_detail_wrap_csr_"]');
1453                        const target = target_now.length > 0 ? target_now.first() : null;
1454
1455
1456                        if (!target) {
1457                            clearInterval(checkInterval); // 감지 종료
1458                            appendContentToDOM();
1459                            return;
1460                        }
1461
1462
1463                        if (target.is(':empty')) {
1464                            return;
1465                        }
1466
1467                        // prod_detail 요소가 있는지 확인
1468                        if (!target.find('#prod_detail').length) {
1469														return;
1470                        }
1471
1472                        clearInterval(checkInterval); // 감지 종료
1473                        appendContentToDOM();
1474                    }, 100); // 100ms 간격으로 검사
1475                }
1476            }
1477        });
1478    };
1479
1480    /**
1481     * 무료배송 안내 모달의 추가옵션 영역(#prod_free_ship_notify_additional_options)이
1482     * 현재 활성 상태인지 확인.
1483     * - PC cocoaDialog: 모달이 열려있어야(`.modal.in`) 활성으로 인정
1484     * - 모바일 imSheet: 닫힐 때 DOM에서 element 자체가 제거되므로 존재만으로 활성으로 간주
1485     * 모달이 닫혔지만 DOM에 잔존한 경우(PC에서 `in` 클래스만 빠진 상태) 일반 옵션 흐름과
1486     * 섞이지 않도록 분기 조건으로 사용한다.
1487     */
1488    var isFreeShipNotifyAdditionalActive = function () {
1489        var $el = $('#prod_free_ship_notify_additional_options');
1490        if (!$el.length) return false;
1491        var $modal = $el.closest('.modal');
1492        return !$modal.length || $modal.hasClass('in');
1493    };
1494
1495    /**
1496     * 장바구니 무료배송 안내 모달 전용 옵션 로더.
1497     * 기본 #prod_options 컨테이너가 없는 환경(cart 페이지 모달)에서 load_prod_additional_option.cm을 호출해
1498     * 모달 전용 컨테이너(#prod_free_ship_notify_additional_options)의 HTML을 갱신한다.
1499     * 최초 렌더와 동일한 템플릿을 재사용하여 스타일 일관성 유지 (loadProdAdditionalOptions 패턴 참조).
1500     */
1501    var loadOptionForFreeShipNotify = function (prod_idx) {
1502        if (_isFreeShipNotifyTemplatePreview) return;
1503        var $freeShipContainer = $('#prod_free_ship_notify_additional_options');
1504        if (!$freeShipContainer.length) return;
1505
1506        $.ajax({
1507            type: 'POST',
1508            data: {
1509                'prod_idx': prod_idx,
1510                'selected_require_options': selected_require_options,
1511                '__': prod_edit_time,
1512                'is_additional': true,
1513                'context': 'cart'
1514            },
1515            url: '/shop/load_prod_additional_option.cm',
1516            dataType: 'json',
1517            cache: false,
1518            success: function (result) {
1519                if (result.msg === 'SUCCESS') {
1520                    $freeShipContainer.html(result.option_html);
1521                } else {
1522                    alert(result.msg);
1523                }
1524            }
1525        });
1526    };
1527
1528    var loadOption = function (type, prod_idx) {
1529        if (_isFreeShipNotifyTemplatePreview) return;
1530        // 장바구니 무료배송 안내 모달이 열려있는 cart 컨텍스트에서는 모달 전용 로더로 위임
1531        if (type === 'cart' && isFreeShipNotifyAdditionalActive()) {
1532            return loadOptionForFreeShipNotify(prod_idx);
1533        }
1534
1535        $.ajax({
1536            type: 'POST',
1537            data: { 'type': type, 'prod_idx': prod_idx, 'selected_require_options': selected_require_options, '__': prod_edit_time },
1538            url: ('/shop/load_option.cm'),
1539            dataType: 'json',
1540            cache: false,
1541            success: function (result) {
1542                if (result.msg == 'SUCCESS') {
1543                    max_prod_quantity = result.max_prod_quantity;
1544                    max_member_quantity = result.max_member_quantity;
1545                    maximum_purchase_quantity_type = result.maximum_purchase_quantity_type;
1546                    optional_limit = result.optional_limit;
1547                    optional_limit_type = result.optional_limit_type;
1548                    if (result.prod_name) prod_name = result.prod_name;
1549                    $options.html(result.option_html);
1550
1551                    // 색상 옵션이 있고 + 색상 옵션의 이미지까지 있다면
1552                    if (options.exist_color_option && options.is_exist_color_option_images) {
1553                        if ($prod_image_list.find('.xzoom').length) {
1554                            // xzoom 사용
1555                            $options.find('label[data-opttype]').hover(function () {
1556                                var $_that = $(this);
1557                                var $_target_wrap = $("#btn_owl_" + $_that.attr('data-optcode'));
1558                                if (!$_target_wrap.length) {
1559                                    $_target_wrap = $("#btn_owl_0");
1560                                }
1561                                $_target_wrap.find('a').trigger('click');
1562                            });
1563                        } else {
1564                            // xzoom 미사용2
1565                            var $_color_option_img = $prod_image_list.find('._color_option_img');
1566                            if ($_color_option_img.length) {
1567                                (function () {
1568                                    var hover_evnets = (IS_MOBILE ? 'touchstart touchend' : 'mouseover');
1569                                    $options.find('label[data-opttype] ._bg').off(hover_evnets).on(hover_evnets, function (e) {
1570                                        // mouse over
1571                                        var $_that = $(this);
1572                                        var opt_code = $_that.attr('data-optcode');
1573                                        var $_target = $_color_option_img.filter('[data-optcode="' + opt_code + '"]');
1574
1575                                        $_color_option_img.attr('data-visible', 'false');
1576
1577                                        if ($_target.length) {
1578                                            if ($_target.attr('src').length == 0) {
1579                                                $_target.attr('src', $_target.attr('data-src'));
1580                                                // 이미지 load 이벤트
1581                                                $_target.on('load', function () {
1582                                                    var _width = this.naturalWi
1582dth;
1583                                                    var _height = this.naturalHeight;
1584
1585                                                    if (_width == _height) {
1586                                                        var _wrap_width = $prod_image_list.width();
1587                                                        var _wrap_height = $prod_image_list.height();
1588
1589                                                        if (_wrap_width == _wrap_height) {
1590                                                            $(this).attr('data-size-type', 'A');
1591                                                        } else if (_wrap_width > _wrap_height) {
1592                                                            $(this).attr('data-size-type', 'B');
1593                                                        } else {
1594                                                            $(this).attr('data-size-type', 'C');
1595                                                        }
1596                                                    } else if (_width > _height) {
1597                                                        $(this).attr('data-size-type', 'B');
1598                                                    } else {
1599                                                        $(this).attr('data-size-type', 'C');
1600                                                    }
1601                                                });
1602                                            }
1603                                            $prod_image_list.attr('data-preview-type', 'option');
1604                                            $_target.attr('data-visible', 'true');
1605                                        }
1606
1607                                        switch (e.type) {
1608                                            case 'touchstart':
1609                                                prod_option_touching = true;
1610                                                break;
1611                                            case 'touchend':
1612                                                prod_option_touching = false;
1613                                                break;
1614                                        }
1615                                    });
1616
1617                                    $options.find('label[data-opttype] ._bg').off('mouseout').on('mouseout', function (e) {
1618                                        // mouse out
1619                                        var $_that = $(this);
1620                                        var opt_code = $_that.attr('data-optcode');
1621                                        var $_target = $_color_option_img.filter('[data-optcode="' + opt_code + '"]');
1622
1623                                        $prod_image_list.attr('data-preview-type', 'prod');
1624
1625                                        if ($_target.length) {
1626                                            $_target.attr('data-visible', 'false');
1627                                        }
1628                                    });
1629
1630                                    if (IS_MOBILE) {
1631                                        // 모바일일 경우 터치이벤트 추가
1632                                        var $_body = $("#doz_body");
1633                                        if (!$_body.hasClass('_bind_touchstart_evt')) {
1634                                            $_body.on('touchstart', function (e) {
1635                                                if (!prod_option_touching) {
1636                                                    $prod_image_list.attr('data-preview-type', 'prod');
1637                                                    if ($_color_option_img.length) {
1638                                                        $_color_option_img.attr('data-visible', 'false');
1639                                                    }
1640                                                }
1641                                            }).addClass('_bind_touchstart_evt');
1642                                        }
1643                                    }
1644                                })();
1645                            }
1646                        }
1647                    }
1648
1649                    if (IS_MOBILE) {
1650                        addEventMobileOptionInput();
1651                    }
1652
1653                } else {
1654                    alert(result.msg);
1655                }
1656            }
1657        });
1658
1659				if($prod_additional.length > 0){
1660					loadProdAdditional(prod_idx);
1661				}
1662    };
1663
1664		var loadProdAdditional = function (prod_idx) {
1665			if (_isFreeShipNotifyTemplatePreview) return;
1666			if(is_mobile_width){
1667				$.ajax({
1668					type : 'POST',
1669					data : {'prod_idx' : prod_idx, 'selected_additional_prod_idx' : current_select_additional_prod_idx, '__' : prod_edit_time},
1670					url : (
1671							'/shop/load_prod_additional_mobile.cm'
1672					),
1673					dataType : 'json',
1674					cache : false,
1675					success : function(result){
1676						if(result.msg == 'SUCCESS'){
1677							// 추가 상품
1678							if(!result.prod_additional.prod_list.length) return;
1679							$prod_additional.html(result.prod_additional_sheet_contents);
1680							// $prod_additional.html(result.prod_additional.html);
1681							// $prod_additional.find('#btnProdAdditionalSelect').off('click').on('click', function(e){
1682							// 	e.stopPropagation();
1683							// 	imSheet.open({
1684							// 		id: 'prod_additional_sheet',
1685							// 		html: result.prod_additional_sheet_contents,
1686							// 		backdrop: 'rgba(0, 0, 0, 0.15)',
1687							// 		zIndex: 17001
1688							// 	});
1689							// });
1690						}else{
1691							alert(result.msg);
1692						}
1693					}
1694				});
1695			}else{
1696				$.ajax({
1697					type: 'POST',
1698					data: {'prod_idx': prod_idx, 'selected_additional_prod_idx': current_select_additional_prod_idx, '__': prod_edit_time },
1699					url: ('/shop/load_prod_additional.cm'),
1700					dataType: 'json',
1701					cache: false,
1702					success: function (result) {
1703						if (result.msg == 'SUCCESS') {
1704							// 추가 상품
1705							if(!result.prod_additional.prod_list.length) return;
1706							$prod_additional.html(result.prod_additional.html);
1707						} else {
1708							alert(result.msg);
1709						}
1710					}
1711				});
1712			}
1713
1714    };
1715
1716		var loadProdAdditionalOptions = function (product_idx, cb) {
1717			if (_isFreeShipNotifyTemplatePreview) return;
1718			if(is_mobile_width){
1719				$.ajax({
1720					type: 'POST',
1721					data: {'prod_idx': product_idx, 'selected_require_options': selected_prod_additional_require_options, '__': prod_edit_time, 'is_additional': true },
1722					url: ('/shop/load_prod_additional_option_mobile.cm'),
1723					dataType: 'json',
1724					cache: false,
1725					success: function (result) {
1726						if (result.msg == 'SUCCESS') {
1727							prod_additional_require_option_count = {
1728								...prod_additional_require_option_count,
1729								...result.require_option_count};
1730							prod_additional_require_input_option_count = {
1731								...prod_additional_require_input_option_count,
1732								...result.require_input_option_count
1733							};
1734							if(isFreeShipNotifyAdditionalActive()){
1735								$('#prod_free_ship_notify_additional_options').html(result.option_html);
1736							}else{
1737								// 모바일에서는 드롭다운이 동작하지 않도록 클릭 이벤트 대치
1738								$prod_additional_options.html(result.option_html).find('._form_parent:not(.disabled) ._form_select_wrap > a, ._requireInputOption').each(function(e){
1739									let $that = $(this);
1740									$that.on('click', function(e){
1741										e.stopPropagation();
1742										loadProdAdditionalOptionRequire(product_idx, $that.attr('data-option-code'));
1743									});
1744								});
1745
1746								var full_input = false;
1747								var $form = $prod_additional_options.find('._form_parent');
1748								// 첫번째 선택 후 창을 닫았을때
1749								$form.each(function (index) {
1750
1751									var is_input = $(this).children().hasClass('option_box_wrap');
1752
1753									if (is_input) {
1754										var input_items = $(this).find('._requireInputOption');
1755										input_items.each(function (index) {
1756											if ($(this).val() != '') {
1757												if ((index + 1) == input_items.length) {
1758													full_input = true;
1759												}
1760											} else {
1761												return false;
1762											}
1763										});
1764
1765									} else {
1766										var prev_is_selected = $(this).prev().find('.dropdown-item').hasClass('selected');
1767										var is_selected = $(this).find('.dropdown-item').hasClass('selected');
1768										// 이전 필수 드롭다운이 선택 되어 있을 경위
1769										if (prev_is_selected || full_input) {
1770											$(this).toggleClass('disabled', false);
1771
1772											full_input = false;
1773
1774											/* 드롭다운 활성화는 loadProdAdditionalOptionRequire의 시트 호출로 대체 */
1775										} else if (index === 0) {
1776											// 첫 드롭다운은 일단 true로 표시
1777											$(this).toggleClass('disabled', false);
1778										} else {
1779											$(this).toggleClass('disabled', true);
1780										}
1781									}
1782								});
1783
1784								if (typeof prod_additional_require_option_count[product_idx] == 'undefined' || selected_prod_additional_require_options.length == prod_additional_require_option_count[product_idx]){
1785									/* 필수 옵션이 모두 선택되었을 경우 시트를 닫음 */
1786									imSheet.close('prod_additional_sheet');
1787								}else{
1788									loadProdAdditionalOptionRequire(product_idx);
1789								}
1790							}
1791
1792							updateSelectedOptions('prod');
1793						} else {
1794							alert(result.msg);
1795						}
1796					}
1797				});
1798			}else{
1799				$.ajax({
1800					type: 'POST',
1801					data: {'prod_idx': product_idx, 'selected_require_options': selected_prod_additional_require_options, '__': prod_edit_time},
1802					url: ('/shop/load_prod_additional_option.cm'),
1803					dataType: 'json',
1804					cache: false,
1805					success: function (result) {
1806						if (result.msg == 'SUCCESS') {
1807							prod_additional_require_option_count = {
1808								...prod_additional_require_option_count,
1809								...result.require_option_count};
1810							prod_additional_require_input_option_count = {
1811								...prod_additional_require_input_option_count,
1812								...result.require_input_option_count
1813							};
1814							if(isFreeShipNotifyAdditionalActive()){
1815								$('#prod_free_ship_notify_additional_options').html(result.option_html);
1816							}else{
1817								$prod_additional_options.html(result.option_html);
1818							}
1819							// PC 인라인 컨테이너 사용 후, 다른 상품 선택 시 기존 인라인 영역 정리
1820							try{
1821								if($prod_additional_options.hasClass('_prod_additional_inline_options')){
1822									$('._prod_additional_inline_options').not($prod_additional_options).remove();
1823								}
1824							}catch(e){}
1825
1826							updateSelectedOptions('prod');
1827						} else {
1828							alert(result.msg);
1829						}
1830					}
1831				});
1832			}
1833		}
1834
1835	/**
1836	 * 모바일 추가 상품 선택 시 다음 필수 옵션에 대한 html을 시트에 출력
1837	 * @param product_idx
1838	 * @param option_code
1839	 */
1840		var loadProdAdditionalOptionRequire = function(product_idx, option_code = null) {
1841			if (_isFreeShipNotifyTemplatePreview) return;
1842			$.ajax({
1843				type: 'POST',
1844				data: {'prod_idx': product_idx, 'option_code': option_code, 'selected_require_options': selected_prod_additional_require_options, '__': prod_edit_time, 'is_additional': true },
1845				url: ('/shop/load_prod_additional_option_require.cm'),
1846				dataType: 'json',
1847				cache: false,
1848				success: function (result) {
1849					if (result.msg == 'SUCCESS') {
1850						prod_additional_require_option_count = {
1851							...prod_additional_require_option_count,
1852							...result.require_option_count};
1853						prod_additional_require_input_option_count = {
1854							...prod_additional_require_input_option_count,
1855							...result.require_input_option_count
1856						};
1857
1858						// 현재 시트의 내용을 받아온 옵션(html)으로 변경(시트를 닫고 새로 열어줌)
1859						imSheet.close('prod_additional_sheet', () => {
1860							imSheet.open({
1861								id: 'prod_additional_sheet',
1862								html: result.prod_additional_sheet_contents,
1863								backdrop: 'rgba(0, 0, 0, 0.15)',
1864								zIndex: 17001
1865							});
1866						});
1867					} else {
1868						alert(result.msg);
1869					}
1870				}
1871			});
1872		}
1873
1874		/**
1875		 * 모바일 추가 상품 선택 옵션에 대한 html을 시트에 출력
1876		 * @param product_idx
1877		 * @param option_code
1878		 */
1879		var loadProdAdditionalOptionOptional = function(product_idx, option_code) {
1880			if (_isFreeShipNotifyTemplatePreview) return;
1881			$.ajax({
1882				type: 'POST',
1883				data: {'prod_idx': product_idx, 'option_code': option_code, '__': prod_edit_time, 'is_additional': true },
1884				url: ('/shop/load_prod_additional_option_optional.cm'),
1885				dataType: 'json',
1886				cache: false,
1887				success: function (result) {
1888					if (result.msg == 'SUCCESS') {
1889						// 현재 시트의 내용을 받아온 옵션(html)으로 변경(시트를 닫고 새로 열어줌)
1890						imSheet.close('prod_additional_sheet', () => {
1891							imSheet.open({
1892								id: 'prod_additional_sheet',
1893								html: result.prod_additional_sheet_contents,
1894								backdrop: 'rgba(0, 0, 0, 0.15)',
1895								zIndex: 17001
1896							});
1897						});
1898					} else {
1899						alert(result.msg);
1900					}
1901				}
1902			});
1903		}
1904
1905		/**
1906		 * 추가 상품 선택 시 선택 옵션에 대한 html을 반환
1907		 * @param product_idx
1908		 * @param option_code
1909		 */
1910		var loadProdAdditionalOptionalList = function() {
1911			if (_isFreeShipNotifyTemplatePreview) return;
1912			$('._prod_additional_option_optional').each((k, v) => {
1913				let $that = $(v);
1914				let product_idx = $that.attr('data-prod-idx');
1915				if($that.attr('data-prod-idx') > 0){
1916					$.ajax({
1917						type: 'POST',
1918						data: {'prod_idx': $that.attr('data-prod-idx'), '__': prod_edit_time, 'is_additional': true },
1919						url: ('/shop/load_prod_additional_optional_list.cm'),
1920						dataType: 'json',
1921						cache: false,
1922						success: function (result) {
1923							if (result.msg == 'SUCCESS') {
1924								if(is_mobile_width){
1925									$that.html(result.option_html).find('._form_select_wrap > a').each(function(e){
1926										let $form = $(this);
1927										$form.on('click', function(e){
1928											e.stopPropagation();
1929											loadProdAdditionalOptionOptional(product_idx, $form.attr('data-option-code'));
1930										});
1931									});
1932								}else{
1933									$that.html(result.option_html);
1934								}
1935							}
1936						}
1937					});
1938				}
1939			});
1940		}
1941
1942    var loadDelivSetting = _.debounce(function (prod_idx, change_country, deliv_type, deliv_pay_type) {
1943        $deliv_country = change_country;
1944        $deliv_pay_type = deliv_pay_type;
1945            var is_design_mode = (location.pathname.indexOf('/admin/design') != -1);
1946            $.ajax({
1947                type: 'GET',
1948                data: {
1949                    'prod_idx': prod_idx,
1950                    'change_country': change_country,
1951                    'deliv_type': deliv_type,
1952                    'deliv_pay_type': deliv_pay_type,
1953                    '__': MEMBER_HASH
1954                },
1955                url: (is_mobile_width ? '/shop/load_deliv_setting.cm' : '/shop/load_deliv_setting_pc.cm'),
1956                dataType: 'json',
1957                cache: !is_design_mode,
1958                async: false,
1959                success: function (result) {
1960                    if (result.msg == 'SUCCESS') {
1961                        $prod_deliv_setting.html(result.deliv_html);
1962                        if (window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK && typeof window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate === 'function') {
1963                            window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($prod_deliv_setting.get(0));
1964                        }
1965                        if (is_mobile_width) {
1966                            $prod_detail.find('._today_arrival_wrap').html(result.today_arrival_html);
1967                        }
1968
1969                        /* 환불 정보 입력 */
1970                        last_refund_data = result.refund;
1971                        setDetailReturnHtml();
1972
1973                        // 표시항목 주입: sanitize 후 주입 (IMOPS-13023)
1974                        if ($prod_detail && $prod_detail.find('._item_detail_wrap').length > 0) {
1975                            $prod_detail.find('._item_detail_wrap').html(_sanitizeHtml(result.item_detail_html));
1976                            // sanitize로 script가 제거되어 popover 초기화가 누락되므로 재초기화 (IMOPS-13118)
1977                            $prod_detail.find('._item_detail_wrap .btn-popover').popover();
1978                            $prod_detail.find('._item_detail_wrap .html-popover').popover({html: true});
1979                        }
1980
1981                        if (IS_MOBILE) {
1982                            addEventMobileOptionInput();
1983                        }
1984
1985                        if (typeof makeNaverPayBtn !== 'undefined') {
1986														try{
1987															// v2 는 show/hide idempotent 호출이라 항상 재호출(국가 노출 갱신). v1 은 기존대로 최초 1회만.
1988															if(window.__npayButtonV2 || !document.getElementById('naverPayWrap').innerHTML){
1989																makeNaverPayBtn('naverPayWrap', (
1990																		change_country == 'KR' || change_country == 'none' || change_country == ''
1991																));
1992															}
1993														} catch(e) {
1994
1995														}
1996                        }
1997
1998                        if (typeof kakaoCheckout != 'undefined' && typeof makeTalkPayButton != 'undefined') {
1999                            var _target_container_id = 'create-kakao-checkout-button';
2000                            _target_container_id = document.getElementById(_target_container_id) ? _target_container_id : 'create-kakao-checkout-button-mobile';
2001                            if (!document.getElementById(_target_container_id).innerHTML) {
2002                                makeTalkPayButton(_target_container_id, (change_country == 'KR' || change_country == 'none' || change_country == ''));
2003                            }
2004                        }
2005
2006                    } else {
2007                        alert(result.msg);
2008                    }
2009                }
2010            });
2011    }, 100, { leading: true, trailing: false});
2012
2013    /**
2014     * 통합 상품정보 AJAX 호출 (item_detail_html + deliv_html 분리 응답)
2015     * Feature flag ON 시 DetailItemMake/loadDelivSetting 대신 사용
2016     */
2017    const loadItemInfo = ({ prod_idx, change_country, deliv_type, deliv_pay_type } = {}) => {
2018        const is_design_mode = location.pathname.includes('/admin/design');
2019        $deliv_country = change_country;
2020        $deliv_pay_type = deliv_pay_type;
2021        const isKrOrEmpty = change_country === 'KR' || change_country === 'none' || change_country === '';
2022        $.ajax({
2023            type: 'GET',
2024            data: {
2025                prod_idx,
2026                change_country,
2027                deliv_type,
2028                deliv_pay_type,
2029                '__': MEMBER_HASH
2030            },
2031            url: '/shop/prod_detail_item_info.cm',
2032            dataType: 'json',
2033            cache: !is_design_mode,
2034            success(result) {
2035                if (result.msg !== 'SUCCESS') return;
2036
2037                if (shop_view_style === 'b' && !IS_MOBILE) {
2038                    // style_b PC: 상품정보 → _item_detail_wrap, 배송정보 → 바텀시트(#prod_deliv_setting)
2039                    var $item_detail_wrap = $prod_detail && $prod_detail.find('._item_detail_wrap');
2040                    if ($item_detail_wrap && $item_detail_wrap.length > 0) {
2041                        $item_detail_wrap.html(result.item_detail_html);
2042                    }
2043                    $prod_deliv_setting.html(result.deliv_html);
2044                    if (window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK && typeof window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate === 'function') {
2045                        if ($item_detail_wrap && $item_detail_wrap.length > 0) {
2046                            window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($item_detail_wrap.get(0));
2047                        }
2048                        window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($prod_deliv_setting.get(0));
2049                    }
2050                } else {
2051                    // style_a + style_b 모바일: 상품정보 + 배송정보 합쳐서 _item_detail_wrap에 주입
2052                    if ($prod_detail && $prod_detail.find('._item_detail_wrap').length > 0) {
2053                        var $item_detail_wrap = $prod_detail.find('._item_detail_wrap');
2054                        $item_detail_wrap.html(result.item_detail_html + result.deliv_html);
2055                        if (window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK && typeof window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate === 'function') {
2056                            window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($item_detail_wrap.get(0));
2057                        }
2058                    }
2059                }
2060
2061                // 배송 타입 업데이트
2062                if (result.deliv_type) SITE_SHOP_DETAIL.addDelivType(result.deliv_type);
2063                if (result.deliv_pay_type) SITE_SHOP_DETAIL.addDelivPayType(result.deliv_pay_type);
2064
2065                var hasFreeShipNotifyTargetResult = typeof result.free_ship_notify_delivery_target_product === 'boolean';
2066                var isFreeShipNotifyDeliveryTarget = hasFreeShipNotifyTargetResult
2067                    ? result.free_ship_notify_delivery_target_product
2068                    : change_country === 'KR';
2069                $('._free_ship_notify_detail_magnet_wrap').toggleClass(
2070                    'tw-hidden',
2071                    hasFreeShipNotifyTargetResult ? !isFreeShipNotifyDeliveryTarget : ($('._deliv_country_selector').length > 0 && change_country !== 'KR')
2072                );
2073
2074                if (typeof result.deliv_price_flexable_key === 'number') {
2075                    var newThreshold = isFreeShipNotifyDeliveryTarget ? result.deliv_price_flexable_key : 0;
2076                    var newThresholdStr = String(newThreshold);
2077                    $('#foFreeShipNotifyProdDetailMagnet').attr('data-init-deliv-price-flexable-key', newThresholdStr);
2078                    $('#shop_detail_add_cart_alarm_with_free_ship_notify').attr('data-init-deliv-price-flexable-key', newThresholdStr);
2079                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:freeShippingThreshold:update', {
2080                        detail: { threshold: newThreshold }
2081                    }));
2082                }
2083
2084                // 환불 정보
2085                last_refund_data = result.refund;
2086                setDetailReturnHtml();
2087
2088                // popover 초기화
2089                $('.btn-popover').popover();
2090                $('.html-popover').popover({html: true});
2091
2092                // 네이버페이 버튼 — v2 에서만 재렌더(show/hide). v1 은 기존 no-op 유지(플래그 미설정).
2093                if (window.__npayButtonV2 && typeof makeNaverPayBtn !== 'undefined') {
2094                    try { makeNaverPayBtn('naverPayWrap', isKrOrEmpty); } catch(e) {}
2095                }
2096
2097                // 카카오페이 버튼
2098                if (typeof kakaoCheckout !== 'undefined' && typeof makeTalkPayButton !== 'undefined') {
2099                    try { makeTalkPayButton('create-kakao-checkout-button', isKrOrEmpty); } catch(e) {}
2100                }
2101            }
2102        });
2103    };
2104
2105    /**
2106     * 선택형 필수 옵션 선택
2107     * @param type cart (장바구니변경), prod(상품상세)
2108     * @param option_code
2109     * @param value_code
2110     */
2111    var selectRequireOption = function (type, prod_idx, option_code, value_code, value_name, success) {
2112        var data = { 'value_type': 'SELECT', 'option_code': option_code, 'value_code': value_code, 'value_name': value_name };
2113        var no = findSelectedRequireOption(option_code);
2114        if (no == -1) {
2115            if (value_code == '') return;
2116            selected_require_options.push(data);
2117            /** 처음 선택된 옵션인 경우 새로 추가*/
2118        } else {
2119            if (value_code == '') {
2120                /** 옵션 삭제 */
2121                selected_require_options.splice(no, (s
2121elected_require_options.length - no));
2122            } else {
2123                selected_require_options[no] = data;
2124                /** 이미 선택된 옵션인 경우 기존 값 교체 */
2125                if (no < selected_require_options.length - 1) {
2126                    selected_require_options.splice(no + 1, (selected_require_options.length - (no + 1)));
2127                }
2128            }
2129        }
2130        if (selected_require_options.length == require_option_count) {
2131            if (isFreeShipNotifyAdditionalActive()) {
2132                // 무료 배송 안내 모달에서는 추가하기 버튼을 눌러야 반영되므로
2133                // selectOption/selected_require_options reset을 생략하고 현재 선택 상태를 유지한 채 화면만 갱신한다.
2134                // (selectProdAdditionalRequireOption의 무료배송 모달 분기와 동일 패턴)
2135                loadOption(type, prod_idx);
2136                if (typeof success === 'function') success();
2137            } else {
2138                /** 필수 옵션이 모두 선택되었을 경우*/
2139                selectOption(
2140                    prod_idx,
2141                    {
2142                        options: selected_require_options,
2143                        require: true,
2144                        count: 1
2145                    },
2146                    function () {
2147                        selected_require_options = [];
2148                        loadOption(type, prod_idx);
2149                        success();
2150                    },
2151                    function (msg) {
2152                        alert(msg);
2153                    }
2154                );
2155            }
2156        } else {
2157            /** 필수옵션 선택이 아직 끝나지 않았을 경우 옵션 재로드 */
2158            loadOption(type, prod_idx);
2159        }
2160    };
2161
2162    /**
2163     * 입력형 필수 옵션 변경시 처리
2164     * @param type cart (장바구니변경), prod(상품상세)
2165     */
2166    var changeRequireInputOption = function (type, prod_idx, option_code, msg, success) {
2167        var data = { 'value_type': 'INPUT', 'option_code': option_code, 'value_code': '', 'value_name': msg };
2168        var no = findSelectedRequireOption(option_code);
2169        if (no == -1) {
2170            if (msg == '') return;
2171            selected_require_options.push(data);
2172            /** 처음 입력한 옵션인 경우 새로 추가*/
2173        } else {
2174            if (msg == '') {
2175                /** 입력형 옵션 삭제 */
2176                selected_require_options.splice(no, (selected_require_options.length - no));
2177            } else {
2178                /** 기존값 교체 */
2179                selected_require_options[no] = data;
2180            }
2181        }
2182        if (selected_require_options.length == require_option_count) {
2183            if (isFreeShipNotifyAdditionalActive()) {
2184                // 무료 배송 안내 모달에서는 추가하기 버튼을 눌러야 반영되므로
2185                // selectOption/selected_require_options reset을 생략하고 현재 선택 상태를 유지한 채 화면만 갱신한다.
2186                loadOption(type, prod_idx);
2187                if (typeof success === 'function') success();
2188            } else {
2189                /** 필수 옵션이 모두 선택되었을 경우*/
2190                selectOption(
2191                    prod_idx,
2192                    {
2193                        options: selected_require_options,
2194                        require: true,
2195                        count: 1
2196                    },
2197                    function () {
2198                        selected_require_options = [];
2199                        loadOption(type, prod_idx);
2200                        success();
2201                    },
2202                    function (msg) {
2203                        alert(msg);
2204                    }
2205                );
2206            }
2207        } else {
2208            // 웹가속기A(edge HTML cache) 사이트는 서버 IS_MOBILE이 항상 false라(UA가 CloudFront로 대체됨)
2209            // 모바일 바텀시트의 순차 필수옵션 unlock 재렌더가 죽는다. 뷰포트 기준 판정을 함께 사용한다.
2210            if ((IS_MOBILE || is_mobile_width) && selected_require_options.length == require_input_option_count) loadOption(type, prod_idx);
2211        }
2212    };
2213
2214		/**
2215     * 입력형 필수 옵션 변경시 처리
2216     * @param type cart (장바구니변경), prod(상품상세)
2217     */
2218    var changeProdAdditionalRequireInputOption = function (type, prod_idx, option_code, msg, success) {
2219        var data = { 'value_type': 'INPUT', 'option_code': option_code, 'value_code': '', 'value_name': msg };
2220        var no = findSelectedRequireOption(option_code);
2221        if (no == -1) {
2222            if (msg == '') return;
2223            selected_prod_additional_require_options.push(data);
2224            /** 처음 입력한 옵션인 경우 새로 추가*/
2225        } else {
2226            if (msg == '') {
2227                /** 입력형 옵션 삭제 */
2228                selected_prod_additional_require_options.splice(no, (selected_prod_additional_require_options.length - no));
2229            } else {
2230                /** 기존값 교체 */
2231                selected_prod_additional_require_options[no] = data;
2232            }
2233        }
2234        if (selected_prod_additional_require_options.length == prod_additional_require_option_count[prod_idx]) {
2235            if (isFreeShipNotifyAdditionalActive()) {
2236                loadProdAdditionalOptions(prod_idx);
2237                if (typeof success === 'function') success();
2238            } else {
2239                /** 필수 옵션이 없거나 모두 선택되었을 경우*/
2240                selectProdAdditionalOption(prod_idx, selected_prod_additional_require_options, true, 1, function () {
2241		                if(is_mobile_width){
2242			                imSheet.close('prod_additional_sheet');
2243		                }
2244	                  current_select_additional_prod_idx = 0;
2245                    selected_prod_additional_require_options = [];
2246	                  $prod_additional_options.html('');
2247                        $('#prod_additional_drop_menu_content').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // PC
2248                        // 인라인 컨테이너 초기화 및 원래 위치로 복구
2249                        try{ $('._prod_additional_inline_options').remove(); $prod_additional_options = $prod_additional_options_default; }catch(e){}
2250	                  $('#btnProdAdditionalSelect').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // 모바일
2251                    success();
2252                }, function (msg) {
2253                    alert(msg);
2254                });
2255            }
2256        } else {
2257	        loadProdAdditionalOptions(prod_idx);
2258        }
2259    };
2260
2261    /**
2262     * 옵션 선택
2263     * @param options [ {value_type: SELECT/INPUT, option_code:, value_code:, value_name:} ]
2264     */
2265    var selectOption = function (prod_idx, {options, require, count, idx = 0, skip_quantity_validation = false}, success, failed) {
2266        var btn_buy_onclick_temp = $('._btn_buy').attr('onclick');
2267        var btn_regularly_onclick_temp = $('._btn_regularly').attr('onclick');
2268        var btn_cart_onclick_temp = $('._btn_cart').attr('onclick');
2269        var btn_regularly_cart_onclick_temp = $('._btn_cart.im-regularly').attr('onclick');
2270        $('._btn_buy').attr('onclick', 'event.cancelBubble=true');
2271        $('._btn_cart').attr('onclick', 'event.cancelBubble=true');
2272        $.ajax({
2273            type: 'POST',
2274            data: {
2275                'prod_idx': prod_idx,
2276                'options': options,
2277                'require': (require ? 'Y' : 'N'),
2278                'count': count,
2279                'idx' : idx,
2280                'skip_quantity_validation': skip_quantity_validation ? true : false
2281            },
2282            url: ('/shop/select_option.cm'),
2283            dataType: 'json',
2284            cache: false,
2285            async: false,
2286            success: function (result) {
2287                if (result.msg == 'SUCCESS') {
2288                    prod_price = result.prod_price;
2289
2290                    var no = findSelectedOption(options);
2291                    if (no == -1) {
2292                        /* 상품 구매수량 체크 (장바구니 로드 시 검증 건너뛰기) */
2293                        if (skip_quantity_validation) {
2294                            selected_options.push(result.selected_option);
2295                            success();
2296                        } else {
2297                            var total_selected_count = getProdTotalQuantity(maximum_purchase_quantity_type);
2298
2299                            var new_total = total_selected_count + count;
2300                            if (max_prod_quantity == 0 || new_total <= max_prod_quantity) {
2301                                selected_options.push(result.selected_option);
2302                                /** 처음 선택된 옵션인 경우 새로 추가*/
2303                                success();
2304                            } else {
2305                                failed(LOCALIZE.설명_최대N개만구매가능한상품입니다(max_prod_quantity));
2306                            }
2307                        }
2308                    } else {
2309                        failed(LOCALIZE.설명_이미선택된옵션입니다());
2310                    }
2311                } else {
2312                    failed(result.msg);
2313                }
2314                $('._btn_buy').attr('onclick', btn_buy_onclick_temp);
2315                $('._btn_regularly').attr('onclick', btn_regularly_onclick_temp);
2316                $('._btn_cart').attr('onclick', btn_cart_onclick_temp);
2317                $('._btn_cart.im-regularly').attr('onclick', btn_regularly_cart_onclick_temp);
2318            }
2319        });
2320    };
2321
2322		/**
2323     * 옵션 선택
2324     * @param options [ {value_type: SELECT/INPUT, option_code:, value_code:, value_name:} ]
2325     */
2326    var selectProdAdditionalOption = function (prod_idx, options, require, count, success, failed) {
2327        //$('.npay_btn_pay').attr('onclick', 'event.cancelBubble=true');
2328        var btn_buy_onclick_temp = $('._btn_buy').attr('onclick');
2329        var btn_regularly_onclick_temp = $('._btn_regularly').attr('onclick');
2330        var btn_cart_onclick_temp = $('._btn_cart').attr('onclick');
2331        var btn_regularly_cart_onclick_temp = $('._btn_cart.im-regularly').attr('onclick');
2332        $('._btn_buy').attr('onclick', 'event.cancelBubble=true');
2333        $('._btn_cart').attr('onclick', 'event.cancelBubble=true');
2334        $.ajax({
2335            type: 'POST',
2336            data: { 'prod_idx': prod_idx, 'options': options, 'require': (require ? 'Y' : 'N'), 'count': count },
2337            url: ('/shop/select_option.cm'),
2338            dataType: 'json',
2339            cache: false,
2340            async: false,
2341            success: function (result) {
2342                if (result.msg == 'SUCCESS') {
2343                    var no = findSelectedProdAdditionalOption(options);
2344										var prod_name = result.prod_name;
2345                    if (no == -1) {
2346                        /* 상품 구매수량 체크 */
2347	                    var selected_prod_data = selected_prod_additionals.find(v=>v.idx===prod_idx);
2348                        var can_add = true;
2349                        var error_msg = '';
2350
2351                        // 추가상품 자체의 제한만 확인한다.
2352                        var total_selected_count = getProdAdditionalTotalQuantity(selected_prod_data.maximum_purchase_quantity_type, prod_idx);
2353                        var new_total = total_selected_count + count;
2354
2355                        if (selected_prod_data.maximum_purchase_quantity > 0 && new_total > selected_prod_data.maximum_purchase_quantity) {
2356                            can_add = false;
2357                            error_msg = LOCALIZE.설명_최대N개만구매가능한상품입니다(selected_prod_data.maximum_purchase_quantity);
2358                        }
2359
2360                        if (can_add) {
2361	                        selected_prod_additional_options.push({prod_idx, prod_name, ...result.selected_option});
2362                            /** 처음 선택된 옵션인 경우 새로 추가*/
2363                            success();
2364                        } else {
2365                            failed(error_msg);
2366                        }
2367                    } else {
2368                        failed(LOCALIZE.설명_이미선택된옵션입니다());
2369                    }
2370                } else {
2371                    failed(result.msg);
2372                }
2373                $('._btn_buy').attr('onclick', btn_buy_onclick_temp);
2374                $('._btn_regularly').attr('onclick', btn_regularly_onclick_temp);
2375                $('._btn_cart').attr('onclick', btn_cart_onclick_temp);
2376                $('._btn_cart.im-regularly').attr('onclick', btn_regularly_cart_onclick_temp);
2377            }
2378        });
2379    };
2380
2381		var checkRequireOption = function() {
2382			var main_require_option_count = parseInt(require_option_count) || 0;
2383			if (main_require_option_count <= 0) return true;
2384
2385			var has_selected_require_option = false;
2386			$.each(selected_options, function (no, data) {
2387				if (data.require == true) {
2388					has_selected_require_option = true;
2389					return false;
2390				}
2391			});
2392
2393			return has_selected_require_option;
2394		};
2395
2396    var getProdTotalQuantity = function (maximum_purchase_quantity_type) {
2397        /* 본상품 구매수량 체크: 추가상품은 별도 상품이므로 포함하지 않는다. */
2398        var total_selected_count = 0;
2399        if (maximum_purchase_quantity_type === 'order') {
2400            // 옵션이 없는 경우의 본품 수량 포함
2401            total_selected_count += order_count;
2402        }
2403        $.each(selected_options, function (idx, obj) {
2404            if (maximum_purchase_quantity_type === 'order') {
2405                total_selected_count += obj.count;
2406            }
2407        });
2408        return total_selected_count;
2409    };
2410
2411    var getProdRequireTotalQuantity = function () {
2412        /* 필수 옵션 구매수량 체크 */
2413        var total_selected_count = 0;
2414        $.each(selected_options, function (idx, obj) {
2415            if (obj.require) total_selected_count += obj.count;
2416        });
2417        return total_selected_count;
2418    };
2419
2420		var getProdAdditionalTotalQuantity = function (maximum_purchase_quantity_type, prod_idx) {
2421        /* 추가상품 구매수량 체크: 같은 추가상품의 옵션만 합산한다. */
2422        var total_selected_count = 0;
2423        $.each(selected_prod_additional_options, function (idx, obj) {
2424            if (maximum_purchase_quantity_type === 'order' && obj.prod_idx === prod_idx) {
2425                total_selected_count += obj.count;
2426            }
2427        });
2428        return total_selected_count;
2429    };
2430
2431    var getProdAdditionalRequireTotalQuantity = function () {
2432        /* 필수 옵션 구매수량 체크 */
2433        var total_selected_count = 0;
2434        $.each(selected_prod_additional_options, function (idx, obj) {
2435            if (obj.require) total_selected_count += obj.count;
2436        });
2437        return total_selected_count;
2438    };
2439
2440    /**
2441     * 선택된 옵션 삭제
2442     * @param optName
2443     * @param success
2444     */
2445    var removeSelectedOption = function (optNo, success) {
2446        if (optNo <= (selected_options.length - 1)) {
2447            selected_options.splice(optNo, 1);
2448            success();
2449        }
2450    };
2451
2452	/**
2453	 * 선택된 추가 상품 삭제
2454	 * @param optNo
2455	 * @param success
2456	 */
2457	var removeSelectedProdAdditional = function (prodIdx, success) {
2458		if (selected_prod_additionals.length) {
2459			selected_prod_additionals = selected_prod_additionals.filter(data => data.idx !== prodIdx);
2460			/* 추가 상품의 하위 옵션도 같이 삭제 */
2461			selected_prod_additional_options = selected_prod_additional_options.filter(data => data.prod_idx !== prodIdx);
2462
2463			success();
2464		}
2465	};
2466
2467		/**
2468     * 선택된 추가 상품 옵션 삭제
2469     * @param optNo
2470     * @param success
2471     */
2472    var removeSelectedProdAdditionalOption = function (optNo, success) {
2473        if (optNo <= (selected_prod_additional_options.length - 1)) {
2474					var deleted = selected_prod_additional_options.splice(optNo, 1)[0];
2475	        if(deleted.require && selected_prod_additional_options.length === 0){
2476		        removeSelectedProdAdditional(deleted.prod_idx, function(){});
2477	        }
2478            success();
2479        }
2480    };
2481
2482    /**
2483     * 해당 옵션이 현재 선택되어있는지 확인
2484     * @param options [{option_code:, value_code:, value_name:}]
2485     * */
2486    var findSelectedOption = function (options) {
2487        var found_no = -1;
2488        $.each(selected_options, function (no, data) {
2489            if (isSameOptionList(data.options, options)) {
2490                found_no = no;
2491                return false;
2492            }
2493        });
2494        return found_no;
2495    };
2496
2497		/**
2498     * 해당 옵션이 현재 선택되어있는지 확인
2499     * @param options [{option_code:, value_code:, value_name:}]
2500     * */
2501    var findSelectedProdAdditionalOption = function (options) {
2502        var found_no = -1;
2503        $.each(selected_prod_additional_options, function (no, data) {
2504	        if(data.prod_idx != current_select_additional_prod_idx){
2505		        return true; // 현재 선택 중인 추가 상품이 아닌 상품의 옵션은 체크하지 않고 continue 처리
2506	        }
2507	        if (isSameOptionList(data.options, options)) {
2508		        found_no = no;
2509		        return false;
2510	        }
2511        });
2512        return found_no;
2513    };
2514
2515    /**
2516     * 해당 필수 옵션이 현재 선택되어있는지 확인
2517     * @param optName
2518     */
2519    var findSelectedRequireOption = function (option_code) {
2520        var foundNo = -1;
2521        $.each(selected_require_options, function (no, data) {
2522            if (data.option_code == option_code) {
2523                foundNo = no;
2524                return false;
2525            }
2526        });
2527        return foundNo;
2528    };
2529
2530	/**
2531	 * 해당 필수 옵션이 현재 선택되어있는지 확인
2532	 * @param optName
2533	 */
2534	var findSelectedProdAdditionalRequireOption = function (option_code) {
2535		var foundNo = -1;
2536		$.each(selected_prod_additional_require_options, function (no, data) {
2537			if (data.option_code == option_code) {
2538				foundNo = no;
2539				return false;
2540			}
2541		});
2542		return foundNo;
2543	};
2544
2545    /**
2546     * 옵션 수량 및 재고 체크하는 기능
2547     * @param optNo
2548     * @param cnt
2549     * @returns {*}
2550     */
2551    var checkOptionCount = function (optNo, cnt) {
2552        // 그냥 재고 체크
2553        if (selected_options[optNo].use_stock && !selected_options[optNo].stock_un_limit) {
2554            if (cnt > selected_options[optNo].stock) {
2555                selected_options[optNo].count = selected_options[optNo].stock;
2556                return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(selected_options[optNo].stock + 1);
2557            }
2558        }
2559
2560
2561
2562        if (selected_options[optNo].require) { // 필수 옵션
2563            if (prod_type === SHOP_CONST.PROD_TYPE.DIGITAL) {
2564                // 만약 디지털 상품일 경우 수량변경이 제한된다.
2565                selected_options[optNo].count = 1;
2566                return LOCALIZE.설명_디지털상품은수량을변경하실수없습니다();
2567            }
2568        } else { // 선택 옵션
2569            if (parseInt(selected_options[optNo].price) == 0) {
2570                // 가격이 0원일 경우... 0원 선택옵션 구매 시 최대 수량 제한 체크를 한다.
2571                switch (optional_limit_type) {
2572                    case 'limit':
2573                        if (cnt > optional_limit) {
2574                            selected_options[optNo].count = optional_limit;
2575                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(optional_limit);
2576                        }
2577                        break;
2578                    case 'unique':
2579                        if (cnt > 1) {
2580                            selected_options[optNo].count = 1;
2581                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(1);
2582                        }
2583                        break;
2584                    case 'relative':
2585                        var _order_cnt = order_count;
2586                        if (_order_cnt == 0) {
2587                            $.each(selected_options, function (no, data) { if (data.require) { _order_cnt += data.count; } });
2588                        }
2589
2590                        if (cnt > _order_cnt) {
2591                            selected_options[optNo].count = _order_cnt;
2592                            return LOCALIZE.설명_0원상품갯수제한();
2593                        }
2594                        break;
2595                }
2596            }
2597        }
2598
2599        return true;
2600    };
2601
2602		/**
2603     * 옵션 수량 및 재고 체크하는 기능
2604     * @param optNo
2605     * @param cnt
2606     * @returns {*}
2607     */
2608    var checkProdAdditionalOptionCount = function (optNo, cnt) {
2609			var prod_idx = selected_prod_additional_options[optNo].prod_idx;
2610			var selected_prod_data = selected_prod_additionals.find(v=>v.idx===prod_idx);
2611        // 그냥 재고 체크
2612        if (selected_prod_additional_options[optNo].use_stock && !selected_prod_additional_options[optNo].stock_un_limit) {
2613            if (cnt > selected_prod_additional_options[optNo].stock) {
2614                selected_prod_additional_options[optNo].count = selected_prod_additional_options[optNo].stock;
2615                return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(selected_prod_additional_options[optNo].stock + 1);
2616            }
2617        }
2618
2619
2620
2621        if (selected_prod_additional_options[optNo].require) { // 필수 옵션
2622            if (prod_type === SHOP_CONST.PROD_TYPE.DIGITAL) {
2623                // 만약 디지털 상품일 경우 수량변경이 제한된다.
2624                selected_prod_additional_options[optNo].count = 1;
2625                return LOCALIZE.설명_디지털상품은수량을변경하실수없습니다();
2626            }
2627        } else { // 선택 옵션
2628            if (parseInt(selected_prod_additional_options[optNo].price) == 0) {
2629                // 가격이 0원일 경우... 0원 선택옵션 구매 시 최대 수량 제한 체크를 한다.
2630                switch (selected_prod_data.optional_limit_type) {
2631                    case 'limit':
2632                        if (cnt > selected_prod_data.optional_limit) {
2633                            selected_prod_additional_options[optNo].count = selected_prod_data.optional_limit;
2634                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(selected_prod_data.optional_limit);
2635                        }
2636                        break;
2637                    case 'unique':
2638                        if (cnt > 1) {
2639                            selected_prod_additional_options[optNo].count = 1;
2640                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(1);
2641                        }
2642                        break;
2643                    case 'relative':
2644                        var _order_cnt = selected_prod_data.count;
2645                        if (_order_cnt == 0) {
2646                            $.each(selected_prod_additional_options, function (no, data) { if (data.require) { _order_cnt += data.count; } });
2647                        }
2648
2649                        if (cnt > _order_cnt) {
2650                            selected_prod_additional_options[optNo].count = _order_cnt;
2651                            return LOCALIZE.설명_0원상품갯수제한();
2652                        }
2653                        break;
2654                }
2655            }
2656        }
2657
2658        return true;
2659    };
2660
2661    var increaseOptionCount = function (optNo, success) {
2662        var $curCount = $('#prdOption' + optNo).find('input._count');
2663        var curCount = $curCount.val();
2664        if (isNaN(curCount))
2665            curCount = 1;
2666        else
2667            curCount = parseInt(curCount) + 1;
2668
2669        var optional_result = checkOptionCount(optNo, curCount);
2670        if (optional_result === true) {
2671            selected_options[optNo].count = curCount;
2672        } else {
2673            alert(optional_result);
2674        }
2675
2676
2677        var is_success = true;
2678        //1회당 구매 가능한 수량 체크
2679        if (max_prod_quantity > 0) {
2680            var _selected_count = 0;
2681            if (maximum_purchase_quantity_type === 'order') {//주문단위 기준인경우
2682                // 본품 수량 포함
2683                _selected_count += order_count;
2684                // 필수/선택 옵션 수량 포함
2685                $.each(selected_options, function (idx, obj) {
2686                    _selected_count += obj.count;
2687                });
2688            } else if (maximum_purchase_quantity_type !== 'order') { // 옵션별 수량 체크인 경우
2689                switch (maximum_purchase_quantity_type) {
2690                    // required_option 타입인 경우 필수옵션만 카운트
2691                    case 'required_option':
2692                        var $option = $('#prdOption' + optNo);
2693                        if ($option.hasClass('_selected_require_option')) {
2694                            _selected_count = selected_options[optNo].count;
2695                        }
2696                        break;
2697                    case 'optional_option':
2698                        var $option = $('#prdOption' + optNo);
2699                        if (!$option.hasClass('_selected_require_option')) {
2700                            _selected_count = selected_options[optNo].count;
2701                        }
2702                        break;
2703                    case 'prod':
2704                    default:
2705                        _selected_count = selected_options[optNo].count;
2706                        break;
2707                }
2708            }
2709            if (_selected_count > max_prod_quantity) {
2710                selected_options[optNo].count -= 1;
2711                is_success = false;
2712                alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
2713            }
2714        }
2715
2716        // 회원당 최대 구매수량 검증 (1회 구매 제한이 옵션별 타입이면 1인 제한 미적용 - 스펙)
2717        if (is_success && max_member_quantity > 0 && maximum_purchase_quantity_type === 'order') {
2718            var _member_count = order_count;
2719            $.each(selected_options, function (idx, obj) {
2720                _member_count += obj.count;
2721            });
2722            if (_member_count > max_member_quantity) {
2723                selected_options[optNo].count -= 1;
2724                is_success = false;
2725                alert(LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity));
2726            }
2727        }
2728
2729        if (is_success) success();
2730    };
2731
2732    var decreaseOptionCount = function (optNo, success) {
2733        var $curCount = $('#prdOption' + optNo).find('input._count');
2734        var curCount = $curCount.val();
2735        if (isNaN(curCount))
2736            curCount = 1;
2737        else
2738            curCount = parseInt(curCount) - 1;
2739        if (curCount < 1) curCount = 1;
2740
2741        var optional_result = checkOptionCount(optNo, curCount);
2742        if (optional_result === true) {
2743            selected_options[optNo].count = curCount;
2744        } else {
2745            alert(optional_result);
2746        }
2747        success();
2748    };
2749
2750    var increaseProdAdditionalOptionCount = function (optNo, success) {
2751        var $curCount = $('#prdAdditionalOption' + optNo).find('input._count');
2752        var curCount = $curCount.val();
2753				var prod_idx = selected_prod_additional_options[optNo].prod_idx;
2754				var selected_prod_data = selected_prod_additionals.find(v=>v.idx===prod_idx);
2755        if (isNaN(curCount))
2756            curCount = 1;
2757        else
2758            curCount = parseInt(curCount) + 1;
2759
2760        var optional_result = checkProdAdditionalOptionCount(optNo, curCount);
2761        if (optional_result === true) {
2762            selected_prod_additional_options[optNo].count = curCount;
2763        } else {
2764            alert(optional_result);
2765        }
2766
2767        var is_success = true;
2768        //1회당 구매 가능한 수량 체크
2769        if (selected_prod_data.maximum_purchase_quantity > 0) {
2770            var _selected_count = 0;
2771            if (selected_prod_data.maximum_purchase_quantity_type === 'order') {//주문단위 기준인경우
2772                _selected_count = getProdAdditionalTotalQuantity(selected_prod_data.maximum_purchase_quantity_type, prod_idx);
2773            } else if (selected_prod_data.maximum_purchase_quantity_type !== 'order') {
2774                switch (selected_prod_data.maximum_purchase_quantity_type) {
2775                    case 'required_option':
2776                        var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2777                        if ($prodAddtionalOption.hasClass('_selected_require_option')) {
2778                            _selected_count = selected_prod_additional_options[optNo].count;
2779                        }
2780                        break;
2781                    case 'optional_option':
2782                        var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2783                        if (!$prodAddtionalOption.hasClass('_selected_require_option')) {
2784                            _selected_count = selected_prod_additional_options[optNo].count;
2785                        }
2786                        break;
2787                    case 'prod':
2788                    default:
2789                        _selected_count = selected_prod_additional_options[optNo].count;
2790                        break;
2791                }
2792            }
2793            if (_selected_count > selected_prod_data.maximum_purchase_quantity) {
2794                selected_prod_additional_options[optNo].count -= 1;
2795                is_success = false;
2796                alert(LOCALIZE.설명_최대구매수량(selected_prod_data.maximum_purchase_quantity));
2797            }
2798        }
2799
2800        if (is_success) success();
2801    };
2802
2803    var decreaseProdAdditionalOptionCount = function (optNo, success) {
2804        var $curCount = $('#prdAdditionalOption' + optNo).find('input._count');
2805        var curCount = $curCount.val();
2806        if (isNaN(curCount))
2807            curCount = 1;
2808        else
2809            curCount = parseInt(curCount) - 1;
2810        if (curCount < 1) curCount = 1;
2811
2812        var optional_result = checkProdAdditionalOptionCount(optNo, curCount);
2813        if (optional_result === true) {
2814            selected_prod_additional_options[optNo].count = curCount;
2815        } else {
2816            alert(optional_result);
2817        }
2818        success();
2819    };
2820
2821    const changeProdAdditionalOptionCount = function (optNo, toBeCount, success) {
2822        toBeCount = parseInt(toBeCount, 10);
2823        if (isNaN(toBeCount) || toBeCount < 1) toBeCount = 1;
2824
2825        const optionalResult = checkProdAdditionalOptionCount(optNo, toBeCount);
2826        if (optionalResult === true) {
2827            selected_prod_additional_options[optNo].count = toBeCount;
2828        } else {
2829            alert(optionalResult);
2830        }
2831
2832        const prodIdx = selected_prod_additional_options[optNo].prod_idx;
2833        const selectedProdData = selected_prod_additionals.find(v => v.idx === prodIdx);
2834
2835        //1회당 구매 가능한 수량 체크
2836        if (!selectedProdData || selectedProdData?.maximum_purchase_quantity === undefined) {
2837            return;
2838        }
2839
2840        if (selectedProdData.maximum_purchase_quantity > 0) {
2841            var shouldCheck = false;
2842            switch (selectedProdData.maximum_purchase_quantity_type) {
2843                case 'required_option':
2844                    var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2845                    shouldCheck = $prodAddtionalOption.hasClass('_selected_require_option');
2846                    break;
2847                case 'optional_option':
2848                    var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2849                    shouldCheck = !$prodAddtionalOption.hasClass('_selected_require_option');
2850                    break;
2851                case 'order':
2852                    // order 타입은 전체 합산 로직 필요
2853                    break;
2854                case 'prod':
2855                default:
2856                    shouldCheck = true;
2857                    break;
2858            }
2859
2860            if (shouldCheck && toBeCount > selectedProdData.maximum_purchase_quantity) {
2861                selected_prod_additional_options[optNo].count = selectedProdData.maximum_purchase_quantity;
2862                alert(LOCALIZE.설명_최대구매수량(selectedProdData.maximum_purchase_quantity));
2863            }
2864        }
2865
2866        if (typeof success === 'function') success();
2867    };
2868
2869    var changeOptionCount = function (optNo, optCount, success) {
2870        var prevCount = selected_options[optNo].count;
2871        optCount = parseInt(optCount);
2872        if (isNaN(optCount)) optCount = 1;
2873        if (optCount < 1) optCount = 1;
2874
2875        var optional_result = checkOptionCount(optNo, optCount);
2876        if (optional_result === true) {
2877            selected_options[optNo].count = optCount;
2878        } else {
2879            alert(optional_result);
2880        }
2881
2882        var is_success = true;
2883        //1회당 구매 가능한 수량 체크
2884        if (max_prod_quantity > 0) {
2885            var _selected_count = 0;
2886            if (maximum_purchase_quantity_type === 'order') {//주문단위 기준인경우
2887                // 본품 수량 포함
2888                _selected_count += order_count;
2889                // 필수/선택 옵션 수량 포함
2890                $.each(selected_options, function (idx, obj) {
2891                    _selected_count += obj.count;
2892                });
2893            } else if (maximum_purchase_quantity_type !== 'order') { // 옵션별 수량 체크인 경우
2894                switch (maximum_purchase_quantity_type) {
2895                    // required_option 타입인 경우 필수옵션만 카운트
2896                    case 'required_option':
2897                        var $option = $('#prdOption' + optNo);
2898                        if ($option.hasClass('_selected_require_option')) {
2899                            _selected_count = selected_options[optNo].count;
2900                        }
2901                        break;
2902                    case 'optional_option':
2903                        var $option = $('#prdOption' + optNo);
2904                        if (!$option.hasClass('_selected_require_option')) {
2905                            _selected_count = selected_options[optNo].count;
2906                        }
2907                        break;
2908                    case 'prod':
2909                    default:
2910                        _selected_count = selected_options[optNo].count;
2911                        break;
2912                }
2913            }
2914            if (_selected_count > max_prod_quantity) {
2915                selected_options[optNo].count = prevCount;
2916                is_success = false;
2917                alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
2918            }
2919        }
2920
2921        // 회원당 최대 구매수량 검증 (1회 구매 제한이 옵션별 타입이면 1인 제한 미적용 - 스펙)
2922        if (is_success && max_member_quantity > 0 && maximum_purchase_quantity_type === 'order') {
2923            var _member_count = order_count;
2924            $.each(selected_options, function (idx, obj) {
2925                _member_count += obj.count;
2926            });
2927            if (_member_count > max_member_quantity) {
2928                selected_options[optNo].count = prevCount;
2929                is_success = false;
2930                alert(LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity));
2931            }
2932        }
2933
2934        if (is_success) success();
2935    };
2936
2937    var checkProdStock = function (cnt) {
2938        // 그냥 재고 체크
2939        if (prod_stock_use && !prod_stock_unlimit) {
2940            if (cnt > prod_stock) {
2941                order_count = prod_stock;
2942                return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(prod_stock + 1);
2943            }
2944        }
2945
2946        return true;
2947    };
2948
2949    var increaseOrderCount = function (type, success) {
2950        var o = $prod_detail.find('input._order_count_' + type);
2951        var curCount = o.val();
2952        if (isNaN(curCount))
2953            curCount = 1;
2954        else
2955            curCount = parseInt(curCount) + 1;
2956
2957        var checkProdStockResult = checkProdStock(curCount);
2958        if (checkProdStockResult === true) {
2959            order_count = curCount;
2960        } else {
2961            alert(checkProdStockResult);
2962        }
2963
2964        var is_success = true;
2965        //1회당 구매 가능한 수량 체크
2966        if (max_prod_quantity > 0 && maximum_purchase_quantity_type !== 'optional_option') {
2967            var _selected_count = order_count;
2968
2969            // order 타입이면 본상품의 필수/선택 옵션도 포함하여 검증
2970            if (maximum_purchase_quantity_type === 'order') {
2971                $.each(selected_options, function (idx, obj) {
2972                    _selected_count += obj.count;
2973                });
2974            }
2975
2976            if (_selected_count > max_prod_quantity) {
2977                order_count -= 1;
2978                is_success = false;
2979                alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
2980            }
2981        }
2982        o.val(order_count);
2983
2984        if (is_success) success();
2985    };
2986
2987    var decreaseOrderCount = function (type, success) {
2988        var o = $prod_detail.find('input._order_count_' + type);
2989        var curCount = o.val();
2990        if (isNaN(curCount))
2991            curCount = 1;
2992        else
2993            curCount = parseInt(curCount) - 1;
2994
2995        if (curCount < 1) curCount = 1;
2996
2997        var checkProdStockResult = checkProdStock(curCount);
2998        if (checkProdStockResult === true) {
2999            order_count = curCount;
3000        } else {
3001            alert(checkProdStockResult);
3002        }
3003
3004        o.val(order_count);
3005        success();
3006    };
3007
3008    var changeOrderCount = function (type, count, success, is_alert) {
3009        if (is_alert == void 0) is_alert = true;
3010        var prevCount = order_count;
3011        if (isNaN(count))
3012            count = 1;
3013        else
3014            count = parseInt(count);
3015        if (count < 1) count = 1;
3016
3017        var checkProdStockResult = checkProdStock(count);
3018        if (checkProdStockResult === true) {
3019            order_count = count;
3020        } else {
3021            if (is_alert) alert(checkProdStockResult);
3022        }
3023
3024        var is_success = true;
3025        //1회당 구매 가능한 수량 체크
3026        if (max_prod_quantity > 0 && maximum_purchase_quantity_type !== 'optional_option') {
3027            var _selected_count = order_count;
3028
3029            // order 타입이면 본상품의 필수/선택 옵션도 포함하여 검증
3030            if (maximum_purchase_quantity_type === 'order') {
3031                $.each(selected_options, function (idx, obj) {
3032                    _selected_count += obj.count;
3033                });
3034            }
3035
3036            if (_selected_count > max_prod_quantity) {
3037                order_count = prevCount;
3038                is_success = false;
3039                if (is_alert) alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
3040            }
3041        }
3042
3043        $prod_detail.find("input._order_count_" + type).val(order_count);
3044        if (is_success) success();
3045    };
3046
3047	var increaseProdAdditionalOrderCount = function (no, type, success) {
3048		var selected_prod_data = selected_prod_additionals[no];
3049		var o = $prod_detail.find(`._area_count[data-no=${no}] input._prod_additional_order_count_${type}`);
3050		var curCount = o.val();
3051		if (isNaN(curCount))
3052			curCount = 1;
3053		else
3054			curCount = parseInt(curCount) + 1;
3055
3056		var checkProdStockResult = checkProdAdditionalStock(no, curCount);
3057		if (checkProdStockResult === true) {
3058			selected_prod_data.count = curCount;
3059		} else {
3060			alert(checkProdStockResult);
3061		}
3062
3063		var is_success = true;
3064
3065		// 추가상품 자체의 제한을 확인한다.
3066		if (is_success && selected_prod_data.maximum_purchase_quantity > 0 && selected_prod_data.maximum_purchase_quantity_type !== 'optional_option') {
3067			if (selected_prod_data.count > selected_prod_data.maximum_purchase_quantity) {
3068				selected_prod_data.count -= 1;
3069				is_success = false;
3070				alert(LOCALIZE.설명_최대구매수량(selected_prod_data.maximum_purchase_quantity));
3071			}
3072		}
3073		o.val(selected_prod_data.count);
3074
3075		if (is_success) success();
3076	};
3077
3078	var decreaseProdAdditionalOrderCount = function (no, type, success) {
3079		var selected_prod_data = selected_prod_additionals[no];
3080		var o = $prod_detail.find(`._area_count[data-no=${no}] input._prod_additional_order_count_${type}`);
3081		var curCount = o.val();
3082		if (isNaN(curCount))
3083			curCount = 1;
3084		else
3085			curCount = parseInt(curCount) - 1;
3086
3087		if (curCount < 1) curCount = 1;
3088
3089		var checkProdStockResult = checkProdAdditionalStock(no, curCount);
3090		if (checkProdStockResult === true) {
3091			selected_prod_data.count = curCount;
3092		} else {
3093			alert(checkProdStockResult);
3094		}
3095
3096		o.val(selected_prod_data.count);
3097		success();
3098	};
3099
3100	var changeProdAdditionalOrderCount = function (no, type, count, success, is_alert) {
3101		var selected_prod_data = selected_prod_additionals[no];
3102		var prevCount = selected_prod_data.count;
3103		if (is_alert == void 0) is_alert = true;
3104		if (isNaN(count))
3105			count = 1;
3106		else
3107			count = parseInt(count);
3108		if (count < 1) count = 1;
3109
3110		var checkProdStockResult = checkProdAdditionalStock(no, count);
3111		if (checkProdStockResult === true) {
3112			selected_prod_data.count = count;
3113		} else {
3114			if (is_alert) alert(checkProdStockResult);
3115		}
3116
3117		var is_success = true;
3118
3119		// 추가상품 자체의 제한을 확인한다.
3120		if (is_success && selected_prod_data.maximum_purchase_quantity > 0 && selected_prod_data.maximum_purchase_quantity_type !== 'optional_option') {
3121			if (selected_prod_data.count > selected_prod_data.maximum_purchase_quantity) {
3122				selected_prod_data.count = prevCount;
3123				is_success = false;
3124				if (is_alert) alert(LOCALIZE.설명_최대구매수량(selected_prod_data.maximum_purchase_quantity));
3125			}
3126		}
3127
3128		$prod_detail.find(`._area_count[data-no=${no}] input._prod_additional_order_count_${type}`).val(selected_prod_data.count);
3129		if (is_success) success();
3130	};
3131
3132	var checkProdAdditionalStock = function (no, cnt) {
3133		var selected_prod_data = selected_prod_additionals[no];
3134		// 그냥 재고 체크
3135		if (selected_prod_data.stock_use && !selected_prod_data.stock_unlimit) {
3136			if (cnt > selected_prod_data.stock) {
3137				selected_prod_data.count = selected_prod_data.stock;
3138				return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(selected_prod_data.stock + 1);
3139			}
3140		}
3141
3142		return true;
3143	};
3144
3145    var selectCartType = function (type) {
3146        if (type === 'regularly') {
3147            $prod_detail.addClass('detail-regularly');
3148            // 정기구독 선택 시 수량별 할인 배너/혜택 숨김 (IMIO-7132)
3149            $prod_detail.find('._quantity_discount_banner_wrap').hide();
3150            $prod_detail.find('._quantity_discount_banner_mobile').hide();
3151            $prod_detail.find('._quantity_discount_benefits').hide();
3152        } else {
3153            $prod_detail.removeClass('detail-regularly');
3154            // 1회구매 복귀 시 수량별 할인 배너/혜택 복원
3155            $prod_detail.find('._quantity_discount_banner_mobile').show();
3156            $prod_detail.find('._quantity_discount_benefits').show();
3157            // 배너는 옵션 선택 여부에 따라 updateSelectedOptions 에서 toggle 됨
3158        }
3159        updateSelectedOptions('prod');
3160    };
3161
3162    var getCreatorDiscountMaxToastTarget = function () {
3163        var $scope = options.is_prod_detail_page ? $('.modal_prod_detail_from_shopping_list') : $(document);
3164        var $target = $scope.find('.buy_btns.mobile:visible .cart_btn:visible').first();
3165        if (!$target.length) {
3166            $target = $scope.find('.buy_btns.mobile .cart_btn').first();
3167        }
3168        if (!$target.length) {
3169            $target = $scope.find('._mobile_action_btn_modal_wrap:visible, ._mobile_action_btn_wrap:visible, .buy_btns.mobile:visible').first();
3170        }
3171        if (!$target.length) {
3172            $target = $scope.find('._mobile_action_btn_modal_wrap, ._mobile_action_btn_wrap, .buy_btns.mobile').first();
3173        }
3174        return $target;
3175    };
3176
3177    var getCreatorDiscountMaxToast = function () {
3178        var $toast = $('body').children('._creator_max_discount_toast');
3179        if (!$toast.length) {
3180            $toast = $('<div class="_creator_max_discount_toast" role="status" aria-live="polite"><i class="btl bt-exclamation-triangle" aria-hidden="true"></i><span></span></div>');
3181            $('body').append($toast);
3182        }
3183        return $toast;
3184    };
3185
3186    var positionCreatorDiscountMaxToast = function ($toast, $target) {
3187        if (!$toast || !$toast.length || !$target || !$target.length) return;
3188
3189        var rect = $target.get(0).getBoundingClientRect();
3190        var bottom = Math.max(window.innerHeight - rect.top + 16, 16);
3191        var center = rect.left + (rect.width / 2);
3192
3193        $toast.css({
3194            bottom: bottom + 'px',
3195            left: center + 'px'
3196        });
3197    };
3198
3199    var hideCreatorDiscountMaxToast = function () {
3200        if (creator_discount_max_toast_timer) {
3201            clearTimeout(creator_discount_max_toast_timer);
3202            creator_discount_max_toast_timer = null;
3203        }
3204        $('body').children('._creator_max_discount_toast').removeClass('is-visible');
3205    };
3206
3207    var showCreatorDiscountMaxToast = function () {
3208        var $target = getCreatorDiscountMaxToastTarget();
3209        if (!$target.length) return;
3210
3211        var $toast = getCreatorDiscountMaxToast();
3212        positionCreatorDiscountMaxToast($toast, $target);
3213
3214        $toast.find('span').text(creator_discount_max_toast_text);
3215        $toast.removeClass('is-visible');
3216        var nextFrame = window.requestAnimationFrame || function (callback) { return setTimeout(callback, 0); };
3217        nextFrame(function () {
3218            positionCreatorDiscountMaxToast($toast, $target);
3219            $toast.addClass('is-visible');
3220        });
3221
3222        if (creator_discount_max_toast_timer) {
3223            clearTimeout(creator_discount_max_toast_timer);
3224        }
3225        creator_discount_max_toast_timer = setTimeout(function () {
3226            $toast.removeClass('is-visible');
3227            creator_discount_max_toast_timer = null;
3228        }, 2200);
3229    };
3230
3231    var parseFreeShipNotifyPrice = function (price) {
3232        var parsed = parseFloat(price);
3233        return isNaN(parsed) ? 0 : parsed;
3234    };
3235
3236    var getFreeShipNotifyRegularPrice = function (regular_price, sale_price) {
3237        var regularPrice = parseFreeShipNotifyPrice(regular_price);
3238        var salePrice = parseFreeShipNotifyPrice(sale_price);
3239        return regularPrice > 0 ? regularPrice : salePrice;
3240    };
3241
3242    var getFreeShipNotifyMainRegularPrice = function () {
3243        return getFreeShipNotifyRegularPrice(free_ship_notify_prod_regular_price, prod_price);
3244    };
3245
3246    var setFreeShipNotifyProdAdditionalRegularPrice = function (prod_idx, regular_price) {
3247        if (!prod_idx) return;
3248        free_ship_notify_prod_additional_regular_price_map[prod_idx] = parseFreeShipNotifyPrice(regular_price);
3249    };
3250
3251    var getFreeShipNotifyProdAdditionalRegularPrice = function (prod_idx, sale_price) {
3252        return getFreeShipNotifyRegularPrice(free_ship_notify_prod_additional_regular_price_map[prod_idx], sale_price);
3253    };
3254
3255    var isFreeShipNotifyAfterSale = function () {
3256        return free_ship_notify_deliv_price_cost_type === FREE_SHIP_NOTIFY_DELIV_PRICE_COST_TYPE_AFTER;
3257    };
3258
3259    var calcFreeShipNotifyRequiredOptionPrice = function (option_price) {
3260        return parseFreeShipNotifyPrice(option_price);
3261    };
3262
3263    var calcFreeShipNotifyCreatorDiscountRaw = function (total_base, total_count) {
3264        if (!creator_discount_data) return 0;
3265        var raw = 0;
3266        if (creator_discount_data.type === 'PDT01') {
3267            raw = (parseInt(total_count, 10) || 0) * (parseInt(creator_discount_data.discount_amount, 10) || 0);
3268        } else if (creator_discount_data.type === 'PDT02') {
3269            var pct = parseFloat(creator_discount_data.percent) || 0;
3270            if (pct <= 0) return 0;
3271            raw = Math.floor((parseFreeShipNotifyPrice(total_base) * pct) / 100);
3272        }
3273        return raw;
3274    };
3275
3276    var calcFreeShipNotifyCreatorDiscount = function (total_base, total_count) {
3277        if (!creator_discount_data) return 0;
3278        var raw = calcFreeShipNotifyCreatorDiscountRaw(total_base, total_count);
3279        var cap = parseFreeShipNotifyPrice(creator_discount_data.max_price);
3280        return cap > 0 ? Math.min(raw, cap) : raw;
3281    };
3282
3283    var calcFreeShipNotifyQuantityDiscount = function (base_amount, total_count) {
3284        if (creator_discount_data || !$selected_options || !$selected_options.length) return 0;
3285
3286        var raw = $selected_options.attr('data-qd-tiers');
3287        if (!raw) return 0;
3288
3289        var tiers = null;
3290        try {
3291            tiers = JSON.parse(raw);
3292        } catch (e) {
3293            return 0;
3294        }
3295        if (!tiers || !tiers.length) return 0;
3296
3297        var matched = tiers
3298            .filter(function (tier) { return total_count >= tier.min_quantity; })
3299            .sort(function (a, b) { return b.min_quantity - a.min_quantity; })[0];
3300        if (!matched) return 0;
3301
3302        var cap = Math.max(parseFreeShipNotifyPrice(base_amount), 0);
3303        if (matched.method === 'percentage') {
3304            return Math.min(Math.ceil(cap * matched.value / 100), cap);
3305        }
3306        return Math.min(parseInt(matched.value, 10) || 0, cap);
3307    };
3308
3309    var getFreeShipNotifyInitialTotal = function () {
3310        if (!isFreeShipNotifyAfterSale()) {
3311            return getFreeShipNotifyMainRegularPrice();
3312        }
3313
3314        var totalPrice = parseFreeShipNotifyPrice(prod_price);
3315        var periodDiscountPrice = 0;
3316        var creatorUsesPeriodDiscountTarget = creator_discount_data && creator_discount_data.reference_source === 'period_discount';
3317
3318        if (!creator_discount_data || creatorUsesPeriodDiscountTarget) {
3319            periodDiscountPrice = calcPeriodDiscount(prod_price, 1);
3320            if (periodDiscountPrice > totalPrice) periodDiscountPrice = totalPrice;
3321            totalPrice -= periodDiscountPrice;
3322        }
3323
3324        var isRegularlyProd = $prod_detail && $prod_detail.hasClass('detail-regularly');
3325        var quantityDiscountPrice = isRegularlyProd ? 0 : calcFreeShipNotifyQuantityDiscount(totalPrice, 1);
3326        totalPrice -= quantityDiscountPrice;
3327
3328        if (is_main_product_membership_discount) {
3329            var membershipDiscountPrice = calcMembershipDiscount(totalPrice);
3330            if (membershipDiscountPrice > totalPrice) membershipDiscountPrice = totalPrice;
3331            totalPrice -= membershipDiscountPrice;
3332        }
3333
3334        if (creator_discount_data) {
3335            var creatorDiscountBase = creatorUsesPeriodDiscountTarget ? totalPrice : parseFreeShipNotifyPrice(prod_price);
3336            var creatorDiscountPrice = calcFreeShipNotifyCreatorDiscount(creatorDiscountBase, 1);
3337            if (creatorDiscountPrice > totalPrice) creatorDiscountPrice = totalPrice;
3338            totalPrice -= creatorDiscountPrice;
3339        }
3340
3341        return Math.max(totalPrice, 0);
3342    };
3343
3344    var refreshFreeShipNotifyInitialTotal = function () {
3345        if (require_option_count == 0 && selected_options.length == 0 && selected_prod_additionals.length == 0 && selected_prod_additional_options.length == 0 && order_count == 0) {
3346            notifyFreeShipNotifySdk(getFreeShipNotifyInitialTotal());
3347        }
3348    };
3349
3350    /**
3351     * 무료배송 안내 SDK 에 현재 합계와 필수옵션 완료 상태를 전달.
3352     * updateSelectedOptions 의 SDK dispatch 부분만 분리하여, DOM 갱신이 필요 없는
3353     * 진입 경로(예: initDetail 의 옵션 없는 상품 초기 가격 반영)에서도 재사용한다.
3354     * SDK 미로드 환경에서는 no-op.
3355     * @param {number} total_price - SDK 에 전달할 무료배송 기준 합계
3356     */
3357    var notifyFreeShipNotifySdk = function (total_price) {
3358        if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK === 'undefined') return;
3359        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.updateSelectedOptionsTotal(total_price);
3360        // REQUIRE_OPTION 스타일에서 인라인 마그넷 표시 토글.
3361        // selectRequireOption은 필수 옵션이 모두 채워지면 selectOption 호출 후 selected_require_options를 []로 리셋하고
3362        // 확정된 옵션 조합을 selected_options에 push하므로, 진짜 "완료" 신호는 selected_options.length > 0이다.
3363        // 필수 옵션이 없는 상품(require_option_count == 0)은 항상 true.
3364        var isRequireOptionCompleted = (require_option_count == 0) || (selected_options.length > 0);
3365        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setRequireOptionCompleted(isRequireOptionCompleted);
3366    };
3367
3368    /**
3369     * 옵션선택화면갱신
3370     * type : prod (상품상세화면용), cart (장바구니 수량 변경용)
3371     */
3372    var updateSelectedOptions = function (type) {
3373        var html = '';
3374        var total_price_html = '';
3375
3376        var total_price = 0;
3377        var total_count = 0;
3378        var period_discount_price = 0;
3379        var show_period_discount = false;
3380				var membership_discount_not_involved_price = 0;  // 등급할인을 사용하지 않는 상품의 가격(등급할인 계산 과정에서 제외됨)
3381        var free_ship_notify_is_after_sale = isFreeShipNotifyAfterSale();
3382        var free_ship_notify_total_price = 0;
3383
3384        // 수량별 할인 데이터 (data-attribute에서 읽기)
3385        const _quantityDiscountTiers = (() => {
3386            const raw = $selected_options.attr('data-qd-tiers');
3387            if (!raw) return null;
3388            try { return JSON.parse(raw); } catch { return null; }
3389        })();
3390        const _quantityDiscountExcludeOptional = $selected_options.attr('data-qd-exclude-optional') === 'true';
3391        let _quantityDiscountRequireOptionCount = 0; // 필수 옵션 수량 합계
3392        let _quantityDiscountBaseRaw = 0; // 본 상품 raw 매출 합계 (기간할인 미적용, 추가상품 제외) - 수량별 할인 base
3393        // 크리에이터 전용 혜택가 — 본품 + 필수 옵션(require) 합산 가격에 한 번 할인 적용
3394        // 정책 B(필수옵션 포함, 선택옵션 제외) / Y(N건 모두 적용) / M(cap 합산 한 번)
3395        //   - PDT01: discount_amount × totalCount (Y), cap 합산 (M)
3396        //   - PDT02: floor(totalBase × percent / 100) (B+Y 자동 누적), cap 합산 (M)
3397        let _creator_discount_base = 0;  // 본품 + 필수옵션 타겟 가격 합산
3398        let _creator_discount_count = 0; // 본품 + 필수옵션 수량 합산
3399        let _prod_additional_creator_discount_price = 0; // 추가상품별 크리에이터 할인 row 누적액
3400        const _creator_uses_period_discount_target = creator_discount_data && creator_discount_data.reference_source === 'period_discount';
3401        const calcCreatorDiscountRaw = (totalBase, totalCount) => {
3402            if (!creator_discount_data) return 0;
3403            var raw = 0;
3404            if (creator_discount_data.type === 'PDT01') {
3405                raw = (parseInt(totalCount, 10) || 0) * (parseInt(creator_discount_data.discount_amount, 10) || 0);
3406            } else if (creator_discount_data.type === 'PDT02') {
3407                var pct = parseFloat(creator_discount_data.percent) || 0;
3408                if (pct <= 0) return 0;
3409                raw = Math.floor((parseInt(totalBase, 10) || 0) * pct / 100);
3410            }
3411            return raw;
3412        };
3413        const calcCreatorDiscount = (totalBase, totalCount) => {
3414            if (!creator_discount_data) return 0;
3415            var raw = calcCreatorDiscountRaw(totalBase, totalCount);
3416            var cap = parseInt(creator_discount_data.max_price, 10) || 0;
3417            return (cap > 0) ? Math.min(raw, cap) : raw;
3418        };
3419        const calcAdditionalCreatorDiscount = (additionalCreatorData, totalBase, totalCount) => {
3420            if (!additionalCreatorData) return 0;
3421            var raw = 0;
3422            if (additionalCreatorData.type === 'PDT01') {
3423                raw = (parseInt(totalCount, 10) || 0) * (parseInt(additionalCreatorData.discount_amount, 10) || 0);
3424            } else if (additionalCreatorData.type === 'PDT02') {
3425                var pct = parseFloat(additionalCreatorData.percent) || 0;
3426                if (pct <= 0) return 0;
3427                raw = Math.floor((parseInt(totalBase, 10) || 0) * pct / 100);
3428            }
3429            var cap = parseInt(additionalCreatorData.max_price, 10) || 0;
3430            return (cap > 0) ? Math.min(raw, cap) : raw;
3431        };
3432
3433        // 크리에이터 혜택가 — DOM insert 후 본품/옵션 행 가격을 합산 cap 비례 분배해 갱신
3434        // [data-creator-row] 셀렉터로 본품(book)/필수옵션(option) 가격 노드 모두 수집 → 비례 분배
3435        const _applyCreatorDiscountDistribution = ($container) => {
3436            if (!creator_discount_data) return;
3437            if (!$container || !$container.length) return;
3438            var $rows = $container.find('[data-creator-row]');
3439            if (!$rows.length) return;
3440            var totalRaw = 0;
3441            $rows.each(function () {
3442                totalRaw += parseInt($(this).attr('data-creator-raw'), 10) || 0;
3443            });
3444            if (totalRaw <= 0) return;
3445            var totalDiscount = calcCreatorDiscount(_creator_discount_base, _creator_discount_count);
3446            if (totalDiscount <= 0) return;
3447            $rows.each(function () {
3448                var $row = $(this);
3449                var raw = parseInt($row.attr('data-creator-raw'), 10) || 0;
3450                if (raw <= 0) return;
3451                var share = Math.floor(totalDiscount * raw / totalRaw);
3452                var finalPrice = raw - share;
3453                $row.text(LOCALIZE.getCurrencyFormat(finalPrice));
3454            });
3455        };
3456
3457        const _calcQuantityDiscount = (baseAmount, totalCount) => {
3458            // 크리에이터 전용 혜택가 활성 시 MSA 수량별 할인(MFE 프로모션) 무시 (정책 #2)
3459            if (creator_discount_data) return 0;
3460            if (!_quantityDiscountTiers) return 0;
3461            const matched = _quantityDiscountTiers
3462                .filter(t => totalCount >= t.min_quantity)
3463                .sort((a, b) => b.min_quantity - a.min_quantity)[0];
3464            if (!matched) return 0;
3465            const cap = Math.max(baseAmount, 0);
3466            if (matched.method === 'percentage') {
3467                // 서버 OMS 식과 동일: ceil((row 매출 - 기간할인) × 비율)
3468                return Math.min(Math.ceil(cap * matched.value / 100), cap);
3469            }
3470            // fixed_amount, fixed_price: value는 총 할인금액 (수량 무관)
3471            // 품목 금액 초과 방지 (결제페이지/OMS 와 동일한 클램핑, IMIO-7184)
3472            return Math.min(parseInt(matched.value, 10) || 0, cap);
3473        };
3474	      var is_use_prod_additional = selected_prod_additionals.length > 0 || selected_prod_additional_options.length > 0;
3475				/* 본 상품 계산 */
3476        if (require_option_count == 0) {
3477					let section_total_price = 0;
3478
3479            // 본품 행 표시 = 타겟 기준가 × 수량. 크리에이터 할인은 합계(total_price)에서만 차감.
3480            if (typeof USE_OMS !== 'undefined' && USE_OMS === true){
3481                if (creator_discount_data && !_creator_uses_period_discount_target) {
3482                    period_discount_price = 0;
3483                } else {
3484                    period_discount_price = calcPeriodDiscount(prod_price, 1);
3485							if(period_discount_price > prod_price){
3486								period_discount_price = prod_price;
3487							}
3488							period_discount_price *= order_count;
3489						}
3490                section_total_price = order_count * prod_price;
3491                section_total_price = section_total_price - period_discount_price;
3492                show_period_discount = true;
3493            } else {
3494                section_total_price = order_count * prod_price;
3495                if (creator_discount_data && !_creator_uses_period_discount_target) {
3496                    period_discount_price = 0;
3497                } else {
3498							period_discount_price = calcPeriodDiscount(prod_price, order_count);
3499							if (period_discount_price > section_total_price) period_discount_price = section_total_price;
3500						}
3501						section_total_price = section_total_price - period_discount_price;
3502						show_period_discount = true;
3503					}
3504
3505            // 필수옵션이 없을 때 기본 상품 수량 조절 html 출력
3506            if (prod_type === SHOP_CONST.PROD_TYPE.NORMAL) {
3507                // 디지털, 이용권 상품은 수량 변경 할 필요가 없으므로 출력하지 않는다
3508                switch (type) {
3509                    case 'prod':
3510                        if (!is_view_price) break;
3511                        // 상품 상세페이지
3512                        html += '<div class="opt_block no-border order_quantity_area" style="height: auto; "> ';
3513                        html += '	<div class="area_tit holder">';
3514                        html += '		<span class="option_title inline-blocked" style="margin-bottom: 7px">' + LOCALIZE.타이틀_수량() + '</span> ';
3515                        html += '	</div> ';
3516                        html += '	<div class="area_count holder">';
3517                        html += '		<div class="option_btn_wrap" style="top:0;"> ';
3518                        html += '			<div class="option_btn_tools" style="float: none;"> ';
3519                        html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.decreaseOrderCount(\'mobile\', function(){SITE_SHOP_DETAIL.updateSelectedOptions(\'' + type + '\')})"><i class="btl bt-minus" aria-hidden="true"></i><span class="sr-only">minus</span></a>';
3520                        html += '				<input type="text" title="number" value="' + order_count + '" class="form-control _order_count_mobile" onchange="SITE_SHOP_DETAIL.changeOrderCount(\'mobile\', $(this).val(), function(){SITE_SHOP_DETAIL.updateSelectedOptions(\'' + type + '\')})"> ';
3521                        html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.increaseOrderCount(\'mobile\', function(){SITE_SHOP_DETAIL.updateSelectedOptions(\'' + type + '\')})"><i class="btl bt-plus" aria-hidden="true"></i><span class="sr-only">plus</span></a>';
3522                        html += '			</div> ';
3523                        // 본품 가격은 위에서 _bookCreatorTotal 로 자체 cap 처리됨 → data-creator-row 미부착 (옵션만 비례 분배)
3524                        html += '			<div class="area_price absolute absolute_right absolute_middle"><span>' + LOCALIZE.getCurrencyFormat(section_total_price) + '</span></div>';
3525                        html += '		</div> ';
3526                        html += '	</div> ';
3527                        html += '</div> ';
3528                        break;
3529                    case 'cart':
3530                        // 장바구니
3531                        html += '<div class="opt_block no-border" style="height: auto; "> ';
3532                        html += '	<div class="col-control row"> ';
3533                        html += '		<div class="col-xs-12"><span class="option_title text-bold inline-blocked" style="margin-bottom: 7px">' + LOCALIZE.타이틀_수량() + '</span></div> ';
3534                        html += '		<div class="col-xs-12 option_btn_wrap" style="top:0;"> ';
3535                        html += '			<div class="option_btn_tools" style="float: none;"> ';
3536                        html += '				<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartDecrease(\'mobile\')"><i class="btl bt-minus" aria-hidden="true"></i><span class="sr-only">minus</span></a>';
3537                        html += '				<input type="text" title="number" value="' + order_count + '" class="form-control _order_count_mobile" onchange="SITE_SHOP_CART.changeCartOrderCount(\'mobile\', $(this).val())"> ';
3538                        html += '				<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartIncrease(\'mobile\')"><i class="btl bt-plus"></i><span class="sr-only">plus</span></a>';
3539                        html += '			</div> ';
3540                        html += '		</div> ';
3541                        html += '	</div> ';
3542                        html += '</div> ';
3543                        break;
3544                }
3545            }
3546						/* 본 상품 합계에 반영 */
3547	          total_count += order_count;
3548            total_price += section_total_price;
3549            if (!free_ship_notify_is_after_sale) {
3550                free_ship_notify_total_price += order_count * getFreeShipNotifyMainRegularPrice();
3551            }
3552            // 수량별 할인 base에 본품 raw 매출 누적
3553            _quantityDiscountBaseRaw += order_count * prod_price;
3554            // 본품 타겟 가격도 합산 cap base 에 누적 (creator 활성 시 합계에서 한 번 차감)
3555            if (creator_discount_data) {
3556                _creator_discount_base += _creator_uses_period_discount_target ? section_total_price : order_count * prod_price;
3557                _creator_discount_count += order_count;
3558            }
3559	          if(!is_main_product_membership_discount){
3560		          membership_discount_not_involved_price += section_total_price;
3561	          }
3562        }
3563
3564				/* 본 상품 옵션 계산 */
3565		    var require_class = '';
3566		    var quantity_changeable = false;	// 상품 옵션부분 디지털, 이용권 상품 및 선택옵션 구분 - 수량 변경 가능
3567	      let option_total_count = 0;
3568	      var option_total_price = 0;
3569		    var option_price = 0;
3570
3571        var prod_option_html = { "Y": "", "N": "" };
3572        var option_html = '';
3573        $.each(selected_options, function (no, data) {
3574            option_price = parseFloat(data.price || 0) * parseFloat(data.count);
3575            // 크리에이터 활성 시 옵션 단가는 타겟 기준가 그대로 — 합산 cap 후 비례 분배 후처리에서 갱신
3576							let option_period_discount_price = 0;
3577            if (data.require == true && (!creator_discount_data || _creator_uses_period_discount_target)){
3578
3579							if(typeof USE_OMS !== 'undefined' && USE_OMS === true){
3580		            option_period_discount_price = calcPeriodDiscount(data.price, 1);
3581		            if(option_period_discount_price > data.price){
3582			            option_period_discount_price = data.price;
3583		            }
3584		            option_period_discount_price *= data.count;
3585	            }else{
3586		            option_period_discount_price = calcPeriodDiscount(data.price, data.count);
3587		            if(option_period_discount_price > option_price){
3588			            option_period_discount_price = option_price;
3589		            }
3590	            }
3591							period_discount_price += option_period_discount_price;
3592							if (data.option_mix_type == 'MIX' || period_dc_type == 'percent') {
3593									show_period_discount = true;
3594									option_price = option_price - option_period_discount_price;
3595							}
3596            }
3597            option_total_count += parseInt(data.count);
3598            if (data.require) _quantityDiscountRequireOptionCount += parseInt(data.count);
3599            // 수량별 할인 base에 옵션 raw 매출 누적 (선택옵션 제외 설정 시 필수 옵션만)
3600            if (!_quantityDiscountExcludeOptional || data.require) {
3601                _quantityDiscountBaseRaw += parseFloat(data.price || 0) * parseFloat(data.count);
3602            }
3603            // 크리에이터 할인 base 에 필수 옵션 타겟 매출만 누적
3604            if (creator_discount_data && data.require) {
3605                _creator_discount_base += _creator_uses_period_discount_target ? option_price : parseFloat(data.price || 0) * parseFloat(data.count);
3606                _creator_discount_count += parseInt(data.count);
3607            }
3608            option_total_price += option_price;
3609            if (!free_ship_notify_is_after_sale) {
3610                var free_ship_notify_option_unit_price = parseFreeShipNotifyPrice(data.price);
3611                if (data.require) {
3612                    free_ship_notify_option_unit_price = calcFreeShipNotifyRequiredOptionPrice(free_ship_notify_option_unit_price, prod_price, free_ship_notify_prod_regular_price);
3613                }
3614                free_ship_notify_total_price += free_ship_notify_option_unit_price * parseFreeShipNotifyPrice(data.count);
3615            }
3616
3617            var option_data_html = [];
3618            $.each(data.options, function (no2, data2) {
3619                option_data_html.push(RemoveTag(data2.option_name + ': ' + data2.value_name));
3620            });
3621            quantity_changeable = !(prod_type !== SHOP_CONST.PROD_TYPE.NORMAL && data.require == true);
3622            require_class = (data.require ? '_selected_require_option ' : '');
3623
3624
3625            // 한 개마다 초기화
3626            option_html = '';
3627            if (type == 'prod') {
3628                // 상품 상세페이지
3629                option_html += '<div class="' + require_class + (no == 0 ? '' : ' middle ') + 'opt_block" id="prdOption' + no + '">';
3630                option_html += '	<div class="full-width opt_product_area">';
3631                option_html += '		<div class="area_tit holder">';
3632                option_html += '		 	<span class="body_font_color_80">' + option_data_html.join(' / ') + '</span>';
3633                option_html += '			<a class="text-18 absolute absolute_right absolute_middle" href="javascript:;
3633" onclick="SITE_SHOP_DETAIL.removeSelectedOption(' + no + ',\'prod\')"><i class="btl bt-times-circle"></i></a>';
3634                option_html += '		</div>';
3635                option_html += '		<div class="area_count holder">';
3636                option_html += '			<div class="option_btn_wrap">';
3637                if (quantity_changeable) {
3638                    option_html += '			<div class="option_btn_tools">';
3639                    option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.decreaseOptionCount(' + no + ',\'prod\')"> <i class="btl bt-minus" aria-hidden="true"></i> </a>';
3640                    option_html += '				<input type="text" value="' + data.count + '" class="form-control count _count" onchange="SITE_SHOP_DETAIL.changeOptionCount(' + no + ', $(this).val(),\'prod\')" />';
3641                    option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.increaseOptionCount(' + no + ',\'prod\')"> <i class="btl bt-plus"></i> </a>';
3642                    option_html += '			</div>';
3643                }
3644                option_html += '			</div>';
3645                // 크리에이터 활성 + 필수 옵션이면 타겟 가격 정보를 data attr 로 노출 (비례 분배 후처리용)
3646                var _optionCreatorRaw = _creator_uses_period_discount_target ? option_price : parseFloat(data.price || 0) * parseFloat(data.count);
3647                var _optionCreatorAttr = (creator_discount_data && data.require) ? ' data-creator-row="option" data-creator-raw="' + _optionCreatorRaw + '"' : '';
3648                if (quantity_changeable) {
3649									option_html += '			<div class="area_price absolute absolute_right absolute_middle"><span' + _optionCreatorAttr + '>' + LOCALIZE.getCurrencyFormat(option_price) + '</span></div>';
3650                } else {
3651									option_html += '<div class="tw-flex tw-justify-between"><div>' + LOCALIZE.타이틀_수량() + ' ' + LOCALIZE.설명_n개띄어쓰기없음(1) + '</div><div' + _optionCreatorAttr + '>' + LOCALIZE.getCurrencyFormat(option_price) + '</div></div>';
3652                }
3653                option_html += '		</div>';
3654                option_html += '	</div>';
3655                option_html += '</div>';
3656            } else {
3657                // 장바구니
3658                option_html += '<div class="' + require_class + (no == 0 ? '' : ' middle ') + 'opt_block" id="prdOption' + no + '">';
3659                option_html += '	<div class="full-width opt_product_area">';
3660                option_html += '		<div class="area_tit holder">';
3661                option_html += '			<span class="body_font_color_80">' + option_data_html.join(' / ') + '</span>';
3662                option_html += '			<a href="javascript:;" class="text-18 absolute absolute_right absolute_middle" onclick="SITE_SHOP_CART.changeCartItemRemove(' + no + ')"><i class="btl bt-times-circle"></i> </a>';
3663                option_html += '		</div>';
3664                option_html += '		<div class="area_count holder">';
3665                option_html += '			<div class="option_btn_wrap">';
3666                if (quantity_changeable) {
3667                    option_html += '				<div class="option_btn_tools">';
3668                    option_html += '					<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartItemDecrease(' + no + ')"> <i class="btl bt-minus" aria-hidden="true"></i> </a>';
3669                    option_html += '					<input type="text" value="' + data.count + '" class="form-control count _count" onchange="SITE_SHOP_CART.changeCartItemCount(' + no + ', $(this).val())" />';
3670                    option_html += '					<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartItemIncrease(' + no + ')"> <i class="btl bt-plus"></i> </a>';
3671                    option_html += '				</div>';
3672                }
3673                option_html += '			</div>';
3674                option_html += '			<div class="area_price absolute absolute_right absolute_middle">';
3675                // 크리에이터 활성 + 필수 옵션이면 raw 정보를 data attr 로 노출 (cart 분기에서도 동일)
3676                var _optionCreatorAttr = (creator_discount_data && data.require) ? ' data-creator-row="option" data-creator-raw="' + (parseFloat(data.price || 0) * parseFloat(data.count)) + '"' : '';
3677                option_html += '				<span' + _optionCreatorAttr + '>' + LOCALIZE.getCurrencyFormat(option_price) + '</span>';
3678                option_html += '			</div>';
3679                option_html += '		</div>';
3680                option_html += '	</div>';
3681                option_html += '</div>';
3682            }
3683
3684            // 기존처럼 옵션 구분 없이 선택 순 출력으로 사용하려면 prod_option_html 사용하는 부분 지우고 html += option_html 하면 됨
3685            prod_option_html[((data.require) ? "Y" : "N")] += option_html;
3686        });
3687
3688        html += prod_option_html["Y"];
3689        html += prod_option_html["N"];
3690
3691	     /* 본 상품 옵션 합계에 반영 */
3692	      total_count += option_total_count;
3693        total_price += option_total_price;
3694		    if(!is_main_product_membership_discount){
3695			    membership_discount_not_involved_price += option_total_price;
3696		    }
3697
3698				/* 추가상품 계산 */
3699				if(is_use_prod_additional){
3700
3701					$.each(selected_prod_additionals, function (no, data) {
3702						let prod_additional_total_count = 0;
3703						var prod_additional_total_price = 0;
3704						var prod_additional_require_option_count_by_data_idx = prod_additional_require_option_count[data.idx] || 0;
3705						if (prod_additional_require_option_count_by_data_idx == 0) {
3706
3707								prod_additional_total_count = parseInt(data.count);
3708								prod_additional_total_price = parseInt(data.count) * parseFloat(data.price);
3709								var prod_additional_creator_data = data.creator_discount_data || null;
3710								var prod_additional_creator_uses_period = prod_additional_creator_data && prod_additional_creator_data.reference_source === 'period_discount';
3711								var prod_additional_period_discount_price = 0;
3712								if (!prod_additional_creator_data || prod_additional_creator_uses_period) {
3713									prod_additional_period_discount_price = calcProdAdditionalPeriodDiscount(data.idx, data.price, data.count);
3714									if (prod_additional_period_discount_price > prod_additional_total_price) prod_additional_period_discount_price = prod_additional_total_price;
3715									// 추가상품의 경우 상품 할인 금액에 별도로 할인가를 포함하지 않고 항상 옵션가에 포함시킴
3716									prod_additional_total_price = prod_additional_total_price - prod_additional_period_discount_price;
3717								}
3718								if (prod_additional_creator_data) {
3719									var prod_additional_creator_discount_price = calcAdditionalCreatorDiscount(prod_additional_creator_data, prod_additional_total_price, data.count);
3720									if (prod_additional_creator_discount_price > prod_additional_total_price) prod_additional_creator_discount_price = prod_additional_total_price;
3721									// 추가상품 행에는 판매가/기간할인가 기준 금액을 담고, 크리에이터 할인은 하단 할인 row 에 누적한다.
3722									_prod_additional_creator_discount_price += prod_additional_creator_discount_price;
3723								}
3724								if (!free_ship_notify_is_after_sale) {
3725									free_ship_notify_total_price += parseInt(data.count) * getFreeShipNotifyProdAdditionalRegularPrice(data.idx, data.price);
3726								}
3727
3728							if(is_view_price){ // @TODO 본상품과 추가상품 가격없는 상품 분기 처리 필요
3729								// 상품 상세페이지
3730								html += '<div class="opt_block no-border order_quantity_area" style="height: auto; "> ';
3731								html += '	<div class="area_tit holder">';
3732								html += '		<div class="tw-flex tw-flex-col tw-gap-[12px]">';
3733								html += '     <span class="body_font_color_80 tw-font-bold">' + getLocalizeString('타이틀_추가상품', '', '추가 상품') + '</span>';
3734								html += '		 	<span class="body_font_color_80">' + data.name + '</span>';
3735								html += `		 	<div class="_prod_additional_option_optional goods-select-prod-additional" data-prod-idx="${data.idx}"></div>`;
3736								html += '		</div>';
3737								html += '		<a class="text-18 absolute absolute_right tw-top-0" href="javascript:;" onclick="SITE_SHOP_DETAIL.removeSelectedProdAdditional(' + data.idx + ', \'prod\')"><i class="btl bt-times-circle"></i></a> ';
3738								html += '	</div> ';
3739								html += '	<div class="area_count holder _area_count" data-no="' + no + '">';
3740								html += '		<div class="option_btn_wrap" style="top:0;"> ';
3741								if(data.prod_type === SHOP_CONST.PROD_TYPE.NORMAL){
3742									html += '			<div class="option_btn_tools" style="float: none;"> ';
3743									html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.decreaseProdAdditionalOrderCount(' + no + ', \'mobile\', function(){SITE_SHOP_DETAIL.updateSelectedOptions(\'' + type + '\')})"><i class="btl bt-minus" aria-hidden="true"></i><span class="sr-only">minus</span></a>';
3744									html += '				<input type="text" title="number" value="' + data.count + '" class="form-control _prod_additional_order_count_mobile" onchange="SITE_SHOP_DETAIL.changeProdAdditionalOrderCount(' + no + ', \'mobile\', $(this).val(), function(){SITE_SHOP_DETAIL.updateSelectedOptions(\'' + type + '\')})"> ';
3745									html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.increaseProdAdditionalOrderCount(' + no + ', \'mobile\', function(){SITE_SHOP_DETAIL.updateSelectedOptions(\'' + type + '\')})"><i class="btl bt-plus" aria-hidden="true"></i><span class="sr-only">plus</span></a>';
3746									html += '			</div> ';
3747								}
3748								html += ' </div>';
3749								if(data.prod_type === SHOP_CONST.PROD_TYPE.NORMAL){
3750									html += '			<div class="area_price absolute absolute_right absolute_middle"><span>' + LOCALIZE.getCurrencyFormat(prod_additional_total_price) + '</span></div>';
3751								}else{
3752									html += '<div class="tw-flex tw-justify-between"><div>' + LOCALIZE.타이틀_수량() + ' ' + LOCALIZE.설명_n개띄어쓰기없음(1) + '</div><div>' + LOCALIZE.getCurrencyFormat(prod_additional_total_price) + '</div></div>';
3753								}
3754								html += '	</div> ';
3755								html += '</div> ';
3756							}
3757
3758								if(!data.membership_discount || prod_additional_creator_data){
3759									membership_discount_not_involved_price += prod_additional_total_price;
3760								}
3761						}
3762
3763						/* 추가상품 합계에 반영 */
3764						total_count += prod_additional_total_count;
3765						total_price += prod_additional_total_price;
3766					});
3767
3768
3769					/* 추가상품 옵션 계산 */
3770					require_class = '';
3771					quantity_changeable = false;	// 상품 옵션부분 디지털, 이용권 상품 및 선택옵션 구분 - 수량 변경 가능
3772					option_total_count = 0;
3773					option_total_price = 0;
3774
3775					prod_option_html = { "Y": "", "N": "" };
3776					option_html = '';
3777					$.each(selected_prod_additional_options, function (no, data) {
3778						let prod_data = selected_prod_additionals.find(v=>v.idx===data.prod_idx);
3779						if(typeof prod_data === 'undefined') return true; // prod_data가 확인되지 않을 경우 continue 처리
3780							let option_price = parseFloat(data.price || 0) * parseFloat(data.count);
3781							var option_period_discount_price = 0;
3782							var prod_additional_option_creator_data = prod_data.creator_discount_data || null;
3783							var prod_additional_option_creator_uses_period = prod_additional_option_creator_data && prod_additional_option_creator_data.reference_source === 'period_discount';
3784							if (data.require && (!prod_additional_option_creator_data || prod_additional_option_creator_uses_period)) {
3785								option_period_discount_price = calcProdAdditionalPeriodDiscount(data.prod_idx, data.price, data.count);
3786								if (option_period_discount_price > option_price) {
3787									option_period_discount_price = option_price;
3788								}
3789
3790								// 추가상품 옵션의 경우 상품 할인 금액에 별도로 할인가를 포함하지 않고 항상 옵션가에 포함시킴
3791								option_price = option_price - option_period_discount_price;
3792							}
3793							if (data.require && prod_additional_option_creator_data) {
3794								var prod_additional_option_creator_discount_price = calcAdditionalCreatorDiscount(prod_additional_option_creator_data, option_price, data.count);
3795								if (prod_additional_option_creator_discount_price > option_price) prod_additional_option_creator_discount_price = option_price;
3796								// 추가상품 옵션 행에는 판매가/기간할인가 기준 금액을 담고, 크리에이터 할인은 하단 할인 row 에 누적한다.
3797								_prod_additional_creator_discount_price += prod_additional_option_creator_discount_price;
3798							}
3799							option_total_count += parseInt(data.count);
3800							option_total_price += option_price;
3801							if (!free_ship_notify_is_after_sale) {
3802								var prod_additional_regular_price = getFreeShipNotifyProdAdditionalRegularPrice(prod_data.idx, prod_data.price);
3803								var free_ship_notify_prod_additional_option_unit_price = parseFreeShipNotifyPrice(data.price);
3804								if (data.require) {
3805									free_ship_notify_prod_additional_option_unit_price = calcFreeShipNotifyRequiredOptionPrice(free_ship_notify_prod_additional_option_unit_price, prod_data.price, prod_additional_regular_price);
3806								}
3807								free_ship_notify_total_price += free_ship_notify_prod_additional_option_unit_price * parseFreeShipNotifyPrice(data.count);
3808							}
3809
3810							if(!prod_data.membership_discount || (data.require && prod_additional_option_creator_data)){
3811								membership_discount_not_involved_price += option_price;
3812							}
3813
3814						var option_data_html = [];
3815						$.each(data.options, function (no2, data2) {
3816							option_data_html.push(RemoveTag(data2.option_name + ': ' + data2.value_name));
3817						});
3818						quantity_changeable = !(prod_data.prod_type !== SHOP_CONST.PROD_TYPE.NORMAL && data.require == true);
3819						require_class = (data.require ? '_selected_require_option ' : '');
3820
3821						// 한 개마다 초기화
3822						option_html = '';
3823						// 상품 상세페이지
3824						option_html += '<div class="' + require_class + (no == 0 ? '' : ' middle ') + 'opt_block" id="prdAdditionalOption' + no + '">';
3825						option_html += '	<div class="full-width opt_product_area">';
3826						option_html += '		<div class="area_tit holder">';
3827						option_html += '		 	<div class="tw-flex tw-flex-col tw-gap-[12px]">';
3828						option_html += '        <span class="body_font_color_80 tw-font-bold">' + getLocalizeString('타이틀_추가상품', '', '추가 상품') + '</span>';
3829						option_html += '		 	  <span class="body_font_color_80">' + prod_data.name + ' ' + option_data_html.join(' / ') + '</span>';
3830						if (data.require){
3831							option_html += `		 		<div class="_prod_additional_option_optional goods-select-prod-additional" data-prod-idx="${data.prod_idx}"></div>`;
3832						}
3833						option_html += '		 	</div>';
3834						option_html += '			<a class="text-18 absolute absolute_right tw-top-0" href="javascript:;" onclick="SITE_SHOP_DETAIL.removeSelectedProdAdditionalOption(' + no + ',\'prod\')"><i class="btl bt-times-circle"></i></a>';
3835						option_html += '		</div>';
3836						option_html += '		<div class="area_count holder">';
3837						option_html += '			<div class="option_btn_wrap">';
3838						if (quantity_changeable) {
3839							option_html += '			<div class="option_btn_tools">';
3840							option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.decreaseProdAdditionalOptionCount(' + no + ')"> <i class="btl bt-minus" aria-hidden="true"></i> </a>';
3841							option_html += '				<input type="text" value="' + data.count + '" class="form-control count _count" onchange="SITE_SHOP_DETAIL.changeProdAdditionalOptionCount(' + no + ', $(this).val())" />';
3842							option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.increaseProdAdditionalOptionCount(' + no + ')"> <i class="btl bt-plus"></i> </a>';
3843							option_html += '			</div>';
3844						}
3845						option_html += '			</div>';
3846						if (quantity_changeable) {
3847							option_html += '			<div class="area_price absolute absolute_right absolute_middle"><span>' + LOCALIZE.getCurrencyFormat(option_price) + '</span></div>';
3848						} else {
3849							option_html += '<div class="tw-flex tw-justify-between"><div>' + LOCALIZE.타이틀_수량() + ' ' + LOCALIZE.설명_n개띄어쓰기없음(1) + '</div><div>' + LOCALIZE.getCurrencyFormat(option_price) + '</div></div>';
3850						}
3851						option_html += '		</div>';
3852						option_html += '	</div>';
3853						option_html += '</div>';
3854
3855						// 기존처럼 옵션 구분 없이 선택 순 출력으로 사용하려면 prod_option_html 사용하는 부분 지우고 html += option_html 하면 됨
3856						prod_option_html[((data.require) ? "Y" : "N")] += option_html;
3857					});
3858
3859					html += prod_option_html["Y"];
3860					html += prod_option_html["N"];
3861
3862					total_count += option_total_count;
3863					total_price += option_total_price;
3864				}
3865
3866				/* 전체 내역 표시 */
3867
3868        // 선택된 옵션이 없으면 출력하지 않음
3869        if (total_count > 0) {
3870            // 하단에 결제 예상 총 금액 표기
3871            switch (type) {
3872                case 'prod':
3873                    // 상품 상세페이지
3874                    if (!is_view_price) break;
3875
3876                    const is_mobile_view = $(window).width() < 768;
3877                    const left_text_class = is_mobile_view ? '' : 'text-left';
3878                    var total_price_class = is_mobile_view ? 'non_member ' : '';
3879                    const discount_item_style = is_mobile_view ? 'padding-bottom: 4px;' : 'padding: 0 0 16px 0';
3880
3881                    // 할인 항목 HTML 생성 헬퍼
3882                    const discountRowHtml = (label, price, hide_row = false) => {
3883                        if (price <= 0) return '';
3884                        total_price -= price;
3885                        // 행을 숨겨도 결제 예상 금액의 할인 차감은 유지한다.
3886                        if (hide_row) return '';
3887                        return `<p class="text-right"><span class="body_font_color_70 ${left_text_class}" style="${discount_item_style}">${label}</span><span class="period_discount_price" style="${discount_item_style}">${LOCALIZE.getCurrencyFormat(price)}</span></p>`;
3888                    };
3889
3890                    // 수량별 할인 계산 (정기구독 시 미적용: IMIO-7132)
3891                    const _isRegularlyProd = $prod_detail.hasClass('detail-regularly');
3892                    const _quantityDiscountCount = _quantityDiscountExcludeOptional ? (order_count + _quantityDiscountRequireOptionCount) : total_count;
3893                    const _quantityDiscountBase = _quantityDiscountBaseRaw - period_discount_price;
3894                    const quantity_discount_price = _isRegularlyProd ? 0 : _calcQuantityDiscount(_quantityDiscountBase, _quantityDiscountCount);
3895
3896                    // 등급 할인 계산 (기간할인 + 수량별 할인 차감 후 금액 기준)
3897                    let membership_discount_target_price = total_price;
3898                    if (membership_discount_not_involved_price > 0) {
3899                        membership_discount_target_price -= membership_discount_not_involved_price;
3900                    }
3901                    if (period_discount_price > 0 && show_period_discount === false) {
3902                        membership_discount_target_price -= period_discount_price;
3903                    }
3904                    membership_discount_target_price -= quantity_discount_price;
3905                    const membership_discount_price = calcMembershipDiscount(membership_discount_target_price);
3906
3907                    // 크리에이터 전용 혜택가 — 본품/옵션 단가는 정상가로 누적되어 있음.
3908                    // 합산 raw(_creator_discount_base) 에 정책 M(합산 cap) 정확 적용 후 합계에서 차감.
3909                    // 본품/옵션 행 표시는 DOM insert 후 _applyCreatorDiscountDistribution() 후처리에서 비례 분배 갱신.
3910                    let creator_discount_price = 0;
3911                    let creator_discount_raw_price = 0;
3912                    let is_creator_discount_max_reached_now = false;
3913                    if (creator_discount_data) {
3914                        creator_discount_raw_price = calcCreatorDiscountRaw(_creator_discount_base, _creator_discount_count);
3915                        creator_discount_price = calcCreatorDiscount(_creator_discount_base, _creator_discount_count);
3916                        if (creator_discount_price > total_price) creator_discount_price = total_price;
3917                        const creator_discount_max_price = parseInt(creator_discount_data.max_price, 10) || 0;
3918                        is_creator_discount_max_reached_now = creator_discount_max_price > 0 && creator_discount_raw_price >= creator_discount_max_price;
3919                    }
3920                    creator_discount_price += _prod_additional_creator_discount_price;
3921                    if (creator_discount_price > total_price) creator_discount_price = total_price;
3922
3923                    if (is_mobile_view) {
3924                        if (is_creator_discount_max_reached_now && !creator_discount_max_reached) {
3925                            showCreatorDiscountMaxToast();
3926                        } else if (!is_creator_discount_max_reached_now) {
3927                            hideCreatorDiscountMaxToast();
3928                        }
3929                        creator_discount_max_reached = is_creator_discount_max_reached_now;
3930                    } else {
3931                        creator_discount_max_reached = is_creator_discount_max_reached_now;
3932                        hideCreatorDiscountMaxToast();
3933                    }
3934
3935                    // 표기 순서: 상품 할인 → 수량별 할인 → 등급 할인 → 크리에이터 할인
3936                    const period_discount_html = (period_discount_price > 0 && show_period_discount === false)
3937                        ? discountRowHtml(LOCALIZE.설명_상품할인금액(), period_discount_price) : '';
3938                    const quantity_discount_html = discountRowHtml('수량별 할인', quantity_discount_price);
3939                    const membership_discount_html = discountRowHtml(LOCALIZE.설명_회원등급할인(), membership_discount_price);
3940                    const hide_creator_discount_row = creator_discount_data
3941                        && creator_discount_data.hide_calculator_row === true;
3942                    const creator_discount_html = discountRowHtml(
3943                        '크리에이터 할인',
3944                        creator_discount_price,
3945                        hide_creator_discount_row
3946                    );
3947
3948                    const discount_style = is_mobile_view ? 'padding-bottom: 4px;' : 'padding: 16px 0';
3949                    const has_visible_creator_discount = creator_discount_price > 0 && !hide_creator_discount_row;
3950                    const hasDiscount = period_discount_price > 0
3951                        || membership_discount_price > 0
3952                        || quantity_discount_price > 0
3953                        || has_visible_creator_discount;
3954
3955                    total_price = Math.max(total_price, 0);
3956                    total_price_html += `<div class="opt_block total bottom">`;
3957                    total_price_html += period_discount_html + quantity_discount_html + membership_discount_html + creator_discount_html;
3958                    if (!is_mobile_view && hasDiscount) {
3959                        total_price_html += `<p style="border-bottom: 1px solid rgba(0, 0, 0, 0.1); position: absolute; width: 100%; display: block;"></p>`;
3960                    }
3961                    total_price_html += `<p class="no-margin text-right"><span class="body_font_color_70 ${left_text_class}" style="${discount_style}">${LOCALIZE.설명_결제예상금액임시(total_count)}</span><span class="total_price ${total_price_class}" style="${discount_style}">${LOCALIZE.getCurrencyFormat(total_price)}</span></p></div>`;
3962
3963                    const $footer_price_wrap = $prod_detail.find('._footer_price_wrap');
3964                    if ($footer_price_wrap.length) {
3965                        $footer_price_wrap.find('._pay_label').text(LOCALIZE.설명_결제예상금액임시(total_count)).show();
3966                        $footer_price_wrap.find('._pay_number').text(LOCALIZE.getCurrencyFormat(total_price)).show();
3967                        $footer_price_wrap.find('._pay_org_label').hide();
3968                        $footer_price_wrap.find('._pay_org_number').hide();
3969                    }
3970                    break;
3971                case 'cart':
3972                    // 장바구니
3973                    if (period_discount_price > 0 && show_period_discount == false) {
3974                        // 상품 할인금액
3975                        total_price_html += '<div class="opt_block total bottom">';
3976                        total_price_html += '	<div class="col-xs-4 vertical-middle"> <span class="body_font_color_70 text-13">' + LOCALIZE.설명_상품할인금액() + '</span> </div>';
3977                        total_price_html += '	<div class="col-xs-8 vertical-middle">';
3978                        total_price_html += '		<p class="align_r body_font_color_50 text-13">' + total_price_localize_text + ' <span class="text-brand vertical-middle">' + LOCALIZE.getCurrencyFormat(period_discount_price) + '</span></p>';
3979                        total_price_html += '	</div>';
3980                        total_price_html += '</div>';
3981                        total_price = (total_price - period_discount_price);
3982                    }
3983
3984                    total_price_html += '<div class="opt_block total bottom">';
3985                    total_price_html += '	<div class="col-xs-4 vertical-middle"> <span class="body_font_color_70 text-13">' + LOCALIZE.타이틀_총수량(total_count) + '</span> </div>';
3986                    total_price_html += '	<div class="col-xs-8 vertical-middle">';
3987                    total_price_html += '		<p class="align_r body_font_color_50 text-13">' + total_price_localize_text + ' <span class="text-brand vertical-middle">' + LOCALIZE.getCurrencyFormat(total_price) + '</span></p>';
3988                    total_price_html += '	</div>';
3989                    total_price_html += '</div>';
3990                    break;
3991            }
3992        } else {
3993            if (type === 'prod') {
3994                creator_discount_max_reached = false;
3995                hideCreatorDiscountMaxToast();
3996                if ($prod_detail.find('._footer_price_wrap').length > 0) {
3997                    // 선택된 옵션이 없을 경우 총 상품금액이 아닌 판매가 출력
3998                    var $footer_price_wrap = $prod_detail.find('._footer_price_wrap');
3999                    $footer_price_wrap.find('._pay_org_label').show();
4000                    $footer_price_wrap.find('._pay_org_number').show();
4001                    $footer_price_wrap.find('._pay_label').hide();
4002                    $footer_price_wrap.find('._pay_number').hide();
4003                }
4004            }
4005        }
4006
4007				// 무료배송 안내 SDK 에 무료배송 기준 합계 / 필수옵션 완료 상태 전달
4008				if (free_ship_notify_is_after_sale) {
4009					free_ship_notify_total_price = total_price;
4010				}
4011				notifyFreeShipNotifySdk(Math.max(free_ship_notify_total_price, 0));
4012				//
4013
4014        html += total_price_html;
4015        $selected_options.html(html);
4016
4017        // 크리에이터 혜택가 — 본품/옵션 행은 정상가 표시. 할인은 합계(total_price) 에서만 차감 완료.
4018        // (이전 비례 분배 후처리는 폐기. _applyCreatorDiscountDistribution 미호출)
4019
4020        // 수량별 할인 배너: 옵션 선택 시 표시, 선택 해제 시 숨김 (정기구독 시 미노출: IMIO-7132)
4021        if (type === 'prod') {
4022            var _isRegularly = $prod_detail.hasClass('detail-regularly');
4023            $prod_detail.find('._quantity_discount_banner_wrap').toggle(total_count > 0 && !_isRegularly);
4024
4025            if (_hasFreeShipNotifyInlineMagnet
4026                && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
4027                && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.syncInlineMagnetItemCountsFromAdditionals === 'function') {
4028                var inlineMagnetSyncList = selected_prod_additionals.map(function (additional) {
4029                    var requireOptionCount = prod_additional_require_option_count[additional.idx] || 0;
4030                    var optionLines = selected_prod_additional_options.filter(function (o) {
4031                        return o.prod_idx === additional.idx;
4032                    });
4033                    var optionLinesCount = optionLines.reduce(function (sum, o) { return sum + (o.count || 0); }, 0);
4034                    var totalCount = requireOptionCount > 0
4035                        ? optionLinesCount
4036                        : (optionLines.length > 0 ? optionLinesCount : (additional.count || 0));
4037                    return { idx: additional.idx, count: totalCount };
4038                });
4039                window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.syncInlineMagnetItemCountsFromAdditionals(inlineMagnetSyncList);
4040            }
4041        }
4042
4043				loadProdAdditionalOptionalList();
4044    };
4045
4046    /**
4047     * 옵션 글자 제한 체크 (네이버페이 주문형이 활성화되어 있는 상품의 경우 글자 제한)
4048     * @param target
4049     * @param limit
4050     */
4051    const checkOptionLength = function (target, limit) {
4052        const input_option_length_tooltip = target.parentNode.querySelector('._input_option_length_tooltip');
4053        if (input_option_length_tooltip) {
4054            if (target.value.length >= limit) {
4055                input_option_length_tooltip.classList.remove('tw-hidden');
4056            } else {
4057                input_option_length_tooltip.classList.add('tw-hidden');
4058            }
4059        }
4060    };
4061
4062    var membership_discount_data = {};
4063		var is_main_product_membership_discount = false;
4064    var setMembershipSaleData = function (data, _is_main_product_membership_discount) {
4065        if (typeof data != 'undefined') {
4066            membership_discount_data = data;
4067	          is_main_product_membership_discount = _is_main_product_membership_discount;
4068            refreshFreeShipNotifyInitialTotal();
4069        }
4070    };
4071
4072    var calcMembershipDiscount = function (price) {
4073        var sale_price = 0;
4074
4075        // 크리에이터 전용 혜택가 활성 시 회원그룹(등급) 할인 무시 (정책 #1)
4076        if (creator_discount_data) {
4077            return sale_price;
4078        }
4079
4080        if ($.isEmptyObject(membership_discount_data)) {
4081            return sale_price;
4082        }
4083
4084        if (price >= membership_discount_data['minimum']) {
4085            sale_price = membership_discount_data['amount'];
4086            if (membership_discount_data['type'] == 'percent') {
4087                sale_price = (price * membership_discount_data['amount']) / 100;
4088                if (sale_price > membership_discount_data['maximum']) {
4089                    sale_price = membership_discount_data['maximum'];
4090                }
4091            }
4092        }
4093
4094        if (typeof USE_OMS !== 'undefined' && USE_OMS === true) {
4095            const decimalCount = LOCALIZE.getCurrencyDecimalCount();
4096            if (decimalCount === 0) {
4097                return Math.ceil(sale_price > price ? price : sale_price);
4098            }
4099
4100            const pow = Math.pow(10, decimalCount);
4101            return Math.ceil((sale_price > price ? price : sale_price) * pow) / pow;
4102        } else {
4103            return sale_price > price ? price : sale_price;
4104        }
4105    };
4106
4107    var period_discount_data = {};
4108    var setPeriodDiscountData = function (flag, data, group_list) {
4109        if (typeof data != 'undefined' && data != null) {
4110            period_discount_data = data;
4111            period_discount_data['switch'] = flag;
4112            period_discount_data['group_list'] = group_list;
4113            refreshFreeShipNotifyInitialTotal();
4114        }
4115    };
4116
4117    var period_dc_type;
4118    var calcPeriodDiscount = function (price, count) {
4119        var sale_price = 0;
4120        var dc_price;
4121        if ($.isEmptyObject(period_discount_data)) {
4122            return sale_price;
4123        }
4124        if (period_discount_data['switch'] === false || period_discount_data['switch'] == 'false') return sale_price;
4125        var group_list = period_discount_data['group_list'];
4126
4127        for (var target in period_discount_data['target']) {
4128            var discount_data = period_discount_data['target'][target];
4129
4130            // dc_amount 검증 및 정규화
4131            var dcAmount;
4132            if (typeof discount_data['dc_amount'] === 'string') {
4133                var amountStr = discount_data['dc_amount'];
4134                // EUR 통화는 점을 천단위 구분자로, 쉼표를 소수점으로 사용
4135                if (typeof LOCALIZE !== 'undefined' && LOCALIZE.getCurrency() === 'EUR') {
4136                    amountStr = amountStr.replace(/\./g, '').replace(',', '.');
4137                } else {
4138                    amountStr = amountStr.replace(/,/g, '');
4139                }
4140                dcAmount = parseFloat(amountStr);
4141            } else if (typeof discount_data['dc_amount'] === 'number') {
4142                dcAmount = discount_data['dc_amount'];
4143            } else {
4144                continue; // 유효하지 않은 타입인 경우 다음 항목으로
4145            }
4146
4147            if (Number.isNaN(dcAmount) || dcAmount < 0) {
4148                continue; // NaN이거나 음수인 경우 다음 항목으로
4149            }
4150
4151            // 정규화된 값을 다시 할당
4152            discount_data['dc_amount'] = dcAmount;
4153
4154            if (IS_GUEST) {
4155                if (discount_data['group_type'] != 'guest') continue;
4156            } else {
4157                if (discount_data['group_type'] == 'group' && group_list.indexOf(discount_data['group_code']) == -1) continue;
4158            }
4159            if (discount_data['dc_type'] == 'percent') {
4160                if (discount_data['dc_amount'] > 100) discount_data['dc_amount'] = 100;
4161
4162                if (typeof USE_OMS !== 'undefined' && USE_OMS === true){
4163                  const decimalCount = LOCALIZE.getCurrencyDecimalCount();
4164	                if (decimalCount === 0) {
4165                      dc_price = Math.ceil(( price * discount_data['dc_amount'] ) / 100 * count );
4166                  } else {
4167										const pow = Math.pow(10, decimalCount);
4168                    dc_price = Math.ceil(( ( price * discount_data['dc_amount'] ) / 100 * count ) * pow) / pow;
4169	                }
4170                } else {
4171	                dc_price = (price * discount_data['dc_amount']) / 100 * count;
4172                }
4173            } else {
4174                dc_price = discount_data['dc_amount'] * count;
4175            }
4176            if (sale_price < dc_price) {
4177                sale_price = dc_price;
4178                period_dc_type = discount_data['dc_type'];
4179            }
4180        }
4181
4182        return sale_price;
4183    };
4184
4185	var prod_additional_period_discount_data = {};
4186	var setProdAdditionalPeriodDiscountData = function (prod_idx, flag, data, group_list) {
4187		if (typeof data != 'undefined' && data != null) {
4188			prod_additional_period_discount_data[prod_idx] = data;
4189			prod_additional_period_discount_data[prod_idx]['switch'] = flag;
4190			prod_additional_period_discount_data[prod_idx]['group_list'] = group_list;
4191		}
4192	};
4193
4194	var prod_additional_period_dc_type = {};
4195	var calcProdAdditionalPeriodDiscount = function (prod_idx, price, count) {
4196		var sale_price = 0;
4197		var dc_price;
4198		if ($.isEmptyObject(prod_additional_period_discount_data[prod_idx])) {
4199			return sale_price;
4200		}
4201		if (prod_additional_period_discount_data[prod_idx]['switch'] === false || prod_additional_period_discount_data[prod_idx]['switch'] == 'false') return sale_price;
4202		var group_list = prod_additional_period_discount_data[prod_idx]['group_list'];
4203
4204		for (var target in prod_additional_period_discount_data[prod_idx]['target']) {
4205			var discount_data = prod_additional_period_discount_data[prod_idx]['target'][target];
4206
4207            // dc_amount 검증 및 정규화
4208            var dcAmount;
4209            if (typeof discount_data['dc_amount'] === 'string') {
4210                var amountStr = discount_data['dc_amount'];
4211                // EUR 통화는 점을 천단위 구분자로, 쉼표를 소수점으로 사용
4212                if (typeof LOCALIZE !== 'undefined' && LOCALIZE.getCurrency() === 'EUR') {
4213                    amountStr = amountStr.replace(/\./g, '').replace(',', '.');
4214                } else {
4215                    amountStr = amountStr.replace(/,/g, '');
4216                }
4217                dcAmount = parseFloat(amountStr);
4218            } else if (typeof discount_data['dc_amount'] === 'number') {
4219                dcAmount = discount_data['dc_amount'];
4220            } else {
4221                continue; // 유효하지 않은 타입인 경우 다음 항목으로
4222            }
4223
4224            if (Number.isNaN(dcAmount) || dcAmount < 0) {
4225                continue; // NaN이거나 음수인 경우 다음 항목으로
4226            }
4227
4228            // 정규화된 값을 다시 할당
4229            discount_data['dc_amount'] = dcAmount;
4230
4231			if (IS_GUEST) {
4232				if (discount_data['group_type'] != 'guest') continue;
4233			} else {
4234				if (discount_data['group_type'] == 'group' && group_list.indexOf(discount_data['group_code']) == -1) continue;
4235			}
4236			if (discount_data['dc_type'] == 'percent') {
4237				if (discount_data['dc_amount'] > 100) discount_data['dc_amount'] = 100;
4238				dc_price = (price * discount_data['dc_amount']) / 100 * count;
4239			} else {
4240				dc_price = discount_data['dc_amount'] * count;
4241			}
4242			if (sale_price < dc_price) {
4243				sale_price = dc_price;
4244				prod_additional_period_dc_type[prod_idx] = discount_data['dc_type'];
4245			}
4246		}
4247
4248		return sale_price;
4249	};
4250
4251    /**
4252     * 장바구니에 추가
4253     */
4254    var addCart = async function (callback, options = {}) {
4255        // 배송 국가 선택 유효성 검사
4256        if ($('._deliv_country_selector').length > 0 && (!$deliv_country || $deliv_country === 'none')) {
4257            $('._deliv_country_selector').addClass('warning_select');
4258            alert(typeof LOCALIZE.설명_배송받을국가를선택해주세요 === 'function' ? LOCALIZE.설명_배송받을국가를선택해주세요() : '배송받을 국가를 선택해주세요.');
4259            return;
4260        }
4261
4262        const skipShippingValidation = options.skipShippingValidation || false;
4263        if (!skipShippingValidation) {
4264            const verified = await SHIPPING_SERVICE.verify();
4265            if (!verified) {
4266                return;
4267            }
4268        }
4269
4270        cart_type = $('input[name=add_cart_type]:checked').val();
4271        $.ajax({
4272            type: 'POST',
4273            data: {
4274                'prodIdx': current_prod_idx,
4275                'options': selected_options,
4276                'orderCount': order_count,
4277                'deliv_type': $deliv_type,
4278                'deliv_pay_type': $deliv_pay_type,
4279                'deliv_country': $deliv_country,
4280                'cart_type': cart_type,
4281                'prod_additional_list': selected_prod_additionals,
4282                'prod_additional_option_list': selected_prod_additional_options,
4283                'shipping_template_code': selected_shipping_template_code
4284            },
4285            url: ('/shop/add_cart.cm'),
4286            dataType: 'json',
4287            success: function (result) {
4288                if (result.msg == 'SUCCESS') {
4289                    if (typeof NP_LOG != 'undefined') NP_LOG.AddToCart();
4290                    if (typeof NP_NDA != 'undefined') NP_NDA.AddToCart(result.prod_code);
4291                    if (typeof CRITEO != 'undefined') CRITEO.AddToCart(result.prod_id, result.total_price);
4292                    if (typeof FB_PIXEL != 'undefined') FB_PIXEL.AddToCart(result.prod_id, result.prod_name, result.cart_price, result.currency, result.prod_count, result.total_price, result.fb_event_id, result.fb_external_id);
4293                    if (typeof ACE_COUNTER != 'undefined') ACE_COUNTER.AddToCart(result.prod_id, result.prod_name, result.prod_count);
4294                    if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddToCart(result.prod_id, result.prod_count, result.total_price, result.currency);
4295                    if (typeof ACE_COUNTER_PARTNER != 'undefined') ACE_COUNTER_PARTNER.AddToCart(result.prod_id, result.prod_count, result.check_quantity);
4296                    if (typeof AW_PRODUCT != 'undefined') AW_PRODUCT(result.prod_count);
4297                    if (typeof AM_PRODUCT != 'undefined') AM_PRODUCT(result.prod_count);
4298                    if (typeof TIKTOK_PIXEL != 'undefined') {
4299                        TIKTOK_PIXEL.track('AddToCart', {
4300                            contents: [
4301                                {
4302                                    content_id: result.prod_id,
4303                                    content_name: result.prod_name,
4304                                    quantity: result.prod_count,
4305                                    price: result.cart_price
4306                                },
4307                            ],
4308                            event_id: result.tiktok_event_id,
4309                            content_type: 'product',
4310                            value: result.total_price,
4311                            currency: result.currency,
4312                        });
4313                    }
4314                    if (typeof CRM_ONSITE != 'undefined') {
4315		                    CRM_ONSITE.track({
4316			                    name: 'ADD_TO_CART',
4317			                    properties: {
4318				                    productCode: result.prod_code,
4319				                    categoryCodes: result.category_codes || []
4320			                    }
4321		                    });
4322                    }
4323                    // BO(마케팅 > 고급 전환추적)에서 설정한 커스텀 추적 코드 주입.
4324                    // append 시 인라인 스크립트가 동기 실행되므로, 잘못된 코드가 저장된 경우
4325                    // 예외가 밖으로 전파되어 뱃지 갱신·담기 완료 알림 등 후속 처리가 막히지 않도록 영역별로 격리한다.
4326                    if (result.advanced_trace_data != null) {
4327                        var appendAdvancedTraceCode = function (selector, code) {
4328                            if (code == '') return;
4329                            try {
4330                                $(selector).append(code);
4331                            } catch (e) {
4332                                if (window.console && window.console.error) {
4333                                    window.console.error('[ADVANCED_TRACE] add_cart 추적 코드 실행 오류 (' + selector + ')', e);
4334                                }
4335                            }
4336                        };
4337                        appendAdvancedTraceCode('head', result.advanced_trace_data.header);
4338                        appendAdvancedTraceCode('body', result.advanced_trace_data.body);
4339                        appendAdvancedTraceCode('footer', result.advanced_trace_data.footer);
4340                    }
4341
4342                    // 같은 상품이 장바구니에 존재하지 않을 경우 장바구니 카운트 증가
4343                    if (!result.prod_found) {
4344                        var $shop_cart_badge_cnt_wrap = $('._shop_cart_badge_cnt_wrap');
4345                        var org_cnt = 0;
4346                        if ($shop_cart_badge_cnt_wrap.eq(0).text() != '') org_cnt = parseInt($shop_cart_badge_cnt_wrap.eq(0).text());
4347                        $shop_cart_badge_cnt_wrap.text(org_cnt + 1);
4348
4349                        // 카운트가 증가할때 기존에 카운트가 0이어서 ui가 안보이던 것을 다시 보이게 활성화
4350                        $shop_cart_badge_cnt_wrap.removeAttr('disabled');
4351                    }
4352
4353                    // 무료배송 안내 모달의 게이지 누적 베이스라인 - 메인 상품 담기 시점의 가격으로 시작.
4354                    // 이후 SITE_SHOP_CART.addCart로 추가상품을 담을 때마다 이 누적값에 더해 imweb:freeShipNotify:cartTotal:update 이벤트를 dispatch한다.
4355                    // 메인 상품 담기 직후 CART_POPUP 모달이 뜨는 경로에서 dispatch 가 누락되면
4356                    // SDK 캐시/mfe store 가 갱신되지 않아 게이지가 0% 로 보이므로 베이스라인 설정과 함께 dispatch.
4357                    var freeShipNotifyCartPrice = (typeof result.free_shipping_price === 'number' ? result.free_shipping_price : (typeof result.total_price === 'number' ? result.total_price : 0));
4358                    var freeShipNotifySdk = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK;
4359                    if (freeShipNotifySdk && typeof freeShipNotifySdk.getCachedSelectedOptionsTotal === 'function') {
4360                        var selectedOptionsTotal = freeShipNotifySdk.getCachedSelectedOptionsTotal();
4361                        if (typeof selectedOptionsTotal === 'number' && selectedOptionsTotal >= 0) {
4362                            freeShipNotifyCartPrice = selectedOptionsTotal;
4363                        }
4364                    }
4365                    window.__imwebFreeShipCartCumulative = freeShipNotifyCartPrice;
4366                    window.__imwebFreeShipCartAdditionalsSum = 0;
4367                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartTotal:update', {
4368                        detail: { totalPrice: window.__imwebFreeShipCartCumulative }
4369                    }));
4370
4371                    if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
4372                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.resetInlineMagnetItemCounts === 'function') {
4373                        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.resetInlineMagnetItemCounts();
4374                    }
4375
4376                    window.__imwebAddToCartProdCodeList = [];
4377                    window.__imwebAddToCartHasFreeShipUpsell = false;
4378                    if (typeof result.prod_code === 'string' && result.prod_code !== '') {
4379                        window.__imwebAddToCartProdCodeList.push(result.prod_code);
4380                    }
4381                    if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
4382                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.getInlineMagnetCodeByIdx === 'function'
4383                        && Array.isArray(selected_prod_additionals)) {
4384                        var codeByIdx = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.getInlineMagnetCodeByIdx();
4385                        selected_prod_additionals.forEach(function (additional) {
4386                            var code = codeByIdx[additional.idx];
4387                            if (typeof code === 'string' && code !== '') {
4388                                window.__imwebAddToCartProdCodeList.push(code);
4389                                window.__imwebAddToCartHasFreeShipUpsell = true;
4390                            }
4391                        });
4392                    }
4393
4394                    callback();
4395                } else {
4396                    // 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4397                    if (result.code === 7) {
4398                        redirectToLoginPage();
4399                    } else {
4400                        alert(result.msg);
4401                        if (result.code === 3) {
4402                            location.reload();
4403                        }
4404                    }
4405                }
4406            }
4407        });
4408    };
4409
4410    /**
4411     * 바로 주문하기  추가, C3 분기를 위해 is_c3 파라미터 추가
4412     */
4413    var _addOrder = function (type, backurl, params) {
4414			let is_oms = params && params.is_oms ? params.is_oms : false;
4415			let callback = params && params.callback ? params.callback : function () {};
4416			let is_gift_buy = params && params.is_gift_buy ? params.is_gift_buy : false;
4417			let selected_freebies = params && params.selected_freebies ? params.selected_freebies : [];
4418
4419
4420			add_order_progress_check = true;
4421			cart_type = $('input[name=add_cart_type]:checked').val();
4422
4423			var _data = {
4424				'backurl': backurl,
4425				'prodIdx': current_prod_idx,
4426				'optDataList': selected_options,
4427				'orderCount': order_count,
4428				'type': type,
4429				'deliv_type': $deliv_type,
4430				'deliv_pay_type': $deliv_pay_type,
4431				'deliv_country': $deliv_country,
4432				'order_type': cart_type,
4433				'is_gift_buy': is_gift_buy,
4434				'prod_additional_list': selected_prod_additionals,
4435				'prod_additional_option_list': selected_prod_additional_options,
4436				'freebie_list': cart_type !== 'regularly' ? selected_freebies : undefined,
4437				'shipping_template_code': selected_shipping_template_code
4438			};
4439
4440			if(is_oms){
4441				if (localStorage.getItem("selectedDeliveryCountry") === "undefined") {
4442					localStorage.removeItem("selectedDeliveryCountry");
4443				}
4444				if (typeof $deliv_country !== 'undefined') {
4445					localStorage.setItem("selectedDeliveryCountry", $deliv_country);
4446				}
4447				_data.infoUrl = location.origin;
4448			}
4449
4450			_data.ace_pid = window._AcePID || window._AceMID;
4451
4452			const [buyBtn, buyBtnMobile, buyBtnDialog, buyBtnMobileDialog, buyBtnRegularly, buyBtnRegularlyMobile] = [$('._btn_buy'), $('._btn_mobile_buy'), $('._btn_dialog_buy'), $('._btn_mobile_dialog_buy'), $('._btn_regularly'), $('._buy_regularly')];
4453			const [buyBtnText, buyBtnMobileText, buyBtnDialogText, buyBtnMobileDialogText, buyBtnRegularlyText, buyBtnRegularlyMobileText] = [buyBtn.first().text(), buyBtnMobile.first().text(), buyBtnDialog.first().text(), buyBtnMobileDialog.first().text(), buyBtnRegularly.first().text(), buyBtnRegularlyMobile.first().text()];
4454
4455			const setSpinner = function ($buttons) {
4456				const spinner = '<div class="loading-spinner-container"><div class="loading-spinner"></div></div>';
4457
4458				if (Array.isArray($buttons)) {
4459					for (const $button of $buttons) {
4460						if ($button.length > 0) {
4461							$button.html(spinner);
4462						}
4463					}
4464				} else {
4465					if ($buttons.length > 0) {
4466						$buttons.html(spinner);
4467					}
4468				}
4469			}
4470
4471			const unsetSpinner = function ($btn, text) {
4472				if ($btn.length > 0) {
4473					$btn.html(text);
4474				}
4475			};
4476
4477			const landingUrlHeaders = {};
4478			const _landingUrlValue = getBsLandingUrlHeaderValue();
4479			if (_landingUrlValue) {
4480				landingUrlHeaders['imweb-landing-url'] = _landingUrlValue;
4481			}
4482
4483			$.ajax({
4484				type: 'POST',
4485				headers: landingUrlHeaders,
4486				data: _data,
4487				// OMS path 분기
4488				url: is_oms ? '/shop/oms/OMS_add_order.cm' : '/shop/add_order.cm',
4489				dataType: 'json',
4490				cache: false,
4491				beforeSend: function () {
4492					// TODO 선물하기 버튼에 스피너 적용
4493					// 간편결제 버튼은 PG사 정책 우려로 인해 스피너 적용하지 않음
4494					if (is_gift_buy || type === 'npay' || type === 'talkpay') {
4495						add_order_progress_check = false;
4496
4497						return;
4498					}
4499
4500					// 배송 국가 선택 유효성 검사
4501					if ($('._deliv_country_selector').length > 0 && (!$deliv_country || $deliv_country === 'none')) {
4502						$('._deliv_country_selector').addClass('warning_select');
4503						alert(typeof LOCALIZE.설명_배송받을국가를선택해주세요 === 'function' ? LOCALIZE.설명_배송받을국가를선택해주세요() : '배송받을 국가를 선택해주세요.');
4504
4505						add_order_progress_check = false;
4506
4507						return false;
4508					}
4509
4510					// 필수옵션 선택 유효성 검사 실행
4511					if (!checkRequireOption()) {
4512						alert(LOCALIZE.설명_필수옵션이모두선택되어있지않습니다());
4513
4514						add_order_progress_check = false;
4515
4516						return false;
4517					}
4518
4519					// 구매하기, 모바일 구매하기, 다이얼로그 구매하기, 모바일 다이얼로그 구매하기, 정기구독 신청, 모바일 정기구독 신청 버튼에 로딩 스피너 추가
4520					setSpinner([buyBtn, buyBtnMobile, buyBtnDialog, buyBtnMobileDialog, buyBtnRegularly, buyBtnRegularlyMobile]);
4521				},
4522				error: function (result) {
4523					// 구매하기 PC 버튼 스피너 초기화
4524					unsetSpinner(buyBtn, buyBtnText);
4525					// 구매하기 모바일 버튼 스피너 초기화
4526					unsetSpinner(buyBtnMobile, buyBtnMobileText);
4527					// 구매하기 다이얼로그 버튼 스피너 초기화
4528					unsetSpinner(buyBtnDialog, buyBtnDialogText);
4529					// 구매하기 모바일 다이얼로그 버튼 스피너 초기화
4530					unsetSpinner(buyBtnMobileDialog, buyBtnMobileDialogText);
4531					// 정기구독 신청 버튼 스피너 초기화
4532					unsetSpinner(buyBtnRegularly, buyBtnRegularlyText);
4533					// 정기구독 신청 모바일 버튼 스피너 초기화
4534					unsetSpinner(buyBtnRegularlyMobile, buyBtnRegularlyMobileText);
4535
4536					add_order_progress_check = false;
4537				},
4538				success: function (result) {
4539					if (result.msg == 'SUCCESS') {
4540								// 페이지가 이동하는 동작의 시각적 연결성을 위해 성공 시에는 스피너를 초기화 하지 않는다.
4541
4542								if (typeof AW_PRODUCT != 'undefined' && typeof AW_F_D != 'undefined') {
4543										AW_F_D(current_prod_idx, 'i', order_count);
4544										AW_PRODUCT(order_count);
4545								}
4546
4547								if (result.advanced_kakao_trace_data != null) {
4548										$('body').append(result.advanced_kakao_trace_data);
4549								}
4550								fetch('/ajax/oms/OMS_auth.cm')
4551									.then((res) => res.json())
4552									.then((res) => {
4553										if (res.msg === 'SUCCESS') {
4554											window.sessionStorage.setItem('oms_token', res.token);
4555										}
4556									})
4557									.finally(() => callback(result))
4558						} else {
4559								add_order_progress_check = false;
4560
4561								// 구매하기 PC 버튼 스피너 초기화
4562								unsetSpinner(buyBtn, buyBtnText);
4563								// 구매하기 모바일 버튼 스피너 초기화
4564								unsetSpinner(buyBtnMobile, buyBtnMobileText);
4565								// 구매하기 다이얼로그 버튼 스피너 초기화
4566								unsetSpinner(buyBtnDialog, buyBtnDialogText);
4567								// 구매하기 모바일 다이얼로그 버튼 스피너 초기화
4568								unsetSpinner(buyBtnMobileDialog, buyBtnMobileDialogText);
4569								// 정기구독 신청 버튼 스피너 초기화
4570								unsetSpinner(buyBtnRegularly, buyBtnRegularlyText);
4571								// 정기구독 신청 모바일 버튼 스피너 초기화
4572								unsetSpinner(buyBtnRegularlyMobile, buyBtnRegularlyMobileText);
4573
4574								if (type == 'talkpay') {
4575										callback(result);
4576								} else {
4577										// 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4578										if (result.code === 7) {
4579												redirectToLoginPage();
4580										} else {
4581												alert(result.msg.replace(/\\n/g, '\n'));
4582												if (result.code === 11) {
4583														callback(result);
4584												}
4585												if (result.code === 6) {
4586														location.reload();
4587												}
4588										}
4589								}
4590
4591								if(result.code === 2){
4592									$('._deliv_country_selector').toggleClass('warning_select');
4593								}
4594						}
4595
4596						add_order_progress_check = false;
4597				}
4598			});
4599    };
4600
4601    var addOrder = function (type, backurl, params) {
4602        if (add_order_progress_check) return false;
4603        if (!ensureAddOrderPermission()) return false;
4604        if (typeof AM_PRODUCT != 'undefined') AM_PRODUCT(order_count);
4605
4606            //type 이 guest_login 으로 넘어오는경우 비회원주문+로그인페이지로이동후주문 방식임 type 을 normal 로처리해야 다른 로직에 영향이 없어 이렇게 처리함
4607            // NOTE: 현재 addOrder 함수를 guest_login 값을 type인자로 호출하는 부분을 php코드베이스 내에서 찾을 수 없음.
4608            var is_guest_login = false;
4609            if (type == 'guest_login') {
4610                is_guest_login = true;
4611                type = 'normal';
4612            }
4613
4614            _addOrder(type, backurl, {
4615							...params,
4616							is_oms: false,
4617							callback: function(result){
4618                switch (type) {
4619                    case 'npay':
4620                        if (result.npay_url == '') {
4621                            if (result.errmsg) {
4622                                alert(escape_javascript(result.errmsg));
4623                            } else {
4624                                alert(LOCALIZE.설명_네이버페이상품구매실패(escape_javascript(result.errmsg)));
4625                            }
4626                            add_order_progress_check = false;
4627                        } else {
4628                            if (!!result.shopping_additional_price_msg) {
4629                                if (!confirm(result.shopping_additional_price_msg + '\n\n' + LOCALIZE.설명_네이버페이를계속해서진행하시겠습니까())) {
4630                                    add_order_progress_check = false;
4631                                    return false;
4632                                }
4633                            }
4634
4635                            if (result.auth_type == 2) {
4636                                // 성인 인증이 필요할 경우
4637                                window.location.href = '/?mode=adult_auth_npay&data=' + result.data;
4638                            } else {
4639                                // 성인 인증이 필요하지 않는 경우
4640                                var npay_order_info = result['npay_order_info'];
4641
4642                                if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder();
4643                                if (typeof FB_PIXEL != 'undefined') {
4644                                    FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4645                                    if (result.fb_npay_switch === 'Y') FB_PIXEL.addNpayOrder(npay_order_info);
4646                                }
4647
4648                                if (typeof TIKTOK_PIXEL != 'undefined') {
4649                                    TIKTOK_PIXEL.track('InitiateCheckout', {
4650                                        event_id: result.order_code,
4651                                        contents: result.prod_list.map(prod => ({
4652                                            content_id: prod.id,
4653                                            content_name: prod.name,
4654                                            price: prod.price,
4655                                        })),
4656                                        content_type: 'product',
4657                                        value: result.total_price,
4658                                        currency: result.currency,
4659                                    });
4660                                }
4661
4662                                if (result.google_analytics_type == 'G' && result.is_ga_api_secret === false) {
4663                                    if (typeof GOOGLE_ANAUYTICS != 'undefined') GOOGLE_ANAUYTICS.addNpayOrder(npay_order_info);
4664                                }
4665                                if (typeof GOOGLE_ADWORDS_TRACE != 'undefined' && result.google_ads_include_npay === 'Y') GOOGLE_ADWORDS_TRACE.addNpayOrder(npay_order_info);
4666                                if (typeof CRITEO != 'undefined') CRITEO.npayTrackTransaction(npay_order_info);
4667	                            if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4668															window.location.href = result.npay_url;
4669                            }
4670                        }
4671                        break;
4672                    case 'talkpay':
4673                        switch (params.type) {
4674                            case 'onOrder':
4675                                if (result.msg == 'SUCCESS') {
4676                                    if (result.order_sheet_id) {
4677                                        params.onSuccess(result.order_sheet_id);
4678                                    } else {
4679                                        params.onFailure({ message: result.msg });
4680                                    }
4681                                } else {
4682                                    params.onFailure({ message: result.msg });
4683                                }
4684                                break;
4685                            case 'onPayOrder':
4686                                if (result.msg == 'SUCCESS') {
4687                                    if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4688                                    if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder(result.order_code);
4689	                                if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4690
4691                                    if (typeof TIKTOK_PIXEL != 'undefined') {
4692                                        TIKTOK_PIXEL.track('InitiateCheckout', {
4693                                            event_id: result.order_code,
4694                                            contents: result.prod_list.map(prod => ({
4695                                                content_id: prod.id,
4696                                                content_name: prod.name,
4697                                                price: prod.price,
4698                                            })),
4699                                            content_type: 'product',
4700                                            value: result.total_price,
4701                                            currency: result.currency,
4702                                        });
4703                                    }
4704
4705                                    window.location.href = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code);
4706                                }
4707                                break;
4708                        };
4709
4710                        add_order_progress_check = false;
4711                        break;
4712                    default:
4713                        if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4714                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder(result.order_code);
4715	                    if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4716
4717                        if (typeof TIKTOK_PIXEL != 'undefined') {
4718                            TIKTOK_PIXEL.track('InitiateCheckout', {
4719                                event_id: result.order_code,
4720                                contents: result.prod_list.map(prod => ({
4721                                    content_id: prod.id,
4722                                    content_name: prod.name,
4723                                    price: prod.price,
4724                                })),
4725                                content_type: 'product',
4726                                value: result.total_price,
4727                                currency: result.currency,
4728                            });
4729                        }
4730
4731                        if (is_guest_login && cm_data.use_login_popup === 'N' && !options.is_using_nomember_order_login_after) {	//비회원주문시 로그인 페이지로 이동처리
4732                            //비회원 구매 불가 조건의 경우 로그인 페이지로 보냄. (주문 생성 x)
4733                            if (result.code === 11) {
4734                                var back_url_base64 = window.location.pathname;
4735                                var url_param = document.location.href.split("?");
4736                                if (url_param.length > 1) {
4737                                    back_url_base64 += "?" + url_param[1];
4738                                }
4739                                window.location.href = '/login?back_url=' + encodeURIComponent(btoa(back_url_base64));
4740                            } else {
4741                                window.location.href = '/login?shopping_order_code=' + result.order_code + '&back_url=' + encodeURIComponent(result.back_url_base64);
4742                            }
4743                        } else if (result.code === 7) {
4744                            // 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4745                            redirectToLoginPage();
4746                        } else if(result.msg === 'SUCCESS'){
4747                            //일반주문이며 주문서 생성에 성공 한 경우 주문서로 이동
4748                            window.location.href = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code);
4749                        }
4750                        break;
4751                }
4752            }
4753					});
4754    }
4755
4756    var OMS_addOrder = async function (type, backurl, params) {
4757			if(add_order_progress_check) return false;
4758			if (!ensureAddOrderPermission()) return false;
4759			if ( typeof AM_PRODUCT != 'undefined' ) AM_PRODUCT(order_count);
4760
4761            if (params.shippingVerification) {
4762                const verified = await SHIPPING_SERVICE.verify({ type });
4763                if (!verified) {
4764                    return false;
4765                }
4766            }
4767
4768			//type 이 guest_login 으로 넘어오는경우 비회원주문+로그인페이지로이동후주문 방식임 type 을 normal 로처리해야 다른 로직에 영향이 없어 이렇게 처리함
4769			var is_guest_login=false;
4770			if (type=='guest_login'){
4771				is_guest_login=true;
4772				type='normal';
4773			}
4774
4775			_addOrder(type, backurl, {
4776				...params,
4777				is_oms: true,
4778				callback: function(result){
4779					switch( type ) {
4780						case 'npay':
4781							if(result.npay_url == '') {
4782								if ( result.errmsg ) {
4783									alert(escape_javascript(result.errmsg));
4784								} else {
4785									alert(LOCALIZE.설명_네이버페이상품구매실패(escape_javascript(result.errmsg)));
4786								}
4787								add_order_progress_check = false;
4788							} else {
4789								if ( !! result.shopping_additional_price_msg ) {
4790									if ( ! confirm(result.shopping_additional_price_msg + '\n\n' + LOCALIZE.설명_네이버페이를계속해서진행하시겠습니까()) ) {
4791										add_order_progress_check = false;
4792										return false;
4793									}
4794								}
4795
4796								if ( result.auth_type == 2 ) {
4797									// 성인 인증이 필요할 경우
4798									window.location.href = '/?mode=adult_auth_npay&data=' + result.data;
4799								} else {
4800									// 성인 인증이 필요하지 않는 경우
4801									var npay_order_info = result['npay_order_info'];
4802
4803									if ( typeof CHANNEL_PLUGIN != 'undefined' ) CHANNEL_PLUGIN.AddOrder();
4804									if ( typeof FB_PIXEL != 'undefined'){
4805										FB_PIXEL.InitiateCheckout(result.ic_event_id,result.total_price,result.currency,result.fb_external_id);
4806										if(result.fb_npay_switch === 'Y') FB_PIXEL.addNpayOrder(npay_order_info);
4807									}
4808
4809									if ( result.google_analytics_type == 'G' && result.is_ga_api_secret === false ) {
4810										if ( typeof GOOGLE_ANAUYTICS != 'undefined') GOOGLE_ANAUYTICS.addNpayOrder(npay_order_info);
4811									}
4812									if ( typeof GOOGLE_ADWORDS_TRACE != 'undefined' && result.google_ads_include_npay === 'Y') GOOGLE_ADWORDS_TRACE.addNpayOrder(npay_order_info);
4813									if ( typeof CRITEO != 'undefined') CRITEO.npayTrackTransaction(npay_order_info);
4814									if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4815
4816                                    if (typeof TIKTOK_PIXEL != 'undefined') {
4817                                        TIKTOK_PIXEL.track('InitiateCheckout', {
4818                                            event_id: result.order_code,
4819                                            contents: result.prod_list.map(prod => ({
4820                                                content_id: prod.id,
4821                                                content_name: prod.name,
4822                                                price: prod.price,
4823                                            })),
4824                                            content_type: 'product',
4825                                            value: result.total_price,
4826                                            currency: result.currency,
4827                                        });
4828                                    }
4829
4830									if (typeof result.npay_url !== 'string' || result.npay_url.indexOf('pay.naver.com') < 0) return false;
4831									window.location.href = result.npay_url;
4832								}
4833							}
4834							break;
4835						case 'talkpay':
4836							switch ( params.type ) {
4837								case 'onOrder':
4838									if ( result.msg == 'SUCCESS' ) {
4839										if ( result.order_sheet_id ) {
4840											params.onSuccess(result.order_sheet_id);
4841										} else {
4842											params.onFailure({message: result.msg});
4843										}
4844									} else {
4845										params.onFailure({message: result.msg});
4846									}
4847									break;
4848								case 'onPayOrder':
4849									if ( result.msg == 'SUCCESS' ) {
4850										if ( typeof FB_PIXEL != 'undefined' ) FB_PIXEL.InitiateCheckout(result.ic_event_id,result.total_price,result.currency,result.fb_external_id);
4851										if ( typeof CHANNEL_PLUGIN != 'undefined' ) CHANNEL_PLUGIN.AddOrder(result.order_code);
4852										if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4853
4854                                        if (typeof TIKTOK_PIXEL != 'undefined') {
4855                                            TIKTOK_PIXEL.track('InitiateCheckout', {
4856                                                event_id: result.order_code,
4857                                                contents: result.prod_list.map(prod => ({
4858                                                    content_id: prod.id,
4859                                                    content_name: prod.name,
4860                                                    price: prod.price,
4861                                                })),
4862                                                content_type: 'product',
4863                                                value: result.total_price,
4864                                                currency: result.currency,
4865                                            });
4866                                        }
4867
4868										var shop_payment_url = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code) + '&order_no=' + encodeURIComponent(result.order_no);
4869										if (result.member_code) {
4870											shop_payment_url += "&order_member=" + encodeURIComponent(result.member_code);
4871										}
4872										if ( result.kakaopay_only === 'Y' ) {
4873											shop_payment_url += "&kakaopay_only=Y";
4874										}
4875										window.location.href = shop_payment_url;
4876									}
4877									break;
4878							};
4879
4880							add_order_progress_check = false;
4881							break;
4882						default:
4883							if ( typeof FB_PIXEL != 'undefined' ) FB_PIXEL.InitiateCheckout(result.ic_event_id,result.total_price,result.currency,result.fb_external_id);
4884							if ( typeof CHANNEL_PLUGIN != 'undefined' ) CHANNEL_PLUGIN.AddOrder(result.order_code);
4885							if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4886
4887                            if (typeof TIKTOK_PIXEL != 'undefined') {
4888                                TIKTOK_PIXEL.track('InitiateCheckout', {
4889                                    event_id: result.order_code,
4890                                    contents: result.prod_list.map(prod => ({
4891                                        content_id: prod.id,
4892                                        content_name: prod.name,
4893                                        price: prod.price,
4894                                    })),
4895                                    content_type: 'product',
4896                                    value: result.total_price,
4897                                    currency: result.currency,
4898                                });
4899                            }
4900
4901							if (is_guest_login && cm_data.use_login_popup === 'N' && !options.is_using_nomember_order_login_after){	//비회원주문시 로그인 페이지로 이동처리
4902															//비회원 구매 불가 조건의 경우 로그인 페이지로 보냄. (주문 생성 x)
4903															if(result.code === 11){
4904																	var back_url_base64 = window.location.pathname;
4905																	var url_param = document.location.href.split("?");
4906																	if(url_param.length > 1){
4907																			back_url_base64 += "?"+url_param[1];
4908																	}
4909																	window.location.href = '/login?back_url='+encodeURIComponent(btoa(back_url_base64));
4910															}else{
4911																	window.location.href = '/login?shopping_order_code=' + result.order_code + '&back_url=' + encodeURIComponent(result.back_url_base64);
4912															}
4913							} else if (result.code === 7) {
4914															// 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4915															redirectToLoginPage();
4916							}else{  //일반주문인경우 결제화면으로 이동
4917								if (typeof result.c3_result === "undefined" || typeof result.c3_result.orderCode === "undefined" || typeof result.c3_result.orderNo === "undefined") return
4918								window.location.href
4919									= "/shop_payment/?order_code="
4920									+ encodeURIComponent(result.c3_result.orderCode)
4921									+ "&order_no="
4922									+  encodeURIComponent(result.c3_result.orderNo)
4923									+ (result.c3_result.memberCode ? `&order_member=${encodeURIComponent(result.c3_result.memberCode)}` : '');
4924							}
4925							break;
4926					}
4927				}
4928			});
4929    }
4930
4931    /**
4932     * 네이버페이지 찜하기  등록
4933     */
4934    var addNPayWish = function () {
4935        $.ajax({
4936            type: 'POST',
4937            data: { 'prod_idx_list': [current_prod_idx] },
4938            url: ('/shop/add_npay_wish.cm'),
4939            dataType: 'json',
4940            cache: false,
4941            success: function (result) {
4942                if (result.msg == 'SUCCESS') {
4943                    if (result.mobile == 'Y')
4944                        window.location.href = result.npay_url;
4945                    else
4946                        window.open(result.npay_url, "", "scrollbars=yes,width=400,height=267");
4947                } else {
4948                    alert(LOCALIZE.설명_네이버페이찜등록실패(escape_javascript(result.msg)));
4949                }
4950            }
4951        });
4952    };
4953
4954
4955    var digitalFileDownload = function (order_no) {
4956        if (prod_type !== SHOP_CONST.PROD_TYPE.DIGITAL) return false;
4957        $.ajax({
4958            "type": "POST",
4959            "data": { "prod_no": current_prod_idx, "order_no": order_no, "mode": "detail" },
4960            "url": "/ajax/shop_digital_prod_download.cm",
4961            "dataType": "JSON",
4962            "success": function (res) {
4963                if (res['msg'] == 'SUCCESS') {
4964                    if (res['download_info_msg'].trim() == '' || confirm(res['download_info_msg'])) {
4965                        location.href = res['file_url'];
4966                    }
4967                } else {
4968                    alert(res['msg']);
4969                    if (typeof res['reload'] != 'undefined') {
4970                        location.reload();
4971                    }
4972                }
4973            }
4974        });
4975    };
4976
4977    /**
4978     * OMS 디지털 상품 다운로드 (쓰로틀링 적용)
4979     */
4980    var lastOmsDownloadClick = 0;
4981    var handleOmsDigitalDownload = function (e, href) {
4982        if (e) e.preventDefault();
4983
4984        var now = new Date().getTime();
4985        if (now - lastOmsDownloadClick < 1000) {
4986            return;
4987        }
4988
4989        lastOmsDownloadClick = now;
4990        window.location.href = href;
4991    };
4992    /**
4993     * 모바일에서 구매하기 클릭시 옵션 표시 처리
4994     * @param type	buy 구매하기, regularly 정기구독, gift 선물하기
4995     */
4996    var showMobileOptions = function (type) {
4997        if (!options.is_price_view_permission && window.IS_GUEST) {
4998            const backUrl = encodeURIComponent(base64Encode(window.location.href));
4999            if (cm_data.use_login_popup === 'Y') {
5000                window.SITE_MEMBER.openLogin(
5001                    backUrl,
5002                    'payment',
5003                    function () {
5004											if (window.USE_OMS === true) {
5005												OMS_addOrder(
5006													'normal',
5007													encodeURIComponent(base64Encode(window.location.href))
5008												)
5009											} else {
5010												addOrder(
5011													'normal',
5012													encodeURIComponent(base64Encode(window.location.href))
5013												);
5014											}
5015										},
5016                    'N',
5017                    'payment',
5018                );
5019            } else {
5020                window.location.href = '/login?back_url=' + backUrl;
5021            }
5022            return;
5023        }
5024        if (type === 'regularly') {
5025            $('input[name=add_cart_type]:checked').val('regularly');
5026            // 모바일 구매/옵션 바텀시트 여는 코드
5027            $prod_detail.addClass('open detail-regularly');
5028        } else {
5029            $('input[name=add_cart_type]:checked').val('normal');
5030            // 모바일 구매/옵션 바텀시트 여는 코드
5031            $prod_detail.addClass('open');
5032			$prod_detail.find('._btn_restock').css('display', 'flex');
5033        }
5034        showMobileFirstSelect();
5035        var full_input = false;
5036        var $form = $options.find('._form_parent');
5037        // 첫번째 선택 후 창을 닫았을때
5038        $form.each(function (index) {
5039
5040            var is_input = $(this).children().hasClass('option_box_wrap');
5041
5042            if (is_input) {
5043                var input_items = $(this).find('._requireInputOption');
5044                input_items.each(function (index) {
5045                    if ($(this).val() != '') {
5046                        if ((index + 1) == input_items.length) {
5047                            full_input = true;
5048                        }
5049                    } else {
5050                        return false;
5051                    }
5052                });
5053
5054            } else {
5055                var prev_is_selected = $(this).prev().find('.dropdown-item').hasClass('selected');
5056                var is_selected = $(this).find('.dropdown-item').hasClass('selected');
5057                // 이전 필수 드롭다운이 선택 되어 있을 경위
5058                if (prev_is_selected || full_input) {
5059                    $(this).toggleClass('disabled', false);
5060
5061                    full_input = false;
5062
5063                    if (!is_selected) {
5064                        var current = 0;
5065                        var $current_form_select = $(this).eq(current);
5066                        var $current_form_dropdown_toggle = $current_form_select.find('.dropdown-toggle');
5067
5068                        $current_form_dropdown_toggle.attr('aria-expanded', true);
5069                        $current_form_dropdown_toggle.toggleClass('active', true);
5070                        $current_form_select.attr('data-val', true);
5071
5072                        setTimeout(function () {
5073                            $current_form_dropdown_toggle.dropdown('toggle');
5074                        }, 10);
5075
5076                        $current_form_dropdown_toggle.off('click').on('click', function () {
5077                            $current_form_dropdown_toggle.attr('aria-expanded', false);
5078                            $current_form_dropdown_toggle.toggleClass('active', false);
5079                            $current_form_select.attr('data-val', false);
5080                        });
5081                    }
5082                } else if (index === 0) {
5083                    // 첫 드롭다운은 일단 true로 표시
5084                    $(this).toggleClass('disabled', false);
5085                } else {
5086                    $(this).toggleClass('disabled', true);
5087                }
5088            }
5089        });
5090        // 모달 또는 바텀시트 표시될 때 전체스크롤 등의 동작 막는 코드
5091        $('html').addClass('mobile-shop-open');
5092        $('body').addClass('mobile-nav-on modal-open');
5093        $('header#doz_header_wrap').addClass('bg-back');
5094        $('.categorize-mobile .site_prod_nav_wrap').addClass('bg-back');
5095
5096        // 실시간 상담 부분
5097        $('#ch-plugin, #kakao-talk-channel-chat-button, .talk_banner_wrap, #fb-root').hide();
5098
5099	    if (options.is_possible_use_gift) {
5100		    handleMobileGiftFooterUI(true, type);
5101	    }
5102    };
5103    const changeValueChecked = function () {
5104        $("input:radio[name='add_cart_type']:radio[value='normal']").prop('checked', false);
5105        $("input:radio[name='add_cart_type']:radio[value='regularly']").prop('checked', true);
5106    };
5107    /**
5108     * 조합형 + 필수 옵션일 때 첫번째 셀렉트 박스는 show 처리
5109     */
5110    var showMobileFirstSelect = function () {
5111        var $form_select = $options.find('._first_form_select');
5112        if ($form_select.length) {
5113            var current = 0;
5114            var $current_form_select = $form_select.eq(current);
5115            var $current_form_dropdown_toggle = $current_form_select.find('.dropdown-toggle');
5116
5117            $current_form_dropdown_toggle.attr('aria-expanded', true);
5118            $current_form_dropdown_toggle.toggleClass('active', true);
5119            $current_form_select.attr('data-val', true);
5120
5121            setTimeout(function () {
5122                $current_form_dropdown_toggle.dropdown('toggle');
5123            }, 50);
5124
5125            $current_form_dropdown_toggle.off('click').on('click', function () {
5126                $current_form_dropdown_toggle.attr('aria-expanded', false);
5127                $current_form_dropdown_toggle.toggleClass('active', false);
5128                $current_form_select.attr('data-val', false);
5129            });
5130            $form_select.parent('._form_parent').nextAll().toggleClass('disabled', true);
5131        }
5132    };
5133
5134    /**
5135     * 모바일에서 구매하기 클릭시 옵션 숨기기 처리
5136     */
5137    var hideMobileOptions = function () {
5138        $prod_detail.removeClass('open detail-regularly');
5139        $('html').removeClass('mobile-shop-open');
5140        $('body').removeClass('mobile-nav-on modal-open');
5141        $('header#doz_header_wrap').removeClass('bg-back');
5142        $('.categorize-mobile .site_prod_nav_wrap').removeClass('bg-back');
5143
5144        // 실시간 상담 부분
5145        $('#ch-plugin, #kakao-talk-channel-chat-button, .talk_banner_wrap, #fb-root').show();
5146				$prod_detail.find('._btn_restock').hide();
5147
5148		if (options.is_possible_use_gift) {
5149			handleMobileGiftFooterUI(false);
5150		}
5151    };
5152
5153    /**
5154     * PC B타입에서 구매하기/장바구니 클릭 시 옵션 표시 처리, 옵션 표시되어있으면 구매하기/장바구니
5155     * @param type order: 구매하기, cart: 장바구니, gift: 선물하기
5156     * @param backurl 비회원 구매 시 로그인 등에서 사용될 back url
5157     */
5158    var showPCOptions = function (type, backurl = location.href, is_c3 = false){
5159        if (backurl == undefined) backurl = location.href;
5160        if ($prod_detail.hasClass('open')) {
5161					// 바텀시트가 열려 있으면
5162            if(type == 'cart'){
5163							if (typeof BrandScope !== "undefined") {
5164								try {
5165									BrandScope.track('click_add_to_cart_shop_view', {
5166										'action': 'click',
5167										'content': 'add_to_cart',
5168										'where': 'shop_view',
5169										'prod_code': current_prod_code,
5170										'is_regularly_prod': $('input[name=add_cart_type]:checked').val() === 'regularly' ? true : false,
5171										'prod_type': prod_type
5172									});
5173								} catch {
5174									// ignore
5175								}
5176							}
5177                SITE_SHOP_DETAIL.addCart();
5178            } else if (type == 'gift') {
5179							  if (is_c3) {
5180									SITE_SHOP_DETAIL.OMS_addGiftOrder('normal', backurl);
5181								} else {
5182									SITE_SHOP_DETAIL.addGiftOrder('normal', backurl);
5183								}
5184            } else {
5185                if (is_c3) {
5186                    SITE_SHOP_DETAIL.OMS_addOrder('normal', backurl);
5187                }else{
5188                    SITE_SHOP_DETAIL.addOrder('normal', backurl);
5189                }
5190            }
5191        } else {
5192					// 바텀시트가 안열려 있으면
5193            $prod_detail.find('._btn_npay').hide();
5194            $prod_detail.find('._btn_kakaopay').hide();
5195			$prod_detail.find('._btn_restock').show();
5196            $prod_detail.addClass('open');
5197            var $add_cart_type = $prod_detail.find('input[name=add_cart_type]:checked');
5198            if ($add_cart_type.length > 0) {
5199                if ($add_cart_type.val() === 'regularly') {
5200                    $prod_detail.addClass('detail-regularly');
5201                }
5202            }
5203
5204			if (options.is_possible_use_gift) {
5205				handlePCGiftFooterUI(true, type);
5206			}
5207        }
5208    };
5209
5210    var hidePCOptions = function () {
5211        $prod_detail.removeClass('open detail-regularly');
5212        $prod_detail.find('._btn_npay').show();
5213        $prod_detail.find('._btn_kakaopay').show();
5214		$prod_detail.find('._btn_restock').hide();
5215
5216	    if (options.is_possible_use_gift) {
5217		    handlePCGiftFooterUI(false);
5218	    }
5219    };
5220
5221	var handlePCGiftFooterUI = function (isOpen, type) {
5222		const $root = $('._buy_footer_fixed');
5223
5224		const $footerPriceWrap = $root.find('._footer_price_wrap');
5225		const $btnCart = $root.find('._btn_cart');
5226		const $btnGift = $root.find('._btn_gift');
5227		const $btnBuy = $root.find('._btn_buy');
5228		const $btnDialogSoldout = $root.find('._btn_dialog_soldout');
5229		const $btnDialogBuy = $root.find('._btn_dialog_buy');
5230		const $btnDialogGift = $root.find('._btn_dialog_gift');
5231
5232		const hideAll = () => {
5233			$footerPriceWrap.hide();
5234			$btnCart.hide();
5235			$btnGift.hide();
5236			$btnBuy.hide();
5237			$btnDialogSoldout.hide();
5238			$btnDialogBuy.hide();
5239			$btnDialogGift.hide();
5240		}
5241
5242		const isSoldout = options.is_soldout;
5243
5244		hideAll();
5245
5246		if (isOpen) {
5247			if (isSoldout) {
5248				$btnDialogSoldout.show();
5249			} else {
5250				$footerPriceWrap.show();
5251				$btnCart.show();
5252
5253				switch(type) {
5254					case 'order':
5255						$btnDialogBuy.show();
5256						break;
5257					case 'gift':
5258						$btnDialogGift.show();
5259						break;
5260					case 'cart':
5261						$btnGift.show();
5262						$btnBuy.show();
5263						break;
5264				}
5265			}
5266		} else {
5267			$footerPriceWrap.show();
5268			$btnCart.show();
5269			$btnGift.show();
5270			$btnBuy.show();
5271		}
5272	}
5273
5274	const handleMobileGiftFooterUI = (isOpen, type) => {
5275		const $root = $('._mobile-gift-footer');
5276		const $socialBtnRoot = $('._social_m_position');
5277
5278		const $btnGift = $root.find('._btn_mobile_gift');
5279		const $btnBuy = $root.find('._btn_mobile_buy');
5280		const $btnNPay = $root.find('._btn_mobile_npay');
5281		const $btnKakaoPay = $root.find('._btn_mobile_kakaopay');
5282		const $btnDialogSoldout = $root.find('._btn_mobile_dialog_soldout');
5283		const $btnDialogCart = $root.find('._btn_mobile_dialog_cart');
5284		const $btnDialogBuy = $root.find('._btn_mobile_dialog_buy');
5285		const $btnDialogGift = $root.find('._btn_mobile_dialog_gift');
5286
5287		const hideAll = () => {
5288			$btnGift.hide();
5289			$btnBuy.hide();
5290			$btnNPay.hide();
5291			$btnKakaoPay.hide();
5292			$btnDialogSoldout.hide();
5293			$btnDialogCart.hide();
5294			$btnDialogBuy.hide();
5295			$btnDialogGift.hide();
5296			$socialBtnRoot.hide();
5297		}
5298
5299		hideAll();
5300
5301		const isSoldout = options.is_soldout;
5302
5303        if (isOpen) {
5304            if (isSoldout) {
5305                $btnDialogSoldout.show();
5306            } else {
5307                switch(type) {
5308                    case 'buy':
5309                        $btnDialogCart.show();
5310                        $btnDialogBuy.show();
5311                        if ($socialBtnRoot.find('._social_pay').children().length > 0) $socialBtnRoot.show();
5312                        break;
5313                    case 'gift':
5314                        $btnDialogGift.show();
5315                        break;
5316                }
5317            }
5318        } else {
5319            $btnGift.show();
5320            $btnBuy.show();
5321            $btnNPay.show();
5322            $btnKakaoPay.show();
5323        }
5324    }
5325
5326    var socialBtnResize = function () {
5327        var $social_box = $('._social_pay');
5328        var $pc_position = $('._social_pc_position');
5329        var $m_position = $('._social_m_position');
5330        var w_width = $(window).width();
5331
5332        if (w_width > 787) {
5333            $pc_position.append($social_box);
5334        } else {
5335            $m_position.append($social_box);
5336        }
5337
5338        $(window).resize(function () {
5339            w_width = $(window).width();
5340            if (w_width > 787) {
5341                $pc_position.append($social_box);
5342                if ($('html').hasClass('mobile-shop-open')) {
5343                    hideMobileOptions();
5344                }
5345                if ($('._style_b_clse').length) {
5346                    $('._style_b_clse').removeAttr('onclick');
5347                    $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hidePCOptions()');
5348                }
5349            } else {
5350                $m_position.append($social_box);
5351                if ($('._style_b_clse').length) {
5352                    $('._style_b_clse').removeAttr('onclick');
5353                    $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hideMobileOptions()');
5354                }
5355            }
5356        });
5357    };
5358
5359	const externalwidgetWrapResize = function(move_target, pc_position, mo_position){
5360		const $target = $(`.${move_target}`);
5361		const $pc_position = $(`.${pc_position}`);
5362		const $m_position = $(`.${mo_position}`);
5363		let w_width = $(window).width();
5364
5365        if($('body').hasClass('device_type_m')) {
5366            w_width = 768;
5367        }
5368
5369		if(w_width > 768){
5370			$pc_position.append($target);
5371		}else{
5372			$m_position.append($target);
5373		}
5374
5375		$(window).resize(function(){
5376			w_width = $(window).width();
5377			if(w_width > 768){
5378                if(!($pc_position.find($target).length)) {
5379                    $pc_position.append($target);
5380                    if($('html').hasClass('mobile-shop-open')){
5381                        hideMobileOptions();
5382                    }
5383                    if($('._style_b_clse').length){
5384                        $('._style_b_clse').removeAttr('onclick');
5385                        $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hidePCOptions()');
5386                    }
5387                }
5388			}else{
5389                if(!($m_position.find($target).length)){
5390                    $m_position.append($target);
5391                    if($('._style_b_clse').length){
5392                        $('._style_b_clse').removeAttr('onclick');
5393                        $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hideMobileOptions()');
5394                    }
5395                }
5396			}
5397		});
5398	};
5399
5400	/**
5401	 * qna 상세페이지 모달
5402	 * @param idx
5403	 * @param qna_page
5404	 * @param is_hash		주소창에 직접 입력 또는 최신글 위젯 등 상세페이지 외부에서 바로 넘어왔는지 여부, Y/N
5405	 */
5406	var viewQnaDetail = function(idx,qna_page,is_hash){
5407		$(function(){
5408			$.ajax({
5409				type : 'POST',
5410				data : {idx : idx, qna_page : qna_page},
5411				url : ('/ajax/qna_detail_view.cm'),
5412				dataType : 'json',
5413				async : false,
5414				cache : false,
5415				success : function(res){
5416					if(res.msg === 'SUCCESS'){
5417						$.cocoaDialog.open({
5418							type : 'prod_detail', custom_popup : res.html, width : 800});
5419						if(checkUseHistory()){
5420							// 모달 히스토리 커스텀(IE 10 이상)
5421							var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
5422							var back_url = document.referrer.indexOf('#') === -1 ? document.referrer : document.referrer.substr(0, document.referrer.indexOf('#'));
5423							if(current_url !== back_url && is_hash !== 'Y'){
5424								history.pushState(null, null, current_url);
5425							}
5426							history.replaceState(null, null, current_url + "#prod_detail_qna!/" + res.idx);
5427						}else{
5428							location.hash = "prod_detail_qna!/" + res.idx;
5429						}
5430						$(window).off('hashchange').on('hashchange',function(){
5431							var hash_qna_spilt = location.hash.split('!/')[1];
5432							if(!hash_qna_spilt){
5433								$.cocoaDialog.close();
5434							}else{
5435								if(checkUseHistory()){
5436									var hash_spilt_tab = location.hash.split('!/')[0];
5437									if(hash_spilt_tab === '#prod_detail_review'){
5438										viewReviewDetail(hash_qna_spilt);
5439									}else if(hash_spilt_tab === '#prod_detail_qna'){
5440										viewQnaDetail(hash_qna_spilt);
5441									}
5442								}
5443							}
5444						});
5445						$('.modal_prod_detail').off('hidden.bs.modal').on('hidden.bs.modal', function (e) {
5446              $('html').toggleClass('modal-scroll-control', false);
5447							if(is_hash !== 'Y'){
5448								removeQnawHash();
5449							}
5450						});
5451					}else{
5452						alert(res.msg);
5453					}
5454				}
5455			});
5456		});
5457	};
5458    /**
5459     * qna 상세페이지 모달
5460     * @param idx
5461     * @param qna_page
5462     * @param is_hash		주소창에 직접 입력 또는 최신글 위젯 등 상세페이지 외부에서 바로 넘어왔는지 여부, Y/N
5463     */
5464    var viewQnaDetail = function (idx, qna_page, is_hash) {
5465        $(function () {
5466            $.ajax({
5467                type: 'POST',
5468                data: { idx: idx, qna_page: qna_page },
5469                url: ('/ajax/qna_detail_view.cm'),
5470                dataType: 'json',
5471                async: false,
5472                cache: false,
5473                success: function (res) {
5474                    if (res.msg === 'SUCCESS') {
5475                        $.cocoaDialog.open({
5476                            type: 'prod_detail', custom_popup: res.html, width: 800
5477                        });
5478                        if (checkUseHistory()) {
5479                            // 모달 히스토리 커스텀(IE 10 이상)
5480                            var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
5481                            var back_url = document.referrer.indexOf('#') === -1 ? document.referrer : document.referrer.substr(0, document.referrer.indexOf('#'));
5482                            if (current_url !== back_url && is_hash !== 'Y') {
5483                                history.pushState(null, null, current_url);
5484                            }
5485                            history.replaceState(null, null, current_url + "#prod_detail_qna!/" + res.idx);
5486                        } else {
5487                            location.hash = "prod_detail_qna!/" + res.idx;
5488                        }
5489                        $(window).off('hashchange').on('hashchange', function () {
5490                            var hash_qna_spilt = location.hash.split('!/')[1];
5491                            if (!hash_qna_spilt) {
5492                                $.cocoaDialog.close();
5493                            } else {
5494                                if (checkUseHistory()) {
5495                                    var hash_spilt_tab = location.hash.split('!/')[0];
5496                                    if (hash_spilt_tab === '#prod_detail_review') {
5497                                        viewReviewDetail(hash_qna_spilt);
5498                                    } else if (hash_spilt_tab === '#prod_detail_qna') {
5499                                        viewQnaDetail(hash_qna_spilt);
5500                                    }
5501                                }
5502                            }
5503                        });
5504                        $('.modal_prod_detail').off('hidden.bs.modal').on('hidden.bs.modal', function (e) {
5505                            $('html').toggleClass('modal-scroll-control', false);
5506                            if (is_hash !== 'Y') {
5507                                removeQnawHash();
5508                            }
5509                        });
5510                    } else {
5511                        alert(res.msg);
5512                    }
5513                }
5514            });
5515        });
5516    };
5517
5518    var removeQnawHash = function () {
5519        $(window).off('hashchange');
5520        var hash_qna_spilt = location.hash.split('!/')[1];
5521        if (hash_qna_spilt) {
5522            if (checkUseHistory()) {
5523                history.back();
5524            } else {
5525                location.href = '#prod_detail_qna';
5526            }
5527        }
5528    };
5529
5530    /**
5531     *  리뷰 모달 html 변경 (앞 뒤 버튼으로 호출)
5532     * @param idx
5533     */
5534    var changeReviewDetail = function (idx) {
5535        $.ajax({
5536            type: 'POST',
5537            data: { idx: idx, review_page: 1, only_photo: "Y" },
5538            url: ('/ajax/review_detail_change.cm'),
5539            dataType: 'json',
5540            async: false,
5541            cache: false,
5542            success: function (res) {
5543                if (res.msg === 'SUCCESS') {
5544                    $('._review_modal_body').html(res.html);
5545                    if (IS_MOBILE) {
5546                        // setReviewSwipe(res.prev_idx, res.next_idx);
5547                        $body.find('.modal.review').after(res.btn_html);
5548                        $body.find('._btn_nav_wrap').off('click').on('click', function () {
5549                            $(this).remove();
5550                        });
5551                        $body.find('#review_detail_close').off('click').on('click', function () {
5552                            $('._btn_nav_wrap').remove();
5553                        });
5554                        setReviewBtnAnimation();
5555                        setReviewClass(idx);
5556                    }
5557                    setReviewCarousel();
5558                    $(window).resize(function () {
5559                        setReviewCarousel();
5560                    });
5561                    if (res.review_code != '') SHOP_REVIEW_COMMENT.getReviewCommentHtml(res.review_code, 'modal');
5562                } else {
5563                    alert(res.msg);
5564                }
5565            }
5566        });
5567    };
5568
5569    /**
5570     * 리뷰 상세페이지 모달
5571     * @param idx
5572     * @param review_page
5573     * @param only_photo
5574     * @param is_hash		주소창에 직접 입력 또는 최신글 위젯 등 상세페이지 외부에서 바로 넘어왔는지 여부, Y/N
5575     */
5576    var viewReviewDetail = function (idx, review_page, only_photo, is_hash) {
5577        $(function () {
5578            $.ajax({
5579                type: 'POST',
5580                data: { idx: idx, review_page: review_page, only_photo: only_photo },
5581                url: ('/ajax/review_detail_view.cm'),
5582                dataType: 'json',
5583                async: false,
5584                cache: false,
5585                success: function (res) {
5586                    if (res.msg === 'SUCCESS') {
5587                        $.cocoaDialog.open({ type: 'prod_detail review', custom_popup: res.html, width: 800 });
5588                        if (checkUseHistory()) {
5589                            // 모달 히스토리 커스텀(IE 10 이상)
5590                            var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
5591                            var back_url = document.referrer.indexOf('#') === -1 ? document.referrer : document.referrer.substr(0, document.referrer.indexOf('#'));
5592                            if (current_url !== back_url && is_hash !== 'Y') {
5593                                history.pushState(null, null, current_url);
5594                            }
5595                            history.replaceState(null, null, current_url + "#prod_detail_review!/" + res.idx);
5596                        } else {
5597                            location.hash = "prod_detail_review!/" + res.idx;
5598                        }
5599                        $(window).off('hashchange').on('hashchange', function () {
5600                            var hash_review_spilt = location.hash.split('!/')[1];
5601                            if (!hash_review_spilt) {
5602                                $.cocoaDialog.close();
5603                            } else {
5604                                if (checkUseHistory()) {
5605                                    var hash_spilt_tab = location.hash.split('!/')[0];
5606                                    if (hash_spilt_tab === '#prod_detail_qna') {
5607                                        viewQnaDetail(hash_review_spilt);
5608                                    } else if (hash_spilt_tab === '#prod_detail_review') {
5609                                        viewReviewDetail(hash_review_spilt);
5610                                    }
5611                                }
5612                            }
5613                        });
5614                        $('.modal_prod_detail').off('hidden.bs.modal').on('hidden.bs.modal', function (e) {
5615                            $('html').toggleClass('modal-scroll-control', false);
5616                            if (is_hash !== 'Y') {
5617                                removeReviewHash();
5618                            }
5619                        });
5620                    } else {
5621                        alert(res.msg);
5622                    }
5623                }
5624            });
5625        });
5626    };
5627
5628    var removeReviewHash = function () {
5629        $(window).off('hashchange');
5630        var hash_review_spilt = location.hash.split('!/')[1];
5631        if (hash_review_spilt) {
5632            if (checkUseHistory()) {
5633                history.back();
5634            } else {
5635                location.href = '#prod_detail_review';
5636            }
5637        }
5638    };
5639
5640
5641
5642    /**
5643     *
5644     */
5645    var getOnlyPhotoReview = function (only_photo_switch, is_mobile, is_one_page, rating) {
5646		    let $target_html;
5647        const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
5648        let isMobile = is_mobile === 'Y';
5649        if (isMobile !== isMobile_width) {
5650            isMobile = !isMobile;
5651        }
5652        const device_name = isMobile ? 'mobile' : 'pc';
5653        const one_page_mode = is_one_page === 'Y';
5654        const ajax_target = `/shop/prod_review_${device_name}_html.cm`;
5655
5656        if (one_page_mode) $target_html = $(`._detail_review_wrap${device_name === 'pc'? '' : '_' + device_name} ._review_wrap`);
5657		    else $target_html = $(`.product_review${device_name === 'pc'? '' : '_' + device_name}`);
5658
5659        if ($target_html.length) {
5660            var $icon_picture = $('.icon-picture');
5661            $.ajax({
5662                type: 'POST',
5663                data: {
5664                  'prod_idx': current_prod_idx,
5665                  'only_photo': only_photo_switch,
5666                  'rating': rating,
5667                },
5668                url: (ajax_target),
5669                dataType: 'html',
5670                cache: false,
5671                async: false,
5672                success: function (result) {
5673                    $target_html.html(result);
5674                    if ($icon_picture.hasClass('active')) {
5675                        $icon_picture.removeClass('active');
5676                    } else {
5677                        $icon_picture.addClass('active');
5678                    }
5679                }
5680            });
5681        }
5682    };
5683
5684    const getStarRatingReview = function (only_photo_switch, is_mobile, is_one_page, rating) {
5685      let $target_html;
5686      const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
5687      let isMobile = is_mobile === 'Y';
5688      if (isMobile !== isMobile_width) {
5689        isMobile = !isMobile;
5690      }
5691      const device_name = isMobile ? 'mobile' : 'pc';
5692      const one_page_mode = is_one_page === 'Y';
5693      const ajax_target = `/shop/prod_review_${device_name}_html.cm`;
5694
5695      if (one_page_mode) $target_html = $(`._detail_review_wrap${device_name === 'pc'? '' : '_' + device_name} ._review_wrap`);
5696      else $target_html = $(`.product_review${device_name === 'pc'? '' : '_' + device_name}`);
5697
5698      if ($target_html.length) {
5699        var $icon_picture = $('.icon-picture');
5700        $.ajax({
5701          type: 'POST',
5702          data: {
5703            'prod_idx': current_prod_idx,
5704            'only_photo': only_photo_switch,
5705            'rating' : rating,
5706          },
5707          url: (ajax_target),
5708          dataType: 'html',
5709          cache: false,
5710          async: false,
5711          success: function (result) {
5712            $target_html.html(result);
5713            const $rating = $('.icon-picture');
5714          }
5715        });
5716      }
5717    }
5718
5719    var getFirstPhotoReview = function (page) {
5720        if ($first_photo_review_wrap.length) {
5721            var is_cache = false;
5722            var callback = function (result) {
5723                $first_photo_review_wrap.html(result);
5724                if (!is_cache) IMWEB_SESSIONSTORAGE.set("PROD_REVIEW_PC_PHOTO_" + current_prod_idx + "_" + page, result.replace(/\t+/g, '').trim(), 60);
5725            };
5726
5727            var html = IMWEB_SESSIONSTORAGE.get("PROD_REVIEW_PC_PHOTO_" + current_prod_idx + "_" + page);
5728            if (html) {
5729                is_cache = true;
5730                callback(html);
5731            } else {
5732                $.ajax({
5733                    type: 'POST',
5734                    data: { prod_idx: current_prod_idx, page: page },
5735                    url: '/shop/prod_photo_review_pc_html.cm',
5736                    dataType: 'html',
5737                    cache: false,
5738                    async: false,
5739                    success: callback
5740                });
5741            }
5742        }
5743    };
5744
5745    var viewPhotoReviewMore = function (is_mobile, is_one_page) {
5746        if (is_mobile !== 'Y') {
5747            // 모바일에서는 구매평 내부에서만 호출하므로 탭 전환 필요 없음
5748            changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW, review_page, qna_page, false);
5749        }
5750        getOnlyPhotoReview('Y', is_mobile, is_one_page);
5751        if (is_mobile !== 'Y') {
5752            if (is_one_page === 'Y') {
5753                $('._detail_review_wrap')[0].scrollIntoView();
5754            } else {
5755                $('._prod_detail_detail_lazy_load')[0].scrollIntoView();
5756            }
5757        } else {
5758            if (is_one_page === 'Y') {
5759                $('._detail_review_wrap_mobile')[0].scrollIntoView();
5760            } else {
5761                $('._prod_detail_detail_lazy_load_mobile')[0].scrollIntoView();
5762            }
5763        }
5764    };
5765
5766    var getReviewSummary = function (target) {
5767        if ($review_summary_wrap_mobile.length) {
5768            $.ajax({
5769                type: 'POST',
5770                data: { 'prod_idx': current_prod_idx, 'type': 'mobile' },
5771                url: ('/shop/prod_review_summary_html.cm'),
5772                dataType: 'html',
5773	              cache: false,
5774                async: false,
5775                success: function (result) {
5776                    if(target) {
5777                        target.style.display = 'block';
5778                        target.innerHTML = result;
5779                    } else {
5780                        $review_summary_wrap_mobile.html(result);
5781                        $review_summary_wrap_mobile.show();
5782                    }
5783                    startReviewImageRolling('mobile');
5784                }
5785            });
5786        }
5787
5788        if (!IS_MOBILE) {
5789            if ($review_summary_wrap.length) {
5790                var pc_type = $prod_detail.width() > 991 ? 'pc' : 'tablet';
5791
5792                $.ajax({
5793                    type: 'POST',
5794                    data: { 'prod_idx': current_prod_idx, 'type': pc_type },
5795                    url: ('/shop/prod_review_summary_html.cm'),
5796                    dataType: 'html',
5797	                  cache: false,
5798                    async: false,
5799                    success: function (result) {
5800                        $review_summary_wrap.html(result);
5801                        if ($prod_detail.width() <= 991) {
5802                            $review_summary_wrap.removeClass('review_summary_wrap');
5803                            $review_summary_wrap.addClass('review_summary_wrap_tablet');
5804                        }
5805                        $review_summary_wrap.show();
5806                        startReviewImageRolling(pc_type);
5807                    }
5808                });
5809            }
5810        }
5811    };
5812
5813    var startReviewImageRolling = function (type) {
5814        if (type === 'mobile') {
5815            $review_image_list = $review_summary_wrap_mobile.find('.review_image_list');
5816        } else {
5817            $review_image_list = $review_summary_wrap.find('.review_image_list');
5818        }
5819        $review_image_list_rolling = $review_image_list.find('div.owl-carousel');
5820
5821        var navtext_left = $('<i class="btl bt-angle-left" style="position: absolute; font-size: 20px; left: -25px; top: 45px;"></i>');
5822        var navtext_right = $('<i class="btl bt-angle-right" style="position: absolute; font-size: 20px; right: -25px; top: 45px;"></i>');
5823
5824        var margin = type === 'mobile' ? 5 : 10;
5825        var items = 4;
5826
5827
5828        var img_width = Math.floor(($review_image_list.innerWidth() - (margin * (items - 1))) / items);
5829        if (type === 'tablet') {
5830            if (img_width > 150) {
5831                // img_width가 150보다 크면 개수 자체를 150에 맞춰서 재계산
5832                items = Math.ceil($review_image_list.innerWidth() / (150 + margin / 2));
5833                img_width = Math.floor(($review_image_list.innerWidth() - (margin * (items - 1))) / items);
5834            }
5835        }
5836        $('._review_carousel_image').each(function () {
5837            $(this).css('width', img_width);
5838            $(this).css('height', img_width);
5839        });
5840
5841        $review_image_list_rolling.owlCarousel({
5842            dots: false,
5843            nav: type === 'pc',
5844            navText: [navtext_left, navtext_right],
5845            slideSpeed: 300,
5846            paginationSpeed: 400,
5847            animateOut: 'fadeOut',
5848            autoWidth: true,
5849            items: items,
5850            margin: margin
5851        });
5852    };
5853
5854
5855    /**
5856     * 한페이지 전체 노출에서 리뷰 삭제 코드
5857     * @param prod_code
5858     * @param only_photo
5859     * @returns {boolean}
5860     */
5861
5862    const deleteReviewInTabDisplay = function (prod_code, only_photo) {
5863
5864        if (options.shop_view_tab_display !== "Y") return false;
5865
5866        $.ajax({
5867            type: 'POST',
5868            data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page, 'only_photo': only_photo },
5869            url: ('/shop/prod_review_pc_html.cm'),
5870            dataType: 'html',
5871            cache: false,
5872            async: true,
5873            success: function (result) {
5874                $('.categorize ._review_wrap').html(result);
5875            }
5876        });
5877
5878        $.ajax({
5879            type: 'POST',
5880            data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page, 'only_photo': only_photo },
5881            url: ('/shop/prod_review_mobile_html.cm'),
5882            dataType: 'html',
5883            cache: false,
5884            async: true,
5885            success: function (result) {
5886                $('.categorize-mobile ._review_wrap').html(result);
5887            }
5888        });
5889
5890    }
5891
5892    /**
5893     *  pc 상세페이지 탭 이벤트 처리
5894     * @param type 탭 type
5895     * @param r_p
5896     * @param q_p
5897     * @param paging_on 스크롤 조정 //deprecated
5898     * @param only_photo
5899     * @param shop_view_body_width
5900     * @param rating
5901     */
5902    var changeContentPCTab = function (type, r_p, q_p, paging_on, only_photo, rating = 0, shop_view_body_width) {
5903        var $site_prod_nav_wrap = $('.categorize .site_prod_nav_wrap');
5904
5905        $site_prod_nav_wrap.find(`a`).removeClass('active');
5906
5907        current_content_tab = type;
5908
5909        $site_prod_nav_wrap.find(`a._${type}`).addClass('active');
5910
5911		var review_paging = false;
5912		var qna_paging = false;
5913
5914		if(parseInt(r_p) > 0){
5915			if(review_page != parseInt(r_p) && type == SHOP_CONST.TAB_TYPE.REVIEW){
5916				review_paging = true;
5917			}
5918			review_page = parseInt(r_p);
5919		}else{
5920			r_p = review_page;
5921		}
5922		if(parseInt(q_p) > 0){
5923			if(qna_page != parseInt(q_p) && type == SHOP_CONST.TAB_TYPE.QNA){
5924				qna_paging = true;
5925			}
5926			qna_page = parseInt(q_p);
5927		}else{
5928			q_p = qna_page;
5929		}
5930
5931	    if(options.shop_view_tab_display !== "Y" || review_paging || qna_paging){
5932		    switch (true) {
5933			    case use_cdn_optimized && (type === SHOP_CONST.TAB_TYPE.DETAIL): {
5934				    if (options.shop_view_tab_display !== "Y"){
5935					    if($review_summary_wrap.html()){
5936						    $review_summary_wrap.empty();
5937						    $review_summary_wrap.hide();
5938					    }
5939				    }
5940					$prod_detail_content_pc.empty();
5941					Promise.resolve(loadTemplate('prodDetailPC', $prod_detail_content_pc, async(node) => {
5942						if(use_lazy_load){
5943							await setImageWidthHeightBeforeLoad(node);
5944						}
5945						return node;
5946					})).then(() => {
5947						// 크리마 리뷰 사용 중일 시 위젯 호출
5948						if(document.querySelector(".crema-product-reviews")){
5949							if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
5950								crema.run();
5951							}
5952						}
5953            setTimeout(()=>{
5954              document.querySelectorAll('#prod_detail table._table_responsive').forEach((table)=>{
5955                if (table.classList.contains('table')) return;
5956
5957                table.classList.add('table');
5958                table.outerHTML = `<div class="table-responsive">${table.outerHTML}</div>`;
5959              });
5960            }, 0);
5961					safeScheduleProdDetailBodyExpandedHeightUpdate();
5962					});
5963				    if (typeof shop_view_body_width != 'undefined') {
5964					    $prod_detail_content_pc.css({ 'width': shop_view_body_width + '%' });
5965				    }
5966
5967				    $prod_detail_content_pc.toggleClass('product_detail', true);
5968				    $prod_detail_content_pc.toggleClass('product_review', false);
5969				    $prod_detail_content_pc.toggleClass('product_qna', false);
5970				    $prod_detail_content_pc.toggleClass('product_return', false);
5971				    break;
5972				}
5973				case type === SHOP_CONST.TAB_TYPE.DETAIL: {
5974					if (options.shop_view_tab_display !== "Y"){
5975					    if($review_summary_wrap.html()){
5976						    $review_summary_wrap.empty();
5977						    $review_summary_wrap.hide();
5978					    }
5979				    }
5980				    var pc_detail_html = IMWEB_TEMPLATE.loadSimple("prodDetailPC");
5981				    $prod_detail_content_pc.html(pc_detail_html);
5982				    if (typeof shop_view_body_width != 'undefined') {
5983					    $prod_detail_content_pc.css({ 'width': shop_view_body_width + '%' });
5984				    }
5985				    if (use_lazy_load) {
5986					    runLazyload();
5987				    }
5988					// 크리마 리뷰 사용 중일 시 위젯 호출
5989					if(document.querySelector(".crema-product-reviews")){
5990						if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
5991							crema.run();
5992						}
5993					}
5994					safeScheduleProdDetailBodyExpandedHeightUpdate();
5995
5996				    $prod_detail_content_pc.toggleClass('product_detail', true);
5997				    $prod_detail_content_pc.toggleClass('product_review', false);
5998				    $prod_detail_content_pc.toggleClass('product_qna', false);
5999				    $prod_detail_content_pc.toggleClass('product_return', false);
6000				    break;
6001				}
6002			    case type === SHOP_CONST.TAB_TYPE.REVIEW: {
6003				    if (!$review_summary_wrap.html()) {
6004					    getReviewSummary();
6005				    }
6006				    $.ajax({
6007					    type: 'POST',
6008					    data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page, 'only_photo': only_photo, 'is_mobile' : SHOP_CONST.IS_MOBILE, 'rating' : rating },
6009					    url: ('/shop/prod_review_pc_html.cm'),
6010					    dataType: 'html',
6011					    cache: false,
6012					    async: false,
6013					    success: function (result) {
6014
6015							if ($('._external_widget_review_mo_position')) {
6016								$('._external_widget_review_mo_position').remove();
6017							}
6018
6019							if (tab_type === 'Y') {
6020							    $('.categorize ._review_wrap').html(result);
6021						    } else {
6022                                // 구조 변경 이후 해당 부분 아닌데, 이부분에 코드 추가됨
6023							    $prod_detail_content_pc.html(result).css({ 'width': '100%' });
6024						    }
6025					    }
6026				    });
6027
6028				    $prod_detail_content_pc.toggleClass('product_detail', false);
6029				    $prod_detail_content_pc.toggleClass('product_review', true);
6030				    $prod_detail_content_pc.toggleClass('product_qna', false);
6031				    $prod_detail_content_pc.toggleClass('product_return', false);
6032				    break;
6033				}
6034			    case type === SHOP_CONST.TAB_TYPE.QNA: {
6035				    if (options.shop_view_tab_display !== "Y"){
6036					    if($review_summary_wrap.html()){
6037						    $review_summary_wrap.empty();
6038						    $review_summary_wrap.hide();
6039					    }
6040				    }
6041				    $.ajax({
6042					    type: 'POST',
6043					    data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page },
6044					    url: ('/shop/prod_qna_pc_html.cm'),
6045					    dataType: 'html',
6046					    cache: false,
6047					    async: false,
6048					    success: function (result) {
6049						    if (tab_type === 'Y') {
6050							    $('.categorize ._qna_wrap').html(result);
6051						    } else {
6052							    $prod_detail_content_pc.html(result).css({ 'width': '100%' });
6053						    }
6054					    }
6055				    });
6056
6057				    $prod_detail_content_pc.toggleClass('product_detail', false);
6058				    $prod_detail_content_pc.toggleClass('product_review', false);
6059				    $prod_detail_content_pc.toggleClass('product_qna', true);
6060				    $prod_detail_content_pc.toggleClass('product_return', false);
6061				    break;
6062				}
6063			    case type === SHOP_CONST.TAB_TYPE.RETURN: {
6064				    if (options.shop_view_tab_display !== "Y"){
6065					    if($review_summary_wrap.html()){
6066						    $review_summary_wrap.empty();
6067						    $review_summary_wrap.hide();
6068					    }
6069				    }
6070				    if (last_refund_data) {
6071					    const prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnPc", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6072					    if (tab_type === 'Y') {
6073						    var $_target = $('.categorize ._return_wrap');
6074					    }else{
6075						    var $_target = $prod_detail_content_pc;
6076					    }
6077					    $_target.empty();
6078					    $_target.html(prod_detail_return_html).css({ 'width': '100%' });
6079				    }
6080
6081				    $prod_detail_content_pc.toggleClass('product_detail', false);
6082				    $prod_detail_content_pc.toggleClass('product_review', false);
6083				    $prod_detail_content_pc.toggleClass('product_qna', false);
6084				    $prod_detail_content_pc.toggleClass('product_return', true);
6085				    break;
6086				}
6087		    }
6088	    }
6089
6090	    let margin_top = 0;
6091	    let prod_tab_target;
6092	    if(options.shop_view_tab_display !== "Y"){
6093		    prod_tab_target = document.querySelector('#prod_tab_target');
6094		    if(options.shop_tab_fixed !== "Y"){
6095			    margin_top = -1 * getFixedHeaderHeight() + 'px';
6096		    }else if(review_paging){
6097			    margin_top = (-1 * document.querySelector('#fixed_tab ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6098		    }
6099	    }else{
6100		    prod_tab_target = document.querySelector(`#prod_detail_${type}_target`);
6101		    margin_top = (-1 * document.querySelector('#fixed_tab ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6102	    }
6103	    if(review_paging){
6104		    prod_tab_target = document.querySelector('.categorize ._prod_detail_review_board_target');
6105	    }
6106
6107	    if(review_paging || qna_paging || is_init_detail){
6108			if(prod_tab_target){
6109				prod_tab_target.style.top = margin_top;
6110				prod_tab_target.scrollIntoView();
6111			}
6112		    if(is_init_detail){
6113			    setTabHistory(type);
6114			    setTimeout(() => {
6115				    checkSeemore(type);
6116			    }, 200);
6117		    }
6118	    }
6119
6120    };
6121
6122    /**
6123     * 상세정보 탭 변경 처리
6124     * @param type
6125     * @param r_p
6126     * @param q_p
6127     * @param paging_on 스크롤 조정 //deprecated
6128     * @param only_photo
6129     * @param rating
6130     */
6131    var changeContentTab = function (type, r_p, q_p, paging_on, only_photo, rating = 0) {
6132        if (current_content_tab != '') {
6133            $('.active').parent('li').removeClass('activeborder');
6134            $prod_detail_content_tab_mobile.find('a').removeClass('active');
6135        }
6136        current_content_tab = type;
6137        $prod_detail_content_tab_mobile.find('a._' + type).addClass('active');
6138        $('.table-cell > .active').parent('li').addClass('activeborder');
6139
6140        var $seemore_wrap = $body.find('._seemore_wrap');
6141
6142	    var review_paging = false;
6143	    var qna_paging = false;
6144
6145	    if(parseInt(r_p) > 0){
6146		    if(review_page != parseInt(r_p) && type == SHOP_CONST.TAB_TYPE.REVIEW){
6147			    review_paging = true;
6148		    }
6149		    review_page = parseInt(r_p);
6150	    }else{
6151		    r_p = review_page;
6152	    }
6153	    if(parseInt(q_p) > 0){
6154		    if(qna_page != parseInt(q_p) && type == SHOP_CONST.TAB_TYPE.QNA){
6155			    qna_paging = true;
6156		    }
6157		    qna_page = parseInt(q_p);
6158	    }else{
6159		    q_p = qna_page;
6160	    }
6161
6162	    if(options.shop_view_tab_display !== "Y" || review_paging || qna_paging){
6163		    switch (true) {
6164			    case use_cdn_optimized && (type === SHOP_CONST.TAB_TYPE.DETAIL): {
6165				    if (options.shop_view_tab_display !== "Y"){
6166					    if($review_summary_wrap_mobile.html()){
6167						    $review_summary_wrap_mobile.empty();
6168						    $review_summary_wrap_mobile.hide();
6169					    }
6170				    }
6171
6172					$prod_detail_content_mobile.empty();
6173					Promise.resolve(loadTemplate('prodDetailMobile', $prod_detail_content_mobile, async (node) => {
6174						if (use_lazy_load) {
6175							await setImageWidthHeightBeforeLoad(node);
6176						}
6177						if (node.children.length === 0) {
6178							$prod_detail_content_mobile.html('<div style="text-align: center; padding: 50px 0;"><div class="body_font_color_40" style="font-size: 18px; margin:30px"><div> </div>');
6179						}
6180						return node;
6181					})).then(() => {
6182						setMobileVideoRatio();
6183						// 크리마 리뷰 사용 중일 시 위젯 호출
6184						if(document.querySelector(".crema-product-reviews")){
6185							if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
6186								crema.run();
6187							}
6188						}
6189            setTimeout(()=>{
6190              document.querySelectorAll('#prod_detail table._table_responsive').forEach((table)=>{
6191                if (table.classList.contains('table')) return;
6192
6193                table.classList.add('table');
6194                table.outerHTML = `<div class="table-responsive">${table.outerHTML}</div>`;
6195              });
6196            }, 0);
6197					safeScheduleProdDetailBodyExpandedHeightUpdate();
6198					});
6199
6200				    $prod_detail_content_mobile.toggleClass('product_detail_mobile', true);
6201				    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6202				    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6203				    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6204				    break;
6205				}
6206			    case type === SHOP_CONST.TAB_TYPE.DETAIL: {
6207				    if (options.shop_view_tab_display !== "Y"){
6208					    if($review_summary_wrap_mobile.html()){
6209						    $review_summary_wrap_mobile.empty();
6210						    $review_summary_wrap_mobile.hide();
6211					    }
6212				    }
6213
6214				    var mobile_detail_html = IMWEB_TEMPLATE.loadSimple('prodDetailMobile');
6215				    if (mobile_detail_html) {
6216					    if (use_lazy_load) {
6217                            $prod_detail_content_mobile.html(mobile_detail_html);
6218                            runLazyload();
6219					    } else {
6220						    $prod_detail_content_mobile.html(mobile_detail_html);
6221					    }
6222				    } else {
6223					    $prod_detail_content_mobile.html('<div style="text-align: center; padding: 50px 0;"><div class="body_font_color_40" style="font-size: 18px; margin:30px"><div> </div>');
6224				    }
6225				    setMobileVideoRatio();
6226
6227					// 크리마 리뷰 사용 중일 시 위젯 호출
6228				    if(document.querySelector(".crema-product-reviews")){
6229					    if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
6230						    crema.run();
6231					    }
6232				    }
6233					safeScheduleProdDetailBodyExpandedHeightUpdate();
6234
6235				    $prod_detail_content_mobile.toggleClass('product_detail_mobile', true);
6236				    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6237				    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6238				    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6239				    break;
6240				}
6241			    case type === SHOP_CONST.TAB_TYPE.REVIEW: {
6242				    $.ajax({
6243					    type: review_paging ? 'POST' : 'GET',
6244					    data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page, 'only_photo': only_photo, 'member_hash': MEMBER_HASH, 'is_mobile' : SHOP_CONST.IS_MOBILE, 'rating' : rating },
6245					    url: ('/shop/prod_review_mobile_html.cm'),
6246					    dataType: 'html',
6247					    async: false,
6248					    success: function (result) {
6249						    if ($('._external_widget_review_pc_position')) {
6250							    $('._external_widget_review_pc_position').remove();
6251						    }
6252						    if (!$review_summary_wrap_mobile.html()) {
6253							    getReviewSummary();
6254						    }
6255						    if (tab_type === 'Y') {
6256							    $('.categorize-mobile ._review_wrap').html(result);
6257						    }else{
6258							    $prod_detail_content_mobile.html(result);
6259						    }
6260
6261						    $prod_detail_content_mobile.toggleClass('product_detail_mobile', false);
6262						    $prod_detail_content_mobile.toggleClass('product_review_mobile', true);
6263						    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6264						    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6265					    }
6266				    });
6267				    break;
6268				}
6269			    case type === SHOP_CONST.TAB_TYPE.QNA: {
6270				    $.ajax({
6271					    type: 'POST',
6272					    data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page },
6273					    url: ('/shop/prod_qna_mobile_html.cm'),
6274					    dataType: 'html',
6275					    cache: false,
6276					    async: false,
6277					    success: function (result) {
6278						    if (options.shop_view_tab_display !== "Y"){
6279							    if($review_summary_wrap_mobile.html()){
6280								    $review_summary_wrap_mobile.empty();
6281								    $review_summary_wrap_mobile.hide();
6282							    }
6283						    }
6284						    if (tab_type === 'Y') {
6285							    $('.categorize-mobile ._qna_wrap').html(result);
6286						    } else {
6287							    $prod_detail_content_mobile.html(result).css({ 'width': '100%' });
6288						    }
6289						    $prod_detail_content_mobile.toggleClass('product_detail_mobile', false);
6290						    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6291						    $prod_detail_content_mobile.toggleClass('product_qna_mobile', true);
6292						    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6293					    }
6294				    });
6295				    break;
6296				}
6297			    case type === SHOP_CONST.TAB_TYPE.RETURN: {
6298				    if (options.shop_view_tab_display !== "Y"){
6299					    if($review_summary_wrap_mobile.html()){
6300						    $review_summary_wrap_mobile.empty();
6301						    $review_summary_wrap_mobile.hide();
6302					    }
6303				    }
6304				    if (last_refund_data) {
6305					    const prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnMobile", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6306					    if (tab_type === 'Y') {
6307						    var $_target = $('.categorize-mobile ._return_wrap');
6308					    }else{
6309						    var $_target = $prod_detail_content_mobile;
6310					    }
6311					    $_target.empty();
6312					    $_target.html(prod_detail_return_html).css({ 'width': '100%' });
6313				    }
6314				    $prod_detail_content_mobile.toggleClass('product_detail_mobile', false);
6315				    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6316				    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6317				    $prod_detail_content_mobile.toggleClass('product_return_mobile', true);
6318				    break;
6319				}
6320		    }
6321	    }
6322
6323		let margin_top = 0;
6324		let prod_tab_target;
6325		if(options.shop_view_tab_display !== "Y"){
6326			prod_tab_target = document.querySelector('#prod_tab_target_mobile');
6327			if(options.shop_tab_fixed !== "Y"){
6328				margin_top = -1 * getFixedHeaderHeight() + 'px';
6329			}else if(review_paging){
6330				margin_top = (-1 * document.querySelector('#fixed_tab_mobile ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6331			}
6332		}else{
6333			prod_tab_target = document.querySelector(`#prod_detail_${type}_target_mobile`);
6334			margin_top = (-1 * document.querySelector('#fixed_tab_mobile ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6335		}
6336	    if(review_paging){
6337		    prod_tab_target = document.querySelector('.categorize-mobile ._prod_detail_review_board_target');
6338	    }
6339
6340		if(review_paging || qna_paging || is_init_detail){
6341			if(prod_tab_target){
6342				prod_tab_target.style.top = margin_top;
6343				prod_tab_target.scrollIntoView();
6344			}
6345			if(is_init_detail){
6346				setTabHistory(type);
6347				setTimeout(() => {
6348					checkSeemore(type);
6349				}, 200);
6350			}
6351		}
6352    };
6353
6354	var getFixedHeaderHeight = function(){
6355		let margin_top = 0;
6356		let $fixed_header_disable;
6357		if(document.querySelector('#inline_header_fixed')){
6358			if (!IS_MOBILE) {
6359				$fixed_header_disable = $('#inline_header_fixed');
6360			} else {
6361				$fixed_header_disable = $('#inline_header_mobile').find('._fixed_header_section');
6362			}
6363		}else{
6364			if (!IS_MOBILE) {
6365				$fixed_header_disable = $('#inline_header_normal').find('._fixed_header_section');
6366			} else {
6367				$fixed_header_disable = $('#inline_header_mobile').find('._fixed_header_section');
6368			}
6369		}
6370		for ( var i = 0; i < $fixed_header_disable.length; i++ ) {
6371			var target = $fixed_header_disable[i].getBoundingClientRect();
6372			margin_top += target.height;
6373		}
6374		return margin_top;
6375	};
6376
6377    /**
6378     * 상세정보 펼쳐보기 버튼 노출 처리
6379     */
6380    let checkSeemore = function (type) {
6381        const $seemore_wrap = $body.find('._seemore_wrap');
6382        if($seemore_wrap.length === 0) return;
6383
6384        // 탭 노출 방식이 한 페이지 노출 방식이거나 탭 노출 방식이 아니면서 상세정보 탭일 때만 펼쳐보기 노출
6385        if(options.shop_view_tab_display == "Y" || (options.shop_view_tab_display == "N" && type == SHOP_CONST.TAB_TYPE.DETAIL)) {
6386			// template 이 변환되는 시점이 비동기가 되어 당장 사이즈를 알 수가 없다.
6387            // if((!is_mobile_width && $prod_detail_content_pc.outerHeight() > SHOP_CONST.SEEMORE_HEIGHT.PC) || (is_mobile_width && $prod_detail_content_mobile.outerHeight() > SHOP_CONST.SEEMORE_HEIGHT.MOBILE)) {
6388                $prod_detail_content_pc.toggleClass('hide_seemore', false);
6389                $prod_detail_content_mobile.toggleClass('hide_seemore', false);
6390				$seemore_wrap.show();
6391				safeScheduleProdDetailBodyExpandedHeightUpdate();
6392				return;
6393            // }
6394        }
6395		$prod_detail_content_pc.toggleClass('hide_seemore', true);
6396		$prod_detail_content_mobile.toggleClass('hide_seemore', true);
6397		$seemore_wrap.hide();
6398		safeScheduleProdDetailBodyExpandedHeightUpdate();
6399	};
6400
6401    var setTabHistory = function (type) {
6402        if (!$('body').hasClass('admin')) {
6403            if (checkUseHistory()) {
6404                var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
6405                switch (type) {
6406                    case SHOP_CONST.TAB_TYPE.DETAIL:
6407                        history.replaceState(null, null, current_url + "#prod_detail_detail");
6408                        break;
6409                    case SHOP_CONST.TAB_TYPE.REVIEW:
6410                        history.replaceState(null, null, current_url + "#prod_detail_review");
6411                        break;
6412                    case SHOP_CONST.TAB_TYPE.QNA:
6413                        history.replaceState(null, null, current_url + "#prod_detail_qna");
6414                        break;
6415                    case SHOP_CONST.TAB_TYPE.RETURN:
6416                        history.replaceState(null, null, current_url + "#prod_detail_return");
6417                        break;
6418                    default:
6419                        let target_tab = options.first_tab === "prod_detail" ? SHOP_CONST.TAB_TYPE.DETAIL : options.first_tab === "prod_review" ? SHOP_CONST.TAB_TYPE.REVIEW : options.first_tab === "prod_qna" ? SHOP_CONST.TAB_TYPE.QNA : options.first_tab === "prod_return" ? SHOP_CONST.TAB_TYPE.RETURN : "";
6420
6421                        history.replaceState(null, null, current_url + `#prod_detail_${target_tab}`);
6422                        break;
6423                }
6424            } else {
6425                switch (type) {
6426                    case SHOP_CONST.TAB_TYPE.DETAIL:
6427                        location.hash = "prod_detail_detail";
6428                        break;
6429                    case SHOP_CONST.TAB_TYPE.REVIEW:
6430                        location.hash = "prod_detail_review";
6431                        break;
6432                    case SHOP_CONST.TAB_TYPE.QNA:
6433                        location.hash = "prod_detail_qna";
6434                        break;
6435                    case SHOP_CONST.TAB_TYPE.RETURN:
6436                        location.hash = "prod_detail_return";
6437                        break;
6438                    default:
6439                        let target_tab = options.first_tab === "prod_detail" ? SHOP_CONST.TAB_TYPE.DETAIL : options.first_tab === "prod_review" ? SHOP_CONST.TAB_TYPE.REVIEW : options.first_tab === "prod_qna" ? SHOP_CONST.TAB_TYPE.QNA : options.first_tab === "prod_return" ? SHOP_CONST.TAB_TYPE.RETURN : "";
6440
6441                        location.hash = `prod_detail_${target_tab}`;
6442                        break;
6443                }
6444            }
6445        }
6446    };
6447
6448    var setMobileVideoRatio = function () {
6449        // 동영상 크기 값이 입력되어 있을 시 모바일에서도 입력된 크기 기준으로 비율 계산해서 반영
6450        var $fr_video = $prod_detail_content_mobile.find('.fr-video.fr-dvb');
6451		if ($fr_video.length > 0) {
6452            $fr_video.each(function (k, v) {
6453                var $fr_video_iframe = $(v).find('iframe');
6454                if ($fr_video_iframe.length > 0) {
6455					var origin_width = $fr_video_iframe[0].style.width;
6456                    var origin_height = $fr_video_iframe[0].style.height;
6457					if (origin_width != '' && origin_height != '') {
6458                        // px 단위일 때만 비율 보정
6459                        var origin_width_split = origin_width.split('px');
6460                        var origin_height_split = origin_height.split('px');
6461						if (origin_width_split.length === 2 && origin_height_split.length === 2) {
6462                            var video_ratio = (origin_height_split[0] / origin_width_split[0]) * 100;
6463                            $(v).css('padding-bottom', video_ratio + '%');
6464                        }
6465                    }
6466                }
6467            });
6468        }
6469    };
6470
6471    var prepareAddCartAlarmModal = function () {
6472        if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
6473            $.cocoaDialog.close();
6474        }
6475        if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
6476            imSheet.close('prod_additional_sheet');
6477        }
6478    };
6479
6480	    var openAddCartAlarmHtml = function (html) {
6481	        if (typeof html !== 'string' || html.trim() === '') {
6482	            $add_cart_alarm.show();
6483	            return;
6484	        }
6485
6486        if (is_mobile_width) {
6487            imSheet.close('shop_detail_add_cart_alarm_sheet', function () {
6488                imSheet.open({
6489                    id: 'shop_detail_add_cart_alarm_sheet',
6490                    html: html,
6491                    backdrop: 'rgba(0, 0, 0, 0.15)',
6492                    zIndex: 17001
6493                });
6494            });
6495        } else {
6496            if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
6497                $.cocoaDialog.close();
6498            }
6499            $.cocoaDialog.open({
6500                type: 'site_shop_free_ship_notify',
6501                custom_popup: html,
6502                pc_width: 440
6503            });
6504	        }
6505	    };
6506
6507	    var PRODUCT_RECOMMENDATION_ADD_CART_ALARM_SHOWN_SESSION_KEY_PREFIX = 'SHOP_PRODUCT_RECOMMEND:SHOWN:PROD:';
6508
6509	    var getProductRecommendationAddCartAlarmShownSessionKey = function (prodIdx) {
6510	        return PRODUCT_RECOMMENDATION_ADD_CART_ALARM_SHOWN_SESSION_KEY_PREFIX + prodIdx;
6511	    };
6512
6513	    var hasProductRecommendationAddCartAlarmShown = function (prodIdx) {
6514	        if (_isFreeShipNotifySalesToolPreview) {
6515	            return true;
6516	        }
6517	        if (!prodIdx) {
6518	            return false;
6519	        }
6520
6521	        try {
6522	            if (typeof window.sessionStorage === 'undefined') {
6523	                return false;
6524	            }
6525	            return window.sessionStorage.getItem(getProductRecommendationAddCartAlarmShownSessionKey(prodIdx)) === 'Y';
6526	        } catch (e) {
6527	            return false;
6528	        }
6529	    };
6530
6531	    var markProductRecommendationAddCartAlarmShown = function (prodIdx) {
6532	        if (_isFreeShipNotifySalesToolPreview) {
6533	            return;
6534	        }
6535	        if (!prodIdx) {
6536	            return;
6537	        }
6538
6539	        try {
6540	            if (typeof window.sessionStorage === 'undefined') {
6541	                return;
6542	            }
6543	            window.sessionStorage.setItem(getProductRecommendationAddCartAlarmShownSessionKey(prodIdx), 'Y');
6544	        } catch (e) {}
6545	    };
6546
6547    function getFreeShipNotifyPreviewMarker() {
6548        var markerEl = document.getElementById('shop_detail_add_cart_alarm_with_free_ship_notify');
6549        if (markerEl && document.documentElement.contains(markerEl)) {
6550            return markerEl;
6551        }
6552
6553        if (!_isFreeShipNotifySalesToolPreview) {
6554            return null;
6555        }
6556
6557        var inlineMarkerEl = document.querySelector('#foFreeShipNotifyProdDetailMagnet.foFreeShipNotifyMagnet, .foFreeShipNotifyMagnet[data-type="prod"]');
6558        var inlineThreshold = inlineMarkerEl
6559            ? parseFloat(inlineMarkerEl.getAttribute('data-init-deliv-price-flexable-key') || '0')
6560            : 0;
6561        return inlineMarkerEl && !isNaN(inlineThreshold) && inlineThreshold > 0 ? inlineMarkerEl : null;
6562    }
6563
6564    function isFreeShipNotifyPreviewReady() {
6565        return !!(
6566            window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK
6567            && getFreeShipNotifyPreviewMarker()
6568        );
6569    }
6570
6571    function flushFreeShipNotifyPreviewReadyCallbacks() {
6572        if (!isFreeShipNotifyPreviewReady()) {
6573            return;
6574        }
6575
6576        window.removeEventListener('imweb:freeShipNotify:sdk-ready', flushFreeShipNotifyPreviewReadyCallbacks);
6577        document.removeEventListener('DOMContentLoaded', flushFreeShipNotifyPreviewReadyCallbacks);
6578        window.removeEventListener('load', flushFreeShipNotifyPreviewReadyCallbacks);
6579        if (_freeShipNotifyPreviewReadyObserver) {
6580            _freeShipNotifyPreviewReadyObserver.disconnect();
6581            _freeShipNotifyPreviewReadyObserver = null;
6582        }
6583
6584        var callbacks = _freeShipNotifyPreviewReadyCallbacks.splice(0);
6585        callbacks.forEach(function (callback) {
6586            callback();
6587        });
6588    }
6589
6590    function waitForFreeShipNotifyPreviewReady(callback) {
6591        if (!_isFreeShipNotifySalesToolPreview) {
6592            callback();
6593            return;
6594        }
6595        if (isFreeShipNotifyPreviewReady()) {
6596            callback();
6597            return;
6598        }
6599
6600        _freeShipNotifyPreviewReadyCallbacks.push(callback);
6601
6602        window.addEventListener('imweb:freeShipNotify:sdk-ready', flushFreeShipNotifyPreviewReadyCallbacks);
6603        document.addEventListener('DOMContentLoaded', flushFreeShipNotifyPreviewReadyCallbacks);
6604        window.addEventListener('load', flushFreeShipNotifyPreviewReadyCallbacks);
6605
6606        if (!_freeShipNotifyPreviewReadyObserver && typeof MutationObserver !== 'undefined') {
6607            _freeShipNotifyPreviewReadyObserver = new MutationObserver(flushFreeShipNotifyPreviewReadyCallbacks);
6608            _freeShipNotifyPreviewReadyObserver.observe(document.documentElement, {
6609                attributes: true,
6610                childList: true,
6611                subtree: true
6612            });
6613        }
6614    }
6615
6616	    /**
6617	     * 장바구니 담기 완료 모달 표시.
6618	     * 무료 배송 안내 대상 상품이면 무료 배송 안내를 우선 노출하고, 이미 노출된 경우 기본 모달로 fallback 한다.
6619	     * 무료 배송 안내 대상 상품이 아닐 때만 구매 상품 추천 모달을 노출한다.
6620	     */
6621    var showAddCartAlarm = function () {
6622        var freeShipNotifySdk = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK;
6623        var freeShipMarkerEl = $add_cart_alarm_with_free_ship_notify && $add_cart_alarm_with_free_ship_notify.length > 0
6624            ? $add_cart_alarm_with_free_ship_notify.get(0)
6625            : null;
6626        if (!freeShipMarkerEl || !document.documentElement.contains(freeShipMarkerEl)) {
6627            freeShipMarkerEl = getFreeShipNotifyPreviewMarker();
6628        }
6629        if (freeShipMarkerEl) {
6630            $add_cart_alarm_with_free_ship_notify = $(freeShipMarkerEl);
6631        }
6632        var initDelivPriceFlexableKey = freeShipMarkerEl
6633            ? parseFloat(freeShipMarkerEl.getAttribute('data-init-deliv-price-flexable-key') || '0')
6634            : 0;
6635        var productRecommendationMarkerEl = $add_cart_alarm_with_product_recommendation && $add_cart_alarm_with_product_recommendation.length > 0
6636            ? $add_cart_alarm_with_product_recommendation.get(0)
6637            : null;
6638        if (!productRecommendationMarkerEl || !document.documentElement.contains(productRecommendationMarkerEl)) {
6639            productRecommendationMarkerEl = document.getElementById('shop_detail_add_cart_alarm_with_product_recommendation');
6640        }
6641        if (productRecommendationMarkerEl) {
6642            $add_cart_alarm_with_product_recommendation = $(productRecommendationMarkerEl);
6643        }
6644        var productRecommendationProdIdx = productRecommendationMarkerEl
6645            ? parseInt(productRecommendationMarkerEl.getAttribute('data-prod-idx') || '0', 10)
6646            : 0;
6647        var isFreeShipNotifyTargetProduct = !!freeShipMarkerEl;
6648        if (_isFreeShipNotifySalesToolPreview && (!isFreeShipNotifyTargetProduct || !freeShipNotifySdk)) {
6649            if (!_isWaitingFreeShipNotifySdkForAddCartAlarm) {
6650                _isWaitingFreeShipNotifySdkForAddCartAlarm = true;
6651                waitForFreeShipNotifyPreviewReady(function () {
6652                    _isWaitingFreeShipNotifySdkForAddCartAlarm = false;
6653                    showAddCartAlarm();
6654                });
6655            }
6656            return;
6657        }
6658        var hasFreeShipNotifyAlarmShown = !!(
6659            freeShipNotifySdk
6660            && freeShipNotifySdk.hasAddCartAlarmShown()
6661        );
6662
6663        var canShowFreeShipNotify = !!(
6664            isFreeShipNotifyTargetProduct
6665            && freeShipNotifySdk
6666            && !hasFreeShipNotifyAlarmShown
6667            && !($('._deliv_country_selector').length > 0 && $deliv_country !== 'KR')
6668            && !isNaN(initDelivPriceFlexableKey)
6669            && initDelivPriceFlexableKey > 0
6670        );
6671	        var canShowProductRecommendation = !!(
6672	            !isFreeShipNotifyTargetProduct
6673	            && productRecommendationMarkerEl
6674	            && !isNaN(productRecommendationProdIdx)
6675	            && productRecommendationProdIdx > 0
6676	            && !hasProductRecommendationAddCartAlarmShown(productRecommendationProdIdx)
6677	        );
6678
6679        if (isFreeShipNotifyTargetProduct && !canShowFreeShipNotify) {
6680            $add_cart_alarm.show();
6681            return;
6682        }
6683
6684        if (!canShowFreeShipNotify && !canShowProductRecommendation) {
6685            $add_cart_alarm.show();
6686            return;
6687        }
6688
6689        if (canShowFreeShipNotify) {
6690            window.__imwebFreeShipCartItemCounts = {};
6691            window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartItems:update', {
6692                detail: { counts: {} }
6693            }));
6694
6695            var prodImage = freeShipMarkerEl.getAttribute('data-prod-image') || '';
6696            var prodName = freeShipMarkerEl.getAttribute('data-prod-name') || '';
6697
6698            freeShipNotifySdk.markAddCartAlarmShown();
6699            prepareAddCartAlarmModal();
6700
6701            $.ajax({
6702                type: 'POST',
6703                url: '/shop/free_ship_notify/get_add_cart_alarm_modal.cm',
6704                data: {
6705                    prod_image: prodImage,
6706                    prod_name: prodName,
6707                    init_deliv_price_flexable_key: initDelivPriceFlexableKey
6708                },
6709                dataType: 'html',
6710                cache: false,
6711                success: openAddCartAlarmHtml,
6712                error: function () {
6713                    $add_cart_alarm.show();
6714                }
6715            });
6716            return;
6717        }
6718
6719        prepareAddCartAlarmModal();
6720
6721        $.ajax({
6722            type: 'POST',
6723            url: '/shop/product_recommendation/get_add_cart_alarm_modal.cm',
6724            data: {
6725                prod_idx: productRecommendationProdIdx,
6726                prod_code: productRecommendationMarkerEl.getAttribute('data-prod-code') || '',
6727                prod_image: productRecommendationMarkerEl.getAttribute('data-prod-image') || '',
6728                prod_name: productRecommendationMarkerEl.getAttribute('data-prod-name') || ''
6729            },
6730            dataType: 'html',
6731            cache: false,
6732            success: function (html) {
6733	                if (typeof html !== 'string' || html.trim() === '') {
6734	                    $add_cart_alarm.show();
6735	                    return;
6736	                }
6737	                markProductRecommendationAddCartAlarmShown(productRecommendationProdIdx);
6738	                openAddCartAlarmHtml(html);
6739	            },
6740            error: function () {
6741                $add_cart_alarm.show();
6742            }
6743        });
6744    };
6745
6746    var hideAddCartAlarm = function () {
6747        trackAddToCartPopupAction('click_continue_shopping_add_to_cart_popup', 'continue_shopping');
6748        $add_cart_alarm.hide();
6749        if (($add_cart_alarm_with_free_ship_notify && $add_cart_alarm_with_free_ship_notify.length > 0)
6750	        || ($add_cart_alarm_with_product_recommendation && $add_cart_alarm_with_product_recommendation.length > 0)) {
6751            // 매출 상승 도구 장바구니 알림 사용 사이트 - cocoaDialog/imSheet 양쪽 모두 닫기
6752            if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
6753                $.cocoaDialog.close();
6754            }
6755            if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
6756                imSheet.close('shop_detail_add_cart_alarm_sheet');
6757            }
6758            // imSheet.close 의 비동기 cleanup 이 다른 시트 진입과 race 가 되면 orphan sheet element 가 남아
6759            // 옵션 시트의 dim 위에 중첩되는 현상을 방어. 같은 id 의 element 를 즉시 DOM 에서 제거.
6760            document.querySelectorAll('#shop_detail_add_cart_alarm_sheet').forEach(function (el) {
6761                el.remove();
6762            });
6763
6764            if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
6765                && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.dispatchInlineMagnetItemCounts === 'function') {
6766                window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.dispatchInlineMagnetItemCounts();
6767            }
6768        }
6769    };
6770
6771    var moveCart = function () {
6772        trackAddToCartPopupAction('click_go_cart_add_to_cart_popup', 'go_cart');
6773        cart_type = $('input[name=add_cart_type]:checked').val();
6774        if (cart_type == 'regularly') {
6775            window.location.href = '/shop_cart?type=regularly';
6776        } else {
6777            window.location.href = '/shop_cart';
6778        }
6779    };
6780
6781    var trackAddToCartPopupAction = function (eventName, content, options) {
6782        if (typeof window.BrandScope === 'undefined' || typeof window.BrandScope.track !== 'function') return;
6783        var isFreeShippingUpsell = (options && typeof options.isFreeShippingUpsell === 'boolean')
6784            ? options.isFreeShippingUpsell
6785            : !!window.__imwebAddToCartHasFreeShipUpsell;
6786        window.BrandScope.track(eventName, {
6787            action: 'click',
6788            content: content,
6789            where: 'add_to_cart_popup',
6790            prod_code_list: Array.isArray(window.__imwebAddToCartProdCodeList)
6791                ? window.__imwebAddToCartProdCodeList.slice()
6792                : [],
6793            is_free_shipping_upsell: isFreeShippingUpsell
6794        });
6795    };
6796
6797    var changeInput = function () {
6798        $(document).on('keypress', 'input._requireInputOption', function (e) {
6799            if (e.which == 13) {
6800                e.preventDefault();
6801                var $next = $('[tabIndex=' + (+this.tabIndex + 1) + ']');
6802                if ($next.length != 0) {
6803                    $next.focus().click();
6804                } else {
6805                    e.target.blur();
6806                }
6807            }
6808        });
6809    };
6810
6811    var changeTab = function (type) {
6812        $('._detail_detail_wrap').hide();
6813        $('._detail_review_wrap').hide();
6814        $('._detail_qna_wrap').hide();
6815        $('._detail_' + type + '_wrap').show();
6816    };
6817
6818    var countryCodeChange = function (country) {
6819        $.ajax({
6820            type: 'POST',
6821            data: { 'country': country },
6822            url: ('/shop/country_code_change.cm'),
6823            dataType: 'json',
6824            cache: false,
6825            success: function (result) {
6826                if (result.msg != "SUCCESS") {
6827                    alert(result.msg);
6828                }
6829            }
6830        });
6831    };
6832
6833    var DetailItemMake = function (idx, change_country, deliv_type, deliv_pay_type) {
6834        var is_design_mode = (location.pathname.indexOf('/admin/design') != -1);
6835        $deliv_country = change_country;
6836        $deliv_pay_type = deliv_pay_type;
6837        $.ajax({
6838            type: 'GET',
6839            data: {
6840                'idx': idx,
6841                'change_country': change_country,
6842                'deliv_type': deliv_type,
6843                'deliv_pay_type': deliv_pay_type,
6844                '__': MEMBER_HASH
6845            },
6846            url: ('/shop/prod_detail_make.cm'),
6847            dataType: 'json',
6848            cache: !is_design_mode,
6849            success: function (result) {
6850                if (result.msg == 'SUCCESS') {
6851                    $prod_detail.find('._item_detail_wrap').html(result.html);
6852
6853                    /* 환불 정보 입력 */
6854                    last_refund_data = result.refund;
6855                    setDetailReturnHtml();
6856
6857                    if (window.__npayButtonV2 && typeof makeNaverPayBtn !== 'undefined') {
6858												try{
6859													makeNaverPayBtn('naverPayWrap', change_country == 'KR' || change_country == 'none' || change_country == '');
6860												}catch(e) {
6861
6862												}
6863                    }
6864
6865                    if (typeof kakaoCheckout != 'undefined' && typeof makeTalkPayButton != 'undefined') {
6866												try{
6867                          makeTalkPayButton('create-kakao-checkout-button', (change_country == 'KR' || change_country == 'none' || change_country == ''));
6868												}catch(e) {
6869
6870												}
6871                    }
6872                }
6873            }
6874        });
6875    };
6876    function setDetailReturnHtml() {
6877        if (!last_refund_data) return;
6878        if (options.shop_view_tab_display == 'Y') {
6879            let prod_detail_return_html = null;
6880            prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnPc", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6881            $prod_detail_return.html(prod_detail_return_html);
6882            prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnMobile", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6883            $prod_detail_return_mobile.html(prod_detail_return_html);
6884
6885            // 반품/교환 주소지 동적 업데이트 (템플릿에서 PHP 변수로 초기 렌더링된 부분)
6886            if (last_refund_data.return_address) {
6887                $prod_detail_return.find('._return_address_text').html(last_refund_data.return_address);
6888                $prod_detail_return_mobile.find('._return_address_text').html(last_refund_data.return_address);
6889            }
6890        } else {
6891            if (current_content_tab !== SHOP_CONST.TAB_TYPE.RETURN) return;
6892            SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.RETURN);
6893            SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.RETURN);
6894        }
6895    }
6896
6897    /**
6898     * 환불 정보 데이터 업데이트 및 템플릿 렌더링
6899     * @param {Object} refundData - 환불 정보 데이터
6900     */
6901    const updateRefundData = (refundData) => {
6902        if (!refundData) {
6903            return;
6904        }
6905        last_refund_data = refundData;
6906        setDetailReturnHtml();
6907    };
6908
6909    /**
6910     * 모바일 옵션 input클릭시 입력키보드 때문에 css예외처리가 필요하여 클래스 추가.
6911     */
6912    var addEventMobileOptionInput = function () {
6913        $options.find('input.form-control').focus(function () {
6914            if (!$('body').hasClass('mobile_focus_on')) {
6915                $('body').addClass('mobile_focus_on');
6916            }
6917        });
6918        $options.find('input').blur(function () {
6919            if ($('body').hasClass('mobile_focus_on')) {
6920                $('body').removeClass('mobile_focus_on');
6921            }
6922        });
6923    };
6924
6925    /**
6926     * 쇼핑 카테고리 목록에서 상품 상세페이지 레이어팝업 띄우는 함수
6927	 * @param idx
6928	 * @param back_url
6929	 * @param is_prod_detail_page: 상품상세페이지에서 레이어팝업 - 상품 상세 Dialog - 을 띄울 경우 'Y'
6930	 * @param is_mobile
6931	 * @param prod_idx_org
6932     */
6933    var openProdDetailFromShoppingList = function (idx, back_url, is_prod_detail_page, is_mobile, prod_idx_org) {
6934        $.ajax({
6935            type: 'GET',
6936            data: { 'idx': idx, 'is_prod_detail_page': is_prod_detail_page },
6937            url: ('/shop/prod_detail_from_shopping_list.cm'),
6938            dataType: 'html',
6939            cache: true,
6940            async: false,
6941            success: function (result) {
6942                $.cocoaDialog.open({ type: 'prod_detail_from_shopping_list', custom_popup: result, 'close_block': false });
6943                const currentUrl = location.href;
6944                history.replaceState(null, null, currentUrl + '?idx=' + idx);
6945                $('.modal_prod_detail_from_shopping_list').addClass('custom_tooltip');
6946
6947                if (is_prod_detail_page == 'Y' && is_mobile) {
6948                    var $mobile_action_btn_wrap = $('._mobile_action_btn_wrap');
6949                    $mobile_action_btn_wrap.hide();
6950                }
6951
6952				// 모달이 닫힐 때 실행되는 콜백 붙이는 부분
6953                $('.modal_prod_detail_from_shopping_list').on('hidden.bs.modal', function (e) {
6954                    $('.modal_prod_detail_from_shopping_list').find('.modal-content').html(''); // close 해도 html 이 남아있어서 스크립트가 재실행이 안 되므로 html 을 삭제시킴
6955                    $('html').removeClass('mobile-shop-open');
6956                    history.replaceState(null, null, currentUrl);
6957
6958                    if (is_prod_detail_page == 'Y') {
6959                        initDetail({
6960                            prod_idx: prod_idx_org,
6961														prod_code: prod_code_org,
6962                            prod_price: prod_price_org,
6963                            require_option_count: require_option_count_org,
6964							use_image_optimizer_on_product_detail_markup: use_lazy_load_org,
6965                            shop_use_full_load: use_lazy_load_org,
6966                            shop_view_tab_display: tab_type_org,
6967                            is_site_page: is_site_page_org,
6968                            prod_type: prod_type_org,
6969                            is_prod_detail_page: false,
6970                            is_price_view_permission: is_view_price,
6971                            cm_style: options.cm_style,
6972                            section_code: section_code
6973                        });
6974
6975                        if (is_mobile) $mobile_action_btn_wrap.show();
6976                    }
6977                });
6978
6979                if (checkUseHistory()) { // 뒤로가기 누를 경우 모달만 꺼지고 현재 페이지에 남아있도록
6980                    history.pushState(null, null, location.href);
6981                    window.onpopstate = function () {
6982                        if ($('.modal_prod_detail_from_shopping_list').hasClass('in')) {
6983                            $.cocoaDialog.close();
6984                        }
6985                    };
6986                }
6987
6988				// 최근 본 상품, refresh
6989				if (window.RECENT_PRODUCT && typeof window.RECENT_PRODUCT.refresh === 'function') {
6990					window.RECENT_PRODUCT.refresh();
6991				}
6992            }
6993        });
6994    };
6995    var getReviewCountFromShoppingList = function (prod_code_list) {
6996        $.ajax({
6997            type: 'POST',
6998            data: { 'prod_code_list': prod_code_list },
6999            url: ('/shop/get_review_count_from_shopping_list.cm'),
7000            dataType: 'json',
7001            cache: false,
7002            async: true,
7003            success: function (res) {
7004                // $('"#' +  + '"').find("._review_count_text").text(res.review_count);
7005                // $(prod_code).find("._wish_count_text").text(res.wish_count);
7006            }
7007        });
7008    };
7009
7010    /**
7011     * review, qna 권한이 buyer일때 체크 모달
7012     * @param prod_code
7013     * @param type
7014     */
7015    var openBuyerReview = function (prod_code) {
7016        $.ajax({
7017            type: 'POST',
7018            data: { 'prod_code': prod_code },
7019            url: ('/shop/open_buyer_review.cm'),
7020            dataType: 'json',
7021            cache: false,
7022            async: false,
7023            success: function (result) {
7024                if (result.msg === 'SUCCESS') {
7025                    $.cocoaDialog.open({
7026                        type: 'eduModal', custom_popup: result.html, width: 800, hide_event: function () {
7027                        }
7028                    });
7029                } else {
7030                    alert(result.msg);
7031                }
7032            }
7033        });
7034    };
7035
7036    var deleteReviewImage = function (obj) {
7037        var box_obj = obj.parent().parent();
7038        obj.parent().remove();
7039        if (box_obj.find('.file-add').length == 0) box_obj.hide();
7040    };
7041
7042    /**
7043     * 상품상세 페이지에 뿌려지는 review, qna 의 카운트 가져ì˜
7043¤ê¸°.
7044     * @param prod_code
7045     */
7046    var getReviewQnaCount = function (prod_code) {
7047        $.ajax({
7048            type: 'POST',
7049            data: { 'prod_code': prod_code },
7050            url: ('/shop/get_review_qna_count.cm'),
7051            dataType: 'json',
7052            cache: false,
7053            async: true,
7054            success: function (res) {
7055                setReviewQnaCountText($("._review_count_text"), res.review_count);
7056                setReviewQnaCountText($("._qna_count_text"), res.qna_count);
7057            }
7058        });
7059    };
7060
7061    var setReviewQnaCountText = function ($target, count) {
7062        $target.text(count);
7063    };
7064
7065    var getCurrentProdNo = function () {
7066        return current_prod_idx;
7067    };
7068
7069    // current_prod_code 는 initDetail 이 마지막으로 초기화한 값이라 페이지를 떠나도 남는다.
7070    // 리스트 페이지에서 퀵뷰를 열면 모달의 initDetail 이 값을 덮어쓰는데, 모달을 닫을 때의 복원은
7071    // 상품 상세 위에서 띄운 경우에만 동작하므로 리스트 페이지에는 잔류값이 그대로 남는다.
7072    // 모달이 닫힐 때 모달 본문 마크업이 지워지는 것을 이용해, 상품 상세 마크업이 지금 문서에
7073    // 붙어 있을 때만 "고객이 지금 보고 있는 상품"으로 인정한다.
7074    var isProdDetailMounted = function () {
7075        return $('#prod_goods_form').length > 0;
7076    };
7077
7078    // 이름과 달리 getCurrentProdNo 가 돌려주는 것은 idx 다. 상품 코드가 필요한 쪽은 이것을 쓴다.
7079    var getCurrentProdCode = function () {
7080        if (!isProdDetailMounted()) return '';
7081        return current_prod_code;
7082    };
7083
7084    var saveSelectedProd = function () {
7085        if (typeof window.sessionStorage === "undefined") {
7086            return false;
7087        }
7088
7089        // 초기화
7090        var session_storage_keys = Object.keys(sessionStorage);
7091        session_storage_keys.forEach(function (_key) {
7092            if (_key.indexOf('PROD_SELECTED_OPTION_') !== -1) {
7093                sessionStorage.removeItem(_key);
7094            }
7095        });
7096
7097        // 마지막으로 본 것만 남도록 추가
7098        sessionStorage.setItem("LAST_SELECTED_PROD", current_prod_idx);
7099        sessionStorage.setItem(("PROD_SELECTED_OPTION_" + current_prod_idx), JSON.stringify({
7100            'prodIdx': current_prod_idx,
7101            'optDataList': selected_options,
7102            'orderCount': order_count
7103        }));
7104    };
7105
7106    /**
7107     * 로그인 이전에 선택한 옵션이 세션에 저장되어 있을 경우 로그인 후 해당 옵션을 다시 설정해줌
7108     * @param is_set
7109     * @returns {boolean}
7110     */
7111    var setSelectedProd = function (is_set) {
7112        if (typeof window.sessionStorage === "undefined") {
7113            return false;
7114        }
7115
7116        var selected_data = JSON.parse(sessionStorage.getItem("PROD_SELECTED_OPTION_" + current_prod_idx));
7117        var last_prod_idx = sessionStorage.getItem("LAST_SELECTED_PROD");
7118
7119        // 초기화
7120        var session_storage_keys = Object.keys(sessionStorage);
7121        session_storage_keys.forEach(function (_key) {
7122            if (_key.indexOf('PROD_SELECTED_OPTION_') !== -1) {
7123                sessionStorage.removeItem(_key);
7124            }
7125        });
7126
7127        // 마지막으로 본 상품이랑 일치하지 않는경우 리셋하고 끝
7128        if (last_prod_idx != current_prod_idx) return false;
7129        if (typeof selected_data == "undefined") return false;
7130        if (selected_data == null) return false;
7131        if (Object.keys(selected_data).length <= 0) return false;
7132        if (!is_set) return false;
7133
7134        for (var _key in selected_data) {
7135            switch (_key) {
7136                case "optDataList":
7137                    selected_options = selected_data[_key];
7138                    break;
7139                case "orderCount":
7140                    order_count = selected_data[_key];
7141                    break;
7142            }
7143        }
7144        updateSelectedOptions('prod');
7145        if (IS_MOBILE) {
7146            showMobileOptions(options.only_regularly ? 'regularly' : 'buy');
7147        } else {
7148            showPCOptions('order');
7149        }
7150    };
7151
7152    var openCouponDownload = function () {
7153        $.ajax({
7154            type: 'POST',
7155            data: {},
7156            url: ('/shop/open_coupon_download.cm'),
7157            dataType: 'json',
7158            cache: false,
7159            async: false,
7160            success: function (result) {
7161                if (result.msg === 'SUCCESS') {
7162                    $.cocoaDialog.open({ type: 'site_alert', custom_popup: result.html, width: 800 });
7163                } else {
7164                    alert(result.msg);
7165                }
7166            }
7167        });
7168    };
7169
7170    var setReviewCarousel = function () {
7171        var checkWidth = $(window).width();
7172        var owl = $('._review_modal_body .owl-carousel');
7173        if (checkWidth > 768) {
7174            owl.owlCarousel({
7175                'loop': owl.children().length > 1,
7176                'margin': 0,
7177                'nav': false,
7178                'items': 1
7179            });
7180        }
7181    };
7182
7183    var setReviewClass = function (idx) {
7184        if (idx <= 0) {
7185            $body.toggleClass('no-images', true);
7186            $body.find('.modal_prod_detail.review .modal-content').toggleClass('clearfix', true);
7187        } else {
7188            $body.toggleClass('no-images', false);
7189            $body.find('.modal_prod_detail.review .modal-content').toggleClass('clearfix', false);
7190        }
7191    };
7192
7193    var setReviewBtnAnimation = function () {
7194        var $btn_nav = $body.find('._btn_nav');
7195        setTimeout(function () {
7196            $btn_nav.attr('aria-hidden', true);
7197        }, 3000);
7198        $body.find('.modal-left').off('touchstart').on('touchstart', function () {
7199            $btn_nav.attr('aria-hidden', false);
7200            setTimeout(function () {
7201                $btn_nav.attr('aria-hidden', true);
7202            }, 3000);
7203        });
7204    };
7205
7206    var setReviewSwipe = function (prev_idx, next_idx) {
7207        $body.find('._review_modal_body').swipe({
7208            swipe: function (event, direction, distance, duration, fingerCount, fingerData) {
7209                if (direction == 'left') {
7210                    if (next_idx > 0) {
7211                        $('body').find('._btn_nav_wrap').remove();
7212                        changeReviewDetail(next_idx);
7213                    }
7214                } else if (direction == 'right') {
7215                    if (prev_idx > 0) {
7216                        $('body').find('._btn_nav_wrap').remove();
7217                        changeReviewDetail(prev_idx);
7218                    }
7219                }
7220            },
7221            threshold: 0,
7222            allowPageScroll: 'vertical',
7223            excludedElements: 'button, a, textarea, input'
7224        });
7225    };
7226
7227    var dispatchSeeMoreEvent = function (expanded) {
7228        try {
7229            document.dispatchEvent(new CustomEvent('imweb:prod_detail:seemore', {
7230                detail: {
7231                    expanded: expanded
7232                }
7233            }));
7234        } catch (e) {
7235            // CustomEvent 미지원 환경 대비
7236        }
7237    };
7238
7239    var showSeeMoreButton = function () {
7240        $prod_detail_content_pc.toggleClass('active', true);
7241        $prod_detail_content_mobile.toggleClass('active', true);
7242        dispatchSeeMoreEvent(true);
7243    };
7244    var hideSeeMoreButton = function () {
7245        $prod_detail_content_pc.toggleClass('active', false);
7246        $prod_detail_content_mobile.toggleClass('active', false);
7247        dispatchSeeMoreEvent(false);
7248    };
7249
7250    var openRequireSelect = function (option_code) {
7251        var $form_select = $('._form_select_wrap_' + option_code);
7252        var $goods_wrap = $body.find('#goods_wrap');
7253        $form_select.addClass('open');
7254        if ($('body').hasClass('mobile_focus_on')) {
7255            $('body').removeClass('mobile_focus_on');
7256        }
7257        var top = Math.floor($form_select.offset().top); // 셀렉트 박스의 위치
7258        var scroll_top = Math.floor($goods_wrap.scrollTop()); // 컨테이너의 스크롤 top 위치
7259        var position_top = $('#option_' + option_code).position().top;
7260        var height = Math.floor($('.opt-group').height()); // 스크롤 영역의 높이
7261        var default_value = 180;
7262
7263        $form_select.parent('._form_parent').nextAll().toggleClass('disabled', true);
7264        if (top - height >= default_value) {
7265            $goods_wrap.animate({ scrollTop: position_top - 50 }, 0);
7266            if (scroll_top >= default_value) {
7267                $goods_wrap.animate({ scrollTop: position_top + $('.option_box_wrap').outerHeight() }, 0);
7268            }
7269        }
7270    };
7271
7272    var addRegularlyCart = function () {
7273        addCart(function () {
7274            showAddCartAlarm();
7275        });
7276    };
7277
7278	var toggleWishButtons = function($wishButtons, wishCount, showWishCount, isActive) {
7279		const handleRoot = ($root) => {
7280			if (isActive) {
7281				$root.addClass('active');
7282			} else {
7283				$root.removeClass('active');
7284			}
7285		}
7286
7287		const handleIcon = ($root) => {
7288			const $icon = $($root).find('._wish_button_icon');
7289
7290			if (isActive) {
7291				$icon.removeClass('im-ico-like');
7292				$root.addClass('active');
7293				$icon.addClass('im-ico-liked');
7294			} else {
7295				$icon.removeClass('im-ico-liked');
7296				$root.removeClass('active');
7297				$icon.addClass('im-ico-like');
7298			}
7299		}
7300
7301		const handleText = ($root) => {
7302			const $label = $($root).find('._wish_button_count_label');
7303
7304			if (showWishCount) {
7305				$label.text(wishCount);
7306			} else {
7307				$label.text('');
7308			}
7309		}
7310
7311		$wishButtons.each(($idx, root) => {
7312			const $root = $(root);
7313
7314			handleRoot($root);
7315			handleIcon($root);
7316			handleText($root);
7317		});
7318	}
7319
7320
7321	/**
7322	 *
7323	 * @returns {Promise}
7324	 * 사은품 선택 다이얼로그를 띄우고, 사은품 선택이 완료되면 resolve 를 호출한다. 이때 유저가 선택한 사은품을 획득한다.
7325	 */
7326	async function selectFreebieAsync () {
7327		cart_type = document.querySelector('#prod_period input[name=add_cart_type]:checked')?.value || 'normal';
7328		if (!isFreebieMfeMounted() || cart_type === 'regularly' || !checkRequireOption()) {
7329			// 사은품 미사용 사이트이거나, 정기결제 상품인 경우 사은품 선택을 하지 않는다.
7330			return Promise.resolve([]);
7331		}
7332
7333		// selected_options
7334		const items = [
7335			...(selected_options.filter(optionSelected => optionSelected.require).length === 0
7336			// 옵션이 없거나, 필수 옵션이 없으면 본품을 추가한다.
7337				? [{
7338						prodIdx: current_prod_idx,
7339						// prod code는 브라우저에서 채워넣는다.
7340						prodCode: null,
7341						optionDetailCode: "",
7342						prodOptions: [],
7343						qty: order_count,
7344					}]
7345				: []),
7346			...(selected_options.map((option) => {
7347				return {
7348					prodIdx: current_prod_idx,
7349					prodCode: null,
7350					qty: option.count,
7351					optionDetailCode: option.option_mix_type === "SINGLE" ? undefined : option.option_detail_code,
7352					prodOptions: option.options.filter(option => option.value_type === 'SELECT').map((option) => {
7353						return {
7354							optionCode: option.option_code,
7355							valueCode: option.value_code,
7356						}
7357					}),
7358				}
7359			}))
7360		];
7361
7362		// selected_prod_additionals
7363		// selected_prod_additional_options
7364		const item_additional = selected_prod_additionals.flatMap((additional) => {
7365			const additionalOptions = selected_prod_additional_options
7366				.filter(option => option.prod_idx === additional.idx);
7367
7368			if (additionalOptions.length === 0) {
7369				return [{
7370					prodIdx: additional.idx,
7371					prodCode: null,
7372					optionDetailCode: '',
7373					prodOptions: [],
7374					qty: additional.count,
7375				}]
7376			}
7377			return additionalOptions.map((option) => {
7378				return {
7379					prodIdx: additional.idx,
7380					prodCode: null,
7381					optionDetailCode: option.option_detail_code,
7382					prodOptions: option.options.filter(option => option.value_type === 'SELECT').map((option) => {
7383						return {
7384							optionCode: option.option_code,
7385							valueCode: option.value_code,
7386						}
7387					}),
7388					qty: option.count,
7389				}
7390			});
7391		})
7392
7393		return new Promise((resolve) => {
7394			// TODO: 사려고 하는 상품의 정보를 전달해줘야 함.
7395			document.dispatchEvent(new CustomEvent('@im/fo-prod-detail-freebie:openDialogSelectFreebie', { detail : {
7396				callback: resolve,
7397				orderItems: [...items, ...item_additional],
7398			} } ))
7399		})
7400	}
7401	/**
7402	 * 장바구니에 상품이 있는지 체크해서 있으면 장바구니 상품 구매유도 알럿을 띄우고 아니면 바로 주문으로 이동하는 메소드입니다.
7403	 * type: "guest_login" | "npay" | "talkpay" | "normal"
7404	 */
7405	async function confirmOrderWithCartItems(type, backurl, params) {
7406		if (!ensureAddOrderPermission()) {
7407			return;
7408		}
7409
7410        const verified = await SHIPPING_SERVICE.verify({ type });
7411        if (!verified) {
7412            return;
7413        }
7414
7415		if (!options.is_price_view_permission &&
7416			window.IS_GUEST &&
7417			cm_data.use_login_popup === 'Y' &&
7418			options.is_using_nomember_order_login_after
7419		) {
7420			// 구매 권한이 전체가 아닌 상품을 비회원이 구매 하려는 경우 사이트 공통 디자인이 로그인 모달을 사용중이라면 비회원 바로구매 기능이 켜져있는 경우 로그인 모달을 띄워준다.
7421			// 비회원 바로 구매기능이 켜져있지 않다면, site_ui.cls 의 getAnchor 함수를 통해 로그인이 모달이 필요한 상황에 로그인 모달을 표시할 수 있지만,
7422			// 비회원 바로 구매기능이 켜져있는 경우에는 로그인 모달을 보여주는 처리를 해주고 있지 않기 때문.
7423			// site_ui.cls 의 getAnchor 함수에서 해당 로직을 처리하지 않는 이유는 getAnchor 함수는 $params 인자로 받는 도착지 url의 상품 또는 상품 카테고리에 대한 정보를 얻을 수 없기 때문
7424			window.SITE_MEMBER.openLogin(
7425				encodeURIComponent(base64Encode(window.location.href)),
7426				'payment',
7427				_confirmOrderWithCartItems,
7428				'N',
7429				'payment',
7430			);
7431		} else {
7432			_confirmOrderWithCartItems()
7433		}
7434
7435
7436		function proceedToOrder () {
7437			if(typeof params.onMoveOrder === 'function') {
7438				// params.onMoveOrder 는 talkpay 주문시 전달되는 콜백 함수
7439				params.onMoveOrder()
7440			} else if (window.USE_OMS === true) {
7441				if (type === 'guest_login' || type === 'npay' || type === 'talkpay') {
7442					OMS_addOrder(type, backurl, params);
7443				} else {
7444					// TODO: 사은품 가능 여부 체크 후 선택 UI등 표시
7445					selectFreebieAsync()
7446						.then((selected_freebies) => {
7447							params.selected_freebies = selected_freebies || [];
7448							OMS_addOrder(type, backurl, params)
7449						});
7450				}
7451			} else {
7452				addOrder(type, backurl, params);
7453			}
7454		}
7455
7456		async function _confirmOrderWithCartItems () {
7457			// "regularly" | "normal"
7458			cart_type = document.querySelector('#prod_period input[name=add_cart_type]:checked')?.value || 'normal';
7459			params = params || {};
7459 // 기본값 설정
7460
7461			if (!ensureAddOrderPermission()) {
7462				return;
7463			}
7464
7465			// 장바구니가 비어있는지 체크 api 호출
7466			const res = await fetch('/shop/check_cart_empty.cm', {
7467				method: 'POST',
7468				headers: {
7469					'Content-Type': 'application/x-www-form-urlencoded',
7470				},
7471				body: serializeToUrlencoded({
7472					'prodIdx': current_prod_idx,
7473					'options': selected_options,
7474					'orderCount': order_count,
7475					'deliv_type': $deliv_type,
7476					'deliv_pay_type': $deliv_pay_type,
7477					'cart_type': cart_type
7478				})
7479			});
7480			const result = await res.json();
7481
7482			// 통신이 실패했거나, 장바구니가 비어있거나, 정기결제 상품이거나, 게스트일 경우 바로 주문으로 이동
7483			if (!res.ok || result.isGuest || result.msg !== 'SUCCESS' || result.cart_count === 0 || cart_type === 'regularly' ) {
7484				proceedToOrder();
7485				return;
7486			}
7487			// 카트에 상품이 있으며 정기결제 상품이 아닐 경우 장바구니 추가 구매유도 알럿을 띄움
7488			// 장바구니 추가 구매유도 다이얼로그 띄우기
7489			$confirm_order_with_cart_alarm.show();
7490			$confirm_order_with_cart_alarm.off('click').click(function (e) {
7491				if(this === e.target) {
7492					$(this).hide()
7493				}
7494			})
7495			$confirm_order_with_cart_alarm.find('#move_to_cart').off('click').click(function () {
7496				if (typeof params.onMoveCart === 'function') {
7497					params.onMoveCart();
7498				} else {
7499					// confirmOrderWithCartItems에서 이미 shippingService 검증을 완료했으므로 스킵
7500					addCart(function () {
7501						moveCart();
7502					}, { skipShippingValidation: true });
7503				}
7504				$confirm_order_with_cart_alarm.hide();
7505			});
7506			$confirm_order_with_cart_alarm.find('#buy_now').off('click').click(function () {
7507				proceedToOrder();
7508				$confirm_order_with_cart_alarm.hide()
7509			});
7510		}
7511	}
7512
7513	const trackClickPurchaseShopView = function (paymentButtonType) {
7514		if (typeof BrandScope !== "undefined") {
7515			try {
7516				BrandScope.track('click_purchase_shop_view', {
7517					'action': 'click',
7518					'content': 'purchase',
7519					'where': 'shop_view',
7520					'payment_button_type': paymentButtonType,
7521					'prod_code': current_prod_code,
7522					'prod_type': prod_type,
7523					'is_regularly_prod': false
7524				});
7525			} catch {
7526				// ignore
7527			}
7528		}
7529	};
7530
7531	function getThumbnailBadgeStyleByPosition({ badgeColor, badgePosition, imageWidth = ''}) {
7532		const BADGE_POSITION_LEFT_TOP = 'left_top';
7533		const BADGE_POSITION_BOTTOM = 'bottom';
7534
7535		const containerWidth = imageWidth;
7536		if (!containerWidth) return '';
7537
7538		// 최소/최대 프레임 너비 설정
7539		const MIN_FRAME_WIDTH = 120;
7540		const MAX_FRAME_WIDTH = 800;
7541
7542		// 프레임 너비 기준
7543		const frameWidth = Math.max(MIN_FRAME_WIDTH, Math.min(MAX_FRAME_WIDTH, containerWidth));
7544
7545		// 프레임 너비에 따른 비율 계산
7546		// 기준 frameWidth: 308px
7547		const leftTopFontRatio = 0.035;   // frameWidth × 0.035 (308px일 때 약 10.78px)
7548		const leftTopPaddingRatio = 0.015; // frameWidth × 0.015 (308px일 때 약 4.62px)
7549		const bottomFontRatio = 0.04;     // frameWidth × 0.04 (308px일 때 약 12.32px)
7550		const bottomPaddingRatio = 0.02;  // frameWidth × 0.02 (308px일 때 약 6.16px)
7551
7552		// 실제 크기 계산
7553		let leftTopFontSize = Math.round(frameWidth * leftTopFontRatio * 1000) / 1000;
7554		let leftTopPadding = Math.round(frameWidth * leftTopPaddingRatio * 1000) / 1000;
7555		let bottomFontSize = Math.round(frameWidth * bottomFontRatio * 1000) / 1000;
7556		let bottomPadding = Math.round(frameWidth * bottomPaddingRatio * 1000) / 1000;
7557
7558		// 최소/최대 값 제한
7559		leftTopFontSize = Math.max(10, Math.min(13, leftTopFontSize));   // min: 9px / max: 13px
7560		leftTopPadding = Math.max(3, Math.min(6, leftTopPadding));     // min: 3px / max: 6px
7561		bottomFontSize = Math.max(12, Math.min(14, bottomFontSize));   // min: 12px / max: 14px
7562		bottomPadding = Math.max(4, Math.min(8, bottomPadding));       // min: 4px / max: 8px
7563
7564		// badgeLeftTopGapSize 추가 (기존 로직 보존)
7565		const badgeLeftTopGapSize = roundTo3(leftTopFontSize * (8 / 28));
7566
7567		if (badgePosition === BADGE_POSITION_LEFT_TOP) {
7568			const style = [
7569				'position: absolute',
7570				'border-radius: 3px',
7571				`top: ${badgeLeftTopGapSize}px`,
7572				`left: ${badgeLeftTopGapSize}px`,
7573				`padding: ${leftTopPadding}px`,
7574				'color: #FFF',
7575				'vertical-align: middle',
7576				'display: inline-flex',
7577				'align-items: center',
7578				'justify-content: center',
7579				`background-color: ${badgeColor}`,
7580				'z-index: 10',
7581				`font-size: ${leftTopFontSize}px;`
7582			].join('; ');
7583			return style;
7584		}
7585
7586		if (badgePosition === BADGE_POSITION_BOTTOM) {
7587			const style = [
7588				'position: absolute',
7589				'left: 0',
7590				'bottom: 0',
7591				'width: 100%',
7592				`padding: ${bottomPadding}px`,
7593				'color: #FFF',
7594				'vertical-align: middle',
7595				'display: inline-flex',
7596				'align-items: center',
7597				'justify-content: center',
7598				`background-color: ${badgeColor}`,
7599				'z-index: 10',
7600				`font-size: ${bottomFontSize}px;`
7601			].join('; ');
7602			return style;
7603		}
7604
7605		return '';
7606	}
7607
7608	function roundTo3(val) {
7609		return Math.round(val * 1000) / 1000;
7610	}
7611
7612    // BO sales tool 미리보기 트리거 수신: URL에 sales_tool_preview 파라미터가 있을 때(=미리보기 모드)에만
7613    // 부모 BO에서 보낸 메시지에 따라 알림 모달이나 옵션 시트를 즉시 띄운다.
7614    var salesToolPreviewParams = (function () {
7615        try {
7616            return new URLSearchParams(window.location.search);
7617        } catch (e) {
7618            return null;
7619        }
7620    })();
7621    var isInSalesToolPreview = salesToolPreviewParams ? salesToolPreviewParams.has('sales_tool_preview') : false;
7622    var salesToolPreviewDevice = salesToolPreviewParams ? salesToolPreviewParams.get('device') : null;
7623    _isFreeShipNotifySalesToolPreview = isInSalesToolPreview
7624        && salesToolPreviewParams.get('sales_tool_preview') === 'freeShipNotify';
7625    var _isFreeShipNotifyTemplatePreview = _isFreeShipNotifySalesToolPreview
7626        && (parseInt(salesToolPreviewParams.get('idx') || '0', 10) || 0) <= 0;
7627
7628    function isAllowedSalesToolPreviewOrigin(origin) {
7629        if (origin === window.location.origin) return true;
7630        if (/(^|\.)imtest\.me$/.test(window.location.hostname) && origin === 'https://localhost:5173') return true;
7631        return /^https:\/\/[a-z0-9-]+\.crm\.(imweb|imtest)\.me$/.test(origin);
7632    }
7633
7634    function getSalesToolPreviewParentOrigin() {
7635        try {
7636            if (document.referrer) {
7637                return new URL(document.referrer).origin;
7638            }
7639        } catch (e) {}
7640        return '*';
7641    }
7642
7643    function notifySalesToolPreviewReady() {
7644        if (!isInSalesToolPreview || !window.parent || window.parent === window) return;
7645        if (!_initDetailDone) return;
7646        if (_isFreeShipNotifySalesToolPreview && !isFreeShipNotifyPreviewReady()) {
7647            if (!_isWaitingFreeShipNotifyPreviewReadyToNotify) {
7648                _isWaitingFreeShipNotifyPreviewReadyToNotify = true;
7649                waitForFreeShipNotifyPreviewReady(function () {
7650                    _isWaitingFreeShipNotifyPreviewReadyToNotify = false;
7651                    notifySalesToolPreviewReady();
7652                });
7653            }
7654            return;
7655        }
7656        window.parent.postMessage({ type: 'sales-tool-preview:ready' }, getSalesToolPreviewParentOrigin());
7657    }
7658
7659    if (isInSalesToolPreview) {
7660        // body 마커 클래스 — BO sales tool 미리보기 컨텍스트 식별 (CSS 분기/디버깅 용도)
7661        // 실제 스크롤바 숨김 등 스타일은 site2.css 의 body.sales-tool-preview 룰이 담당.
7662        var addSalesToolPreviewBodyClass = function () {
7663            if (document.body) {
7664                document.body.classList.add('sales-tool-preview');
7665            }
7666        };
7667        if (document.body) {
7668            addSalesToolPreviewBodyClass();
7669        } else {
7670            document.addEventListener('DOMContentLoaded', addSalesToolPreviewBodyClass);
7671        }
7672        window.addEventListener('imweb:freeShipNotify:sdk-ready', notifySalesToolPreviewReady);
7673
7674        _handleSalesToolPreviewMessage = function (data) {
7675            if (data.type === 'sales-tool-preview:request-ready') {
7676                notifySalesToolPreviewReady();
7677                return;
7678            }
7679
7680            if (data.type === 'sales-tool-preview:show-add-cart-alarm') {
7681                showAddCartAlarm();
7682                return;
7683            }
7684
7685            // BO 미리보기 — 마스터 토글 off 나 스타일 전환 시 명시적으로 알림 모달을 닫는다.
7686            if (data.type === 'sales-tool-preview:hide-add-cart-alarm') {
7687                hideAddCartAlarm();
7688                return;
7689            }
7690
7691            if (data.type === 'sales-tool-preview:show-options') {
7692                // 기존 알림 모달이 떠 있다면 닫기 (cocoaDialog/imSheet 양쪽 idempotent 처리)
7693                hideAddCartAlarm();
7694
7695                if (salesToolPreviewDevice === 'mobile') {
7696                    showMobileOptions('buy');
7697                } else if (salesToolPreviewDevice === 'desktop' && shop_view_style === 'b') {
7698                    showPCOptions('buy', undefined, true);
7699                }
7700                return;
7701            }
7702
7703            // BO 미리보기 — 마스터 토글 off 나 스타일 전환 시 옵션 패널을 닫는다 (mobile/PC 양쪽 idempotent).
7704            if (data.type === 'sales-tool-preview:hide-options') {
7705                hideMobileOptions();
7706                hidePCOptions();
7707                return;
7708            }
7709
7710            // mfe magnet에 직접 전달되는 동적 설정 — 페이로드를 그대로 CustomEvent로 re-dispatch.
7711            // mfe(fo-shopping/free-ship-notify)의 freeShipNotifySettings 스토어가 이 이벤트를 구독해 즉시 반영한다.
7712            // 파일 상단의 imweb:freeShipNotify:settings:update 리스너가 --free-ship-notify-accent도 함께 따라간다.
7713            if (data.type === 'sales-tool-preview:free-ship-notify-settings'
7714                || data.type === 'sales-tool-preview:settings') {
7715                var settingsPayload = data.payload || {};
7716                window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:settings:update', {
7717                    detail: settingsPayload
7718                }));
7719
7720                // shopDetailStyle이 CART_POPUP이 아닌 값으로 전환되면 떠있는 장바구니 담기 알림 시트/모달을 닫는다.
7721                // (장바구니 담기 알림은 CART_POPUP 모드 전용이므로 다른 모드 전환 시 잔존하면 backdrop이 남는 증상이 생긴다.)
7722                // BO가 명시적 hide-add-cart-alarm을 보내지 않더라도 settings 전이만으로 안전하게 정리되도록 방어한다.
7723                if (typeof settingsPayload.shopDetailStyle === 'string'
7724                    && settingsPayload.shopDetailStyle !== 'cart_popup') {
7725                    hideAddCartAlarm();
7726                }
7727            }
7728        };
7729
7730        window.addEventListener('message', function (event) {
7731            if (!isAllowedSalesToolPreviewOrigin(event.origin)) return;
7732            var data = event.data;
7733            if (!data || typeof data !== 'object') return;
7734
7735            if (!_initDetailDone) {
7736                _pendingSalesToolPreviewMessages.push(data);
7737                return;
7738            }
7739            _handleSalesToolPreviewMessage(data);
7740        });
7741    }
7742
7743    return {
7744        addProdWish: function (prod_code, back_url) {
7745            addProdWish(prod_code, back_url);
7746        },
7747        /** type (prod/cart) */
7748        increaseOptionCount: function (optNo, type) {
7749            increaseOptionCount(optNo, function () {
7750                updateSelectedOptions(type);
7751            });
7752        },
7753        /** type (prod/cart) */
7754        decreaseOptionCount: function (optNo, type) {
7755            decreaseOptionCount(optNo, function () {
7756                updateSelectedOptions(type);
7757            });
7758        },
7759	      /** type (prod/cart) */
7760        increaseProdAdditionalOptionCount: function (optNo) {
7761            increaseProdAdditionalOptionCount(optNo, function () {
7762                updateSelectedOptions('prod');
7763            });
7764        },
7765        /** type (prod/cart) */
7766        decreaseProdAdditionalOptionCount: function (optNo) {
7767            decreaseProdAdditionalOptionCount(optNo, function () {
7768                updateSelectedOptions('prod');
7769            });
7770        },
7771        changeProdAdditionalOptionCount: function (optNo, toBeCount) {
7772            changeProdAdditionalOptionCount(optNo, toBeCount, function () {
7773                updateSelectedOptions('prod');
7774            });
7775        },
7776        // initDetail : function(prodIdx, price, requireOptionCnt, use_np_mobile, use_lazyload, tab_type,is_site_page, prod_type, is_prod_detail_page, view_price, cm_data, section_code){
7777        // 	initDetail(prodIdx, price, requireOptionCnt, use_np_mobile, use_lazyload, tab_type,is_site_page, prod_type, is_prod_detail_page, view_price, cm_data, section_code);
7778        // },
7779        initDetail: function (option) {
7780            initDetail(option);
7781            this.setShippingTemplateCode(options.shipping_template_code || '');
7782        },
7783        initProdStock: function (stock_use, stock, stock_un_limit) {
7784            initProdStock(stock_use, stock, stock_un_limit);
7785        },
7786        initLocalize: function (code) {
7787            initLocalize(code);
7788        },
7789        selectOption: function (prod_idx, {options, require, count, idx = null, skip_quantity_validation = false}, success, failed) {
7790            selectOption(
7791                prod_idx,
7792                {
7793                    options: options,
7794                    require : require,
7795                    count : count,
7796                    idx : idx,
7797                    skip_quantity_validation : skip_quantity_validation
7798                },
7799                function () {
7800                    success();
7801                },
7802                function (msg) {
7803                    failed(msg);
7804                }
7805            );
7806        },
7807        removeSelectedOption: function (optNo, type) {
7808            removeSelectedOption(optNo, function () {
7809                selected_require_options = [];		// 선택된 필수 옵션 초기화
7810                loadOption(type, current_prod_idx);		// 옵션을 다시 로드함
7811                updateSelectedOptions(type);
7812            });
7813        },
7814        resetSelectedRequireOptions: function () {
7815            selected_require_options = [];
7816        },
7817	      removeSelectedProdAdditional: function (prodIdx, type) {
7818		      removeSelectedProdAdditional(prodIdx, function () {
7819                loadProdAdditional(current_prod_idx);		// 옵션을 다시 로드함
7820	              updateSelectedOptions(type);
7821            });
7822        },
7823	      removeSelectedProdAdditionalOption: function (optNo, type) {
7824		      removeSelectedProdAdditionalOption(optNo, function () {
7825                loadProdAdditional(current_prod_idx);		// 옵션을 다시 로드함
7826	              updateSelectedOptions(type);
7827            });
7828        },
7829        selectRequireOption: function (type, prod_idx, option_code, value_code, value_name, success) {
7830            selectRequireOption(type, prod_idx, option_code, value_code, value_name, function () {
7831                success();
7832            });
7833        },
7834	      selectProdAdditionalRequireOption: (prod_idx, option_code, value_code, value_name, success, is_shop_free_ship_notify = false) => {
7835			      var data = { 'value_type': 'SELECT', 'option_code': option_code, 'value_code': value_code, 'value_name': value_name };
7836			      var no = findSelectedProdAdditionalRequireOption(option_code);
7837						/* @TODO selected_require_options 세분화해야 함(아니면 option_code만으로 구분 가능하게 처리하거나 -> length 비교하므로 분리되어야 하는 게 맞음 */
7838			      if (no == -1) {
7839				      if (value_code == '') return;
7840				      /** 처음 선택된 옵션인 경우 새로 추가*/
7841				      selected_prod_additional_require_options.push(data);
7842			      } else {
7843				      if (value_code == '') {
7844					      /** 옵션 삭제 */
7845					      selected_prod_additional_require_options.splice(no, (selected_prod_additional_require_options.length - no));
7846				      } else {
7847					      selected_prod_additional_require_options[no] = data;
7848					      /** 이미 선택된 옵션인 경우 기존 값 교체 */
7849					      if (no < selected_prod_additional_require_options.length - 1) {
7850						      selected_prod_additional_require_options.splice(no + 1, (selected_prod_additional_require_options.length - (no + 1)));
7851					      }
7852				      }
7853			      }
7854			      if (selected_prod_additional_require_options.length == prod_additional_require_option_count[prod_idx]) {
7855							if(isFreeShipNotifyAdditionalActive()){
7856								// 필수 옵션이 모두 선택되었다고 하더라도 무료 배송 안내 유도 상품 모달에서는 추가 버튼을 눌러야 반영
7857								// 추가하기 버튼 누를 때까지 저장처리하지 않고 선택 처리만 한 상태로 대기
7858								loadProdAdditionalOptions(prod_idx);
7859							}else{
7860								/** 필수 옵션이 모두 선택되었을 경우*/
7861								selectProdAdditionalOption(prod_idx, selected_prod_additional_require_options, true, 1, function () {
7862									if(is_mobile_width){
7863										imSheet.close('prod_additional_sheet');
7864									}
7865									current_select_additional_prod_idx = 0;
7866									selected_prod_additional_require_options = [];
7867									$prod_additional_options.html('');
7868									$('#prod_additional_drop_menu_content').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // PC
7869									$('#btnProdAdditionalSelect').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // 모바일
7870									success();
7871								}, function (msg) {
7872									alert(msg);
7873								});
7874							}
7875			      } else {
7876				      /** 필수옵션 선택이 아직 끝나지 않았을 경우 옵션 재로드 */
7877				      loadProdAdditionalOptions(prod_idx);
7878			      }
7879	      },
7880	      completeAdditionalProductSelection: () => {
7881		      try {
7882			      // 선택된 추가 상품이 있는 경우 selectProdAdditionalOption 호출
7883			      if (current_select_additional_prod_idx && current_select_additional_prod_idx > 0) {
7884				      var requireCount = prod_additional_require_option_count[current_select_additional_prod_idx] || 0;
7885				      if (selected_prod_additional_require_options.length < requireCount) {
7886					      alert(LOCALIZE.설명_필수옵션이모두선택되어있ì
7886§€ì•ŠìŠµë‹ˆë‹¤());
7887					      return;
7888				      }
7889				      selectProdAdditionalOption(current_select_additional_prod_idx, selected_prod_additional_require_options, true, 1, function () {
7890					      if(is_mobile_width){
7891						      imSheet.close('prod_additional_sheet');
7892					      }
7893					      current_select_additional_prod_idx = 0;
7894					      selected_prod_additional_require_options = [];
7895					      $prod_additional_options.html('');
7896					      $('#prod_additional_drop_menu_content').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // PC
7897					      $('#btnProdAdditionalSelect').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // 모바일
7898
7899					      var $optionModal = $('.modal_site_shop_free_ship_notify_option.in');
7900					      if ($optionModal.length > 0) {
7901						      $optionModal.modal('hide');
7902					      } else if (typeof $ !== 'undefined' && typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
7903						      $.cocoaDialog.close();
7904					      }
7905
7906					      updateSelectedOptions('prod');
7907				      }, function (msg) {
7908					      alert(msg);
7909				      });
7910			      } else {
7911				      var $optionModal = $('.modal_site_shop_free_ship_notify_option.in');
7912				      if ($optionModal.length > 0) {
7913					      $optionModal.modal('hide');
7914				      } else if (typeof $ !== 'undefined' && typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
7915					      $.cocoaDialog.close();
7916				      }
7917
7918				      updateSelectedOptions('prod');
7919			      }
7920		      } catch (error) {
7921			      console.error('[SITE_SHOP_DETAIL] 추가 상품 선택 완료 처리 중 오류:', error);
7922		      }
7923	      },
7924	      // 추가 상품 옵션 모달 진입 시 호출. 해당 prod_idx의 현재 selected_prod_additionals 항목과
7925	      // selected_prod_additional_options를 snapshot으로 반환한다. cancel/backdrop close 시
7926	      // restoreProdAdditionalForModal에 전달하면 진입 직전 상태로 복원되어, 같은 상품을 재추가하다가
7927	      // 취소해도 기존에 담았던 옵션이 잘못 사라지지 않는다.
7928	      snapshotProdAdditionalForModal: function (idx) {
7929		      var additional = selected_prod_additionals.find(function (v) { return v.idx === idx; });
7930		      var optionsForIdx = selected_prod_additional_options.filter(function (o) { return o.prod_idx === idx; });
7931		      return {
7932			      exists: !!additional,
7933			      count: additional ? additional.count : 0,
7934			      options: optionsForIdx.map(function (o) { return Object.assign({}, o); })
7935		      };
7936	      },
7937	      // snapshot으로 selected_prod_additionals/options를 복원한다. 진입 직전 항목이 없었으면 제거,
7938	      // 있었으면 count를 snapshot 값으로 되돌리고 options 배열도 snapshot 항목들로 교체.
7939	      restoreProdAdditionalForModal: function (idx, snapshot) {
7940		      if (!snapshot) return;
7941		      if (snapshot.exists) {
7942			      var found = selected_prod_additionals.find(function (v) { return v.idx === idx; });
7943			      if (found) {
7944				      found.count = snapshot.count;
7945			      }
7946		      } else {
7947			      selected_prod_additionals = selected_prod_additionals.filter(function (v) { return v.idx !== idx; });
7948			      delete prod_additional_require_option_count[idx];
7949		      }
7950		      selected_prod_additional_options = selected_prod_additional_options.filter(function (o) { return o.prod_idx !== idx; });
7951		      if (snapshot.options && snapshot.options.length > 0) {
7952			      selected_prod_additional_options = selected_prod_additional_options.concat(snapshot.options);
7953		      }
7954	      },
7955	      selectAdditionalProductForModal: (product_idx, price, name, maximum_purchase_quantity, member_maximum_purchase_quantity, maximum_purchase_quantity_type, optional_limit, optional_limit_type, stock_use, stock, stock_unlimit, option_mix_type, prod_type, period_discount_flag, period_discount_data, period_discout_group_list, membership_discount, additional_creator_discount_data) => {
7956		      try {
7957			      let unselected = [];
7958			      Object.keys(prod_additional_require_option_count).map((key) => {
7959				      if (prod_additional_require_option_count[key] > 0) {
7960					      if(selected_prod_additional_options.filter((option) => option.prod_idx == key)[0]?.options?.length < prod_additional_require_option_count[key]){
7961						      unselected.push(key);
7962					      }
7963				      }
7964			      });
7965
7966			      unselected.forEach((idx) => {
7967				      selected_prod_additionals = selected_prod_additionals.filter((prod_additional) =>
7967 prod_additional.idx != idx);
7968				      selected_prod_additional_options = selected_prod_additional_options.filter((option) => option.prod_idx != idx);
7969				      delete prod_additional_require_option_count[idx];
7970			      });
7971
7972			      // @TODO 기존 추가 상품 옵션 초기화, 추가 상품 완료가 안되었다면 selected_prod_additionals에서도 빠져야 함
7973			      selected_prod_additional_require_options = [];
7974
7975			      // 6837-6842라인: 상품 정보를 selected_prod_additionals에 추가
7976			      const prod_additional_found = selected_prod_additionals.find(v=>v.idx===product_idx);
7977			      if(prod_additional_found) {
7978				      prod_additional_found.count++;
7979			      }else{
7980				      selected_prod_additionals.push({
7981					      idx: product_idx,
7982					      price,
7983					      name,
7984					      maximum_purchase_quantity,
7985					      member_maximum_purchase_quantity,
7986					      maximum_purchase_quantity_type,
7987					      optional_limit,
7988					      optional_limit_type,
7989					      stock_use,
7990					      stock,
7991					      stock_unlimit,
7992					      option_mix_type,
7993					      prod_type,
7994					      membership_discount,
7995					      creator_discount_data: additional_creator_discount_data || null,
7996					      count: 1
7997				      });
7998				      setProdAdditionalPeriodDiscountData(product_idx, period_discount_flag, period_discount_data, period_discout_group_list);
7999			      }
8000
8001			      // current_select_additional_prod_idx 설정
8002			      current_select_additional_prod_idx = product_idx;
8003
8004			      if (typeof TEST_SERVER !== 'undefined' && TEST_SERVER) {
8005				      console.log('[SITE_SHOP_DETAIL] selectAdditionalProductForModal 완료:', current_select_additional_prod_idx);
8006			      }
8007		      } catch (error) {
8008			      console.error('[SITE_SHOP_DETAIL] selectAdditionalProductForModal 실행 중 오류:', error);
8009		      }
8010	      },
8011			    selectAdditionalProduct: (product_idx, price, name, maximum_purchase_quantity, member_maximum_purchase_quantity, maximum_purchase_quantity_type, optional_limit, optional_limit_type, stock_use, stock, stock_unlimit, option_mix_type, prod_type, period_discount_flag, period_discount_data, period_discout_group_list, membership_discount, additional_creator_discount_data, is_shop_free_ship_notify = false) => {
8012					// 수량을 조절할 수 없는 상품(디지털/이용권 상품인데 옵션이 없는 경우)이 이미 추가된 상태에서 다시 선택할 시 얼럿 노출
8013			    const prod_additional_found = selected_prod_additionals.find(v=>v.idx===product_idx);
8014			    if(prod_type !== SHOP_CONST.PROD_TYPE.NORMAL && prod_additional_found){
8015						if(selected_prod_additional_options.filter((option) => option.prod_idx == product_idx).length === 0){
8016							// 옵션이 있다면 기존에 추가 시에 옵션도 함께 등록되게 됨
8017							alert(getLocalizeString("설명_이미선택된상품입니다", "", "이미 선택된 상품입니다."));
8018							return false;
8019						}
8020			    }
8021
8022                let unselected = [];
8023                Object.keys(prod_additional_require_option_count).map((key) => {
8024                        if (prod_additional_require_option_count[key] > 0) {
8025                            if(selected_prod_additional_options.filter((option) => option.prod_idx == key)[0]?.options?.length < prod_additional_require_option_count[key]){
8026                                unselected.push(key);
8027                            }
8028                        }
8029                    }
8030                );
8031
8032                if(unselected.length > 0){
8033                    Object.keys(unselected).map((key) => {
8034                        if (prod_additional_require_option_count[key] > 0) {
8035                            selected_prod_additionals = selected_prod_additionals.filter((prod_additional) => prod_additional.idx != key);
8036                            selected_prod_additional_options = selected_prod_additional_options.filter((option) => option.prod_idx != key);
8037                            delete prod_additional_require_option_count[key];
8038                        }
8039                    });
8040                }
8041
8042					// @TODO 기존 추가 상품 옵션 초기화, 추가 상품 완료가 안되었다면 selected_prod_additionals에서도 빠져야 함
8043			    selected_prod_additional_require_options = [];
8044			    if(prod_additional_found) {
8045				    prod_additional_found.count++;
8046			    }else{
8047					    selected_prod_additionals.push({idx: product_idx, price, name, maximum_purchase_quantity, member_maximum_purchase_quantity, maximum_purchase_quantity_type, optional_limit, optional_limit_type, stock_use, stock, stock_unlimit, option_mix_type, prod_type, membership_discount, creator_discount_data: additional_creator_discount_data || null, count: 1});
8048						setProdAdditionalPeriodDiscountData(product_idx, period_discount_flag, period_discount_data, period_discout_group_list);
8049			    }
8050			    current_select_additional_prod_idx = product_idx;
8051			    if(is_mobile_width){
8052						// 상품을 선택했을 때 1depth에는 옵션 목록을 띄
8052우고 2depth에는 다음 옵션 시트를 띄움
8053						// @TODO 1depth 모든 옵션 추가(선택 옵션 포함) > 열리지는 않게 처리하면 될 듯
8054				    loadOption('prod', current_prod_idx);		// 옵션을 다시 로드함
8055				    loadProdAdditionalOptions(product_idx);
8056			    }else{
8057						if(is_shop_free_ship_notify && isFreeShipNotifyAdditionalActive()){
8058							$prod_additional_options = $('#prod_free_ship_notify_additional_options');
8059						}else {
8060							// PC: 메인 옵션은 리로드하지 않음 (리로드시 추가상품 영역이 다시 그려져 인라인 옵션이 사라짐)
8061							// PC: 클릭한 추가상품 바로 아래에 옵션을 그리기 위해 임시 컨테이너를 삽입
8062							try {
8063								var $clicked = $(
8064									event && event.currentTarget ? event.currentTarget : null);
8065								var $listItem = ($clicked && $clicked.length)
8066									? $clicked.closest('.list-item')
8067									: $();
8068								if ($listItem.length) {
8069									var $inline = $(
8070										'<div class="_prod_additional_inline_options"></div>');
8071									$listItem.after($inline);
8072									$prod_additional_options = $inline;
8073								} else {
8074									$prod_additional_options = $prod_additional_options_default;
8075								}
8076							} catch (e) {
8077								$prod_additional_options = $prod_additional_options_default;
8078							}
8079						}
8080
8081				    loadProdAdditionalOptions(product_idx);
8082			    }
8083		    },
8084        selectOptionalOption: function (prod_idx, option_code, value_code, value_name, success) {
8085            selectOption(
8086                prod_idx,
8087                {
8088                    options:[
8089                        {
8090                            'value_type': 'SELECT',
8091                            'option_code': option_code,
8092                            'value_code': value_code,
8093                            'value_name': value_name
8094                        }
8095                    ],
8096                    require: false,
8097                    count: 1
8098                },
8099                function () {
8100                    success();
8101                },
8102                function (msg) {
8103                    alert(msg);
8104                }
8105            );
8106        },
8107	      selectProdAdditionalOptionalOption: function (prod_idx, option_code, value_code, value_name, success) {
8108            selectProdAdditionalOption(prod_idx, [{
8109                'value_type': 'SELECT',
8110                'option_code': option_code,
8111                'value_code': value_code,
8112                'value_name': value_name
8113            }], false, 1, function () {
8114                success();
8115		            if(is_mobile_width){
8116			            imSheet.close('prod_additional_sheet');
8117		            }
8118            }, function (msg) {
8119                alert(msg);
8120            });
8121        },
8122        /** type (prod/cart) */
8123        changeOptionCount: function (optNo, optCount, type) {
8124            changeOptionCount(optNo, optCount, function () {
8125                updateSelectedOptions(type);
8126            });
8127        },
8128        "getCurrentProdNo": function () {
8129            return getCurrentProdNo();
8130        },
8131        "getCurrentProdCode": function () {
8132            return getCurrentProdCode();
8133        },
8134        "saveSelectedProd": function () {
8135            saveSelectedProd();
8136        },
8137        "setSelectedProd": function (is_set) {
8138            setSelectedProd(is_set);
8139        },
8140        addOrder: function (type, backurl, params) {
8141            addOrder(type, backurl, params);
8142        },
8143        addGiftOrder: function (type, backurl, params = {}) {
8144            // 선물하기 구매 플래그 활성화
8145			params.is_gift_buy = true;
8146            this.addOrder(type, backurl, params);
8147        },
8148        addCart: function () {
8149            addCart(function () {
8150                showAddCartAlarm();
8151            });
8152        },
8153        setShippingTemplateCode: function (code) {
8154            selected_shipping_template_code = code || '';
8155        },
8156        getShippingTemplateCode: function () {
8157            return selected_shipping_template_code;
8158        },
8159        "digitalFileDownload": function (order_no) {
8160            digitalFileDownload(order_no);
8161        },
8162        "handleOmsDigitalDownload": function (e, href) {
8163            handleOmsDigitalDownload(e, href);
8164        },
8165        updateSelectedOptions: function (type) {
8166            updateSelectedOptions(type);
8167        },
8168        checkOptionLength: function (target, limit) {
8169            checkOptionLength(target, limit);
8170        },
8171        getSelectedOption: function () {
8172            return selected_options;
8173        },
8174        getRequireOptionCount: function () {
8175            return require_option_count;
8176        },
8177        getSelectedRequireOptions: function () {
8178            return selected_require_options;
8179        },
8180        changeOrderCount: function (type, count, success, is_alert) {
8181            return changeOrderCount(type, count, success, is_alert);
8182        },
8183		    changeProdAdditionalOrderCount: function (no, type, count, success, is_alert) {
8184			    return changeProdAdditionalOrderCount(no, type, count, success, is_alert);
8185		    },
8186        selectCartType: function (type) {
8187            return selectCartType(type);
8188        },
8189        checkProdStock: function (cnt) {
8190            return checkProdStock(cnt);
8191        },
8192        increaseOrderCount: function (type, success) {
8193            return increaseOrderCount(type, success);
8194        },
8195        decreaseOrderCount: function (type, success) {
8196            return decreaseOrderCount(type, success);
8197        },
8198		    increaseProdAdditionalOrderCount: function (no, type, success) {
8199			    return increaseProdAdditionalOrderCount(no, type, success);
8200		    },
8201		    decreaseProdAdditionalOrderCount: function (no, type, success) {
8202			    return decreaseProdAdditionalOrderCount(no, type, success);
8203		    },
8204        showMobileOptions: function (type) {
8205            return showMobileOptions(type);
8206        },
8207        changeValueChecked: function (type) {
8208            return changeValueChecked(type);
8209        },
8210        showMobileFirstSelect: function () {
8211            return showMobileFirstSelect();
8212        },
8213        hideMobileOptions: function () {
8214            return hideMobileOptions();
8215        },
8216        showPCOptions: function (type, backurl) {
8217            return showPCOptions(type, backurl, false);
8218        },
8219        hidePCOptions: function () {
8220            return hidePCOptions();
8221        },
8222        socialBtnResize: function () {
8223            return socialBtnResize();
8224        },
8225		externalwidgetWrapResize : function(target, pc_position, mo_position) {
8226			return externalwidgetWrapResize(target, pc_position, mo_position);
8227		},
8228        changeContentTab: function (type, r_p, q_p, paging_on, only_photo, rating = 0) {
8229            return changeContentTab(type, r_p, q_p, paging_on, only_photo, rating);
8230        },
8231        deleteReviewInTabDisplay: function (prod_code, only_photo) {
8232            deleteReviewInTabDisplay(prod_code, only_photo);
8233        },
8234        changeContentPCTab: function (type, r_p, q_p, paging_on, only_photo,  rating = 0 , shop_view_body_width) {
8235            return changeContentPCTab(type, r_p, q_p, paging_on, only_photo, rating, shop_view_body_width);
8236        },
8237        removeReviewHash: function () {
8238            return removeReviewHash();
8239        },
8240        removeQnawHash: function () {
8241            return removeQnawHash();
8242        },
8243        getOnlyPhotoReview: function (only_photo_switch, is_mobile, is_one_page, rating) {
8244            return getOnlyPhotoReview(only_photo_switch, is_mobile, is_one_page, rating);
8245        },
8246        getStarRatingReview: function (only_photo_switch, is_mobile, is_one_page, rating) {
8247            return getStarRatingReview(only_photo_switch, is_mobile, is_one_page, rating);
8248        },
8249        getFirstPhotoReview: function (page) {
8250            return getFirstPhotoReview(page);
8251        },
8252        viewPhotoReviewMore: function (is_mobile, is_one_page) {
8253            return viewPhotoReviewMore(is_mobile, is_one_page);
8254        },
8255        getReviewSummary: function (target) {
8256            return getReviewSummary(target);
8257        },
8258        changeReviewDetail: function (idx) {
8259            return changeReviewDetail(idx);
8260        },
8261        viewReviewDetail: function (idx, r_p, only_photo, is_hash) {
8262            return viewReviewDetail(idx, r_p, only_photo, is_hash);
8263        },
8264        "setMembershipSaleData": function (data, is_main_product_membership_discount) {
8265            setMembershipSaleData(data, is_main_product_membership_discount);
8266        },
8267        "setPeriodDiscountData": function (flag, $data2, group_list) {
8268            setPeriodDiscountData(flag, $data2, group_list);
8269        },
8270        "setCreatorDiscountData": function (data) {
8271            // 크리에이터 전용 혜택가 데이터 주입 (PHP detail.sub 에서 호출).
8272            // 배너 DOM 은 PHP 측 detail/creator-benefit-banner.sub 가 출력한다 — JS 측에서 다시 그리지 않는다 (DOM 중복 회피).
8273            creator_discount_data = data || null;
8274            creator_discount_max_reached = false;
8275            hideCreatorDiscountMaxToast();
8276            refreshFreeShipNotifyInitialTotal();
8277        },
8278        "setFreeShipNotifyProdAdditionalRegularPrice": function (prod_idx, regular_price) {
8279            setFreeShipNotifyProdAdditionalRegularPrice(prod_idx, regular_price);
8280        },
8281		    "setProdAdditionalPeriodDiscountData": function (no, flag, $data2, group_list) {
8282			    setProdAdditionalPeriodDiscountData(no, flag, $data2, group_list);
8283		    },
8284		    "setProdAdditionalRequireOptionCount": function (require_option_count, require_input_option_count) {
8285			    prod_additional_require_option_count = {
8286				    ...prod_additional_require_option_count,
8287				    ...(require_option_count || {})
8288			    };
8289			    prod_additional_require_input_option_count = {
8290				    ...prod_additional_require_input_option_count,
8291				    ...(require_input_option_count || {})
8292			    };
8293		    },
8294        viewQnaDetail: function (idx, q_p, is_hash) {
8295            return viewQnaDetail(idx, q_p, is_hash);
8296        },
8297        changeProdImageRolling: function (no) {
8298            return changeProdImageRolling(no);
8299        },
8300        startProdImageRolling: function (autoWidth) {
8301            return startProdImageRolling(autoWidth);
8302        },
8303        setOrderCount: function (count) {
8304            order_count = count;
8305        },
8306        getOrderCount: function () {
8307            return order_count;
8308        },
8309        addNPayWish: function () {
8310            return addNPayWish();
8311        },
8312        hideAddCartAlarm: function () {
8313            hideAddCartAlarm();
8314        },
8315        moveCart: function () {
8316            moveCart();
8317        },
8318        trackAddToCartPopupAction: function (eventName, content, options) {
8319            trackAddToCartPopupAction(eventName, content, options);
8320        },
8321        loadProductDetail: function (code, prod_idx, menu_code, current_url) {
8322            loadProductDetail(code, prod_idx, menu_code, current_url);
8323        },
8324		    loadBookingDetail: function (code, idx, day, endDay, current_url) {
8325			    loadBookingDetail(code, idx, day, endDay, current_url);
8326		    },
8327		    loadCode: function (code) {
8328			    loadCode(code);
8329		    },
8330        loadOption: function (type, prod_idx) {
8331            return loadOption(type, prod_idx);
8332        },
8333        loadDelivSetting: function (prod_idx, change_country, deliv_type, deliv_pay_type) {
8334            return loadDelivSetting(prod_idx, change_country, deliv_type, deliv_pay_type);
8335        },
8336        updateRefundData: (refundData) => {
8337            return updateRefundData(refundData);
8338        },
8339        changeRequireInputOption: function (type, prod_idx, option_code, msg, success) {
8340            changeRequireInputOption(type, prod_idx, option_code, msg, function () {
8341                success();
8342            });
8343        },
8344	      changeProdAdditionalRequireInputOption: function (type, prod_idx, option_code, msg, success) {
8345		        changeProdAdditionalRequireInputOption(type, prod_idx, option_code, msg, function () {
8346                success();
8347            });
8348        },
8349        changeInput: function () {
8350            changeInput();
8351        },
8352        changeTab: function (type) {
8353            changeTab(type);
8354        },
8355        countryCodeChange: function (country) {
8356            countryCodeChange(country);
8357        },
8358        DetailItemMake: function (idx, change_country, deliv_type, deliv_pay_type) {
8359            return DetailItemMake(idx, change_country, deliv_type, deliv_pay_type);
8360        },
8361        loadItemInfo(params) {
8362            return loadItemInfo(params);
8363        },
8364        addDelivType: function (type) {
8365	        $deliv_type = type;
8366        },
8367        addDelivPayType: function (type) {
8368            $deliv_pay_type = type;
8369        },
8370        addDelivCountry: function (country) {
8371            $deliv_country = country;
8372        },
8373        getDelivType: function () {
8374            return $deliv_type || '';
8375        },
8376        getDelivPayType: function () {
8377            return $deliv_pay_type || '';
8378        },
8379        getDelivCountry: function () {
8380            return $deliv_country || '';
8381        },
8382        visitFormMake: function () {
8383            $deliv_visit_wrap.show();
8384        },
8385        openProdDetailFromShoppingList: function (idx, back_url, is_prod_detail_page, is_mobile, prod_idx_org) {
8386            return openProdDetailFromShoppingList(idx, back_url, is_prod_detail_page, is_mobile, prod_idx_org);
8387        },
8388        'getReviewQnaCount': function (prod_code) {
8389            return getReviewQnaCount(prod_code);
8390        },
8391        'setReviewQnaCountText': function ($target, count) {
8392            return setReviewQnaCountText($target, count);
8393        },
8394        getReviewCountFromShoppingList: function (prod_code_list) {
8395            return getReviewCountFromShoppingList(prod_code_list);
8396        },
8397        openBuyerReview: function (prod_code) {
8398            return openBuyerReview(prod_code);
8399        },
8400        deleteReviewImage: function (obj) {
8401            return deleteReviewImage(obj);
8402        },
8403        openCouponDownload: function () {
8404            return openCouponDownload();
8405        },
8406        setReviewCarousel: function () {
8407            return setReviewCarousel();
8408        },
8409        setReviewClass: function (idx) {
8410            return setReviewClass(idx);
8411        },
8412        setReviewBtnAnimation: function () {
8413            return setReviewBtnAnimation();
8414        },
8415        setReviewSwipe: function (prev_idx, next_idx) {
8416            return setReviewSwipe(prev_idx, next_idx);
8417        },
8418        showSeeMoreButton: function () {
8419            return showSeeMoreButton();
8420        },
8421        hideSeeMoreButton: function () {
8422            return hideSeeMoreButton();
8423        },
8424        openRequireSelect: function (option_code) {
8425            return openRequireSelect(option_code);
8426        },
8427        addRegularlyCart: function () {
8428            return addRegularlyCart();
8429        },
8430	    toggleWishButtons: function($wishButtons, wishCount, isActive) {
8431			return toggleWishButtons($wishButtons, wishCount, isActive);
8432	    },
8433        // C3 메서드 추가
8434		OMS_addOrder : function(type, backurl, params){
8435			// 외부채널은 사은품 미지원
8436			if (type === 'npay' || type === 'talkpay') {
8437				OMS_addOrder(type, backurl, params);
8438				return;
8439			}
8440			selectFreebieAsync()
8441				.then((selected_freebies) => {
8442					params.selected_freebies = selected_freebies || [];
8443					OMS_addOrder(type, backurl, params);
8444				});
8445		},
8446		OMS_showPCOptions : function(type, backurl){
8447			return showPCOptions(type, backurl, true);
8448		},
8449		OMS_addGiftOrder: async function (type, backurl, params = {}) {
8450			params.is_gift_buy = true; // 선물하기 구매 플래그 활성화
8451
8452			// 빠른배송이 활성화된 경우 선물하기 불가 안내 모달
8453			const isShippingServiceActive = typeof SHIPPING_SERVICE !== 'undefined' && SHIPPING_SERVICE.isActive();
8454			if (isShippingServiceActive) {
8455				const confirmedGift = await SHIPPING_SERVICE.confirmGift();
8456				if (!confirmedGift) {
8457					return;
8458				}
8459				// 일반배송으로 구매 선택 시 일반배송 템플릿 코드 설정
8460				SITE_SHOP_DETAIL.setShippingTemplateCode('');
8461			}
8462
8463			if (type === 'guest_login') {
8464				OMS_addOrder(type, backurl, params)
8465				return;
8466			}
8467
8468			// 외부채널은 사은품 미지원
8469			if (type === 'npay' || type === 'talkpay') {
8470				OMS_addOrder(type, backurl, params)
8471				return;
8472			}
8473
8474			selectFreebieAsync()
8475				.then((selected_freebies) => {
8476					params.selected_freebies = selected_freebies || [];
8477					OMS_addOrder(type, backurl, params)
8478				});
8479		},
8480		confirmOrderWithCartItems: function (type, backurl, params) {
8481			// type: "guest_login" | "npay" | "talkpay" | "normal"
8482				confirmOrderWithCartItems(type, backurl, params);
8483		},
8484		trackClickPurchaseShopView: function (paymentButtonType) {
8485			trackClickPurchaseShopView(paymentButtonType);
8486		},
8487		hideConfirmOrderWithCartItems: function () {
8488			if($confirm_order_with_cart_alarm) $confirm_order_with_cart_alarm.hide();
8489		},
8490		getThumbnailBadgeStyleByPosition: function ({ badgeColor, badgePosition, imageWidth = ''}){
8491			return getThumbnailBadgeStyleByPosition({ badgeColor, badgePosition, imageWidth});
8492		},
8493		selectFreebieAsync: selectFreebieAsync.bind(this),
8494		// 장바구니 수량 제한 관련 함수 추가
8495		setMaxPurchaseQuantity: function(_max_prod, _max_member, _type, _opt_limit, _opt_limit_type, _prod_name) {
8496			max_prod_quantity = _max_prod || 0;
8497			max_member_quantity = _max_member || 0;
8498			maximum_purchase_quantity_type = _type || 'order';
8499			optional_limit = _opt_limit || 0;
8500			optional_limit_type = _opt_limit_type || 'relative';
8501			if (_prod_name) prod_name = _prod_name;
8502		},
8503		validateMaxPurchaseQuantity: function() {
8504			if (max_prod_quantity <= 0) return true;
8505
8506			var is_valid = true;
8507			var error_msg = '';
8508
8509			$.each(selected_options, function(idx, opt) {
8510				if (!is_valid) return false;
8511
8512				var count = opt.count;
8513				var is_require_option = opt.require;  // DOM 대신 opt.require 사용
8514				var should_check = false;
8515
8516				switch (maximum_purchase_quantity_type) {
8517					case 'prod':
8518						// 옵션별 수량: 본품, 필수옵션, 선택옵션 모두 각각 검증
8519						should_check = true;
8520						break;
8521					case 'required_option':
8522						// 옵션별 수량(필수옵션): 본품, 필수옵션만 검증 (선택옵션 제외)
8523						should_check = is_require_option;
8524						break;
8525					case 'optional_option':
8526						// 옵션별 수량(선택옵션): 선택옵션만 검증 (본품, 필수옵션 제외)
8527						should_check = !is_require_option;
8528						break;
8529					case 'order':
8530					default:
8531						// order 타입은 아래에서 전체 수량으로 체크
8532						break;
8533				}
8534
8535				if (should_check && count > max_prod_quantity) {
8536					is_valid = false;
8537					error_msg = LOCALIZE.설명_최대구매수량(max_prod_quantity);
8538				}
8539			});
8540
8541			// order 타입인 경우 전체 수량 합계 체크
8542			if (is_valid && maximum_purchase_quantity_type === 'order') {
8543				var total_count = 0;
8544				$.each(selected_options, function(idx, opt) {
8545					total_count += opt.count;
8546				});
8547				if (total_count > max_prod_quantity) {
8548					is_valid = false;
8549					error_msg = LOCALIZE.설명_최대구매수량(max_prod_quantity);
8550				}
8551
8552				// 회원별 최대 구매수량 검증 추가
8553				if (is_valid && max_member_quantity > 0) {
8554					if (total_count > max_member_quantity) {
8555						is_valid = false;
8556						error_msg = LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity);
8557					}
8558				}
8559			}
8560
8561			return is_valid ? true : error_msg;
8562		},
8563		// 본품 수량 검증 함수 (optional_option 타입 제외)
8564		checkMaxPurchaseQuantityForOrderCount: function(count) {
8565			if (max_prod_quantity > 0 && maximum_purchase_quantity_type !== 'optional_option') {
8566				var _total_count = count; // 본품 수량
8567
8568				// order 타입이면 본상품의 필수/선택 옵션도 합산
8569				if (maximum_purchase_quantity_type === 'order') {
8570					// 필수/선택 옵션 수량 포함
8571					$.each(selected_options, function (idx, obj) {
8572						_total_count += obj.count;
8573					});
8574				}
8575
8576				if (_total_count > max_prod_quantity) {
8577					return LOCALIZE.설명_최대구매수량(max_prod_quantity);
8578				}
8579			}
8580
8581			// 회원당 최대 구매수량 검증 (1회 구매 제한이 옵션별 타입이면 1인 제한 미적용 - 스펙)
8582			if (max_member_quantity > 0 && maximum_purchase_quantity_type === 'order') {
8583				var _member_total = count;
8584				// 필수/선택 옵션 수량 포함
8585				$.each(selected_options, function (idx, obj) {
8586					_member_total += obj.count;
8587				});
8588				if (_member_total > max_member_quantity) {
8589					return LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity);
8590				}
8591			}
8592
8593			return true;
8594		},
8595    };
8596}();
8597
8598window.addEventListener('imweb:productRecommendationModal:requestClose', function () {
8599    if (window.SITE_SHOP_DETAIL && typeof window.SITE_SHOP_DETAIL.hideAddCartAlarm === 'function') {
8600        window.SITE_SHOP_DETAIL.hideAddCartAlarm();
8601    }
8602});
8603
8604var SITE_SHOP_CART = function () {
8605    var selectedCartItem = [];
8606    var $cartItemCheckboxList;
8607    var $cartAllCheckBox;
8608    var $shop_cart_list;
8609    var $shop_cart_wish_list;
8610    var $shop_cart_wish_list_empty;
8611    var $changeCartItemLayer;
8612    var $total_price;
8613    /** 크리에이터 전용 혜택가 데이터 (cart 컨텍스트) — PHP 에서 setCreatorDiscountData 로 주입. null 이면 비활성 */
8614    var creator_cart_data = null;
8615
8616    var $cart_list_wrap;
8617    var $global_select;
8618    var $cart_result_container;
8619
8620    var get_selected_price_request_seq = 0;
8621    var is_cart_changed = false;
8622    var currentCartCode = '';
8623    var current_backurl = '';
8624    var currentChangeCartItemCode = '';
8625
8626    var currentCartItemMaxQuantity = 0;
8627    var currentCartItemMaxQuantityType = 'order';
8628		var $deliv_country = '';
8629    var add_order_progress_check = false;
8630    var cart_type = '';
8631    // 무료배송 안내 모달이 떠있는 동안 원래 addOrderWithCart 호출 인자를 보류해둔다.
8632    // 사용자가 모달의 "주문하기" 버튼을 누르면 proceedOrderAfterFreeShipAlarm에서 이 인자로 함수를 재호출.
8633    var _pendingOrderWithCart = null;
8634
8635    // 수량별 할인 계산 헬퍼 (장바구니)
8636    // data-qd-exclude="Y" 카운터(선택옵션 등)는 조건 수량/할인 base 모두에서 제외 (IMIO-7150)
8637    // 품목 금액 초과 방지 클램핑 적용 (결제페이지/OMS 동일, IMIO-7184)
8638    const _calcCartQuantityDiscount = ($row, priceRaw) => {
8639        if (!$row || !$row.length) return 0;
8640        const tiersRaw = $row.attr('data-qd-tiers');
8641        if (!tiersRaw) return 0;
8642        try {
8643            const tiers = JSON.parse(tiersRaw);
8644            if (!Array.isArray(tiers) || tiers.length === 0) return 0;
8645
8646            // 카운터 순회: 제외 대상이면 선택옵션 금액 누적, 아니면 조건 수량 누적
8647            let conditionQty = 0;
8648            let excludedOptionPrice = 0;
8649            $row.find('._counter_value').each(function () {
8650                const $counter = $(this).closest('._cart_counter');
8651                const qty = parseInt(this.tagName === 'INPUT' ? this.value : this.textContent, 10) || 0;
8652                if ($counter.attr('data-qd-exclude') === 'Y') {
8653                    const unitPrice = parseFloat(
8654                        $counter.closest('._cart_option_card').find('._cart_option_price').attr('data-unit-price')
8655                    ) || 0;
8656                    excludedOptionPrice += unitPrice * qty;
8657                    return;
8658                }
8659                conditionQty += qty;
8660            });
8661            if (conditionQty <= 0) return 0;
8662
8663            // 충족된 tier 중 min_quantity 최댓값 선택
8664            const matchedTier = tiers
8665                .filter(t => conditionQty >= t.min_quantity)
8666                .sort((a, b) => b.min_quantity - a.min_quantity)[0];
8667            if (!matchedTier) return 0;
8668
8669            // base = 품목 금액 − 선택옵션 금액 (0 이상으로 클램핑)
8670            const base = Math.max((parseInt(priceRaw, 10) || 0) - excludedOptionPrice, 0);
8671            if (matchedTier.method === 'percentage') {
8672                return Math.min(Math.ceil(base * matchedTier.value / 100), base);
8673            }
8674            return Math.min(parseInt(matchedTier.value, 10) || 0, base);
8675        } catch { return 0; }
8676    };
8677
8678    /**
8679     * 카트 row 의 가격 표시를 일관되게 갱신.
8680     * basePrice는 period 차감/QD 미반영 상태(= data-discounted-price와 동일).
8681     * QD는 data-qd-tiers 기준으로 계산한다.
8682     * PC/mobile 양쪽의 가격 텍스트와 data 속성, origin 표시까지 한 번에 처리.
8683     *
8684     * @param {jQuery} $row - 카트 tr.content 요소
8685     * @param {Object} opts
8686     * @param {number} opts.basePrice - period 차감 후 row 단가 (QD 미반영)
8687     * @param {number} [opts.originPrice] - 원가 (strikethrough). 미지정 시 origin 갱신 안 함
8688     * @param {number} [opts.fpDiscount=0] - 첫 구매 혜택 차감액
8689     */
8690    const renderCartRowPrice = ($row, opts) => {
8691        if (!$row || !$row.length) return;
8692        const basePrice = parseInt(opts.basePrice, 10) || 0;
8693        const fpDiscount = parseInt(opts.fpDiscount, 10) || 0;
8694        const qd = _calcCartQuantityDiscount($row, basePrice);
8695        const finalPrice = Math.max(basePrice - qd - fpDiscount, 0);
8696        const finalText = LOCALIZE.getCurrencyFormat(finalPrice);
8697
8698        // PC 가격
8699        const $pcDefault = $row.find('._cart_price_default');
8700        if ($pcDefault.length) {
8701            $pcDefault.data('discounted-price', basePrice);
8702            $pcDefault.find('._cart_price_default_value').text(finalText);
8703        }
8704
8705        // Mobile 가격
8706        const $mobilePrice = $row.find('._cart_mobile_order_price');
8707        if ($mobilePrice.length) {
8708            $mobilePrice.data('discounted-price', basePrice);
8709            $mobilePrice.text(finalText);
8710        }
8711
8712        // Origin 표시 (선택)
8713        if (opts.originPrice !== undefined) {
8714            const originPrice = parseInt(opts.originPrice, 10) || 0;
8715            const originText = LOCALIZE.getCurrencyFormat(originPrice);
8716            const hasDiscount = originPrice !== finalPrice;
8717
8718            const $pcOrigin = $row.find('._cart_price_origin');
8719            if ($pcOrigin.length) {
8720                $pcOrigin.data('origin-price', originPrice).text(originText);
8721                $pcOrigin.data('default-visible', hasDiscount ? 'Y' : 'N').toggle(hasDiscount);
8722            }
8723
8724            const $mobileOrigin = $row.find('._cart_mobile_order_price_origin');
8725            if ($mobileOrigin.length) {
8726                $mobileOrigin.data('origin-price', originPrice).text(originText);
8727                $mobileOrigin.data('default-visible', hasDiscount ? 'Y' : 'N').toggle(hasDiscount);
8728            }
8729        }
8730    };
8731
8732    var initCart = function (cart_code, backurl, is_regularly, is_c3 = false){
8733        currentCartCode = cart_code;
8734        current_backurl = backurl;
8735        $shop_cart_list = $('#shop_cart_list');
8736        $changeCartItemLayer = $('#shop_cart_change_layer');
8737        $shop_cart_wish_list = $('#shop_cart_wish_list');
8738        $shop_cart_wish_list_empty = $('#shop_cart_wish_list_empty');
8739
8740        $cart_list_wrap = $shop_cart_list.find('._cart_list_wrap');
8741        $global_select = $shop_cart_list.find('._global_select');
8742        // 모듈 스코프 cart_type 에 일반/정기 구분값을 반영 (외부 함수에서 재사용)
8743        cart_type = SHOP_CONST.CART_TYPE_NORMAL;
8744        if (is_regularly == 'Y') cart_type = SHOP_CONST.CART_TYPE_REGULARLY;
8745        cartListMake(backurl, cart_type, is_c3);
8746    };
8747
8748    var findCartItemDelivTd = function ($row, itemCode) {
8749        var $delivTd = $cart_list_wrap.find('._cart_item_deliv_td[data-item-code="' + itemCode + '"]');
8750        if ($delivTd.length) return $delivTd;
8751
8752        $delivTd = $row.find('._cart_item_deliv_td');
8753        if ($delivTd.length) return $delivTd;
8754
8755        // PC 테이블은 묶음배송 그룹의 첫 행에만 배송비 셀이 있고 rowspan으로 묶인다.
8756        return $row.prevAll('tr.content').filter(function () {
8757            return $(this).find('._cart_item_deliv_td').length > 0;
8758        }).first().find('._cart_item_deliv_td');
8759    };
8760
8761    var findCartBundleShippingRow = function ($row, itemCode) {
8762        var $shippingRow = $cart_list_wrap.find('tr.im-tr-shipping[data-item-code="' + itemCode + '"]');
8763        if ($shippingRow.length) return $shippingRow;
8764
8765        return $row.nextAll('tr.im-tr-shipping').first();
8766    };
8767
8768    var updateCartItemDelivText = function ($delivTd, delivPriceText) {
8769        if (!$delivTd.length || delivPriceText === undefined) return;
8770
8771        var $delivPrice = $delivTd.find('._cart_item_deliv_price');
8772        if (!$delivPrice.length) return;
8773
8774        var $textWrap = $delivPrice.find('span.text-bold').first();
8775        if ($textWrap.length) {
8776            $textWrap.html(delivPriceText);
8777            return;
8778        }
8779
8780        if (delivPriceText !== '') {
8781            $delivPrice.prepend('<span class="text-bold">' + delivPriceText + '</span>');
8782        }
8783    };
8784
8785    var updateCartDeliveryData = function (itemDeliveryData) {
8786        if (!itemDeliveryData) return;
8787
8788        $.each(itemDeliveryData, function (itemCode, info) {
8789            const $itemRow = $cart_list_wrap.find(`tr[data-item-code="${itemCode}"]`);
8790            if (!$itemRow.length) return;
8791
8792            if (info.deliv_price_text !== undefined && info.deliv_price_text !== '') {
8793                updateCartItemDelivText(findCartItemDelivTd($itemRow, itemCode), info.deliv_price_text);
8794
8795                const $mobileDelivText = $cart_list_wrap.find(`._cart_mobile_deliv_price[data-item-code="${itemCode}"] ._cart_mobile_deliv_price_text`);
8796                if ($mobileDelivText.length) {
8797                    $mobileDelivText.html(info.deliv_price_text);
8798                }
8799            }
8800
8801            if (info.bundle_deliv_text !== undefined && info.bundle_deliv_text !== '') {
8802                const $shippingRow = findCartBundleShippingRow($itemRow, itemCode);
8803                if ($shippingRow.length) {
8804                    $shippingRow.find('._cart_bundle_deliv_text').html(info.bundle_deliv_text);
8805                }
8806            }
8807        });
8808    };
8809
8810    var getEmptySelectionDelivPriceText = function ($target) {
8811        var delivPriceText = $target.attr('data-free-deliv-price-text');
8812        if (delivPriceText !== undefined && delivPriceText !== '') return delivPriceText;
8813        return '<span>' + getLocalizeString('설명_무료', '', '무료') + '</span>';
8814    };
8815
8816    var updateCartDeliveryForEmptySelection = function () {
8817        $cart_list_wrap.find('._cart_item_deliv_td').each(function () {
8818            var $delivTd = $(this);
8819            updateCartItemDelivText($delivTd, getEmptySelectionDelivPriceText($delivTd));
8820        });
8821
8822        $cart_list_wrap.find('._cart_mobile_deliv_price').each(function () {
8823            var $mobileDelivPrice = $(this);
8824            var $mobileDelivText = $mobileDelivPrice.find('._cart_mobile_deliv_price_text');
8825            if ($mobileDelivText.length) {
8826                $mobileDelivText.html(getEmptySelectionDelivPriceText($mobileDelivPrice));
8827            }
8828        });
8829
8830        $cart_list_wrap.find('tr.im-tr-shipping').each(function () {
8831            var $shippingRow = $(this);
8832            var bundleDelivText = $shippingRow.attr('data-free-bundle-deliv-text');
8833            if (bundleDelivText !== undefined && bundleDelivText !== '') {
8834                $shippingRow.find('._cart_bundle_deliv_text').html(bundleDelivText);
8835            }
8836        });
8837    };
8838
8839    /**
8840     * 선택한 항목의 가격 계산
8841     */
8842    var getSelectedPrice = function (is_regularly) {
8843        var requestSeq = ++get_selected_price_request_seq;
8844        $.ajax({
8845            type: 'POST',
8846            data: {
8847                'item_list': selectedCartItem,
8848                'is_regularly': is_regularly,
8849                // 매출 상승 도구 미리보기 모드 감지용 - 서버에서 current_full_url의 GET 파라미터를 파싱
8850                'current_full_url': current_backurl || ''
8851            },
8852            url: ('/shop/get_cart_price.cm'),
8853            dataType: 'json',
8854            success: function (res) {
8855                if (requestSeq !== get_selected_price_request_seq) return;
8856                if (res.msg == 'SUCCESS') {
8857                    // CB-3865: setCartPriceHtml 보다 먼저 사은품 dispatch — UI 가격 갱신과 독립.
8858                    // setCartPriceHtml 내부의 jQuery 체인이 swallow 되면 dispatch 누락 방지.
8859                    var selectedSet = new Set(selectedCartItem);
8860                    _cartRawItems.forEach(function (item) {
8861                        item.selected = selectedSet.has(item.item_code);
8862                    });
8863                    dispatchFreebieCartChange();
8864                    setCartPriceHtml(res['price_data']);
8865                } else {
8866                    if (res.res_code != 1) {
8867                        alert(res.msg);
8868                    }
8869                    is_cart_changed = false;
8870                }
8871            }
8872        });
8873    };
8874
8875    var setCartPriceHtml = function (data) {
8876        var freeShipProgressPrice = data ? parseFloat(data['free_shipping_progress_price']) : NaN;
8877        var adjustedFreeShipProgressPrice = freeShipProgressPrice;
8878
8879        $total_price = $cart_list_wrap.find('._cart_main_total_price');
8880        $cart_result_container = $cart_list_wrap.find('._cart_result_container');
8881        var $mobile_cart_result_container = $cart_list_wrap.find('tr._cart_result_container');
8882        var $pc_cart_result_container = $cart_list_wrap.find('._cart_result_table').find('._cart_result_container');
8883
8884        $cart_list_wrap.find('._cart_count').text(data['count']);
8885        $cart_result_container.find('._cart_price').html(data['price']);
8886        $cart_result_container.find('._cart_deliv_price').html(data['deliv_price']);
8887        $cart_result_container.find('._cart_add_point').html(data['add_point']);
8888        if ($cart_list_wrap.find('._cartItemCheckbox:checked').length === 0) {
8889            updateCartDeliveryForEmptySelection();
8890        } else {
8891            updateCartDeliveryData(data['item_delivery_data']);
8892        }
8893        // 수량별 할인 합계 계산
8894        let _cartQuantityDiscountTotal = 0;
8895        $cart_list_wrap.find('tr.content[data-qd-tiers]').each(function () {
8896            const $row = $(this);
8897            const itemCode = String($row.data('item-code'));
8898            if ($.inArray(itemCode, selectedCartItem) === -1) return;
8899            // 크리에이터 매칭 item 은 수량별 할인 대상에서 제외 (회원할인과 일관)
8900            if (creator_cart_data && creator_cart_data.matched_items && creator_cart_data.matched_items[itemCode]) return;
8901            const priceRaw = parseInt($row.find('._cart_price_default').data('discounted-price'), 10) || 0;
8902            _cartQuantityDiscountTotal += _calcCartQuantityDiscount($row, priceRaw);
8903        });
8904
8905        // 회원 할인금액: 등급할인 base에서 수량별 할인 차감 후 재계산
8906        const serverMembershipRaw = parseInt(data['membership_discount_raw'], 10) || 0;
8907        const membershipTargetRaw = parseInt(data['membership_discount_target_raw'], 10) || 0;
8908        const membershipConfig = data['membership_discount_config'] || null;
8909        let membershipDiscountRaw = serverMembershipRaw;
8910        if (_cartQuantityDiscountTotal > 0 && membershipConfig && membershipTargetRaw > 0) {
8911            const adjustedTarget = Math.max(membershipTargetRaw - _cartQuantityDiscountTotal, 0);
8912            // 등급할인 재계산 (checkMembershipBenefit 서버 로직과 동일)
8913            let _mcDiscount = 0;
8914            if (membershipConfig['amount'] && (membershipConfig['minimum'] <= adjustedTarget)) {
8915                if (membershipConfig['type'] !== 'price') {
8916                    const _mcAmount = Math.min(membershipConfig['amount'], 100);
8917                    _mcDiscount = (adjustedTarget / 100) * _mcAmount;
8918                    if (_mcDiscount > membershipConfig['maximum']) {
8919                        _mcDiscount = membershipConfig['maximum'];
8920                    }
8921                } else {
8922                    _mcDiscount = membershipConfig['amount'];
8923                }
8924            }
8925            if (_mcDiscount > adjustedTarget) _mcDiscount = adjustedTarget;
8926            const decimalCount = LOCALIZE.getCurrencyDecimalCount();
8927            membershipDiscountRaw = decimalCount === 0
8928                ? Math.ceil(_mcDiscount)
8929                : Math.ceil(_mcDiscount * Math.pow(10, decimalCount)) / Math.pow(10, decimalCount);
8930        }
8931        const membershipDiscountText = membershipDiscountRaw > 0 ? LOCALIZE.getCurrencyFormat(membershipDiscountRaw) : '-';
8932        const membershipPriceExist = membershipDiscountRaw > 0;
8933        $cart_result_container.find('._cart_membership_discount').html(membershipDiscountText);
8934        $cart_list_wrap.find('._cart_membership_field').toggle(membershipPriceExist);
8935
8936        var isFreeShipNotifyAfterSale = data && data['free_shipping_deliv_price_cost_type'] === 'after_sale';
8937        if (isFreeShipNotifyAfterSale && !isNaN(adjustedFreeShipProgressPrice)) {
8938            var freeShipDiscountAdjust = _cartQuantityDiscountTotal;
8939            if (membershipDiscountRaw > 0) {
8940                var serverMembershipAppliedToFreeShip = false;
8941                if (serverMembershipRaw > 0 && membershipTargetRaw > 0) {
8942                    serverMembershipAppliedToFreeShip = adjustedFreeShipProgressPrice <= Math.max(membershipTargetRaw - serverMembershipRaw, 0) + 0.0001;
8943                }
8944                freeShipDiscountAdjust += membershipDiscountRaw;
8945                if (serverMembershipAppliedToFreeShip) {
8946                    freeShipDiscountAdjust -= serverMembershipRaw;
8947                }
8948            }
8949            adjustedFreeShipProgressPrice = Math.max(adjustedFreeShipProgressPrice - freeShipDiscountAdjust, 0);
8950        }
8951
8952        // 무료배송 안내 마그넷 — 실제 배송비 계산값은 건드리지 않고 안내용 진행/잔여 금액만 화면 할인값과 맞춘다.
8953        if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
8954            && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice === 'function'
8955            && data && 'free_shipping_remaining_price' in data) {
8956            var freeShipRemaining = ($('#shop_cart_list ._global_select').length > 0 && $deliv_country !== 'KR')
8957                ? null
8958                : data.free_shipping_remaining_price;
8959            var freeShipRemainingPrice = parseFloat(freeShipRemaining);
8960            if (isFreeShipNotifyAfterSale && !isNaN(freeShipRemainingPrice) && !isNaN(freeShipProgressPrice) && !isNaN(adjustedFreeShipProgressPrice)) {
8961                freeShipRemaining = Math.max((freeShipRemainingPrice + Math.max(freeShipProgressPrice, 0)) - adjustedFreeShipProgressPrice, 0);
8962                freeShipRemainingPrice = parseFloat(freeShipRemaining);
8963            }
8964            if (!isNaN(adjustedFreeShipProgressPrice) && adjustedFreeShipProgressPrice <= 0
8965                && !isNaN(freeShipRemainingPrice) && freeShipRemainingPrice <= 0) {
8966                freeShipRemaining = null;
8967            }
8968            window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice(freeShipRemaining);
8969        }
8970
8971        // 크리에이터 전용 혜택가 — PC 합계는 별도 row, 모바일 합계는 상품할인금액 하위 row 로 노출
8972        var creatorDiscountRaw = parseInt(data['creator_discount_raw'], 10) || 0;
8973        var creatorDiscountExist = creatorDiscountRaw > 0;
8974        var creatorDiscountText = data['creator_discount_text'] || '-';
8975
8976        // 상품할인금액 = 서버 period_price + 첫구매혜택 + 수량별 할인
8977        const periodPriceRaw = parseInt(data['period_price_raw'], 10) || 0;
8978        const benefitDiscountRaw = parseInt(data['benefit_discount_raw'], 10) || 0;
8979        const pcDiscountPrice = periodPriceRaw + benefitDiscountRaw + _cartQuantityDiscountTotal;
8980        const mobileDiscountPrice = pcDiscountPrice + creatorDiscountRaw;
8981        const pcDiscountPriceExist = pcDiscountPrice > 0;
8982        const mobileDiscountPriceExist = mobileDiscountPrice > 0;
8983
8984        $mobile_cart_result_container.find('._cart_period_field').toggle(mobileDiscountPriceExist);
8985        $mobile_cart_result_container.find('._cart_period_discount').text(mobileDiscountPriceExist ? LOCALIZE.getCurrencyFormat(mobileDiscountPrice) : '-');
8986        $mobile_cart_result_container.find('._cart_mobile_creator_discount_total_field').toggle(creatorDiscountExist);
8987        $mobile_cart_result_container.find('._cart_mobile_creator_discount_total').text(creatorDiscountExist ? '- ' + creatorDiscountText : '');
8988        $mobile_cart_result_container.find('._cart_mobile_discount_detail_wrap').toggle(creatorDiscountExist);
8989        $pc_cart_result_container.find('._cart_period_field').toggle(pcDiscountPriceExist);
8990        $pc_cart_result_container.find('._cart_period_discount').text(pcDiscountPriceExist ? LOCALIZE.getCurrencyFormat(pcDiscountPrice) : '-');
8991
8992        $mobile_cart_result_container.find('._cart_creator_field').hide();
8993        $pc_cart_result_container.find('._cart_creator_field').toggle(creatorDiscountExist);
8994        $pc_cart_result_container.find('._cart_creator_discount').text(creatorDiscountExist ? creatorDiscountText : '-');
8995        const cartSelectedCount = parseInt(data['count'], 10) || 0;
8996        const cartNoDeliveryCount = parseInt(data['no_deliv_count'], 10) || 0;
8997        if (cartSelectedCount === 0) {
8998            // 선택된 상품이 없을 때는 0원 합계의 기본 항목을 모두 노출한다.
8999            $cart_list_wrap.find('._cart_price_field').show();
9000            $cart_list_wrap.find('._cart_deliv_price_field').show();
9001            $cart_list_wrap.find('.im-ico-equals-plain').show();
9002            $mobile_cart_result_container.find('._plus_deliv_price').show();
9003        } else {
9004            $cart_list_wrap.find('._cart_deliv_price_field').toggle(cartSelectedCount !== cartNoDeliveryCount);
9005            if (data['membership_price_exist'] === false && mobileDiscountPriceExist === false && (cartSelectedCount === cartNoDeliveryCount)) {
9006                $cart_list_wrap.find('.im-ico-equals-plain').hide();
9007                $pc_cart_result_container.find('._cart_price_field').hide();
9008            } else {
9009                if (data['only_deliv_price_after']) {
9010                    $cart_list_wrap.find('._cart_deliv_price_field').show();
9011                    $cart_list_wrap.find('.im-ico-equals-plain').show();
9012                    $pc_cart_result_container.find('._cart_price_field').show();
9013                    $mobile_cart_result_container.find('._plus_deliv_price').hide();
9014                } else {
9015                    $cart_list_wrap.find('.im-ico-equals-plain').show();
9016                    $pc_cart_result_container.find('._cart_price_field').show();
9017                    $mobile_cart_result_container.find('._plus_deliv_price').show();
9018                }
9019            }
9020        }
9021
9022        // 총 주문금액 (수량별 할인 + 등급할인 보정 반영)
9023        const membershipAdjust = serverMembershipRaw - membershipDiscountRaw;
9024        const totalPriceRaw = Math.max(parseInt(data['total_price_raw'], 10) - _cartQuantityDiscountTotal + membershipAdjust, 0);
9025        $total_price.text(LOCALIZE.getCurrencyFormat(totalPriceRaw));
9026        $cart_list_wrap.find('._cart_result_table').show();
9027        setCartBenefitInfo(data);
9028
9029        // MFE 무료배송 안내 위젯(fo-shopping/free-ship-notify)의 게이지/임계값 계산용으로 숫자 총액을 브로드캐스트.
9030        // 배송 그룹이 분리된 경우에는 서버가 내려준 무료배송 안내 대상 그룹의 진행 금액만 사용한다.
9031        var totalPriceValue = !isNaN(adjustedFreeShipProgressPrice)
9032            ? adjustedFreeShipProgressPrice
9033            : (typeof data['total_price_raw'] === 'number' ? data['total_price_raw'] : 0);
9034        window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartTotal:update', {
9035            detail: { totalPrice: totalPriceValue }
9036        }));
9037
9038        // 같은 시점에 prod_code 단위 카트 수량 스냅샷도 dispatch.
9039        // 추천 상품 카드(fo-shopping/free-ship-notify ProductCard)가 "+ 아이콘 ↔ 담긴 수량 숫자" 전환에 사용.
9040        // 같은 prod_code 의 옵션 라인이 여러 row 로 나뉠 수 있어 합산.
9041        var itemCounts = {};
9042        $cart_list_wrap.find('tr.content[data-prod-code]').each(function () {
9043            var prodCode = this.getAttribute('data-prod-code');
9044            if (!prodCode) return;
9045            var rowCount = 0;
9046            this.querySelectorAll('._counter_value').forEach(function (input) {
9047                var qty = parseInt(input.value, 10);
9048                if (Number.isFinite(qty)) rowCount += qty;
9049            });
9050            itemCounts[prodCode] = (itemCounts[prodCode] || 0) + rowCount;
9051        });
9052        window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartItems:update', {
9053            detail: { counts: itemCounts }
9054        }));
9055    };
9056
9057    var setCartBenefitInfo = function (data) {
9058        var benefitInfo = data ? data['benefit_info'] : null;
9059        var $benefitItems = $cart_list_wrap.find('._cart_benefit_info');
9060        var $mobileCartResultContainer = $cart_list_wrap.find('tr._cart_result_container');
9061        var $pcCartResultContainer = $cart_list_wrap.find('._cart_result_table').find('._cart_result_container');
9062        var $mobileBenefitField = $mobileCartResultContainer.find('._cart_benefit_field');
9063        var $pcBenefitField = $pcCartResultContainer.find('._cart_benefit_field');
9064        var $benefitName = $cart_list_wrap.find('._cart_benefit_name');
9065        var $benefitPrice = $cart_list_wrap.find('._cart_benefit_price');
9066        var $pcBenefitLabel = $cart_list_wrap.find('._cart_benefit_pc_label');
9067        var $pcBenefitPrice = $cart_list_wrap.find('._cart_benefit_pc_price');
9068        var $pcBenefitSub = $cart_list_wrap.find('._cart_benefit_pc_sub');
9069        var $pcPriceDefault = $cart_list_wrap.find('._cart_price_default');
9070        var $mobileBenefitDiscountField = $cart_list_wrap.find('._cart_mobile_benefit_discount_field');
9071        var $mobileBenefitDiscountPrice = $cart_list_wrap.find('._cart_mobile_benefit_discount_price');
9072        var $mobileBenefitDetailField = $cart_list_wrap.find('._cart_mobile_benefit_detail_field');
9073        var $mobileBenefitDetailName = $cart_list_wrap.find('._cart_mobile_benefit_name');
9074        var $mobileBenefitDetailPrice = $cart_list_wrap.find('._cart_mobile_benefit_detail_price');
9075        var $mobileOrderHintField = $cart_list_wrap.find('._cart_mobile_order_hint_field');
9076        var $mobileOrderHint = $cart_list_wrap.find('._cart_mobile_order_hint');
9077        var $mobileOrderPriceOrigin = $cart_list_wrap.find('._cart_mobile_order_price_origin');
9078        var $pcPriceOrigin = $cart_list_wrap.find('._cart_price_origin');
9079
9080        $benefitItems.hide().text('');
9081        $mobileBenefitField.hide();
9082        $pcBenefitField.hide();
9083        $pcBenefitLabel.hide().text('');
9084        $pcBenefitPrice.hide().text('');
9085        $pcBenefitSub.hide().text('');
9086        $pcPriceDefault.show();
9087        $mobileBenefitDiscountField.hide();
9088        $mobileBenefitDiscountPrice.text('');
9089        $mobileBenefitDetailField.hide();
9090        $mobileBenefitDetailName.text('');
9091        $mobileBenefitDetailPrice.text('');
9092        $mobileOrderHintField.hide();
9093        $mobileOrderHint.text('');
9094        if ($mobileOrderPriceOrigin.length > 0) {
9095            $mobileOrderPriceOrigin.each(function () {
9096                var $origin = $(this);
9097                $origin.toggle($origin.data('default-visible') === 'Y');
9098            });
9099        }
9100        if ($pcPriceOrigin.length > 0) {
9101            $pcPriceOrigin.each(function () {
9102                var $origin = $(this);
9103                $origin.toggle($origin.data('default-visible') === 'Y');
9104            });
9105        }
9106
9107        // 첫구매 혜택 모달 description 서버 응답 기준으로 갱신 (모달 열릴 때 shadow DOM 재생성으로 반영)
9108        var $promotionModalShell = $('#_cart_promotion_modal_shell');
9109        if ($promotionModalShell.length > 0) {
9110            var modalDescription;
9111            if (!benefitInfo || benefitInfo['promotion_exists'] !== true) {
9112                modalDescription = '조건 충족 시 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9113            } else {
9114                var shortfallAmount = parseInt(benefitInfo['benefit_shortfall_amount'] || 0, 10) || 0;
9115                var shortfallQuantity = parseInt(benefitInfo['benefit_shortfall_quantity'] || 0, 10) || 0;
9116                if (shortfallAmount > 0) {
9117                    modalDescription = '<strong>' + LOCALIZE.getCurrencyFormat(shortfallAmount) + '</strong> 더 담으면 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9118                } else if (shortfallQuantity > 0) {
9119                    modalDescription = '<strong>' + shortfallQuantity.toLocaleString('ko-KR') + '개</strong> 더 담으면 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9120                } else {
9121                    modalDescription = '조건 충족 시 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9122                }
9123            }
9124            $promotionModalShell.attr('data-description', modalDescription);
9125        }
9126
9127        if (!benefitInfo || benefitInfo['promotion_exists'] !== true) {
9128            return;
9129        }
9130
9131        var benefitProducts = benefitInfo['benefit_products'] || [];
9132        var promotionName = benefitInfo['promotion_name'] || '';
9133        var isQualified = benefitInfo['is_qualified'] === true;
9134        var appliedItemCode = benefitInfo['applied_item_code'] || '';
9135        var benefitDiscountPrice = parseInt(benefitInfo['benefit_discount_price'] || 0, 10) || 0;
9136        var benefitDiscountText = benefitInfo['benefit_discount_price_text'] || '';
9137        if (benefitProducts.length === 0 || promotionName === '') {
9138            return;
9139        }
9140
9141        var benefitMap = {};
9142        $.each(benefitProducts, function (idx, benefitProduct) {
9143            if (!benefitProduct || !benefitProduct['prod_code']) {
9144                return;
9145            }
9146            benefitMap[benefitProduct['prod_code']] = benefitProduct;
9147        });
9148
9149        // 모든 row 가격을 base 기준으로 reset (FP 합성 분기 진입 전 초기화). QD는 헬퍼가 자동 차감.
9150        $cart_list_wrap.find('tr.content').each(function () {
9151            var $row = $(this);
9152            var $base = $row.find('._cart_price_default');
9153            if (!$base.length) $base = $row.find('._cart_mobile_order_price');
9154            var basePrice = parseInt($base.data('discounted-price'), 10);
9155            if (!isNaN(basePrice) && basePrice >= 0) {
9156                renderCartRowPrice($row, { basePrice: basePrice });
9157            }
9158        });
9159
9160        $benefitItems.each(function () {
9161            var $el = $(this);
9162            var itemCode = String($el.data('item-code'));
9163            var prodCode = $el.data('prod-code');
9164
9165            if ($.inArray(itemCode, selectedCartItem) === -1) {
9166                return;
9167            }
9168
9169            if (!benefitMap[prodCode]) {
9170                return;
9171            }
9172
9173            // 크리에이터 매칭 item 은 첫구매 BOGO row sub 영역 비노출 (회원할인과 일관)
9174            if (creator_cart_data && creator_cart_data.matched_items && creator_cart_data.matched_items[itemCode]) {
9175                return;
9176            }
9177
9178            var priceText = benefitMap[prodCode]['benefit_price_text'] || '';
9179            var $row = $cart_list_wrap.find('tr[data-item-code="' + itemCode + '"]');
9180            if ($row.length > 0) {
9181                if (isQualified && benefitMap[prodCode]['is_applied'] === true) {
9182                    if (appliedItemCode && itemCode !== appliedItemCode) {
9183                        return;
9184                    }
9185                    $row.find('._cart_benefit_pc_label').text('첫 구매 혜택').show();
9186                    var $pcOrderPrice = $row.find('._cart_price_default');
9187                    var basePriceForFp = parseInt($pcOrderPrice.data('discounted-price'), 10);
9188                    if (isNaN(basePriceForFp) || basePriceForFp < 0) {
9189                        basePriceForFp = parseInt($row.find('._cart_mobile_order_price').data('discounted-price'), 10);
9190                    }
9191                    var hasFpPrice = false;
9192                    if (!isNaN(basePriceForFp) && benefitDiscountPrice > 0) {
9193                        // 가격 표시는 헬퍼가 PC/mobile 동시 처리 (QD + FP 합성)
9194                        renderCartRowPrice($row, { basePrice: basePriceForFp, fpDiscount: benefitDiscountPrice });
9195                        hasFpPrice = true;
9196                    } else if (priceText) {
9197                        // 서버가 직접 표시 텍스트를 내려주는 fallback (할인 금액 없이 텍스트만)
9198                        $pcOrderPrice.find('._cart_price_default_value').text(priceText);
9199                        hasFpPrice = true;
9200                    }
9201
9202                    // PC 메타 (라벨/보조 가격/sub 영역)
9203                    $row.find('._cart_benefit_pc_price').hide().text('');
9204                    $row.find('._cart_price_default').show();
9205                    $row.find('._cart_benefit_pc_sub').hide().text('');
9206                    if (hasFpPrice) {
9207                        $row.find('._cart_price_origin').show();
9208                    }
9209
9210                    // Mobile 메타 (할인 합계, 혜택 명칭, 필드 토글) — 가격 자체는 위 헬퍼에서 처리됨
9211                    if (benefitDiscountPrice > 0 && benefitDiscountText) {
9212                        var periodDiscountPrice = 0;
9213                        var $mobilePeriodDiscountPrice = $row.find('._cart_mobile_period_discount_price');
9214                        if ($mobilePeriodDiscountPrice.length > 0) {
9215                            periodDiscountPrice = parseInt($mobilePeriodDiscountPrice.data('period-discount-price'), 10) || 0;
9216                            $row.find('._cart_mobile_period_discount_field').hide();
9217                        }
9218                        var totalDiscountPrice = periodDiscountPrice + benefitDiscountPrice;
9219                        $row.find('._cart_mobile_benefit_discount_price').text('- ' + LOCALIZE.getCurrencyFormat(totalDiscountPrice));
9220                        $row.find('._cart_mobile_benefit_detail_price').text('- ' + benefitDiscountText);
9221                        $row.find('._cart_mobile_benefit_name').text(promotionName);
9222                        $row.find('._cart_mobile_benefit_discount_field').show();
9223                        $row.find('._cart_mobile_benefit_detail_field').show();
9224                        $row.find('._cart_mobile_order_price_origin').show();
9225                    }
9226                } else if (!isQualified) {
9227                    if (priceText) {
9228                        var benefitLinkStyle = 'color:#0090D4;font-size:12px;font-weight:600;line-height:16px;display:inline-flex;align-items:center;';
9229                        var benefitInfoIcon = '<svg width="16" height="16" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg" style="vertical-align:middle;margin-left:2px;"><g clip-path="url(#clip0_1166_73396)"><path d="M7.9987 14.6668C11.6806 14.6668 14.6654 11.6821 14.6654 8.00016C14.6654 4.31826 11.6806 1.3335 7.9987 1.3335C4.3168 1.3335 1.33203 4.31826 1.33203 8.00016C1.33203 11.6821 4.3168 14.6668 7.9987 14.6668Z" stroke="#717680" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/><path d="M8 11.3335H8.00667" stroke="#717680" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/><path d="M6.19922 6.12738C6.34708 5.70704 6.63894 5.3526 7.02309 5.12683C7.40724 4.90106 7.8589 4.81853 8.29807 4.89386C8.73724 4.96919 9.13558 5.19752 9.42254 5.5384C9.70949 5.87928 9.86655 6.31072 9.86588 6.75631C9.86588 8.01417 7.97909 8.6431 7.97909 8.6431V9.33337" stroke="#717680" stroke-width="1.2" stroke-linecap="round" stroke-linejoin="round"/></g><defs><clipPath id="clip0_1166_73396"><rect width="16" height="16" fill="white"/></clipPath></defs></svg>';
9230                        // view_component_promotion_cart (PTY02) 트래킹용 도메인 데이터
9231                        var cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
9232                        var fpPromotionCode = $row.attr('data-fp-promotion-code') || '';
9233                        var prodType = $row.attr('data-prod-type') || 'normal';
9234                        var trackPromotionAttr = '';
9235                        if (fpPromotionCode) {
9236                            var trackDomain = {
9237                                prod_code: prodCode,
9238                                prod_type: prodType,
9239                                promotion_code: fpPromotionCode,
9240                                promotion_type: 'first_purchase',
9241                                promotion_product_role: 'target',
9242                                cart_code: cartCode,
9243                                cart_item_code: itemCode,
9244                            };
9245                            // attribute escape: 큰따옴표 attribute 안에 JSON 의 " 가 그대로 들어가면 attribute 가 깨지므로 & " 만 escape
9246                            var trackJson = JSON.stringify(trackDomain).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
9247                            trackPromotionAttr = ' data-bs-promotion="' + trackJson + '"';
9248                        }
9249                        var pcBenefitLinkHtml = '<a href="#" onclick="SITE_SHOP_CART.openPromotionModal(); return false;" class="_cart_promotion_modal_link" style="' + benefitLinkStyle + '"' + trackPromotionAttr + '>첫 구매 혜택가 ' + priceText + benefitInfoIcon + '</a>';
9250                        var mobileBenefitLinkHtml = '<a href="#" onclick="SITE_SHOP_CART.openPromotionModal(); return false;" class="_cart_promotion_modal_link" style="' + benefitLinkStyle + '"' + trackPromotionAttr + '>첫 구매 혜택가 ' + priceText + benefitInfoIcon + '</a>';
9251                        $row.find('._cart_benefit_pc_sub').html(pcBenefitLinkHtml).show();
9252                        $row.find('._cart_mobile_order_hint').html(mobileBenefitLinkHtml);
9253                        $row.find('._cart_mobile_order_hint_field').show();
9254                    }
9255                }
9256            }
9257        });
9258
9259        var benefitDiscountText = benefitInfo['benefit_discount_price_text'] || '';
9260        if (isQualified && benefitDiscountText) {
9261            $benefitName.text(promotionName);
9262            $benefitPrice.text('- ' + benefitDiscountText);
9263            $mobileBenefitField.show();
9264        }
9265
9266    };
9267
9268    var openPromotionModal = function () {
9269        // shadow DOM은 최초 렌더 시 data-description을 1회만 읽으므로, promotion-modal 을 제거-재추가해 shadow DOM 을 재생성한다
9270        var $promotionModalShell = $('#_cart_promotion_modal_shell');
9271        if ($promotionModalShell.length > 0) {
9272            var shell = $promotionModalShell.get(0);
9273            if (shell) {
9274                var modalElement = shell.querySelector('promotion-modal');
9275                if (modalElement && modalElement.parentNode) {
9276                    modalElement.parentNode.removeChild(modalElement);
9277                    shell.appendChild(modalElement);
9278                }
9279            }
9280        }
9281        history.replaceState(null, '', '#promotion-modal');
9282        window.dispatchEvent(new HashChangeEvent('hashchange'));
9283    };
9284
9285    /**
9286     * 상품 위시리스트 추가 처리
9287     * @param cart_item_list
9288     */
9289    var addProdWish = function (cart_item_list, is_regularly) {
9290        $.ajax({
9291            type: 'POST',
9292            data: { 'type': 'add', 'cart_item_list': cart_item_list, 'is_regularly': is_regularly },
9293            url: ('/shop/add_prod_wish_cart.cm'),
9294            dataType: 'json',
9295            success: function (res) {
9296                if (res.msg == 'SUCCESS') {
9297                    for (var i = 0; i < cart_item_list.length; i++) {
9298                        if (typeof FB_PIXEL != 'undefined') FB_PIXEL.AddToWishlist();
9299                        $shop_cart_list.find("._wish_status_" + cart_item_list[i]).addClass('check');
9300                    }
9301                    loadProdWishList();
9302                } else
9303                    alert(res.msg);
9304            }
9305        });
9306    };
9307
9308    /**
9309     * 위시리스트 가져오기
9310     * @param prod_code
9311     */
9312    var loadProdWishList = function () {
9313			var is_design_mode = (location.pathname.indexOf('/admin/design') != -1);
9314        $.ajax({
9315            type: 'POST',
9316            data: { 'type': 'cart', 'is_design_mode': is_design_mode ? 'Y' : 'N' },
9317            url: ('/shop/get_prod_wish_list.cm'),
9318            dataType: 'json',
9319            success: function (res) {
9320                if (res.msg == 'SUCCESS') {
9321                    if (res.count > 0) {
9322                        $shop_cart_wish_list_empty.hide();
9323                        $shop_cart_wish_list.show().html(res.html);
9324                        setTimesale();
9325                    } else {
9326                        $shop_cart_wish_list_empty.show();
9327                        $shop_cart_wish_list.hide();
9328                    }
9329		                // 크리마 리뷰 사용 중일 시 위젯 호출
9330		                if(document.querySelector(".crema-product-reviews-score")){
9331			                if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
9332				                crema.run();
9333			                }
9334		                }
9335                } else
9336                    alert(res.msg);
9337            }
9338        });
9339    };
9340
9341    /**
9342     * 위시리스트 제거
9343     * @param cart_item_list
9344     */
9345    var deleteProdWish = function (cart_item_list, is_regularly) {
9346        $.ajax({
9347            type: 'POST',
9348            data: { 'type': 'delete', 'cart_item_list': cart_item_list, 'is_regularly': is_regularly },
9349            url: ('/shop/add_prod_wish_cart.cm'),
9350            dataType: 'json',
9351            success: function (res) {
9352                if (res.msg == 'SUCCESS') {
9353                    for (var i = 0; i < cart_item_list.length; i++) {
9354                        $shop_cart_list.find("._wish_status_" + cart_item_list[i]).removeClass('check');
9355                    }
9356                    loadProdWishList();
9357                } else
9358                    alert(res.msg);
9359            }
9360        });
9361    };
9362
9363    /**
9364     * 위시리스트 제거 (상품코드사용
9365     * @param prod_code
9366     */
9367    var deleteProdWishByProdCode = function (prod_code) {
9368        $.ajax({
9369            type: 'POST',
9370            data: { 'type': 'delete_prodcode', 'prod_code': prod_code },
9371            url: ('/shop/add_prod_wish_cart.cm'),
9372            dataType: 'json',
9373            success: function (res) {
9374                if (res.msg == 'SUCCESS') {
9375                    if ($('#wish-item-' + prod_code).length > 0) {
9376                        $('#wish-item-' + prod_code).remove();
9377                    } else {
9378                        window.location.reload();
9379                    }
9380                } else
9381                    alert(res.msg);
9382            }
9383        });
9384    };
9385
9386    /**
9387     * 묶음배송 그룹에서 상품 행 삭제 전 rowspan/모바일 배송정보 행 보정
9388     */
9389    const _adjustBundleOnRemove = ($row) => {
9390        const $rowspanTd = $row.find('td[rowspan]');
9391        let $bundleFirstRow = null;
9392
9393        if ($rowspanTd.length) {
9394            const rowspan = parseInt($rowspanTd.attr('rowspan'), 10);
9395            if (rowspan > 1) {
9396                // 첫 번째 행 삭제: 배송비 td를 다음 행으로 이동
9397                const $next = $row.nextAll('tr.content').first();
9398                if ($next.length) {
9399                    $rowspanTd.attr('rowspan', rowspan - 1);
9400                    $next.append($rowspanTd);
9401                    $bundleFirstRow = $next;
9402                }
9403            }
9404        } else {
9405            // 첫 번째가 아닌 행 삭제: 그룹 첫 행의 rowspan 감소
9406            $row.prevAll('tr.content').each(function () {
9407                const $td = $(this).find('td[rowspan]');
9408                if ($td.length) {
9409                    const rs = parseInt($td.attr('rowspan'), 10);
9410                    if (rs > 1) $td.attr('rowspan', rs - 1);
9411                    $bundleFirstRow = $(this);
9412                    return false;
9413                }
9414            });
9415        }
9416
9417        // 모바일 묶음배송 정보 행: 삭제 후 남은 상품이 1개 이하면 숨김
9418        if ($bundleFirstRow && $bundleFirstRow.length) {
9419            const newRowspan = parseInt($bundleFirstRow.find('td[rowspan]').attr('rowspan'), 10) || 1;
9420            if (newRowspan <= 1) {
9421                let $cur = $bundleFirstRow;
9422                while ($cur.length) {
9423                    $cur = $cur.next('tr');
9424                    if ($cur.hasClass('im-tr-shipping')) { $cur.addClass('hide'); break; }
9425                    if (!$cur.hasClass('content')) break;
9426                }
9427            }
9428        }
9429    };
9430
9431    /**
9432     * click_remove_item_cart 트래킹 (서버 응답 200 + cart 화면 반영 직후)
9433     * @param {('option'|'product')} unit
9434     * @param {{prodCode, prodType, quantityBefore}} ctx
9435     * @param {string|null} cartItemCode option 삭제 시 채움. product 삭제 시 null
9436     * @param {string|null} optionInfo option 삭제 시 채움. product 삭제 시 null
9437     */
9438    const _trackRemoveItemCart = (unit, ctx, cartItemCode, optionInfo) => {
9439        if (typeof window.BrandScope === 'undefined' || !ctx) return;
9440        const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
9441        window.BrandScope.track('click_remove_item_cart', {
9442            action:          'click',
9443            content:         'remove_item',
9444            where:           'cart',
9445            prod_code:       ctx.prodCode || '',
9446            prod_type:       ctx.prodType || 'normal',
9447            cart_code:       cartCode,
9448            cart_item_code:  cartItemCode,
9449            option_info:     optionInfo,
9450            quantity_unit:   unit,
9451            quantity_before: ctx.quantityBefore,
9452            quantity_after:  0,
9453        });
9454    };
9455
9456    /**
9457     * 특정 카트 아이템 삭제
9458     */
9459    const removeCartItem = (item_codeList, is_regularly) => {
9460        // 트래킹 데이터 사전 캡처 (success 시 row 제거되므로 prod_code/prod_type/quantity_before 가 사라짐)
9461        const trackingContexts = item_codeList.map((code) => {
9462            const $row = $cart_list_wrap.find(`tr[data-item-code="${code}"]`);
9463            if (!$row.length) return null;
9464            // 상품 단위 총 수량 = 행 내 모든 _counter_value 합산 (옵션 라인 + product_counter)
9465            const totalCount = $row.find('._counter_value').toArray()
9466                .reduce((sum, el) => sum + (parseInt(el.value, 10) || 0), 0);
9467            return {
9468                prodCode:       $row.attr('data-prod-code') || '',
9469                prodType:       $row.attr('data-prod-type') || 'normal',
9470                quantityBefore: totalCount,
9471            };
9472        });
9473
9474        $.ajax({
9475            type: 'POST',
9476            data: { item_codeList, is_regularly },
9477            url: '/shop/remove_cart_item.cm',
9478            dataType: 'json',
9479            cache: false,
9480            success: ({ msg, delete_cart_info }) => {
9481                if (msg !== 'SUCCESS') return alert(msg);
9482
9483                item_codeList.forEach((item_code) => {
9484                    const info = delete_cart_info[item_code];
9485                    if (typeof AW_DEL !== 'undefined') AW_DEL(info['prod_no'], info['count']);
9486                    if (typeof AM_DEL !== 'undefined') AM_DEL(info['prod_no'], info['count']);
9487                });
9488                if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
9489
9490                // 페이지 리로드 대신 DOM 요소 제거
9491                const $rows = item_codeList
9492                    .map((code) => $cart_list_wrap.find(`tr[data-item-code="${code}"]`))
9493                    .filter(($el) => $el.length);
9494
9495                if (!$rows.length) return window.location.reload();
9496
9497                $rows.forEach(($row) => {
9498                    _adjustBundleOnRemove($row);
9499                    $row.remove();
9500                });
9501
9502                // click_remove_item_cart 트래킹 (휴지통: product 단위 삭제. cart_item_code/option_info 는 null)
9503                trackingContexts.forEach((ctx) => _trackRemoveItemCart('product', ctx, null, null));
9504
9505                // 장바구니가 비었으면 리로드하여 빈 상태 UI 표시
9506                if (!$cart_list_wrap.find('._cartItemCheckbox').length) return window.location.reload();
9507
9508                CartItemChanged(is_regularly === 'Y' ? 'Y' : 'N');
9509            }
9510        });
9511    };
9512
9513    /**
9514     * 단일 상품을 카트에 담기.
9515     * 무료 배송 안내 SDK 등 카트 페이지 컨텍스트에서 사용한다.
9516     *
9517     * 오버로드:
9518     *   addCart(prodIdx, callback)                                            // 옵션 없음 + 수량 1
9519     *   addCart(prodIdx, options, orderCount, callback, requestOptions)       // 옵션/수량/요청 옵션 지정
9520     *
9521     * @param {number} prodIdx 추가할 상품 idx
9522     * @param {Array|function} optionsOrCallback 옵션 배열 또는 콜백 (오버로드 분기)
9523     * @param {number} [orderCount] 주문 수량 (기본 1)
9524     * @param {function(boolean,string,object=):void} [callback] 완료 콜백 (성공 여부, 메시지, add_cart.cm 응답)
9525     * @param {object} [requestOptions] add_cart.cm 추가 요청 옵션
9526     */
9527    const addCart = (prodIdx, optionsOrCallback, orderCount, callback, requestOptions) => {
9528        // 오버로드 분기: 두 번째 인자가 function이면 (prodIdx, callback) 호출로 간주
9529        var options = [];
9530        var count = 1;
9531        var cb = null;
9532        if (typeof optionsOrCallback === 'function') {
9533            cb = optionsOrCallback;
9534        } else {
9535            options = Array.isArray(optionsOrCallback) ? optionsOrCallback : [];
9536            count = (typeof orderCount === 'number' && orderCount > 0) ? orderCount : 1;
9537            cb = (typeof callback === 'function') ? callback : null;
9538        }
9539        var cartRequestOptions = (requestOptions && typeof requestOptions === 'object') ? requestOptions : {};
9540        var shippingTemplateCode = typeof cartRequestOptions.shipping_template_code === 'string'
9541            ? cartRequestOptions.shipping_template_code
9542            : '';
9543        var requestCartType = typeof cartRequestOptions.cart_type === 'string'
9544            ? cartRequestOptions.cart_type
9545            : '';
9546        if (requestCartType !== SHOP_CONST.CART_TYPE_NORMAL && requestCartType !== SHOP_CONST.CART_TYPE_REGULARLY) {
9547            requestCartType = '';
9548        }
9549        var effectiveCartType = requestCartType || SHOP_CONST.CART_TYPE_NORMAL;
9550
9551        $.ajax({
9552            type: 'POST',
9553            url: '/shop/add_cart.cm',
9554            dataType: 'json',
9555            data: {
9556                prodIdx: prodIdx,
9557                options: options,
9558                orderCount: count,
9559                deliv_type: '',
9560                deliv_pay_type: '',
9561                cart_type: effectiveCartType,
9562                prod_additional_list: [],
9563                prod_additional_option_list: [],
9564                shipping_template_code: shippingTemplateCode
9565            },
9566            success: (result) => {
9567                if (result.msg !== 'SUCCESS') {
9568                    if (cb) cb(false, result.msg);
9569                    return;
9570                }
9571
9572                // 전환추적 (SITE_SHOP_DETAIL.addCart 와 동일 배터리)
9573                if (typeof NP_LOG !== 'undefined') NP_LOG.AddToCart();
9574                if (typeof NP_NDA !== 'undefined') NP_NDA.AddToCart(result.prod_code);
9575                if (typeof CRITEO !== 'undefined') CRITEO.AddToCart(result.prod_id, result.total_price);
9576                if (typeof FB_PIXEL !== 'undefined') FB_PIXEL.AddToCart(result.prod_id, result.prod_name, result.cart_price, result.currency, result.prod_count, result.total_price, result.fb_event_id, result.fb_external_id);
9577                if (typeof ACE_COUNTER !== 'undefined') ACE_COUNTER.AddToCart(result.prod_id, result.prod_name, result.prod_count);
9578                if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.AddToCart(result.prod_id, result.prod_count, result.total_price, result.currency);
9579                if (typeof ACE_COUNTER_PARTNER !== 'undefined') ACE_COUNTER_PARTNER.AddToCart(result.prod_id, result.prod_count, result.check_quantity);
9580                if (typeof AW_PRODUCT !== 'undefined') AW_PRODUCT(result.prod_count);
9581                if (typeof AM_PRODUCT !== 'undefined') AM_PRODUCT(result.prod_count);
9582                if (typeof TIKTOK_PIXEL !== 'undefined') {
9583                    TIKTOK_PIXEL.track('AddToCart', {
9584                        contents: [{
9585                            content_id: result.prod_id,
9586                            content_name: result.prod_name,
9587                            quantity: result.prod_count,
9588                            price: result.cart_price
9589                        }],
9590                        event_id: result.tiktok_event_id,
9591                        content_type: 'product',
9592                        value: result.total_price,
9593                        currency: result.currency
9594                    });
9595                }
9596                if (typeof CRM_ONSITE !== 'undefined') {
9597                    CRM_ONSITE.track({ name: 'ADD_TO_CART' });
9598                }
9599
9600                // 카트 리스트 부분 갱신 (전체 페이지 reload 대신 카트 영역만 재렌더링).
9601                // 단, 카트 페이지가 아닌 컨텍스트(상세 페이지의 장바구니 담기 완료 모달 내 매그넷 등)에서
9602                // SITE_SHOP_CART.addCart가 호출될 수 있는데, 이 경우 카트 DOM($cart_list_wrap)이 존재하지 않으므로 스킵한다.
9603                // initCart가 호출된 카트 페이지에서만 $cart_list_wrap이 셋업되어 있다.
9604                if (typeof $cart_list_wrap !== 'undefined' && $cart_list_wrap && $cart_list_wrap.length > 0) {
9605                    // addCart 흐름은 항상 C3/OMS 컨텍스트이므로 is_c3=true 로 고정
9606                    cartListMake(current_backurl, effectiveCartType, true);
9607                } else {
9608                    // 상세 페이지 컨텍스트: 카트 DOM이 없으므로 cartListMake 대신
9609                    // 무료배송 안내 모달용 누적 총액을 갱신하여 MFE 위젯에 imweb:freeShipNotify:cartTotal:update 이벤트 dispatch.
9610                    // 베이스라인은 SITE_SHOP_DETAIL.addCart 성공 시 메인 상품 가격으로 세팅된 window.__imwebFreeShipCartCumulative.
9611                    if (typeof window.__imwebFreeShipCartCumulative !== 'number') {
9612                        window.__imwebFreeShipCartCumulative = 0;
9613                    }
9614                    if (typeof window.__imwebFreeShipCartAdditionalsSum !== 'number') {
9615                        window.__imwebFreeShipCartAdditionalsSum = 0;
9616                    }
9617                    if (typeof window.__imwebFreeShipCartItemCounts !== 'object' || window.__imwebFreeShipCartItemCounts === null) {
9618                        window.__imwebFreeShipCartItemCounts = {};
9619                    }
9620
9621                    var freeShipNotifyCartPrice = (typeof result.free_shipping_price === 'number' ? result.free_shipping_price : (typeof result.total_price === 'number' ? result.total_price : 0));
9622                    window.__imwebFreeShipCartCumulative += freeShipNotifyCartPrice;
9623                    window.__imwebFreeShipCartAdditionalsSum += freeShipNotifyCartPrice;
9624
9625                    if (typeof result.prod_code === 'string' && result.prod_code !== '') {
9626                        window.__imwebFreeShipCartItemCounts[result.prod_code] =
9627                            (window.__imwebFreeShipCartItemCounts[result.prod_code] || 0) + 1;
9628
9629                        if (!Array.isArray(window.__imwebAddToCartProdCodeList)) {
9630                            window.__imwebAddToCartProdCodeList = [];
9631                        }
9632                        window.__imwebAddToCartProdCodeList.push(result.prod_code);
9633                        window.__imwebAddToCartHasFreeShipUpsell = true;
9634                    }
9635
9636                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartTotal:update', {
9637                        detail: { totalPrice: window.__imwebFreeShipCartCumulative }
9638                    }));
9639
9640                    var markerEl = document.getElementById('shop_detail_add_cart_alarm_with_free_ship_notify');
9641                    if (markerEl
9642                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
9643                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice === 'function') {
9644                        var initRemaining = parseFloat(markerEl.getAttribute('data-init-deliv-price-flexable-key') || '0');
9645                        var newRemaining = Math.max(0, initRemaining - window.__imwebFreeShipCartCumulative);
9646                        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice(newRemaining);
9647                    }
9648
9649                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartItems:update', {
9650                        detail: { counts: Object.assign({}, window.__imwebFreeShipCartItemCounts) }
9651                    }));
9652                }
9653
9654                if (cb) cb(true, 'SUCCESS', result);
9655            },
9656            error: () => {
9657                if (cb) cb(false, '상품 담기 중 오류가 발생했습니다.');
9658            }
9659        });
9660    };
9661
9662    /**
9663     *  품절된 상품만 삭제
9664     * @param item_codeList
9665     * @param is_regularly
9666     */
9667    const removeSelectedCartSoldOutItem = (item_codeList, is_regularly) => {
9668        $.ajax({
9669            type: 'POST',
9670            data: { item_codeList, is_regularly },
9671            url: '/shop/remove_cart_soldout_item.cm',
9672            dataType: 'json',
9673            cache: false,
9674            success: ({ msg, delete_cart_info }) => {
9675                if (msg !== 'SUCCESS') return alert(msg);
9676
9677                item_codeList.forEach((item_code) => {
9678                    const info = delete_cart_info[item_code];
9679                    if (info) {
9680                        if (typeof AW_DEL !== 'undefined') AW_DEL(info['prod_no'], info['count']);
9681                        if (typeof AM_DEL !== 'undefined') AM_DEL(info['prod_no'], info['count']);
9682                    }
9683                });
9684                if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
9685
9686                // 페이지 리로드 대신 DOM 요소 제거
9687                const deletedCodes = Object.keys(delete_cart_info || {});
9688                const $rows = deletedCodes
9689                    .map((code) => $cart_list_wrap.find(`tr[data-item-code="${code}"]`))
9690                    .filter(($el) => $el.length);
9691
9692                if (!$rows.length) return window.location.reload();
9693
9694                $rows.forEach(($row) => {
9695                    _adjustBundleOnRemove($row);
9696                    $row.remove();
9697                });
9698
9699                // 장바구니가 비었으면 리로드하여 빈 상태 UI 표시
9700                if (!$cart_list_wrap.find('._cartItemCheckbox').length) return window.location.reload();
9701
9702                CartItemChanged(is_regularly === 'Y' ? 'Y' : 'N');
9703            }
9704        });
9705    };
9706
9707    /**
9708     * 특정 카트 아이템 옵션 삭제
9709     */
9710    const removeCartItemOption = (item_code, optionNo, is_regularly) => {
9711        // 트래킹 데이터 사전 캡처 (success 시 $card 제거되므로)
9712        const $cardBefore = $cart_list_wrap.find(`._cart_option_card[data-item-code="${item_code}"][data-option-no="${optionNo}"]`);
9713        const $rowBefore = $cardBefore.closest(`tr[data-item-code="${item_code}"]`);
9714        const trackingCtx = $cardBefore.length ? {
9715            prodCode:       $rowBefore.attr('data-prod-code') || '',
9716            prodType:       $rowBefore.attr('data-prod-type') || 'normal',
9717            quantityBefore: parseInt($cardBefore.find('._counter_value').val(), 10) || 0,
9718        } : null;
9719        let trackingOptionInfo = null;
9720        if ($cardBefore.length) {
9721            const parts = [];
9722            $cardBefore.find('.opt-name').each(function () {
9723                const text = $(this).parent().text().replace(/\s+/g, ' ').trim();
9724                if (text) parts.push(text);
9725            });
9726            if (parts.length) trackingOptionInfo = parts.join(' / ');
9727        }
9728
9729        $.ajax({
9730            type: 'POST',
9731            data: { item_code, optionNo, is_regularly },
9732            url: '/shop/remove_cart_item_option.cm',
9733            dataType: 'json',
9734            cache: false,
9735            success: ({ msg }) => {
9736                if (msg !== 'SUCCESS') return alert(msg);
9737
9738                const $card = $cart_list_wrap.find(`._cart_option_card[data-item-code="${item_code}"][data-option-no="${optionNo}"]`);
9739                if (!$card.length) return window.location.reload();
9740
9741                const $row = $card.closest(`tr[data-item-code="${item_code}"]`);
9742
9743                // 삭제되는 옵션의 가격을 행 가격에서 차감
9744                const optCount = parseInt($card.find('._counter_value').val(), 10) || 0;
9745                const $optPrice = $card.find('._cart_option_price');
9746                const $optOrigin = $card.find('._cart_option_origin_price');
9747                const discountedUnit = parseInt($optPrice.data('unit-price'), 10) || 0;
9748                const originUnit = $optOrigin.length ? (parseInt($optOrigin.data('unit-price'), 10) || 0) : discountedUnit;
9749                const discountedDelta = discountedUnit * optCount;
9750                const originDelta = originUnit * optCount;
9751
9752                // 가격 갱신 (PC/mobile 헬퍼 통합)
9753                const $pcDefault = $row.find('._cart_price_default');
9754                const $pcOrigin = $row.find('._cart_price_origin');
9755                const newDiscounted = (parseInt($pcDefault.data('discounted-price'), 10) || 0) - discountedDelta;
9756                const newOrigin = (parseInt($pcOrigin.data('origin-price'), 10) || 0) - originDelta;
9757                renderCartRowPrice($row, { basePrice: newDiscounted, originPrice: newOrigin });
9758
9759                // 타이틀 행 아이템 가격(strikethrough 영역) 별도 갱신
9760                $row.find('._cart_item_price').text(LOCALIZE.getCurrencyFormat(newOrigin));
9761
9762                $card.remove();
9763                // 옵션 삭제 후 남은 카드의 option-no를 재할당 (서버 인덱스와 동기화)
9764                $row.find('._cart_option_card').each(function (i) {
9765                    $(this).attr('data-option-no', i);
9766                    $(this).find('._cart_counter').data('optionNo', i).attr('data-option-no', i);
9767                });
9768                // 옵션 카드가 없어도 본품 카운터가 있으면 행 유지
9769                const hasProductCounter = $row.find('._cart_counter[data-mode="product_count"]').length > 0;
9770                if (!$row.find('._cart_option_card').length && !hasProductCounter) $row.remove();
9771                CartItemChanged(is_regularly);
9772                // click_remove_item_cart 트래킹 (옵션 X 버튼: option 단위 삭제)
9773                _trackRemoveItemCart('option', trackingCtx, item_code, trackingOptionInfo);
9774            }
9775        });
9776    };
9777
9778    /**
9779     * 체크박스 전체 선택 제어
9780     * @param bool b
9781     */
9782    var toggleAllCheckCartItem = function (b, is_regularly) {
9783        $cart_list_wrap.find('._cartItemCheckbox').each(function () {
9784            $(this).prop('checked', (b === true));
9785        });
9786        CartItemChanged(is_regularly);
9787    };
9788
9789    /**
9790     * 체크박스 선택 제어
9791     */
9792    var toggleCheckCartItem = function (is_regularly) {
9793        var $cartItemCheckbox = $cart_list_wrap.find('._cartItemCheckbox');
9794        var $cartItemCheckboxSeleted = $cart_list_wrap.find('._cartItemCheckbox:checked');
9795        $cart_list_wrap.find('._all_check').prop('checked', ($cartItemCheckbox.length == $cartItemCheckboxSeleted.length));
9796        CartItemChanged(is_regularly);
9797    };
9798
9799    /**
9800     * 체크박스 변경시 재계산
9801     */
9802    var CartItemChanged = function (is_regularly) {
9803        // 초기화
9804        selectedCartItem = [];
9805        $cart_list_wrap.find('._cartItemCheckbox:checked').each(function () {
9806            selectedCartItem.push($(this).val());
9807        });
9808        getSelectedPrice(is_regularly);
9809    };
9810
9811    /**
9812     * 전체 선택인지 아닌지 체크
9813     */
9814    var toggleA
9814llSelectCk = function () {
9815        var $cartItemSelectCount = 0;
9816        $cartItemCheckboxList = $shop_cart_list.find("input._cartItemCheckbox");
9817        $cartAllCheckBox = $shop_cart_list.find("input._all_check");
9818        $cartItemCheckboxList.each(function () {
9819            if ($(this).is(":checked") === true) {
9820                $cartItemSelectCount++;
9821            }
9822        });
9823        if ($cartItemCheckboxList.length == $cartItemSelectCount) {
9824            $cartAllCheckBox.prop('checked', true);
9825        } else {
9826            $cartAllCheckBox.prop('checked', false);
9827        }
9828    };
9829
9830    /**
9831     * 카트 전체 주문하기, C3 분기 추가
9832     * @param direct true/false (선택유무와 상관없이 바로 주문)
9833		 * TODO: 실행시 유저가 받을 사은품을 전달받아 페이로드에 담도록 한다.
9834     */
9835    /**
9836     * CB-3985 — 주문 생성 응답으로 머천트 전환/마케팅 픽셀 발화 (단일 소스).
9837     *
9838     * addOrderWithCart 콜백(npay / talkpay onPayOrder / default)의 픽셀 블록을 함수로 추출.
9839     * 레거시 장바구니 주문(바로구매 포함)과 MFE 장바구니(SITE_SHOP_CART.OMS_fireOrderTracking 노출)가
9840     * 동일 코드를 호출해 멱등성을 보장한다 — 픽셀 로직을 MFE 가 재구현하지 않는다.
9841     *
9842     * - 각 SDK 는 머천트 설정에 따라 주입되므로 typeof 가드 필수 (미설정 머천트는 자동 skip).
9843     * - npay 성인인증(auth_type==2)은 레거시와 동일하게 픽셀 미발화.
9844     * - npay 추가배송비 안내 / adult_auth redirect / advanced_kakao_trace 등 부수 흐름은 호출자 책임(범위 밖).
9845     */
9846    var fireOrderTracking = function (type, result) {
9847        // CB-3985 — 픽셀 SDK(머천트 설치)에서 예외가 나도 주문 redirect 등 후속 로직을 끊지 않도록 차단.
9848        try {
9849        if (type === 'npay') {
9850            if (result.auth_type == 2) return;
9851            var npay_order_info = result['npay_order_info'];
9852            if (typeof FB_PIXEL != 'undefined') {
9853                FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
9854                if (result.fb_npay_switch === 'Y') FB_PIXEL.addNpayOrder(npay_order_info);
9855            }
9856            if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder();
9857            if (result.google_analytics_type == 'G' && result.is_ga_api_secret === false) {
9858                if (typeof GOOGLE_ANAUYTICS != 'undefined') GOOGLE_ANAUYTICS.addNpayOrder(npay_order_info);
9859            }
9860            if (typeof GOOGLE_ADWORDS_TRACE != 'undefined' && result.google_ads_include_npay === 'Y') GOOGLE_ADWORDS_TRACE.addNpayOrder(npay_order_info);
9861            if (typeof CRITEO != 'undefined') CRITEO.npayTrackTransaction(npay_order_info);
9862            if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
9863            if (typeof TIKTOK_PIXEL != 'undefined') {
9864                TIKTOK_PIXEL.track('InitiateCheckout', {
9865                    event_id: result.order_code,
9866                    contents: result.prod_list.map(prod => ({
9867                        content_id: prod.id,
9868                        content_name: prod.name,
9869                        price: prod.price,
9870                    })),
9871                    content_type: 'product',
9872                    value: result.total_price,
9873                    currency: result.currency,
9874                });
9875            }
9876        } else {
9877            // normal / regularly / talkpay onPayOrder — 일반 주문 픽셀.
9878            if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
9879            if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder();
9880            if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
9881            if (typeof TIKTOK_PIXEL != 'undefined') {
9882                TIKTOK_PIXEL.track('InitiateCheckout', {
9883                    event_id: result.order_code,
9884                    contents: result.prod_list.map(prod => ({
9885                        content_id: prod.id,
9886                        content_name: prod.name,
9887                        price: prod.price,
9888                    })),
9889                    content_type: 'product',
9890                    value: result.total_price,
9891                    currency: result.currency,
9892                });
9893            }
9894        }
9895        } catch (e) {
9896            // 픽셀 발화 실패는 무시 (주문 흐름 보호).
9897        }
9898    };
9899
9900    var addOrderWithCart = async function (type, item_code, backurl, direct, is_oms, params, is_gift_buy, skipFreeShipNotifyAlarm){
9901			if (is_cart_changed) return false;
9902			if (add_order_progress_check) return false;
9903			if (currentCartCode == '') {
9904				alert(LOCALIZE.설명_장바구니가비어있습니다());
9905				return false;
9906			}
9907
9908			//type 이 guest_login 으로 넘어오는경우 비회원주문+로그인페이지로이동후주문 방식임 type 을 normal 로처리해야 다른 로직에 영향이 없어 이렇게 처리함
9909			var is_guest_login = false;
9910			if (type == 'guest_login') {
9911				is_guest_login = true;
9912				type = 'normal';
9913			}
9914
9915			var item_code_list = [];
9916			if (item_code == '')
9917				item_code_list = selectedCartItem;
9918			else
9919				item_code_list = [item_code];
9920
9921			if (item_code_list.length <= 0) {
9922				alert(LOCALIZE.설명_주문하실상품을선택해주세요());
9923				return false;
9924			}
9925
9926			// 배송 국가 선택 유효성 검사
9927			if ($global_select && $global_select.children().length > 0 && (!$deliv_country || $deliv_country === 'none')) {
9928				$('._deliv_country_selector').addClass('warning_select');
9929				alert(typeof LOCALIZE.설명_배송받을국가를선택해주세요 === 'function' ? LOCALIZE.설명_배송받을국가를선택해주세요() : '배송받을 국가를 선택해주세요.');
9930				return false;
9931			}
9932
9933			// 무료배송 안내 모달 - 주문 진행 전 사용자에게 노출 (마커가 DOM에 있을 때만)
9934			// 페이지뷰 1회만 노출하기 위해 SDK의 가드를 사용한다. 다른 페이지를 다녀오면 SDK 클로저가 리셋되어 다시 노출됨.
9935			var freeShipNotifySdk = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK;
9936			if (!skipFreeShipNotifyAlarm && freeShipNotifySdk && !freeShipNotifySdk.hasOrderAlarmShown()
9937				&& ($('#shop_cart_list ._global_select').length === 0 || $deliv_country === 'KR')) {
9938				var $orderAlarmMarker = $('#shop_cart_order_alarm_with_free_ship_notify');
9939				if ($orderAlarmMarker.length > 0) {
9940					var initDelivPriceFlexableKey = parseFloat($orderAlarmMarker.attr('data-init-deliv-price-flexable-key') || '0');
9941					if (initDelivPriceFlexableKey > 0) {
9942						freeShipNotifySdk.markOrderAlarmShown();
9943						_pendingOrderWithCart = {
9944							type: type,
9945							item_code: item_code,
9946							backurl: backurl,
9947							direct: direct,
9948							is_oms: is_oms,
9949							params: params,
9950							is_gift_buy: is_gift_buy
9951						};
9952						$.ajax({
9953							type: 'POST',
9954							url: '/shop/free_ship_notify/get_order_alarm_modal.cm',
9955							data: { init_deliv_price_flexable_key: initDelivPriceFlexableKey },
9956							dataType: 'html',
9957							cache: false,
9958							success: function (html) {
9959								// SITE_SHOP_DETAIL.is_mobile_width와 동일 기준 (window.innerWidth < 768)
9960								var is_mobile_width = (window.innerWidth < 768);
9961								if (is_mobile_width) {
9962									imSheet.close('shop_cart_order_alarm_sheet', function () {
9963										imSheet.open({
9964											id: 'shop_cart_order_alarm_sheet',
9965											html: html,
9966											backdrop: 'rgba(0, 0, 0, 0.15)',
9967											zIndex: 17001
9968										});
9969									});
9970								} else {
9971									if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
9972										$.cocoaDialog.close();
9973									}
9974									$.cocoaDialog.open({
9975										type: 'site_shop_free_ship_notify',
9976										custom_popup: html,
9977										pc_width: 440
9978									});
9979								}
9980							},
9981							error: function () {
9982								// fetch 실패 시 보류 인자 정리하고 모달 없이 주문 진행
9983								_pendingOrderWithCart = null;
9984								addOrderWithCart(type, item_code, backurl, direct, is_oms, params, is_gift_buy, true);
9985							}
9986						});
9987						return false;
9988					}
9989				}
9990			}
9991
9992			if (is_oms) {
9993				const $cartContainer = $shop_cart_list || $('#shop_cart_list');
9994				const $cartTable = $cartContainer.find('._shop_table').first();
9995				const hasDefaultShippingAddress = $cartTable.attr('data-has-default-shipping-address') === 'Y';
9996				let hasFastShipping = false;
9997				for (let i = 0; i < item_code_list.length; i++) {
9998					const code = item_code_list[i];
9999					const $checkbox = $cartContainer.find(`input._cartItemCheckbox[value="${code}"]`).first();
10000					if ($checkbox.length === 0) continue;
10001					if ($checkbox.attr('data-fast-shipping') === 'Y') {
10002						hasFastShipping = true;
10003						break;
10004					}
10005				}
10006				if (hasFastShipping && !hasDefaultShippingAddress) {
10007					alert('기본 배송지가 등록되지 않아 일반배송으로 자동 변경돼요.');
10008				}
10009				if (hasFastShipping && (is_gift_buy || type === 'npay' || type === 'talkpay')) {
10010					if (is_gift_buy) {
10011						alert('일반배송으로 선물하기가 진행돼요');
10012					} else {
10013						alert('선택한 배송은 톡체크아웃·네이버페이와 같은 간편구매에서는 이용할 수 없어요. 구매는 일반배송으로 자동 변경돼요.');
10014					}
10015				}
10016			}
10017
10018			add_order_progress_check = true;
10019
10020			if(is_oms){
10021				if (localStorage.getItem("selectedDeliveryCountry") === "undefined") {
10022					localStorage.removeItem("selectedDeliveryCountry");
10023				}
10024				if (typeof $deliv_country !== 'undefined') {
10025					localStorage.setItem("selectedDeliveryCountry", $deliv_country);
10026				}
10027			}
10028
10029			const [orderBtn, orderRegularlyBtn, orderGiftBtn] = [$('._btn_order'), $('._btn_order_regularly'), $('._btn_order_gift')];
10030			const [orderBtnText, orderRegularlyBtnText, orderGiftBtnText] = [orderBtn.first().text(), orderRegularlyBtn.first().text(), orderGiftBtn.first().text()];
10031
10032			const setSpinner = function ($buttons) {
10033				const spinner = '<div class="loading-spinner-container"><div class="loading-spinner"></div></div>';
10034
10035				if (Array.isArray($buttons)) {
10036					for (const $button of $buttons) {
10037						if ($button.length > 0) {
10038							$button.html(spinner);
10039						}
10040					}
10041				} else {
10042					if ($buttons.length > 0) {
10043						$buttons.html(spinner);
10044					}
10045				}
10046			}
10047
10048			const unsetSpinner = function ($btn, text) {
10049				if ($btn.length > 0) {
10050					$btn.text(text);
10051				}
10052			}
10053
10054			// 간편결제 버튼은 PG사 정책 우려로 인해 스피너 적용하지 않음
10055			if (type !== 'npay' && type !== 'talkpay') {
10056				// 주문하기, 정기구독 버튼에 로딩 스피너 추가
10057				setSpinner([orderBtn, orderRegularlyBtn, orderGiftBtn]);
10058			}
10059
10060			const { freebiesUserSelected, selectedAllFreebiesRequired } = await new Promise((resolve) => {
10061				// MFE 장바구니가 사은품 선택값을 직접 전달한 경우(모바일 바로구매 — 포함형 cart MFE라
10062				// 이벤트 pull 응답자 없음). 이벤트 발송/타임아웃/게이팅 없이 전달분을 그대로 사용한다.
10063				// MFE 측에서 이미 선택형 사은품 충족을 검증하므로 selectedAllFreebiesRequired=true.
10064				if (params && Array.isArray(params.freebie_list)) {
10065					resolve({ freebiesUserSelected: params.freebie_list, selectedAllFreebiesRequired: true });
10066					return;
10067				}
10068				// OMS 주문이 아니거나 정기구독인 경우 사은품 선택 없이 바로 진행
10069				if (!is_oms || type === 'regularly') {
10070					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10071					return;
10072				}
10073
10074				if (!isFreebieMfeMounted()) {
10075					// 사은품 미사용 사이트는 MFE 미적재 → 사은품 선택 없이 바로 진행
10076					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10077					return;
10078				}
10079
10080				// 모바일 장바구니 MFE 는 사은품 선택 UI(freebiesUserSelectedOnCartMobile 이벤트)를
10081				// 사용하지 않는다. 응답자가 없어 아래 dispatch 가 10초 타임아웃까지 대기 → 모바일 바로구매/
10082				// 주문 지연의 원인. cart MFE 활성(magnet-shell[data-mfe="fo-shopping/cart"], cartListMake
10083				// 와 동일 식별) + 모바일이면 사은품 선택 없이 바로 진행한다.
10084				// (카트 셸이 없는 화면에서는 기존 사은품 선택 흐름 그대로 유지)
10085				if (window.IS_MOBILE && $('#shop_cart_list magnet-shell[data-mfe="fo-shopping/cart"]').length > 0) {
10086					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10087					return;
10088				}
10089
10090				// 네이버페이, 톡체크아웃인 경우 안내 후 사은품 없이 진행
10091				if (type === 'npay' || type === 'talkpay') {
10092					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10093					return;
10094				}
10095
10096				// 일반 OMS 주문인 경우 사은품 선택 이벤트 발송
10097				let settled = false;
10098				const timeoutId = setTimeout(() => {
10099					if (settled) return;
10100					settled = true;
10101					// 10000ms 내 MFE 응답 없으면 사은품 없이 주문 진행 (사은품 미사용 사이트 포함)
10102					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10103				}, 10000);
10104
10105				window.document.dispatchEvent(new CustomEvent('@im/fo-prod-detail-freebie:freebiesUserSelectedOnCart' + (window.IS_MOBILE ? 'Mobile' : ''), {
10106					detail: {
10107						// (args: { freebiesUserSelected: Freebie[]; selectedAllFreebiesRequired: boolean }) => void
10108						resolveCallback: (args) => {
10109							if (settled) return;
10110							settled = true;
10111							clearTimeout(timeoutId);
10112
10113							if (args.isReady === false) {
10114								alert('사은품 정보를 불러오는 중입니다. 잠시 후 다시 시도해 주세요.');
10115								unsetSpinner(orderBtn, orderBtnText);
10116								unsetSpinner(orderRegularlyBtn, orderRegularlyBtnText);
10117								unsetSpinner(orderGiftBtn, orderGiftBtnText);
10118								add_order_progress_check = false;
10119								return;
10120							}
10121
10122							resolve(args);
10123						}
10124					}
10125				}))
10126			})
10127
10128			if (!selectedAllFreebiesRequired) {
10129				// 일반 주문에서만 사은품 관련 처리 - 정기구독에서의 사은품은 스팩아웃
10130				unsetSpinner(orderBtn, orderBtnText);
10131				alert('아직 사은품을 선택하지 않았습니다. 사은품을 선택해주세요.');
10132				add_order_progress_check = false;
10133				return false;
10134			}
10135
10136			let shippingTemplateCodeMap = {};
10137			if (is_oms) {
10138				const $cartContainer = $shop_cart_list || $('#shop_cart_list');
10139				const templateMap = {};
10140				for (let i = 0; i < item_code_list.length; i++) {
10141					const code = item_code_list[i];
10142					const $checkbox = $cartContainer.find(`input._cartItemCheckbox[value="${code}"]`).first();
10143					if ($checkbox.length === 0) continue;
10144					const templateCode = $checkbox.attr('data-shipping-template-code');
10145					if (templateCode) {
10146						templateMap[code] = templateCode;
10147					}
10148				}
10149				if (Object.keys(templateMap).length > 0) {
10150					shippingTemplateCodeMap = templateMap;
10151				}
10152			}
10153
10154			const url = is_oms ? '/shop/oms/OMS_add_order_cart.cm' : "/shop/add_order_cart.cm";
10155			const headers = new Headers();
10156			headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
10157			const _landingUrlValue = getBsLandingUrlHeaderValue();
10158			if (_landingUrlValue) {
10159				headers.append('imweb-landing-url', _landingUrlValue);
10160			}
10161			const requestPayload = {
10162				'type': type,
10163				'cart_code': currentCartCode,
10164				'item_code_list': item_code_list,
10165				'backurl': backurl,
10166				'direct': (direct ? 'Y' : 'N'),
10167				'is_gift_buy': is_gift_buy,
10168				'deliv_country': $deliv_country || '',
10169				freebie_list: (type !== 'npay' && type !== 'talkpay') ? freebiesUserSelected : undefined,
10170				ace_pid: is_oms ?  (window._AcePID || window._AceMID) : undefined,
10171				'infoUrl': is_oms ? location.origin : undefined,
10172			};
10173			if (is_oms && Object.keys(shippingTemplateCodeMap).length > 0) {
10174				requestPayload.shipping_template_code = JSON.stringify(shippingTemplateCodeMap);
10175			}
10176			const body = serializeToUrlencoded(requestPayload);
10177
10178			try {
10179				const response = await fetch(url, { method: 'POST', headers, body });
10180				if (!response.ok) {
10181					throw new Error(`HTTP error! status: ${response.status}`);
10182				}
10183				const result = await response.json();
10184				add_order_progress_check = false;
10185
10186				if (result.msg == 'SUCCESS') {
10187					switch (type) {
10188						case 'npay': {
10189							if (result.npay_url == '') {
10190								if (result.errmsg) {
10191									alert(escape_javascript(result.errmsg));
10192								} else {
10193									alert(LOCALIZE.설명_네이버페이상품구매실패(escape_javascript(result.errmsg)));
10194								}
10195							} else {
10196								if (!!result.shopping_additional_price_msg) {
10197									alert(result.shopping_additional_price_msg);
10198								}
10199								if (result.advanced_kakao_trace_data != null) {
10200									$('body').append(result.advanced_kakao_trace_data);
10201								}
10202								if (result.auth_type == 2) {
10203										// 성인 인증이 필요할 경우
10204										window.location.href = '/?mode=adult_auth_npay&data=' + result.data;
10205								} else {
10206									// 성인 인증이 필요하지 않는 경우
10207									// 네이버 페이 구매시 전환추적 추가
10208									// CB-3985 — 머천트 PG/마케팅 픽셀 발화 (fireOrderTracking 단일 소스). 네이버페이.
10209									fireOrderTracking('npay', result);
10210									window.location.href = result.npay_url;
10211								}
10212							}
10213							break;
10214						}
10215						case 'talkpay': {
10216							switch (params.type) {
10217								case 'onOrder':
10218									if (result.msg == 'SUCCESS') {
10219										if (result.order_sheet_id) {
10220											params.onSuccess(result.order_sheet_id);
10221										} else {
10222											params.onFailure({ message: result.msg });
10223										}
10224									} else {
10225										params.onFailure({ message: result.msg });
10226									}
10227									break;
10228								case 'onPayOrder':
10229									if (result.msg == 'SUCCESS') {
10230											// CB-3985 — 머천트 PG/마케팅 픽셀 발화 (fireOrderTracking 단일 소스). 톡체크아웃 onPayOrder.
10231											fireOrderTracking('talkpay', result);
10232										var shop_payment_url = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code) + '&order_no=' + encodeURIComponent(result.order_no);
10233										if (result.member_code) {
10234											shop_payment_url += "&order_member=" + encodeURIComponent(result.member_code);
10235										}
10236										if ( result.kakaopay_only === 'Y' ) {
10237											shop_payment_url += "&kakaopay_only=Y";
10238										}
10239										window.location.href = shop_payment_url;
10240									}
10241									break;
10242							};
10243							break;
10244						}
10245						default: {
10246							// CB-3985 — 머천트 PG/마케팅 픽셀 발화 (fireOrderTracking 단일 소스). 일반/정기 주문.
10247							fireOrderTracking(type, result);
10248							if (is_guest_login) {	//비회원주문시 로그인 페이지로 이동처리
10249								window.location.href = '/login?shopping_order_code=' + result.order_code + '&back_url=' + encodeURIComponent(result.back_url_base64);
10250							}
10251							else {	//일반주문인경우 결제화면으로 이동
10252								if (is_oms) {
10253									if (typeof result.c3_result === "undefined" || typeof result.c3_result.orderCode === "undefined" || typeof result.c3_result.orderNo === "undefined") return
10254									var shop_payment_url = "/shop_payment/?order_code=" + encodeURIComponent(result.c3_result.orderCode) + "&order_no=" + encodeURIComponent(result.c3_result.orderNo);
10255
10256									if (result.c3_result.memberCode) {
10257										shop_payment_url += "&order_member=" + encodeURIComponent(result.c3_result.memberCode);
10258									}
10259									window.location.href = shop_payment_url;
10260								} else {
10261									window.location.href = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code);
10262								}
10263							}
10264						}
10265					}
10266				} else {
10267					// 주문하기 버튼에 로딩 스피너 제거
10268					unsetSpinner(orderBtn, orderBtnText);
10269					// 정기구독 버튼에 로딩 스피너 제거
10270					unsetSpinner(orderRegularlyBtn, orderRegularlyBtnText);
10271					// 선물하기 버튼에 로딩 스피너 제거
10272					unsetSpinner(orderGiftBtn, orderGiftBtnText);
10273
10274					if (type == 'talkpay') {
10275						switch (params.type) {
10276							case 'onOrder': {
10277								params.onFailure({ message: result.msg });
10278								break;
10279							}
10280							case 'onPayOrder': {
10281								alert(result.msg);
10282								break;
10283							}
10284						}
10285					} else {
10286						if(result.code === 2){
10287							$('._deliv_country_selector').toggleClass('warning_select');
10288						}
10289						// 코드 7: 비회원 이용권 구매 시 로그인 페이지로
10290						if (result.code === 7) {
10291							redirectToLoginPage();
10292						} else {
10293							alert(result.msg.replace(/\\n/g, '\n'));
10294							// 코드 11: 기존 로직 유지
10295							if (is_guest_login && result.code === 11) {
10296								var back_url_base64 = window.location.pathname;
10297								var url_param = document.location.href.split("?");
10298								if (url_param.length > 1) {
10299									back_url_base64 += "?" + url_param[1];
10300								}
10301								window.location.href = '/login?back_url=' + encodeURIComponent(btoa(back_url_base64));
10302							}
10303						}
10304					}
10305				}
10306
10307			} catch (e) {
10308				add_order_progress_check = false;
10309				// 주문하기 버튼에 로딩 스피너 제거
10310				unsetSpinner(orderBtn, orderBtnText);
10311				// 정기구독 버튼에 로딩 스피너 제거
10312				unsetSpinner(orderRegularlyBtn, orderRegularlyBtnText);
10313				// 선물하기 버튼에 로딩 스피너 제거
10314				unsetSpinner(orderGiftBtn, orderGiftBtnText);
10315			}
10316    };
10317
10318    var addOrderRegularly = function (backurl, params, is_c3 = false){
10319        addOrderWithCart('regularly', '', backurl, false, is_c3, params);
10320    };
10321
10322		const trackClickPurchaseCart = function (paymentButtonType) {
10323			if (typeof BrandScope !== "undefined") {
10324				try {
10325					BrandScope.track('click_purchase_cart', {
10326						'action': 'click',
10327						'content': 'purchase',
10328						'where': 'cart',
10329						'is_single_purchase': false,
10330						'payment_button_type': paymentButtonType,
10331						'is_regularly_prod': false
10332					});
10333				} catch {
10334					// ignore
10335				}
10336			}
10337		}
10338
10339    /**
10340     * 카트 전체 네이버찜등록
10341     */
10342    var addNPayWishWithCart = function (item_code) {
10343        if (currentCartCode == '') {
10344            alert(LOCALIZE.설명_장바구니가비어있습니다());
10345            return false;
10346        }
10347        $.ajax({
10348            type: 'POST',
10349            data: { 'cart_code': currentCartCode, 'item_code': item_code },
10350            url: ('/shop/add_npay_wish_cart.cm'),
10351            dataType: 'json',
10352            cache: false,
10353            success: function (result) {
10354                if (result.msg == 'SUCCESS') {
10355                    if (result.npay_url == '')
10356                        alert(LOCALIZE.설명_네이버페이찜등록실패(escape_javascript(result.errmsg)));
10357                    else {
10358                        if (result.mobile == 'Y')
10359                            window.location.href = result.npay_url;
10360                        else
10361                            window.open(result.npay_url);
10362                    }
10363                } else {
10364                    alert(result.msg);
10365                }
10366            }
10367        });
10368    };
10369    /**
10370     * click_change_options_cart 트래킹 (모달 진입 버튼 클릭 즉시)
10371     */
10372    const _trackChangeOptionsCart = (item_code) => {
10373        if (typeof window.BrandScope === 'undefined') return;
10374        const $row = $cart_list_wrap.find(`tr.content[data-item-code="${item_code}"]`);
10375        if (!$row.length) return;
10376        const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
10377        window.BrandScope.track('click_change_options_cart', {
10378            action:         'click',
10379            content:        'change_options',
10380            where:          'cart',
10381            prod_code:      $row.attr('data-prod-code') || '',
10382            prod_type:      $row.attr('data-prod-type') || 'normal',
10383            cart_code:      cartCode,
10384            cart_item_code: item_code,
10385            has_options:    $row.find('._cart_option_card').length > 0,
10386        });
10387    };
10388
10389    /**
10390     * click_complete_change_options 트래킹 (서버 응답 200 + cart 재렌더링 직후)
10391     * cartListMake 가 비동기라 발사 시점엔 cart row 가 변경 전 옵션 카드 보존 →
10392     * dt.options(변경 후) 와 row(변경 전) 직접 비교로 added/removed/qty_changed 산출
10393     * 옵션 라인 키 = 라인 내 value_name 들 sorted-join (DOM·API 응답 양쪽 동일 형식)
10394     */
10395    const _trackCompleteChangeOptions = (dt) => {
10396        if (typeof window.BrandScope === 'undefined') return;
10397        const $row = $cart_list_wrap.find(`tr.content[data-item-code="${dt.item_code}"]`);
10398        if (!$row.length) return;
10399
10400        const oldOptions = $row.find('._cart_option_card').toArray().map(card => ({
10401            key:   $(card).find('.opt-value').toArray().map(el => el.textContent.trim()).sort().join('|'),
10402            count: parseInt($(card).find('._counter_value').val(), 10) || 0,
10403        }));
10404        const newOptions = (dt.options || []).map(sel => ({
10405            key:   (sel.options || []).map(o => o.value_name).sort().join('|'),
10406            count: parseInt(sel.count, 10) || 0,
10407        }));
10408
10409        const oldByKey = new Map(oldOptions.map(o => [o.key, o.count]));
10410        const newByKey = new Map(newOptions.map(o => [o.key, o.count]));
10411        let added = 0, removed = 0, qtyChanged = 0;
10412        new Set([...oldByKey.keys(), ...newByKey.keys()]).forEac
10412h(k => {
10413            if (!oldByKey.has(k)) added++;
10414            else if (!newByKey.has(k)) removed++;
10415            else if (oldByKey.get(k) !== newByKey.get(k)) qtyChanged++;
10416        });
10417
10418        const sum = (arr) => arr.reduce((s, o) => s + o.count, 0);
10419        const oldQty = oldOptions.length ? sum(oldOptions)
10420            : parseInt($row.find('._cart_counter[data-mode="product_count"] ._counter_value').val(), 10) || 0;
10421        const newQty = newOptions.length ? sum(newOptions)
10422            : parseInt(dt.order_count, 10) || 0;
10423
10424        const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
10425        window.BrandScope.track('click_complete_change_options', {
10426            action:                        'click',
10427            content:                       'complete',
10428            where:                         'change_options',
10429            prod_code:                     $row.attr('data-prod-code') || '',
10430            prod_type:                     $row.attr('data-prod-type') || 'normal',
10431            cart_code:                     cartCode,
10432            cart_item_code:                dt.item_code,
10433            option_added_count:            added,
10434            option_removed_count:          removed,
10435            option_quantity_changed_count: qtyChanged,
10436            quantity_unit:                 'product',
10437            quantity_before:               oldQty,
10438            quantity_after:                newQty,
10439        });
10440    };
10441
10442    /**
10443     * 장바구니 옵션변경 화면 표시
10444     */
10445    var showChangeCartItem = function (item_code, is_regularly) {
10446        _trackChangeOptionsCart(item_code);
10447        $.ajax({
10448            type: 'POST',
10449            data: { 'item_code': item_code, 'is_regularly': is_regularly },
10450            url: ('/shop/change_cart_item_request.cm'),
10451            dataType: 'json',
10452            cache: false,
10453            success: function (result) {
10454                if (result.msg == 'SUCCESS') {
10455                    $changeCartItemLayer.find('div.container-fluid').html(result.html);
10456                    $changeCartItemLayer.show();
10457
10458                    SITE_SHOP_DETAIL.initDetail({
10459                        prod_idx: result.prodIdx,
10460                        prod_price: result.price,
10461                        require_option_count: result.require_option_count,
10462						use_image_optimizer_on_product_detail_markup: false,
10463                        shop_use_full_load: false,
10464                    });
10465
10466                    SITE_SHOP_DETAIL.initProdStock(result.prod_stock.stock_use, result.prod_stock.stock_no_option, result.prod_stock.stock_unlimit);
10467
10468                    // 수량 제한 설정 - AJAX 성공 직후 바로 호출 (옵션 복원 전)
10469                    if (result.max_prod_quantity !== undefined) {
10470                        SITE_SHOP_DETAIL.setMaxPurchaseQuantity(
10471                            result.max_prod_quantity,
10472                            result.max_member_quantity,
10473                            result.maximum_purchase_quantity_type,
10474                            result.optional_limit,
10475                            result.optional_limit_type,
10476                            result.prod_name
10477                        );
10478                    }
10479
10480                    SITE_SHOP_CART.changeCartLoadOption(result.prodIdx);
10481                    var selected_option_count = result.selected_option_list.length;
10482                    var cnt = 0;
10483                    currentChangeCartItemCode = item_code;
10484                    currentCartItemMaxQuantity = result.maximum_purchase_quantity || 0;
10485                    currentCartItemMaxQuantityType = result.maximum_purchase_quantity_type || 'order';
10486                    $.each(result.selected_option_list, function (no, data) {
10487                        SITE_SHOP_DETAIL.selectOption(
10488                            result.prodIdx,
10489                            {
10490                                options: data.options,
10491                                require: data.require,
10492                                count: data.count,
10493                                idx: data.idx,
10494                                skip_quantity_validation: true
10495                            },
10496                            function () {
10497                                cnt++;
10498                                if (cnt >= selected_option_count){
10499                                    SITE_SHOP_DETAIL.updateSelectedOptions('cart');
10500                                }
10501                            },
10502                            function () {
10503                                cnt++;
10504                                if (cnt >= selected_option_count) {
10505                                    SITE_SHOP_DETAIL.updateSelectedOptions('cart');
10506                                }
10507                            }
10508                        );
10509                    });
10510                    if (result.require_option_count == 0) {
10511                        SITE_SHOP_CART.changeCartOrderCount("pc", result.count, false);
10512                        SITE_SHOP_CART.changeCartOrderCount("mobile", result.count, false);
10513                    }
10514                } else {
10515                    alert(result.msg);
10516                }
10517            }
10518        });
10519    };
10520    var hideChangeCartItem = function () {
10521        $changeCartItemLayer.find('div.container-fluid').empty();
10522        $changeCartItemLayer.hide();
10523        currentChangeCartItemCode = '';
10524    };
10525    /**
10526     * 장바구니 옵션 변경 완료 처리
10527     */
10528    var changeCartItemComplete = function (current_ul, is_c3 = false) {
10529        var is_regularly = 'N';
10530        if (cart_type == SHOP_CONST.CART_TYPE_REGULARLY) {
10531            is_regularly = 'Y';
10532        }
10533        if (currentChangeCartItemCode != '') {
10534            // 프론트엔드 수량 검증 (옵션별 최대 구매수량)
10535            var validation_result = SITE_SHOP_DETAIL.validateMaxPurchaseQuantity();
10536            if (validation_result !== true) {
10537                alert(validation_result);
10538                return;
10539            }
10540            changeCartItemData({
10541                "mode": "detail",
10542                "item_code": currentChangeCartItemCode,
10543                "options": SITE_SHOP_DETAIL.getSelectedOption(),
10544                "order_count": SITE_SHOP_DETAIL.getOrderCount(),
10545                "is_regularly": is_regularly,
10546                "current_ul": current_ul
10547            }, is_c3);
10548        }
10549    };
10550
10551    var changeCartItemDelivType = function (item_code, deliv_type, is_regularly, is_c3 = false) {
10552        changeCartItemData({
10553            "mode": "delivery",
10554            "item_code": item_code,
10555            "deliv_type": deliv_type,
10556            "is_regularly": is_regularly
10557        }, is_c3);
10558    };
10559
10560    var changeCartItemDelivPayType = function (item_code, deliv_pay_type, is_regularly, is_c3 = false) {
10561        changeCartItemData({
10562            "mode": "delivery",
10563            "item_code": item_code,
10564            "deliv_pay_type": deliv_pay_type,
10565            "is_regularly": is_regularly
10566        }, is_c3);
10567    };
10568
10569    var changeCartItemData = function (dt, is_c3 = false) {
10570        if (typeof dt['item_code'] == 'undefined') return false;
10571        $.ajax({
10572            type: 'POST',
10573            data: dt,
10574			url : is_c3 ? '/shop/oms/OMS_change_cart_item.cm' : '/shop/change_cart_item.cm',
10575            dataType: 'json',
10576            cache: false,
10577            success: function (result) {
10578                if (result.msg == 'SUCCESS') {
10579                    if (typeof AW_INOUT != 'undefined') {
10580                        AW_INOUT(result.prod_no, result.after_count);
10581                    }
10582                    if (typeof AM_INOUT !== 'undefined') {
10583                        AM_INOUT(result.prod_no, result.after_count);
10584                    }
10585                    if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
10586                    var cart_type = SHOP_CONST.CART_TYPE_NORMAL;
10587                    if (dt['is_regularly'] == 'Y') {
10588                        cart_type = SHOP_CONST.CART_TYPE_REGULARLY;
10589                    }
10590                    $changeCartItemLayer.hide();
10591                    cartListMake(dt['current_url'], cart_type, is_c3);
10592                    if (dt['mode'] === 'detail') _trackCompleteChangeOptions(dt);
10593                } else {
10594                    alert(result.msg);
10595                }
10596            }
10597        });
10598    };
10599
10600    var countryCodeChange = function (country) {
10601			$deliv_country = country;
10602        $.ajax({
10603            type: 'POST',
10604            data: { 'country': country },
10605            url: ('/shop/country_code_change.cm'),
10606            dataType: 'json',
10607            cache: false,
10608            success: function (result) {
10609                if (result.msg != "SUCCESS") {
10610                    alert(result.msg);
10611                } else {
10612                    window.location.reload();
10613                }
10614            }
10615        });
10616    };
10617
10618    // ───── CB-3865: 사은품 MFE 에 cartChange payload(cart raw) push ───────────────
10619    // freebie MFE 가 자체 OMS endpoint 호출 없이 사은품 결정하도록 cart raw 를 push.
10620    // OMS_get_cart_list_make 응답의 cart 를 closure 캐시 → mutation 시 count 부분 patch.
10621    // raw → orderItems 변환은 freebie 측 책임 (single source of truth — apply-promotion 입력).
10622    // BE 변경 0. dedupe: 동일 raw 면 dispatch skip (React Query 의 새 queryKey 회피).
10623    var _cartRawItems = [];
10624    var _lastDispatchedCartSig = '';
10625
10626    var dispatchFreebieCartChange = function () {
10627        var sig = JSON.stringify(_cartRawItems);
10628        if (sig === _lastDispatchedCartSig) return;
10629        _lastDispatchedCartSig = sig;
10630        window.document.dispatchEvent(new CustomEvent(
10631            '@im/fo-prod-detail-freebie:cartChange',
10632            { detail: { cart: _cartRawItems } }
10633        ));
10634    };
10635
10636    /**
10637     * 장바구니 목록 생성 -  C3 바로구매 분기 추가
10638     * @param current_full_url
10639     * @param type		normal 일반구매, regularly 정기구독
10640     */
10641    var cartListMake = function (current_full_url, type, is_c3 = false) {
10642        selectedCartItem = [];
10643        // MFE 모드인 경우 PHP AJAX 렌더를 스킵 (MFE 셸이 자체적으로 데이터 페치)
10644        // cart 전용 magnet-shell([data-mfe="fo-shopping/cart"]) 직속 자식 존재 여부로 식별 — 같은 페이지의 사은품/프로모션 shell 과 구분
10645        if ($cart_list_wrap && $cart_list_wrap.length && $cart_list_wrap.children('magnet-shell[data-mfe="fo-shopping/cart"]').length) {
10646            cart_type = type;
10647            is_cart_changed = false;
10648            // CB-3816: 호스트 측 cart 변경 (옵션 다이얼로그 확정 등) 후 MFE 에 재조회 요청.
10649            // MFE 가 자체 mutation 으로 트리거한 변경은 이 경로를 타지 않으므로 중복 발행 없음.
10650            // detail.cart_type 으로 변경된 탭을 명시 — MFE 의 현재 활성 탭과 다를 수 있어
10651            // (탭 전환 race) 정확한 query key 를 invalidate 하기 위해 발행자가 알려야 함.
10652            window.dispatchEvent(new CustomEvent('imweb:cart:invalidated', { detail: { cart_type: type } }));
10653            return;
10654        }
10655        $.ajax({
10656            type: 'POST',
10657            data: { 'current_full_url': current_full_url, 'type': type },
10658			url : is_c3 ? '/shop/oms/OMS_get_cart_list_make.cm' : '/shop/get_cart_list_make.cm',
10659            dataType: 'json',
10660            success: function (res) {
10661                if (res.msg == 'SUCCESS') {
10662                    // CB-3865: 페이지 로드 / 항목 추가·삭제·옵션변경 후 cart raw 캐시 갱신.
10663                    // 이후 getSelectedPrice → dispatchFreebieCartChange 로 사은품 MFE push.
10664                    _cartRawItems = res.cart || [];
10665                    $cart_list_wrap.html('');
10666										const range = document.createRange();
10667										// <script type="module"> 태그가 .html() 으로 삽입되면 모듈이 로드되지 않으므로, createContextualFragment를 사용하여 HTML을 삽입
10668                    const table_fragment = range.createContextualFragment(res.table_html);
10669                    $cart_list_wrap[0].appendChild(table_fragment);
10670                    if (res.global_select_html !== '') {
10671                        $global_select.html(res.global_select_html);
10672												$deliv_country = $global_select.find('[selected]').val();
10673                    } else {
10674                        $global_select.remove();
10675                    }
10676                    if (res.cart_item_count > 0) {
10677                        $('.pay-box').show();
10678                        $shop_cart_list.removeClass('cart_empty_wrap');
10679                        fixedShopTableHeader();
10680                        resizeShopTableHeader();
10681                    } else {
10682                        $('.pay-box').hide();
10683                        $shop_cart_list.addClass('cart_empty_wrap');
10684                    }
10685                    cart_type = type;
10686                    if (type === SHOP_CONST.CART_TYPE_REGULARLY) {
10687                        currentCartCode = res.cart_code;
10688                        $shop_cart_list.find('._btn_order').hide();
10689                        $shop_cart_list.find('._btn_order_gift').hide();
10690                        $shop_cart_list.find('._btn_order_regularly').css('display', 'flex');
10691                        $shop_cart_list.find('._social_pay').hide();
10692                        $('#cart_normal_tab').removeClass('active');
10693                        $('#cart_regularly_tab').addClass('active');
10694                    } else {
10695                        currentCartCode = res.cart_code;
10696                        $shop_cart_list.find('._btn_order_regularly').hide();
10697                        $shop_cart_list.find('._btn_order').css('display', 'flex');
10698                        $shop_cart_list.find('._btn_order_gift').css('display', 'flex');
10699                        $shop_cart_list.find('._social_pay').show();
10700                        $('#cart_regularly_tab').removeClass('active');
10701                        $('#cart_normal_tab').addClass('active');
10702                    }
10703                    // 미리보기 모드든 일반 모드든 동일하게 getSelectedPrice 호출 (
10703서버에서 current_full_url로 미리보기 분기).
10704                    getSelectedPrice(type === SHOP_CONST.CART_TYPE_REGULARLY);
10705
10706                    // 매출 상승 도구 미리보기 모드 - 주문/체크박스/수량/삭제 등 후속 동작 차단 + 안내 배너
10707                    if (res.is_preview_mode) {
10708                        applyCartPreviewLock();
10709                    }
10710                } else
10711                    alert(res.msg);
10712
10713                is_cart_changed = false;
10714            },
10715            error: function () {
10716                alert(getLocalizeString('설명_잠시후다시시도해주세요', '', '잠시 후 다시 시도해주세요.'));
10717            }
10718        });
10719    };
10720
10721    /**
10722     * 매출 상승 도구 미리보기 모드 - 카트의 모든 인터랙션(주문/체크/수량/삭제)을 잠그고 안내 배너 표시.
10723     * cartListMake가 응답에 is_preview_mode=true를 받았을 때 호출된다.
10724     * 주문/결제 버튼은 시각적으로는 일반 상태 그대로 노출하고 클릭 동작만 차단한다.
10725     * totals/할인/배송비 등은 서버 측 get_cart_price.cm이 동일한 미리보기 분기로 정상 응답하므로 별도 주입 불필요.
10726     */
10727    var applyCartPreviewLock = function () {
10728        if (!$cart_list_wrap || $cart_list_wrap.length === 0) return;
10729
10730        // 카트 영역 인터랙션 차단
10731        $cart_list_wrap.find('._cartItemCheckbox, ._all_check').prop('disabled', true);
10732        $cart_list_wrap.find('._cart_counter button, ._cart_counter input').prop('disabled', true);
10733        $cart_list_wrap.css('pointer-events', 'none');
10734
10735        // 결제/주문 버튼: 노출은 그대로 두고 클릭 동작만 차단 (capture 단계에서 가로채 inline onclick까지 무력화)
10736        $shop_cart_list.find('._btn_order, ._btn_order_regularly, ._btn_order_gift, ._social_pay').each(function () {
10737            var el = this;
10738            el.removeEventListener('click', _previewLockClickHandler, true);
10739            el.addEventListener('click', _previewLockClickHandler, true);
10740        });
10741    };
10742
10743    var _previewLockClickHandler = function (e) {
10744        e.preventDefault();
10745        e.stopImmediatePropagation();
10746        return false;
10747    };
10748
10749    var fixedShopTableHeader = function () {
10750        var window_width = window.innerWidth;
10751        if (window_width < 768) {
10752            var section_fixed_height = getMobileSectionFixedHeight();
10753            var $cart_list_thead = $cart_list_wrap.find('._shop_table thead');
10754            $cart_list_thead.css('top', section_fixed_height + 'px');
10755        }
10756    };
10757
10758    var getMobileSectionFixedHeight = function () {
10759        /* 기본 top을 0px로 설정 시 1px 가량의 여백이 생겨 뒤가 비쳐보이는 경우가 있어 -1px로 설정 */
10760        var section_fixed_height = -1;
10761        var $fixed_header_disable = $('#inline_header_mobile').find('._fixed_header_section');
10762        for (var i = 0; i < $fixed_header_disable.length; i++) {
10763            var target = $fixed_header_disable[i].getBoundingClientRect();
10764            section_fixed_height += target.height;
10765        }
10766        var prev_fixed_section = $shop_cart_list.parents('div[doz_type="section"]').prevAll('._fixed_section[doz_mobile_hide="N"]')[0];
10767        if (typeof prev_fixed_section !== 'undefined') {
10768            section_fixed_height += prev_fixed_section.getBoundingClientRect().height;
10769        }
10770        return section_fixed_height;
10771    };
10772
10773    var resizeShopTableHeader = function () {
10774        var is_resizing = false;
10775        $(window).resize(function () {
10776            if (!is_resizing) {
10777                is_resizing = true;
10778                setTimeout(function () {
10779                    fixedShopTableHeader();
10780                    is_resizing = false;
10781                }, 100);
10782            }
10783        });
10784    };
10785
10786    var setTimesale = function () {
10787        var $doz_timesale_wrap = $shop_cart_wish_list.find('._doz_timesale_wrap');
10788        if ($doz_timesale_wrap.length > 0) {
10789            $doz_timesale_wrap.each(function () {
10790                var $that = $(this);
10791                var start_time = ($that.find('._doz_timesale').attr('data-start-time') * 1000);
10792                var $doz_timesale = $that.find('._doz_timesale');
10793                var timesale_interval = setInterval(function () {
10794                    var remain_ms = ($doz_timesale.attr('data-end-time') * 1000) - start_time;
10795                    if (remain_ms > 0) {
10796                        var remain_d = Math.floor(remain_ms / 86400000);
10797                        var remain_h = Math.floor((remain_ms % 86400000) / 3600000);
10798                        var remain_m = Math.floor((remain_ms % 3600000) / 60000);
10799                        var remain_s = Math.floor((remain_ms % 60000) / 1000);
10800
10801                        var remain_hh = remain_h < 10 ? '0' + remain_h : '' + remain_h;
10802                        var remain_mm = remain_m < 10 ? '0' + remain_m : '' + remain_m;
10803                        var remain_ss = remain_s < 10 ? '0' + remain_s : '' + remain_s;
10804                        if (remain_d >= 1) {
10805                            $doz_timesale.text(getLocalizeString('설명_종료까지n1일n2시n3분n4초남음', [remain_d, remain_hh, remain_mm, remain_ss], '종료까지 %1일 %2:%3:%4'));
10806                        } else {
10807                            $doz_timesale.text(getLocalizeString('설명_종류까지n1시n2분n3초남음', [remain_hh, remain_mm, remain_ss], '종료까지 %1:%2:%3 남음'));
10808                        }
10809                        start_time = start_time + 1000;
10810                    } else {
10811                        /* 타임세일 종료 */
10812                        clearInterval(timesale_interval);
10813                        $that.remove();
10814                    }
10815                }, 1000);
10816            });
10817        }
10818    };
10819
10820    // BO sales tool 미리보기 트리거 수신: URL에 sales_tool_preview 파라미터가 있을 때만 동작
10821    var salesToolPreviewParams = (function () {
10822        try {
10823            return new URLSearchParams(window.location.search);
10824        } catch (e) {
10825            return null;
10826        }
10827    })();
10828    var isInSalesToolPreview = salesToolPreviewParams ? salesToolPreviewParams.has('sales_tool_preview') : false;
10829
10830    function isAllowedSalesToolPreviewOrigin(origin) {
10831        if (origin === window.location.origin) return true;
10832        if (/(^|\.)imtest\.me$/.test(window.location.hostname) && origin === 'https://localhost:5173') return true;
10833        return /^https:\/\/[a-z0-9-]+\.crm\.(imweb|imtest)\.me$/.test(origin);
10834    }
10835
10836    /**
10837     * 미리보기 전용: 주문 알림 모달(get_order_alarm_modal.cm)을 유효성 검사 없이 즉시 표시.
10838     * 운영 흐름의 addOrderWithCart는 다수 검증을 거친 뒤에야 모달을 띄우는데, 미리보기에서는 검증을 모두 건너뛰고 모달만 노출한다.
10839     */
10840    var showOrderAlarmModalForPreview = function () {
10841        var $orderAlarmMarker = $('#shop_cart_order_alarm_with_free_ship_notify');
10842        if ($orderAlarmMarker.length === 0) return;
10843        var initDelivPriceFlexableKey = parseFloat($orderAlarmMarker.attr('data-init-deliv-price-flexable-key') || '0');
10844        if (initDelivPriceFlexableKey <= 0) return;
10845
10846        $.ajax({
10847            type: 'POST',
10848            url: '/shop/free_ship_notify/get_order_alarm_modal.cm',
10849            data: { init_deliv_price_flexable_key: initDelivPriceFlexableKey },
10850            dataType: 'html',
10851            cache: false,
10852            success: function (html) {
10853                var is_mobile_width = (window.innerWidth < 768);
10854                if (is_mobile_width) {
10855                    imSheet.close('shop_cart_order_alarm_sheet', function () {
10856                        imSheet.open({
10857                            id: 'shop_cart_order_alarm_sheet',
10858                            html: html,
10859                            backdrop: 'rgba(0, 0, 0, 0.15)',
10860                            zIndex: 17001
10861                        });
10862                    });
10863                } else {
10864                    if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
10865                        $.cocoaDialog.close();
10866                    }
10867                    $.cocoaDialog.open({
10868                        type: 'site_shop_free_ship_notify',
10869                        custom_popup: html,
10870                        pc_width: 440
10871                    });
10872                }
10873            }
10874        });
10875    };
10876
10877    /**
10878     * 미리보기 전용: 표시 중인 주문 알림 모달을 닫는다 (cocoaDialog/imSheet 양쪽 idempotent).
10879     */
10880    var hideOrderAlarmModalForPreview = function () {
10881        if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
10882            $.cocoaDialog.close();
10883        }
10884        if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
10885            imSheet.close('shop_cart_order_alarm_sheet');
10886        }
10887    };
10888
10889    /**
10890     * 미리보기 전용: 장바구니 페이지의 magnet-shell(cart.sub의 #foFreeShipNotifyCartPageMagnet) 표시 토글.
10891     * 구매하기 버튼 클릭 후 팝업 스타일에서는 in-page magnet이 중복으로 보이지 않도록 감추고,
10892     * 페이지 최상단 스타일에선 다시 보이도록 한다. wrapper div 단위로 padding까지 함께 사라지도록 부모를 토글.
10893     */
10894    var setCartMagnetVisibilityForPreview = function (visible) {
10895        var $magnet = $('#foFreeShipNotifyCartPageMagnet');
10896        if ($magnet.length === 0) return;
10897        var $wrapper = $magnet.parent();
10898        if (visible) {
10899            $wrapper.show();
10900        } else {
10901            $wrapper.hide();
10902        }
10903    };
10904
10905    if (isInSalesToolPreview) {
10906        window.addEventListener('message', function (event) {
10907            if (!isAllowedSalesToolPreviewOrigin(event.origin)) return;
10908            var data = event.data;
10909            if (!data || typeof data !== 'object') return;
10910
10911            if (data.type === 'sales-tool-preview:show-order-alarm') {
10912                // 팝업 스타일이므로 in-page magnet은 감추고 모달만 노출
10913                setCartMagnetVisibilityForPreview(false);
10914                showOrderAlarmModalForPreview();
10915                return;
10916            }
10917            if (data.type === 'sales-tool-preview:hide-order-alarm') {
10918                // 페이지 최상단 스타일로 복귀 — magnet 다시 보이도록
10919                hideOrderAlarmModalForPreview();
10920                setCartMagnetVisibilityForPreview(true);
10921                return;
10922            }
10923            if (data.type === 'sales-tool-preview:free-ship-notify-settings'
10924                || data.type === 'sales-tool-preview:settings') {
10925                var settingsPayload = data.payload || {};
10926                window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:settings:update', {
10927                    detail: settingsPayload
10928                }));
10929            }
10930        });
10931    }
10932
10933    // '장바구니에 담았습니다' 확인 모달을 document.body 에 1회 생성해 노출/재사용
10934    // (정적 마크업은 상세 템플릿 전용이라 다른 페이지엔 없음). 계속쇼핑=닫기 / 장바구니가기=/shop_cart.
10935    var showAddCartAnywhereAlarm = function () {
10936        var MODAL_ID = 'shop_cart_add_to_cart_anywhere_alarm';
10937
10938        var existing = document.getElementById(MODAL_ID);
10939        if (existing) {
10940            existing.style.display = 'block';
10941            return;
10942        }
10943
10944        // DOM API 로 구성 (textContent 만 사용, XSS 무관). 스타일은 전역 custom.sub 의 modal_site_alert 재사용.
10945        var el = function (tag, className) {
10946            var node = document.createElement(tag);
10947            if (className) node.className = className;
10948            return node;
10949        };
10950
10951        var message = el('p', 'text-center');
10952        message.style.marginBottom = '0';
10953        message.textContent = getLocalizeString('설명_선택하신상품을장바구니에담았습니다', '', '선택하신 상품을 장바구니에 담았습니다.');
10954        var container = el('div', 'container-fluid');
10955        container.appendChild(message);
10956
10957        var continueBtn = el('a', 'btn');
10958        continueBtn.setAttribute('href', 'javascript:');
10959        continueBtn.textContent = getLocalizeString('버튼_계속쇼핑', '', '계속 쇼핑');
10960        continueBtn.addEventListener('click', function () {
10961            try {
10962                SITE_SHOP_DETAIL.trackAddToCartPopupAction('click_continue_shopping_add_to_cart_popup', 'continue_shopping', { isFreeShippingUpsell: false });
10963            } catch (e) {}
10964            var m = document.getElementById(MODAL_ID);
10965            if (m) m.style.display = 'none';
10966        });
10967
10968        var goCartBtn = el('a', 'btn right');
10969        goCartBtn.setAttribute('href', 'javascript:');
10970        goCartBtn.textContent = getLocalizeString('버튼_장바구니가기', '', '장바구니 가기');
10971        goCartBtn.addEventListener('click', function () {
10972            try {
10973                SITE_SHOP_DETAIL.trackAddToCartPopupAction('click_go_cart_add_to_cart_popup', 'go_cart', { isFreeShippingUpsell: false });
10974            } catch (e) {}
10975            window.location.href = '/shop_cart';
10976        });
10977
10978        var buttons = el('div', 'btn-group-justified');
10979        buttons.appendChild(continueBtn);
10980        buttons.appendChild(goCartBtn);
10981
10982        var layer = el('div', 'layer_pop');
10983        layer.appendChild(container);
10984        layer.appendChild(buttons);
10985        var content = el('div', 'modal-content');
10986        content.appendChild(layer);
10987        var dialog = el('div', 'modal-dialog');
10988        dialog.appendChild(content);
10989
10990        var modal = el('div', 'modal in modal_site_alert');
10991        modal.id = MODAL_ID;
10992        modal.setAttribute('tabindex', '-1');
10993        modal.setAttribute('role', 'dialog');
10994        modal.appendChild(dialog);
10995
10996        document.body.appendChild(modal);
10997        modal.style.display = 'block';
10998    };
10999
11000    // 외부 위젯(이프두 SUGGEST 등)이 SITE_SHOP_CART.addCartAnywhere(idx) 로 호출하는 공개 진입점.
11001    // 코드베이스 내 호출부가 없어도 외부에서 사용하므로, 미사용으로 오인해 제거하지 말 것.
11002    var addCartAnywhere = function (idx) {
11003        var pid = parseInt(idx, 10);
11004        if (!pid || pid <= 0) return;
11005        if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK === 'undefined'
11006            || typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.selectAdditionalProductByIdx !== 'function') {
11007            return;
11008        }
11009
11010        // 담기 전 상품 존재 확인 → 없으면 안내 후 중단 (SDK 는 무수정)
11011        $.ajax({
11012            type: 'POST',
11013            url: '/shop/free_ship_notify/get_additional_product_info.cm',
11014            data: { 'product_idx': pid },
11015            dataType: 'json',
11016            cache: false,
11017            timeout: 10000,
11018            success: function (result) {
11019                if (!result || result.msg !== 'SUCCESS' || !result.product_info) {
11020                    alert(getLocalizeString('설명_상품이존재하지않습니다', '', '존재하지 않는 상품입니다.'));
11021                    return;
11022                }
11023
11024                // 확인 모달은 콜백이 아닌 담기 완료 이벤트 기준 + idx 일치 스코프
11025                // (옵션 상품은 콜백이 '옵션 모달 표시' 시점에 오고, 이벤트는 타 담기 흐름에서도 발화하므로).
11026                var ADDED_EVENT = 'imweb:freeShipNotify:additionalProductAdded';
11027                var cleanup;
11028                var onAdded = function (e) {
11029                    var addedIdx = (e && e.detail) ? parseInt(e.detail.prodIdx, 10) : 0;
11030                    if (addedIdx !== pid) return;
11031                    cleanup();
11032                    showAddCartAnywhereAlarm();
11033                };
11034                var timer = setTimeout(function () { cleanup(); }, 5 * 60 * 1000);
11035                cleanup = function () {
11036                    window.removeEventListener(ADDED_EVENT, onAdded);
11037                    clearTimeout(timer);
11038                };
11039                window.addEventListener(ADDED_EVENT, onAdded);
11040
11041                try {
11042                    window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.selectAdditionalProductByIdx(pid, 'cart', function (success, message) {
11043                        if (!success) {
11044                            cleanup();
11045                            if (message) alert(message);
11046                        }
11047                    });
11048                } catch (e) {
11049                    cleanup();
11050                }
11051            },
11052            error: function () {
11053                alert(getLocalizeString('설명_오류가발생했습니다', '', '오류가 발생했습니다.'));
11054            }
11055        });
11056    };
11057
11058    return {
11059        initCart: function (cart_code, backurl, is_regularly) {
11060            initCart(cart_code, backurl, is_regularly);
11061        },
11062        loadProdWishList: function () {
11063            loadProdWishList();
11064        },
11065        deleteProdWish: function (cart_item, is_regularly) {
11066            deleteProdWish([cart_item], is_regularly);
11067        },
11068        deleteProdWishByProdCode: function (prod_code) {
11069            deleteProdWishByProdCode(prod_code);
11070        },
11071        addProdWish: function (cart_item, is_regularly) {
11072            addProdWish([cart_item], is_regularly);
11073        },
11074        addProdWishMulti: function (is_regularly) {
11075            if (selectedCartItem.length == 0) {
11076                alert(LOCALIZE.설명_선택한항목이없습니다());
11077                return false;
11078            }
11079            addProdWish(selectedCartItem, is_regularly);
11080        },
11081        removeCartItemOption: function (item_code, optionNo, is_regularly) {
11082            removeCartItemOption(item_code, optionNo, is_regularly);
11083        },
11084        removeCartItem: function (item_code, is_regularly) {
11085            removeCartItem([item_code], is_regularly);
11086        },
11087        addCart: function (prodIdx, optionsOrCallback, orderCount, callback, requestOptions) {
11088            addCart(prodIdx, optionsOrCallback, orderCount, callback, requestOptions);
11089        },
11090        addCartAnywhere: function (idx) {
11091            addCartAnywhere(idx);
11092        },
11093        removeSelectedCartItem: function (is_regularly) {
11094            if (selectedCartItem.length == 0) {
11095                alert(LOCALIZE.설명_선택한상품이없습니다());
11096                return false;
11097            }
11098            removeCartItem(selectedCartItem, is_regularly);
11099        },
11100        removeSelectedCartSoldOutItem: function (is_regularly) {
11101            if (selectedCartItem.length == 0) {
11102                alert(LOCALIZE.설명_선택한상품이없습니다());
11103                return false;
11104            }
11105            removeSelectedCartSoldOutItem(selectedCartItem, is_regularly);
11106        },
11107        "toggleCheckCartItem": function (is_regularly) {
11108            toggleCheckCartItem(is_regularly);
11109        },
11110        "toggleAllCheckCartItem": function (b, is_regularly) {
11111            toggleAllCheckCartItem(b, is_regularly);
11112        },
11113        addOrderWithCart: function (type, item_code, backurl, params) {
11114            addOrderWithCart(type, item_code, backurl, false, false, params);
11115        },
11116        addOrderWithCartDirect: function (type, item_code, backurl) {
11117            addOrderWithCart(type, item_code, backurl, true, false);
11118        },
11119        /**
11120         * 무료배송 안내 모달의 "주문하기" 버튼에서 호출.
11121         * 보류된 addOrderWithCart 인자를 사용해 skipFreeShipNotifyAlarm=true로 재호출하여 원래 주문 흐름 진행.
11122         * 모달이 떠있는 동안 매그넷에서 추가상품을 담아 카트가 갱신된 경우, cartListMake가 selectedCartItem을
11123         * 빈 배열로 리셋한 상태이므로 현재 체크된 카트 아이템을 재수집해야 신규 아이템도 주문에 포함된다.
11124         */
11125        proceedOrderAfterFreeShipAlarm: function () {
11126            if (!_pendingOrderWithCart) return;
11127            var a = _pendingOrderWithCart;
11128            _pendingOrderWithCart = null;
11129            // 모달/시트 양쪽 모두 닫기 (어느 쪽이 떴든 idempotent)
11130            if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
11131                $.cocoaDialog.close();
11132            }
11133            if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
11134                imSheet.close('shop_cart_order_alarm_sheet');
11135            }
11136
11137            // 원래 item_code가 비어있을 때(전체 선택 주문)는 모달에서 추가된 아이템도 포함되도록 selectedCartItem 재구성.
11138            // 특정 item_code 직접 주문(addOrderWithCartDirect 등)은 원래 의도 보존을 위해 건드리지 않는다.
11139            if (!a.item_code && $cart_list_wrap && $cart_list_wrap.length > 0) {
11140                selectedCartItem = [];
11141                $cart_list_wrap.find('._cartItemCheckbox:checked').each(function () {
11142                    selectedCartItem.push($(this).val());
11143                });
11144            }
11145
11146            addOrderWithCart(a.type, a.item_code, a.backurl, a.direct, a.is_oms, a.params, a.is_gift_buy, true);
11147        },
11148        addOrderRegularly: function (backurl, params) {
11149            addOrderRegularly(backurl, params, false);
11150        },
11151        trackClickPurchaseCart: function (paymentButtonType) {
11152            trackClickPurchaseCart(paymentButtonType);
11153        },
11154        showChangeCartItem: function (item_code, is_regularly) {
11155            showChangeCartItem(item_code, is_regularly);
11156        },
11157        hideChangeCartItem: function () {
11158            hideChangeCartItem();
11159        },
11160        changeCartSelectRequireOption: function (prod_idx, option_code, value_code, value_name) {
11161            SITE_SHOP_DETAIL.selectRequireOption('cart', prod_idx, option_code, value_code, value_name, function () {
11162                SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11163            });
11164        },
11165        changeCartChangeRequireInputOption: function (prod_idx, option_code, msg) {
11166            SITE_SHOP_DETAIL.changeRequireInputOption('cart', prod_idx, option_code, msg, function () {
11167                SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11168            });
11169        },
11170        changeCartSelectOptionalOption: function (prod_idx, option_code, value_code, value_name) {
11171            SITE_SHOP_DETAIL.selectOptionalOption(prod_idx, option_code, value_code, value_name, function () {
11172                SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11173            });
11174        },
11175        changeCartItemIncrease: function (optNo, reload) {
11176            SITE_SHOP_DETAIL.increaseOptionCount(optNo, 'cart');
11177        },
11178        changeCartItemDecrease: function (optNo) {
11179            SITE_SHOP_DETAIL.decreaseOptionCount(optNo, 'cart');
11180        },
11181        changeCartIncrease: function (type) {
11182            var o = $changeCartItemLayer.find('input._order_count_' + type);
11183            var curCount = o.val();
11184            if (isNaN(curCount))
11185                curCount = 1;
11186            else
11187                curCount = parseInt(curCount) + 1;
11188
11189            var res = SITE_SHOP_DETAIL.checkProdStock(curCount);
11190            if (res === true) {
11191                // 최대 구매수량 검증 추가 (optional_option 타입 제외)
11192                var max_quantity_check = SITE_SHOP_DETAIL.checkMaxPurchaseQuantityForOrderCount(curCount);
11193                if (max_quantity_check !== true) {
11194                    alert(max_quantity_check);
11195                    return;
11196                }
11197                o.val(curCount);
11198                SITE_SHOP_DETAIL.setOrderCount(curCount);
11199            } else {
11200                alert(res);
11201            }
11202
11203            SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11204        },
11205        changeCartDecrease: function (type) {
11206            var o = $changeCartItemLayer.find('input._order_count_' + type);
11207            var curCount = o.val();
11208            if (isNaN(curCount))
11209                curCount = 1;
11210            else
11211                curCount = parseInt(curCount) - 1;
11212            if (curCount < 1) curCount = 1;
11213
11214            var res = SITE_SHOP_DETAIL.checkProdStock(curCount);
11215            if (res === true) {
11216                o.val(curCount);
11217                SITE_SHOP_DETAIL.setOrderCount(curCount);
11218            } else {
11219                alert(res);
11220            }
11221
11222            SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11223        },
11224        changeCartOrderCount: function (type, count, is_alert) {
11225            if (is_alert == void 0) is_alert = true;
11226
11227            if (isNaN(count))
11228                count = 1;
11229            else
11230                count = parseInt(count);
11231            if (count < 1) count = 1;
11232
11233            var res = SITE_SHOP_DETAIL.checkProdStock(count);
11234            if (res === true) {
11235                if (is_alert) {
11236                    // 최대 구매수량 검증 추가 (optional_option 타입 제외)
11237                    var max_quantity_check = SITE_SHOP_DETAIL.checkMaxPurchaseQuantityForOrderCount(count);
11238                    if (max_quantity_check !== true) {
11239                        alert(max_quantity_check);
11240                        return;
11241                    }
11242                }
11243                $changeCartItemLayer.find("input._order_count_" + type).val(count);
11244                SITE_SHOP_DETAIL.setOrderCount(count);
11245            } else {
11246                if (is_alert) alert(res);
11247            }
11248
11249            SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11250        },
11251        changeCartItemRemove: function (optNo) {
11252            SITE_SHOP_DETAIL.removeSelectedOption(optNo, 'cart');
11253        },
11254        changeCartItemCount: function (optNo, optCount) {
11255            SITE_SHOP_DETAIL.changeOptionCount(optNo, optCount, 'cart');
11256        },
11257        // 스피너 애니메이션 keyframes 삽입 (1회)
11258        _spinnerStyleInjected: false,
11259        _ensureSpinnerStyle() {
11260            if (this._spinnerStyleInjected) return;
11261            const style = document.createElement('style');
11262            style.textContent = '@keyframes cartBtnSpin{100%{transform:rotate(360deg)}}';
11263            document.head.appendChild(style);
11264            this._spinnerStyleInjected = true;
11265        },
11266        // 수량 변경 AJAX 진행 카운터 (0이 되어야 로딩 해제)
11267        _pendingCount: 0,
11268        _btnOriginalTexts: null,
11269        _setLoading() {
11270            this._pendingCount++;
11271            if (this._pendingCount > 1) return; // 이미 로딩 중
11272            this._ensureSpinnerStyle();
11273            const spinnerSvg = '<span class="_cart_btn_spinner" style="display:inline-flex;align-items:center;justify-content:center;width:16px;height:16px;animation:cartBtnSpin 1s linear infinite"><svg width="100%" height="100%" viewBox="0 0 16 16" fill="none" xmlns="http://www.w3.org/2000/svg"><path d="M14.75 8C15.1642 8 15.5039 7.66338 15.4625 7.25123C15.3501 6.13087 14.9865 5.04679 14.3948 4.08126C13.6721 2.90192 12.6373 1.9454 11.4049 1.31745C10.1725 0.689506 8.79046 0.414598 7.41156 0.52312C6.03265 0.631642 4.71062 1.11937 3.59161 1.93237C2.4726 2.74538 1.60022 3.85199 1.0709 5.12988C0.541588 6.40776 0.375962 7.80712 0.592338 9.17326C0.808713 10.5394 1.39866 11.8191 2.29696 12.8709C3.03239 13.7319 3.95104 14.4128 4.98183 14.8659C5.36102 15.0326 5.78614 14.8136 5.91414 14.4196C6.04214 14.0257 5.82382 13.6068 5.44891 13.4307C4.67966 13.0693 3.99324 12.5473 3.43756 11.8967C2.71893 11.0553 2.24697 10.0315 2.07387 8.93861C1.90077 7.8457 2.03327 6.7262 2.45672 5.7039C2.88017 4.6816 3.57808 3.7963 4.47329 3.1459C5.36849 2.49549 6.42612 2.10531 7.52924 2.0185C8.63237 1.93168 9.73801 2.1516 10.7239 2.65396C11.7099 3.15632 12.5377 3.92153 13.1158 4.86501C13.5629 5.59453 13.8472 6.40866 13.9532 7.25194C14.0048 7.66292 14.3358 8 14.75 8Z" fill="currentColor"/></svg></span>';
11274            const $btns = $('.bottom-btn.btn-wrap').find('a.btn');
11275            this._btnOriginalTexts = [];
11276            $btns.each((i, el) => { this._btnOriginalTexts.push($(el).html()); });
11277            $btns.each(function () { $(this).html(spinnerSvg); }
11277).addClass('disabled').css('pointer-events', 'none');
11278            $('._social_pay').css({ 'pointer-events': 'none', 'opacity': '0.4' });
11279        },
11280        _clearLoading() {
11281            this._pendingCount--;
11282            if (this._pendingCount > 0) return; // 아직 진행 중인 요청 있음
11283            this._pendingCount = 0;
11284            const $btns = $('.bottom-btn.btn-wrap').find('a.btn');
11285            if (this._btnOriginalTexts) {
11286                $btns.each((i, el) => {
11287                    if (this._btnOriginalTexts[i]) $(el).html(this._btnOriginalTexts[i]);
11288                });
11289                this._btnOriginalTexts = null;
11290            }
11291            $btns.removeClass('disabled').css('pointer-events', '');
11292            $('._social_pay').css({ 'pointer-events': '', 'opacity': '' });
11293        },
11294        // 키 기반 debounce 유틸리티
11295        _debounceTimers: {},
11296        _debounce(key, fn, delay) {
11297            clearTimeout(this._debounceTimers[key]);
11298            this._debounceTimers[key] = setTimeout(() => {
11299                delete this._debounceTimers[key];
11300                fn();
11301            }, delay);
11302        },
11303        // debounce 전 원래 수량 저장 (복원용)
11304        _originalCounts: {},
11305        // 수량 cell UI 즉시 업데이트 (단일상품만 즉시 반영, 옵션 상품의 품목 수량은 서버 응답으로 갱신)
11306        _updateCartCountCell($row, mode, option_no, count) {
11307            if (mode === 'product_count') {
11308                $row.find('._cart_item_count').text(count);
11309                const $countText = $row.find('._cart_item_count_text');
11310                if ($countText.length) {
11311                    $countText.text($countText.text().replace(/\d+/, count));
11312                }
11313            }
11314            // 옵션/상품 가격 업데이트 (정가 + 할인가)
11315            const updatePriceEl = ($container, priceClass, originPriceClass) => {
11316                const $price = $container.find('.' + priceClass);
11317                const $origin = $container.find('.' + originPriceClass);
11318                if ($price.length) {
11319                    const unitPrice = parseInt($price.data('unit-price'), 10);
11320                    if (unitPrice) $price.text(LOCALIZE.getCurrencyFormat(unitPrice * count));
11321                }
11322                if ($origin.length) {
11323                    const unitOrigin = parseInt($origin.data('unit-price'), 10);
11324                    if (unitOrigin) $origin.text(LOCALIZE.getCurrencyFormat(unitOrigin * count));
11325                }
11326            };
11327            if (mode === 'option_count') {
11328                const $card = $row.find('._cart_option_card[data-option-no="' + option_no + '"]');
11329                updatePriceEl($card, '_cart_option_price', '_cart_option_origin_price');
11330            } else if (mode === 'product_count') {
11331                updatePriceEl($row, '_cart_product_price', '_cart_product_origin_price');
11332            }
11333        },
11334        // click_change_quantity_cart 트래킹 (서버 응답 200 + cart 화면 반영 직후 호출)
11335        _trackChangeQuantityCart($counter, $row, mode, before, after, method) {
11336            if (typeof window.BrandScope === 'undefined' || !method) return;
11337            const isOption = mode === 'option_count';
11338            let optionInfo = null;
11339            if (isOption) {
11340                // 옵션 라벨 + 값 wrapper 텍스트 추출 (예: "포장방법: 포장백 / 색상: 빨강")
11341                // .opt-name 의 parent span 이 라벨+값 한 옵션을 묶고, 같은 카드에 옵션 N개면 N개 parent span
11342                const $optCard = $counter.closest('._cart_option_card');
11343                if ($optCard.length) {
11344                    const parts = [];
11345                    $optCard.find('.opt-name').each(function () {
11346                        const text = $(this).parent().text().replace(/\s+/g, ' ').trim();
11347                        if (text) parts.push(text);
11348                    });
11349                    if (parts.length) optionInfo = parts.join(' / ');
11350                }
11351            }
11352            const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
11353            window.BrandScope.track('click_change_quantity_cart', {
11354                action:                 'click',
11355                content:                'change_quantity',
11356                where:                  'cart',
11357                prod_code:              $row.attr('data-prod-code') || '',
11358                prod_type:              $row.attr('data-prod-type') || 'normal',
11359                cart_code:              cartCode,
11360                cart_item_code:         $counter.data('itemCode') || '',
11361                option_info:            optionInfo,
11362                quantity_change_method: method,
11363                quantity_unit:          isOption ? 'option' : 'product',
11364                quantity_before:        before,
11365                quantity_after:         after,
11366            });
11367        },
11368        // 수량 변경 후 서버 동기화 (debounce + AJAX, +/- 와 직접입력 공통)
11369        // trackingMethod: 'button_increase' | 'button_decrease' | 'input'
11370        // (click_change_quantity_cart 트래킹 시 사용 — 응답 200 + 화면 반영 직후 발사)
11371        _syncCartCount($counter, $val, $row, mode, option_no, timerKey, trackingMethod) {
11372            const { itemCode: item_code, isRegularly: is_regularly, isC3: is_c3 } = $counter.data();
11373            const rollback = (count) => {
11374                $val.val(count);
11375                this._updateCartCountCell($row, mode, option_no, count);
11376            };
11377            this._debounce(timerKey, () => {
11378                const originalCount = this._originalCounts[timerKey];
11379                delete this._originalCounts[timerKey];
11380                // debounce 대기 중 옵션이 삭제된 경우 요청 취소
11381                if (!$.contains(document, $counter[0])) return;
11382                const finalCount = parseInt($val.val()) || 1;
11383
11384                // 선택 상품이 없으면 item_list가 직렬화되지 않으므로 빈 선택 목록도 명시한다.
11385                const postData = { mode, item_code, count: finalCount, is_regularly, item_list: selectedCartItem, item_list_sent: 'Y' };
11386                if (mode === 'option_count') postData.option_no = option_no;
11387
11388                this._setLoading();
11389
11390                $.ajax({
11391                    type: 'POST',
11392                    data: postData,
11393                    url: is_c3 ? '/shop/oms/OMS_change_cart_item.cm' : '/shop/change_cart_item.cm',
11394                    dataType: 'json',
11395                    cache: false,
11396                    success: (result) => {
11397                        if (result.msg === 'SUCCESS') {
11398                            if (typeof AW_INOUT !== 'undefined') AW_INOUT(result.prod_no, result.after_count);
11399                            if (typeof AM_INOUT !== 'undefined') AM_INOUT(result.prod_no, result.after_count);
11400                            if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
11401                            if (result.item_prices) {
11402                                $.each(result.item_prices, (itemCode, info) => {
11403                                    const $itemRow = $cart_list_wrap.find(`tr[data-item-code="${itemCode}"]`);
11404                                    if (!$itemRow.length) return;
11405                                    // 서버에서 계산된 정확한 품목 수량 반영
11406                                    if (info.count !== undefined) {
11407                                        $itemRow.find('._cart_item_count').text(info.count);
11408                                        const $countText = $itemRow.find('._cart_item_count_text');
11409                                        if ($countText.length) {
11410                                            $countText.text($countText.text().replace(/\d+/, info.count));
11411                                        }
11412                                    }
11413                                    // 가격 갱신 (PC/mobile + origin 헬퍼 통합, QD는 헬퍼에서 차감)
11414                                    renderCartRowPrice($itemRow, { basePrice: info.price_raw, originPrice: info.origin_price_raw });
11415                                    // 타이틀 행 아이템 가격(strikethrough 영역) 별도 갱신
11416                                    $itemRow.find('._cart_item_price').text(info.origin_price);
11417                                    // 수량별 할인 (JS 계산)
11418                                    const _quantityDiscountItemPrice = _calcCartQuantityDiscount($itemRow, parseInt(info.price_raw, 10) || 0);
11419
11420                                    // 배송비 업데이트
11421                                    if (info.deliv_price_text !== undefined && info.deliv_price_text !== '') {
11422                                        updateCartItemDelivText(findCartItemDelivTd($itemRow, itemCode), info.deliv_price_text);
11423
11424                                        // 모바일 개별 배송비 업데이트
11425                                        const $mobileDelivText = $cart_list_wrap.find(`._cart_mobile_deliv_price[data-item-code="${itemCode}"] ._cart_mobile_deliv_price_text`);
11426                                        if ($mobileDelivText.length) {
11427                                            $mobileDelivText.html(info.deliv_price_text);
11428                                        }
11429                                    }
11430                                    // 모바일 묶음배송 배송비 업데이트
11431                                    if (info.bundle_deliv_text !== undefined && info.bundle_deliv_text !== '') {
11432                                        const $shippingRow = findCartBundleShippingRow($itemRow, itemCode);
11433                                        if ($shippingRow.length) {
11434                                            $shippingRow.find('._cart_bundle_deliv_text').html(info.bundle_deliv_text);
11435                                        }
11436                                    }
11437                                    // 모바일 아이템별 할인 영역 업데이트
11438                                    if (info.period_discount !== undefined) {
11439                                        const $discountWrap = $itemRow.find('._cart_mobile_discount_wrap');
11440                                        const $periodField = $itemRow.find('._cart_mobile_period_discount_field');
11441                                        const $periodPrice = $itemRow.find('._cart_mobile_period_discount_price');
11442
11443                                        // _quantityDiscountItemPrice는 위에서 이미 계산됨
11444                                        const _periodDiscount = info.peri
11444od_discount;
11445                                        const _creatorDiscountItemPrice = parseInt(info.creator_discount, 10) || 0;
11446                                        const _totalItemDiscount = _periodDiscount + _quantityDiscountItemPrice + _creatorDiscountItemPrice;
11447
11448                                        if (_totalItemDiscount > 0) {
11449                                            // 상품 할인 금액 (합계)
11450                                            $periodPrice.data('period-discount-price', _totalItemDiscount);
11451                                            $periodPrice.text('- ' + LOCALIZE.getCurrencyFormat(_totalItemDiscount));
11452                                            $periodField.show();
11453
11454                                            // 즉시/기간 할인 하위 항목
11455                                            const $periodSub = $itemRow.find('._cart_mobile_period_sub_field');
11456                                            if (_periodDiscount > 0 && $periodSub.length) {
11457                                                $periodSub.find('._cart_mobile_period_sub_price').text('- ' + LOCALIZE.getCurrencyFormat(_periodDiscount));
11458                                                $periodSub.show();
11459                                            } else if ($periodSub.length) {
11460                                                $periodSub.hide();
11461                                            }
11462
11463                                            // 수량별 할인 하위 항목
11464                                            const $quantityDiscountField = $itemRow.find('._cart_mobile_qd_discount_field');
11465                                            const $quantityDiscountPrice = $itemRow.find('._cart_mobile_quantity_discount_price');
11466                                            if (_quantityDiscountItemPrice > 0) {
11467                                                $quantityDiscountPrice.text('- ' + LOCALIZE.getCurrencyFormat(_quantityDiscountItemPrice));
11468                                                $quantityDiscountField.show();
11469                                            } else {
11470                                                $quantityDiscountField.hide();
11471                                            }
11472
11473                                            // 크리에이터 할인 하위 항목
11474                                            const $creatorDiscountField = $itemRow.find('._cart_mobile_creator_discount_field');
11475                                            const $creatorDiscountPrice = $itemRow.find('._cart_mobile_creator_discount_price');
11476                                            if (_creatorDiscountItemPrice > 0) {
11477                                                $creatorDiscountPrice
11478                                                    .data('creator-discount-price', _creatorDiscountItemPrice)
11479                                                    .text('- ' + LOCALIZE.getCurrencyFormat(_creatorDiscountItemPrice));
11480                                                $creatorDiscountField.show();
11481                                            } else {
11482                                                $creatorDiscountPrice.data('creator-discount-price', 0).text('');
11483                                                $creatorDiscountField.hide();
11484                                            }
11485
11486                                            $discountWrap.show();
11487                                        } else {
11488                                            $periodPrice.data('period-discount-price', 0);
11489                                            $periodPrice.text('');
11490                                            $periodField.hide();
11491                                            $itemRow.find('._cart_mobile_creator_discount_price').data('creator-discount-price', 0).text('');
11492                                            $itemRow.find('._cart_mobile_creator_discount_field').hide();
11493                                            if ($discountWrap.length) $discountWrap.hide();
11494                                        }
11495                                    }
11496                                });
11497                            }
11498                            if (result.totals) {
11499                                setCartPriceHtml(result.totals);
11500                            }
11501                            // 사은품 MFE에 실제 콘텐츠가 렌더링되어 있으면 장바구니 변경 이벤트 발송
11502                            const freebieEl = document.querySelector('fo-prod-detail-freebie-cart') || document.querySelector('fo-prod-detail-freebie-cart-mobile');
11503                            const freebieRoot = freebieEl && freebieEl.shadowRoot ? freebieEl.shadowRoot.querySelector('[data-root] > div') : null;
11504                            if (freebieRoot) {
11505                                // CB-3865: 수량 변경 후 closure 의 count 만 patch.
11506                                // option_no 는 DOM data-option-no (0-based array index). 본품은 -1.
11507                                // totalItemPrice 는 patch 안 함 — apply-promotion schema 상 optional,
11508                                // BE 가 prodCode/qty 로 자체 계산. tiered pricing 부정확 회피.
11509                                var _patchedItem = _cartRawItems.find(function (i) { return i.item_code === $counter.data('itemCode'); });
11510                                if (_patchedItem) {
11511                                    var _target = (option_no === -1)
11512                                        ? _patchedItem
11513                                        : ((_patchedItem.options && _patchedItem.options.list) || [])[option_no];
11514                                    if (_target) _target.count = finalCount;
11515                                }
11516                                dispatchFreebieCartChange();
11517                            }
11518                            // click_change_quantity_cart 트래킹 (서버 응답 200 + 화면 반영 직후)
11519                            this._trackChangeQuantityCart($counter, $row, mode, originalCount, finalCount, trackingMethod);
11520                        } else {
11521                            rollback(originalCount);
11522                            alert(result.msg);
11523                        }
11524                    },
11525                    error: () => rollback(originalCount),
11526                    complete: () => {
11527                        this._clearLoading();
11528                    }
11529                });
11530            }, 300);
11531        },
11532        // 장바구니 1품목당 최대 수량 (프론트 입력 한도
11532)
11533        _MAX_CART_ITEM_COUNT: 9999,
11534        // 장바구니 목록에서 수량 직접 변경 (옵션/단일상품 공통, +/- 버튼 핸들러)
11535        directChangeCount(el, delta) {
11536            const $counter = $(el).closest('._cart_counter');
11537            const $val = $counter.find('._counter_value');
11538            const $row = $counter.closest('tr');
11539            const currentCount = parseInt($val.val()) || 1;
11540            let newCount = currentCount + delta;
11541            if (newCount < 1) newCount = 1;
11542            if (newCount > this._MAX_CART_ITEM_COUNT) newCount = this._MAX_CART_ITEM_COUNT;
11543            if (newCount === currentCount) return;
11544
11545            // 수량 증가 시 품절/재고 검증 (클라이언트 즉각 피드백)
11546            // 최대구매수량 검증은 서버에서 일괄 처리 (모달과 동일한 검증 로직 적용)
11547            if (delta > 0 && !this._checkStockLimit($counter, newCount)) return;
11548
11549            const { mode, optionNo } = $counter.data();
11550            const option_no = (mode === 'option_count') ? optionNo : -1;
11551            const timerKey = `${$counter.data('itemCode')}_${option_no}`;
11552
11553            // 최초 클릭 시 원래 수량 보관
11554            if (!(timerKey in this._originalCounts)) {
11555                this._originalCounts[timerKey] = currentCount;
11556            }
11557
11558            // 즉시 UI 반영
11559            $val.val(newCount);
11560            this._updateCartCountCell($row, mode, option_no, newCount);
11561
11562            // 트래킹용 변경 방식 (click_change_quantity_cart)
11563            const trackingMethod = delta > 0 ? 'button_increase' : 'button_decrease';
11564            this._syncCartCount($counter, $val, $row, mode, option_no, timerKey, trackingMethod);
11565        },
11566        // 장바구니 목록에서 수량 직접 입력 (input onchange 핸들러)
11567        directInputCount(el) {
11568            const $counter = $(el).closest('._cart_counter');
11569            const $val = $counter.find('._counter_value');
11570            const $row = $counter.closest('tr');
11571
11572            // focus 시점에 저장된 직전 값 (없으면 input 의 현재값을 fallback)
11573            const previousCount = parseInt(el.dataset.prevValue, 10) || (parseInt($val.val(), 10) || 1);
11574
11575            // 입력값 정규화:
11576            //   1) Number() 로 우선 파싱 → finite 면 Math.floor 적용 (소수점 절삭, 음수 보존)
11577            //   2) finite 가 아니면 (NaN/Infinity) 숫자 외 문자 제거 후 parseInt 폴백
11578            //   3) 최종값을 [1, _MAX_CART_ITEM_COUNT] 범위로 클램프 후 input 에 반영
11579            // 예: "1.5" → 1, "-5" → 1, "abc1234" → 1234, "12,000" → 9999, 빈문자열 → 1
11580            const rawInput = String($val.val());
11581            const parsed = Number(rawInput);
11582            let newCount;
11583            if (Number.isFinite(parsed)) {
11584                newCount = Math.floor(parsed);
11585            } else {
11586                const stripped = rawInput.replace(/[^0-9]/g, '');
11587                newCount = parseInt(stripped, 10);
11588            }
11589            if (isNaN(newCount) || newCount < 1) newCount = 1;
11590            if (newCount > this._MAX_CART_ITEM_COUNT) newCount = this._MAX_CART_ITEM_COUNT;
11591            $val.val(newCount);
11592
11593            if (newCount === previousCount) return;
11594
11595            // 수량 증가 시 품절/재고 검증
11596            if (newCount > previousCount && !this._checkStockLimit($counter, newCount)) {
11597                $val.val(previousCount);
11598                return;
11599            }
11600
11601            const { mode, optionNo } = $counter.data();
11602            const option_no = (mode === 'option_count') ? optionNo : -1;
11603            const timerKey = `${$counter.data('itemCode')}_${option_no}`;
11604
11605            // 최초 변경 시 원래 수량 보관 (서버 응답 실패 시 복원용)
11606            if (!(timerKey in this._originalCounts)) {
11607                this._originalCounts[timerKey] = previousCount;
11608            }
11609
11610            // 즉시 가격 셀 갱신
11611            this._updateCartCountCell($row, mode, option_no, newCount);
11612
11613            this._syncCartCount($counter, $val, $row, mode, option_no, timerKey, 'input');
11614        },
11615        // 품절/재고 한도 검증 (true: 통과, false: 차단)
11616        _checkStockLimit($counter, newCount) {
11617            if ($counter.data('soldout') === 'Y') {
11618                alert(LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(1));
11619                return false;
11620            }
11621            const maxStock = $counter.data('stock');
11622            if (maxStock !== undefined && newCount > parseInt(maxStock)) {
11623                alert(LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(parseInt(maxStock) + 1));
11624                return false;
11625            }
11626            return true;
11627        },
11628        "changeCartItemDelivType": function (item_code, deliv_type, is_regularly) {
11629            changeCartItemDelivType(item_code, deliv_type, is_regularly);
11630        },
11631        "changeCartItemDelivPayType": function (item_code, deliv_pay_type, is_regularly) {
11632            changeCartItemDelivPayType(item_code, deliv_pay_type, is_regularly);
11633        },
11634        changeCartItemComplete: function (current_ul) {
11635            changeCartItemComplete(current_ul);
11636        },
11637        addNPayWishWithCart: function (item_code) {
11638            addNPayWishWithCart(item_code);
11639        },
11640        addSelectedCartItem: function (item_code) {
11641            selectedCartItem.push(item_code);
11642        },
11643        // MFE 주문 재시도 시 이전 선택을 누적하지 않고 현재 목록으로 교체한다.
11644        setSelectedCartItems: function (item_codes) {
11645            selectedCartItem = item_codes.slice();
11646        },
11647        changeCartLoadOption: function (prod_idx) {
11648            SITE_SHOP_DETAIL.loadOption('cart', prod_idx);
11649        },
11650        cartListMake: function (current_full_url, type) {
11651            cartListMake(current_full_url, type);
11652        },
11653        "setCreatorDiscountData": function (data) {
11654            // 크리에이터 전용 혜택가 데이터 주입 (PHP cart_main.sub 에서 호출).
11655            // BE 차감은 OMS 자동 처리이므로 여기선 표시용 데이터만 보관.
11656            creator_cart_data = data || null;
11657        },
11658        countryCodeChange: function (country) {
11659            countryCodeChange(country);
11660        },
11661        openPromotionModal: function () {
11662            openPromotionModal();
11663        },
11664        toggleAllSelectCk: function () {
11665            toggleAllSelectCk();
11666        },
11667        // C3 에 주문을 생성하는 endpoint를 바라보는 함수
11668		C3_initCart : function(cart_code, backurl, is_regularly){
11669			initCart(cart_code, backurl, is_regularly, true);
11670		},
11671		OMS_addOrderWithCart : function(type, item_code, backurl, params, is_gift_buy = false){
11672			addOrderWithCart(type, item_code, backurl, false, true, params, is_gift_buy);
11673		},
11674		OMS_addOrderWithCartDirect : function(type, item_code, backurl, cart_code, freebie_list){
11675			// IMIO-7501 — MFE 장바구니는 탭 전환 시 리로드가 없어 C3_initCart 가 재실행되지 않는다.
11676			// currentCartCode 가 첫 로드 type 에 고정돼 다른 탭 바로구매가 빈 cart 로 판정되므로,
11677			// MFE 가 넘긴 활성 탭 cart_code 로 호출 시점에 동기화한다.
11678			if (typeof cart_code === 'string' && cart_code !== '') {
11679				currentCartCode = cart_code;
11680			}
11681			// 모바일 MFE 장바구니는 사은품이 포함형(별도 freebie MFE 미적재)이라 host 의 사은품 pull
11682			// 이벤트에 응답할 주체가 없다. 바로구매는 MFE 가 선택한 사은품을 직접 인자로 넘기므로,
11683			// params.freebie_list 로 addOrderWithCart 에 전달해 주문서에 담는다 (레거시 바로구매 사은품 회귀 복구).
11684			addOrderWithCart(type, item_code, backurl, true, true, freebie_list ? { freebie_list: freebie_list } : undefined);
11685		},
11686		// CB-3985 — MFE 장바구니가 주문 응답으로 머천트 픽셀을 호스트에서 발화하기 위해 노출.
11687		OMS_fireOrderTracking : function (type, result) {
11688		    fireOrderTracking(type, result);
11689		},
11690		C3_cartListMake : function(current_full_url, type){
11691			cartListMake(current_full_url, type, true);
11692		},
11693		C3_changeCartItemComplete : function(current_ul){
11694			changeCartItemComplete(current_ul, true);
11695		},
11696		C3_changeCartItemDelivType: function(item_code, deliv_type,is_regularly){
11697			changeCartItemDelivType(item_code, deliv_type,is_regularly, true);
11698		},
11699		C3_changeCartItemDelivPayType: function(item_code, deliv_pay_type,is_regularly) {
11700			changeCartItemDelivPayType(item_code, deliv_pay_type,is_regularly, true);
11701		},
11702    };
11703}();
11704
11705var SITE_SHOP_REVIEW = function () {
11706    var $review_wrap;
11707    var $mobile_review_wrap;
11708    var $mobile_form;
11709    var $rating;
11710    var $star;
11711    var $m_rating;
11712    var $m_star;
11713    var $comment_body;
11714    var $comment_area;
11715    var review_body;
11716    var $review_container;
11717    var $review_form;
11718    var body_input;
11719    var placeholderText;
11720    var isIOS, isSafari, $fr_m_custom, $write_header, m_sticky_container_trigger_top, $toolbarContainer;
11721    var images = {};
11722    var add_review_wrap;
11723    var is_submit = false;
11724
11725		// 이미지 엑박 여부를 확인하는 함수
11726		const isImageBroken = (img) => {
11727			return img.complete && (img.naturalWidth === 0 || img.naturalHeight === 0);
11728		}
11729
11730		// 이미지를 다시 로드하는 함수
11731		// 업로드 직후 imgo 최적화본이 없으면 원본이 폴백으로 온다. jpg·png 는 그대로 표시되지만
11732		// heic·heif 는 브라우저가 디코드하지 못해 엑박이 된다. 변환이 끝나면 webp 로 바뀌므로 다시 받아 본다.
11733		// 재시도 간격(ms). 뒤로 갈수록 벌려 총 12초 안에 4번만 시도한다.
11734		const RELOAD_IMAGE_RETRY_DELAYS = [1000, 2000, 3000, 6000];
11735		const reloadImage = (img, src) => {
11736			// src 에 이미 ?w= 가 있으므로 "?t=" 를 이어 붙이면 w 값이 "300?t=..." 로 파싱되어
11737			// 폭이 NaN 이 되고 최소 프리셋(100px)이 내려온다. URL API 로 파라미터를 교체한다.
11738			const tried = Number(img.dataset.reloadRetry || 0);
11739			if (tried >= RELOAD_IMAGE_RETRY_DELAYS.length) {
11740				return;
11741			}
11742			img.dataset.reloadRetry = tried + 1;
11743			setTimeout(() => {
11744				const retryUrl = new URL(src);
11745				retryUrl.searchParams.set('t', Date.now()); // 캐시를 피하기 위해 쿼리 파라미터 추가
11746				img.src = retryUrl.toString();
11747			}, RELOAD_IMAGE_RETRY_DELAYS[tried]);
11748		}
11749
11750    var init = function (code) {
11751        $review_wrap = $('._review_wrap');
11752        $review_form = $('#review_form');
11753        $rating = $('#rating');
11754        $star = $('._star');
11755        $review_form.find('._btn_add_image').toggleClass('no-margin', true);
11756        review_body = $('#review_modal_body');
11757        $review_container = $('#review_container');
11758        body_input = $('#body_input');
11759        placeholderText = $('#placeholderText').val();
11760        $review_form.find('#review_image_upload_btn').fileupload({
11761            url: '/ajax/review_image_upload.cm',
11762            formData: { temp: 'Y', target: 'site_review', type: 'image' },
11763            dataType: 'json',
11764            singleFileUploads: true,
11765            limitMultiFileUploads: 1,
11766            start: function (e, data) {
11767            },
11768            progress: function (e, data) {
11769            },
11770            done: function (e, data) {
11771                $.each(data.result.files, function (e, tmp) {
11772                    if (tmp.error == null) {
11773                        $review_form.find("#review_image_box").show();
11774                        $review_form.find('._btn_add_image').toggleClass('no-margin', false);
11775												// 경로에 쿼리가 이미 있으면 '?w=300' 을 이어 붙일 때 w 가 파싱되지 않으므로 URL API 로 붙인다
11776												const _optimizedImageUrl = new URL(CDN_OPTIMIZED_URL + tmp.url);
11777												_optimizedImageUrl.searchParams.set('w', 300);
11778												const url = _optimizedImageUrl.toString();
11779                        images[tmp.code] = tmp.url;
11780                        const html = '<span class="file-add _img_' + tmp.code + '"><input type="hidden" name="img" value="' + tmp.name + '"><div class="img-thumb-wrap"><img id="review_' + tmp.url + '" src="' + url + '"></div><a class="del" href="javascript:;" onclick="SITE_SHOP_REVIEW.deleteReviewImage(\'' + tmp.code + '\')"><i class="btm bt-times vertical-middle" aria-hidden="true"></i></a></span>';
11781                        $review_form.find("#review_image_box ._btn_add_image").before(html);
11782
11783		                    const reviewImage = document.getElementById(`review_${tmp.url}`);
11784												reviewImage.style.display = 'none';
11785
11786		                    // 이미지 로드 완료 후 확인
11787		                    reviewImage.onload = reviewImage.onerror = function() {
11788			                    if (isImageBroken(reviewImage)) {
11789				                    reloadImage(reviewImage, reviewImage.src);
11790			                    } else {
11791				                    reviewImage.style.display = 'block';
11792			                    }
11793		                    };
11794
11795		                    // 만약 이미 이미지가 로드되었으면 즉시 확인
11796		                    if (reviewImage.complete) {
11797			                    if (isImageBroken(reviewImage)) {
11798				                    reloadImage(reviewImage, reviewImage.src);
11799			                    } else {
11800				                    reviewImage.style.display = 'block';
11801			                    }
11802		                    }
11803                    } else {
11804                        alert(tmp.error);
11805                    }
11806                });
11807            },
11808            fail: function (e, data) {
11809                alert(getLocalizeString("설명_업로드에실패하였습니다", "", "업로드에 실패 하였습니다."));
11810            }
11811        });
11812    };
11813
11814    var deleteReviewImage = function (code) {
11815        if (typeof images[code] != 'undefined') {
11816            delete images[code];
11817        }
11818        $("span._img_" + code).remove();
11819    };
11820
11821    var openAddReview = function (idx, prod_code, prod_order_code, is_c3 = false) {
11822        $.cocoaDialog.close();
11823        $('#myModalReview').modal("hide");//review_remind 모달 id분리로 인한 처리
11824        $.ajax({
11825            type: 'POST',
11826            data: { 'idx': idx, 'prod_code': prod_code, 'prod_order_code': prod_order_code },
11827            url: ('/shop/open_add_review.cm'),
11828            dataType: 'json',
11829            cache: false,
11830            async: false,
11831            success: function (result) {
11832                if (result.msg === 'SUCCESS') {
11833                    $.cocoaDialog.open({
11834                        type: 'add_review', custom_popup: result.html, 'close_block': true, width: 800, hide_event: function () {
11835                            images = {};
11836                        }
11837                    });
11838                } else {
11839                    alert(result.msg);
11840                }
11841            }
11842        });
11843    };
11844
11845    var convertReviewImage = function (image_list) {
11846        $review_form.find('._btn_add_image').toggleClass('no-margin', false);
11847        for (var i = 0; i < image_list.length; i++) {
11848            let html;
11849			images[i] = image_list[i];
11850	        if (images[i].indexOf('.mp4') > -1) {
11851		        html = '<span class="file-add _img_' + i + '"><input type="hidden" name="img" value="' + i + '"><div class="img-thumb-wrap tw-flex tw-flex-col tw-items-center tw-justify-center tw-gap-[8px] tw-bg-[#F8F9FB]"><img style="border-width: 0px; width: 24px; height: 24px;" src="' + VENDOR_DOMAIN + '/images/circle-warning.png"><p class="tw-text-[10px] tw-leading-[15px] tw-text-center !tw-mb-0 tw-text-[#717680]">동영상은<br/>표시할 수 없습니다.</p></div><a class="del" href="javascript:;" onclick="SITE_SHOP_REVIEW.deleteReviewImage(\'' + i + '\')"><i class="btm bt-times vertical-middle" aria-hidden="true"></i></a></span>';
11852	        } else {
11853                html = '<span class="file-add _img_' + i + '"><input type="hidden" name="img" value="' + i + '"><div class="img-thumb-wrap"><img src="' + CDN_UPLOAD_URL + image_list[i] + '"></div><a class="del" href="javascript:;" onclick="SITE_SHOP_REVIEW.deleteReviewImage(\'' + i + '\')"><i class="btm bt-times vertical-middle" aria-hidden="true"></i></a></span>';
11854	        }
11855            $review_form.find("#review_image_box ._btn_add_image").before(html);
11856        }
11857    };
11858
11859    function resizeStickyContainer() {
11860        var s_top = $(this).scrollTop();
11861        if (isIOS && isSafari) {
11862            $write_header.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
11863            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
11864            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
11865            if (s_top > m_sticky_container_trigger_top) {
11866                $toolbarContainer.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
11867                review_body.find('.fr-view').css('padding-top', '90px');
11868            } else {
11869                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
11870                review_body.find('.fr-view').css('padding-top', '');
11871            }
11872        } else {
11873            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
11874        }
11875        $toolbarContainer.find('.fr-toolbar').css('width', review_body.find('.fr-view').width());
11876        if ($(window).width() >= 768) {
11877            if ($review_container.hasClass('bg_on'))
11878                $review_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 487);
11879            else
11880                $review_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 180);
11881        }
11882    }
11883
11884    var initMobileReview = function () {
11885        $mobile_review_wrap = $('#prod_detail_content_mobile');
11886        $mobile_form = $mobile_review_wrap.find('#mobile_review_form');
11887        $m_rating = $mobile_form.find('#mobile_rating');
11888        $m_star = $mobile_form.find('._star');
11889        $mobile_form.find("#mobile_review_image_box").hide();
11890
11891        $mobile_form.find('#mobile_review_image_upload_btn').setUploadImage({
11892            url: '/shop/upload_image.cm',
11893            dropZone: 'icon_img_upload_wrap',
11894            singleFileUploads: true,
11895            formData: { temp: 'Y' }
11896        }, function (res, data) {
11897            $("#mobile_review_image_box").show();
11898            $.each(data, function (e, tmp) {
11899                if (tmp.error == "" || tmp.error == null) {
11900                    var url = CDN_UPLOAD_URL + tmp.url;
11901                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
11902                    $("#mobile_review_image_box").append(html);
11903                } else {
11904                    alert(tmp.error);
11905                }
11906            });
11907        });
11908        autosize($('.textarea_block textarea'));
11909    };
11910
11911    var changeRating = function (t, n) {
11912
11913        if (t == 'desktop') {
11914            $rating.val(n + 1);
11915            $star.each(function (e) {
11916                if (n <= 0 && e == 0) {
11917                    if (n == -1) {
11918                        $(this).removeClass('active');
11919                    } else {
11920                        $(this).addClass('active');
11921                    }
11922                } else {
11923                    $(this).removeClass('active');
11924                    if (e <= n) {
11925                        $(this).addClass('active');
11926                    }
11927                }
11928            });
11929        } else {
11930            $m_rating.val(n + 1);
11931            $m_star.each(function (e) {
11932                if (n <= 0 && e == 0) {
11933                    if (n == -1) {
11934                        $(this).removeClass('active');
11935                    } else {
11936                        $(this).addClass('active');
11937                    }
11938                } else {
11939                    $(this).removeClass('active');
11940                    if (e <= n) {
11941                        $(this).addClass('active');
11942                    }
11943                }
11944            });
11945        }
11946    };
11947
11948    var reviewFormShow = function (t) {
11949        var sub_form = $("._sub_form_" + t);
11950
11951        sub_form.data('show', 'Y');
11952        sub_form.show();
11953        var comment_add_body = sub_form.find('._comment_add_body_' + t);
11954
11955        $('body').off('mouseup.sub_comment')
11956            .on('mouseup.sub_comment', function (e) {
11957                var $c_target = $(e.target);
11958                var $s_form = $c_target.closest('._sub_form_' + t + ', ._show_sub_form_btn_' + t);
11959                if ($s_form.length == 0) {
11960
11961                    var text = comment_add_body.val();
11962                    sub_form.data('show', 'N');
11963                    if (text == '') {
11964                        $('body').off('mouseup.sub_comment');
11965                        reviewFormHide();
11966                    }
11967                }
11968            });
11969    };
11970
11971    var reviewEditShow = function (t) {
11972        var editor_form = $("._sub_form_editor_" + t);
11973        editor_form.siblings().hide();
11974
11975        editor_form.data('show', 'Y');
11976        editor_form.show();
11977        autosize.update(editor_form.find('textarea'));
11978
11979    };
11980
11981    var reviewEditHide = function (t) {
11982        var editor_form = $("._sub_form_editor_" + t);
11983        editor_form.hide();
11984    };
11985
11986    var reviewFormHide = function () {
11987        $("._sub_review_form").hide();
11988    };
11989
11990    var reviewDelete = function (t, c, r_p, only_photo, buyer_permission, secret_pass) {
11991        only_photo = only_photo == 'Y' ? true : false;
11992        $.ajax({
11993            type: 'POST',
11994            data: { code: t, prod_code: c },
11995            url: ('/shop/check_review_point.cm'),
11996            dataType: 'json',
11997            success: function (res) {
11998                if (res.msg == 'SUCCESS') {
11999                    var msg = buyer_permission == 'Y' ? LOCALIZE.설명_삭제하시겠습니까삭제후재등록불가() : LOCALIZE.설명_삭제하시겠습니까();
12000                    if (res.alert_msg != '' && res.alert_msg != null) msg = res.alert_msg;
12001                    if (confirm(msg)) {
12002                        $.ajax({
12003                            type: 'POST',
12004                            data: { code: t, prod_code: c, change_member_point: res.change_member_point, review_pass: secret_pass || '' },
12005                            url: ('/shop/delete_review.cm'),
12006                            dataType: 'json',
12007                            success: function (result) {
12008                                if (result.msg == 'SUCCESS') {
12009                                    if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p);
12010                                    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p, 0, false, only_photo);
12011
12012
12013                                    SITE_SHOP_DETAIL.deleteReviewInTabDisplay(c, only_photo);
12014                                    SITE_SHOP_DETAIL.getReviewQnaCount(c);
12015
12016
12017                                    $.cocoaDialog.close();
12018                                } else
12019                                    alert(result.msg);
12020                            }
12021                        });
12022                    }
12023                } else
12024                    alert(res.msg);
12025            }
12026        });
12027    };
12028
12029    var reviewHide = function (t, c, r_p) {
12030        if (confirm(LOCALIZE.설명_숨기시겠습니까())) {
12031            $.ajax({
12032                type: 'POST',
12033                data: { code: t, prod_code: c, is_visible: false },
12034                url: ('/shop/delete_review.cm'),
12035                dataType: 'json',
12036                success: function (result) {
12037                    if (result.msg == 'SUCCESS') {
12038                        if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p);
12039                        else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p);
12040                        $.cocoaDialog.close();
12041                    } else
12042                        alert(result.msg);
12043                }
12044            });
12045        }
12046    };
12047
12048    var CheckSecret = function (code, secret_pass, callback) {
12049        $.ajax({
12050            type: 'post',
12051            data: { code: code, secret_pass: secret_pass, type: 'review' },
12052            url: '/ajax/check_review_pass.cm',
12053            dataType: 'json',
12054            success: function (result) {
12055                if (result.msg == 'SUCCESS') {
12056                    if (typeof callback == 'function')
12057                        callback();
12058                } else {
12059                    alert(result.msg);
12060                }
12061            }
12062        });
12063    };
12064    var EditReviewShow = function (t, c, idx) {
12065        var $show_secret_password = $('#show_secret_password');
12066        var $show_link = $(event.target);
12067        if ($show_secret_password.length == 0) {
12068            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" onclick="event.cancelBubble = true;" tabindex="0" style="position:absolute;z-index:99999;"><p>' + LOCALIZE.설명_작성시등록하신비밀번호를입력해주세요() + '</p><div class="input_area"><input type="password" placeholder="' + LOCALIZE.설명_비밀번호() + '"><button class="btn btn-primary _confirm">' + LOCALIZE.버튼_확인닫기() + '</button></div></div>').hide();
12069            $show_link.after($show_secret_password);
12070        }
12071        $show_secret_password.find('input').val('');
12072        $show_secret_password.show();
12073        $show_secret_password.off('click', '._confirm')
12074            .on('click', '._confirm', function () {
12075                var secret_pass = $show_secret_password.find('input').val();
12076                CheckSecret(t, secret_pass, function () {
12077                    SITE_SHOP_REVIEW.openAddReview(idx, c);
12078                });
12079                $show_secret_password.remove();
12080            });
12081        $('body').off('mousedown.show_secret')
12082            .on('mousedown.show_secret', function (e) {
12083                var $tmp = $(e.target).closest('#show_secret_password');
12084                if ($tmp.length == 0) {
12085					$show_secret_password.remove();
12086                    $('body').off('click.show_secret');
12087                }
12088            });
12089    };
12090
12091    var reviewDeleteShow = function (t, c, only_photo) {
12092        var $show_secret_password = $('#show_secret_password');
12093        var $show_link = $(event.target);
12094        if ($show_secret_password.length == 0) {
12095            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" onclick="event.cancelBubble = true;" tabindex="0" style="position:absolute;z-index:99999;"><p>' + LOCALIZE.설명_작성시등록하신비밀번호를입력해주세요() + '</p><div class="input_area"><input type="password" placeholder="' + LOCALIZE.설명_비밀번호() + '"><button class="btn btn-primary _confirm">' + LOCALIZE.버튼_확인닫기() + '</button></div></div>').hide();
12096            $show_link.after($show_secret_password);
12097        }
12098        $show_secret_password.find('input').val('');
12099        $show_secret_password.show();
12100        $show_secret_password.off('click', '._confirm')
12101            .on('click', '._confirm', function () {
12102                var secret_pass = $show_secret_password.find('input').val();
12103                CheckSecret(t, secret_pass, function () {
12104                    // 확인한 비밀번호를 삭제 요청에도 그대로 싣는다. 서버가 일괄등록 행에서
12105                    // 소유권 세션을 근거로 삼지 않으므로, 이 값이 없으면 정상 삭제도 거절된다.
12106                    reviewDelete(t, c, '', only_photo, undefined, secret_pass);
12107                });
12108				$show_secret_password.remove();
12109            });
12110        $('body').off('mousedown.show_secret')
12111            .on('mousedown.show_secret', function (e) {
12112                var $tmp = $(e.target).closest('#show_secret_password');
12113                if ($tmp.length == 0) {
12114					$show_secret_password.remove();
12115                    $('body').off('click.show_secret');
12116                }
12117            });
12118    };
12119
12120    var imageUploadInit = function (n) {
12121
12122        const $wrapper = $('.categorize, .categorize-mobile');
12123        $("#sub_review_image_box_" + n).hide();
12124        //$("#editor_review_image_box_"+n).hide();
12125
12126        $('#sub_review_image_upload_btn_' + n).setUploadImage({
12127            url: '/shop/upload_image.cm',
12128            dropZone: 'icon_img_upload_wrap',
12129            singleFileUploads: true,
12130            formData: { temp: 'Y' }
12131        }, function (res, data) {
12132            $("#sub_review_image_box_" + n).show();
12133            $.each(data, function (e, tmp) {
12134                if (tmp.error == "" || tmp.error == null) {
12135                    var url = CDN_UPLOAD_URL + tmp.url;
12136                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.url + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12137                    $("#sub_review_image_box_" + n).append(html);
12138                } else {
12139                    alert(tmp.error);
12140                }
12141            });
12142        });
12143
12144        $('#editor_review_image_upload_btn_' + n).setUploadImage({
12145            url: '/shop/upload_image.cm',
12146            dropZone: 'icon_img_upload_wrap',
12147            singleFileUploads: true,
12148            formData: { temp: 'Y' }
12149        }, function (res, data) {
12150            $("#editor_review_image_box_" + n).show();
12151            $.each(data, function (e, tmp) {
12152                if (tmp.error == "" || tmp.error == null) {
12153                    var url = CDN_UPLOAD_URL + tmp.url;
12154                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.url + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12155                    $("#editor_review_image_box_" + n).append(html);
12156                } else {
12157                    alert(tmp.error);
12158                }
12159            });
12160        });
12161    };
12162
12163    var submit = function () {
12164        if (is_submit) return false;
12165        is_submit = true;
12166        var data = $review_form.serializeObject();
12167        data.body = data.body.replace('/(?!br\\s*\\/?)[^>]+>/gi', '&gt;').replace('/<(?!img\\s*\\/?)[^>]/gi', '&lt;');
12168        data.images = images;
12169        $.ajax({
12170            type: 'POST',
12171            data: data,
12172            url: ('/shop/add_review.cm'),
12173            dataType: 'json',
12174            success: function (res) {
12175                if (res.msg == 'SUCCESS') {
12176                    IMWEB_SESSIONSTORAGE.clear("PROD_REVIEW.*");
12177                    reviewCompleted(res.received_point, res.check_received_point);
12178                    if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.CompleteReview();
12179                } else {
12180                    is_submit = false;
12181                    alert(res.msg);
12182                }
12183            }
12184        });
12185    };
12186
12187    var reviewCompleted = function (received_point, check_received_point) {
12188        $.cocoaDialog.close();
12189        $.ajax({
12190            type: 'POST',
12191            data: { 'received_point': received_point, 'check_received_point': check_received_point },
12192            url: ('/shop/add_review_completed.cm'),
12193            dataType: 'html',
12194            async: false,
12195            cache: false,
12196            success: function (html) {
12197                $.cocoaDialog.open({
12198                    type: 'prod_review_completed',
12199                    custom_popup: html,
12200                    'close_block': false,
12201                    hide_event: function () {
12202                        location.reload();
12203                    }
12204                });
12205            }
12206        });
12207    };
12208
12209    var reviewCancel = function () {
12210        if (isIOS && isSafari) {
12211            var s_top = $(this).scrollTop();
12212            $write_header.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': 0 });
12213            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
12214            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
12215            if (s_top > m_sticky_container_trigger_top) {
12216                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': $write_header.height() + 'px' });
12217            } else {
12218                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
12219            }
12220        }
12221        history.go(-1);
12222    };
12223
12224
12225    var createHtml = function (prod_idx, review_page, qna_page, paging_on, only_photo, rating) {
12226        const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
12227
12228        $review_wrap = $('._review_wrap');
12229
12230        const qna_url = isMobile_width ? '/shop/prod_review_mobile_html.cm' : '/shop/prod_review_pc_html.cm';
12231
12232        if ($review_wrap.length && !($review_wrap.hasClass('one_page_mode'))) {
12233            review_page = review_page || 0;
12234            qna_page = qna_page || 0;
12235
12236            var is_method_get = (review_page == 0 && qna_page == 0);
12237
12238            $.ajax({
12239                type: (is_method_get ? 'GET' : 'POST'),
12240                data: { prod_idx: prod_idx, review_page: review_page, qna_page: qna_page, only_photo: only_photo, rating: rating },
12241                url: qna_url,
12242                dataType: 'html',
12243                success: function (result) {
12244                    $review_wrap.html(result);
12245
12246                    if(isMobile_width) {
12247                        SITE_SHOP_DETAIL.getReviewSummary(document.querySelector(".detail_review_wrap_mobile ._review_summary_wrap_mobile"));
12248                    }
12249                }
12250            });
12251        }
12252
12253    };
12254
12255    var checkReviewData = function () {
12256        var check = Object.keys(images).length > 0 || review_body.val() != '';
12257        if (check) {
12258            if (confirm(LOCALIZE.설명_작성한내용이모두사라집니다())) {
12259                $.cocoaDialog.close();
12260            } else {
12261                return false;
12262            }
12263        } else {
12264            $.cocoaDialog.close();
12265        }
12266    };
12267
12268    var addBlockPost = function (login_member_code, review_code) {
12269        if (confirm(getLocalizeString('설명_게시글차단안내', '', '게시글을 차단하
12269시겠습니까? 작성자의 다른 글과 댓글도 확인할 수 없습니다.'))) {
12270            addBlock(login_member_code, review_code, 'post');
12271        }
12272    };
12273
12274    var addBlockComment = function (login_member_code, review_code) {
12275        if (confirm(getLocalizeString('설명_님을차단안내', '', '댓글을 차단하시겠습니까? 작성자의 다른 댓글도 확인할 수 없습니다.'))) {
12276            addBlock(login_member_code, review_code, 'comment');
12277        }
12278    };
12279
12280    var switchToBlockCancelButton = function (login_member_code, review_code, review_member_code, type) {
12281        $('._review_button._block_' + review_member_code)
12282            .text(getLocalizeString("설명_차단해제", "", "차단해제"))
12283            .removeAttr('onclick')
12284            .off('click')
12285            .on('click', function (e) {
12286                e.stopPropagation();
12287                if (type === 'comment') {
12288                    SITE_SHOP_REVIEW.deleteBlockComment(login_member_code, review_code, review_member_code);
12289                } else {
12290                    SITE_SHOP_REVIEW.deleteBlockPost(login_member_code, review_code, review_member_code);
12291                }
12292            });
12293
12294    };
12295
12296    var switchToBlockCancelBody = function (review_member_code) {
12297        $('._review_body._block_' + review_member_code)
12298            .html("<p class='text-gray'><i aria-hidden='true' class='icon icon-exclamation' style='margin-right: 5px;'></i>" + getLocalizeString("설명_차단한작성자의구매평입니다", "", "차단한 작성자의 구매평입니다.") + "</p>")
12299            .attr('block', 'true');
12300
12301        //이미지 안보이게 수정
12302        $('._review_img._block_' + review_member_code).css('display', 'none');
12303
12304        //모달 리뷰 차단하였을 경우 이미지 변경
12305        $('._block_no_img._block_' + review_member_code)
12306            .css('display', 'block');
12307    };
12308
12309    var addBlock = function (login_member_code, review_code, type) {
12310        $.ajax({
12311            type: 'POST',
12312            data: {
12313                'login_member_code': login_member_code,
12314                'review_code': review_code,
12315                'type': type,
12316            },
12317            url: ('/shop/add_review_block.cm'),
12318            dataType: 'json',
12319            success: function (res) {
12320                if (res.msg === 'SUCCESS') {
12321                    switchToBlockCancelButton(login_member_code, review_code, res.review_member_code, type);
12322                    switchToBlockCancelBody(res.review_member_code);
12323
12324                    $('._block_review_command_' + res.review_member_code).css("display", "none");
12325                    // 댓글은 위에서 화면을 갱신한다. 목록 조회에는 후기 원문 코드만 보낸다.
12326                    if (type === 'post') {
12327                        SHOP_REVIEW_COMMENT.getReviewCommentHtml(review_code, 'accordion', $(this));
12328                    }
12329                } else {
12330                    alert(res.msg);
12331                }
12332            }
12333        });
12334    };
12335
12336    var deleteBlockPost = function (login_member_code, review_code, block_member_code) {
12337        if (confirm(getLocalizeString('설명_게시글차단해제안내', '', '게시글을 차단 해제하시겠습니까? 작성자의 다른 글과 댓글을 다시 확인할 수 있습니다.'))) {
12338            deleteBlock(login_member_code, review_code, block_member_code, 'post');
12339        }
12340    };
12341
12342    var deleteBlockComment = function (login_member_code, review_code, block_member_code) {
12343        var block_review_list_code = [];
12344
12345        if (confirm(getLocalizeString('설명_님을차단해제안내', '', '댓글을 차단 해제하시겠습니까? 작성자의 다른 댓글을 다시 확인할 수 있습니다.'))) {
12346            deleteBlock(login_member_code, review_code, block_member_code, 'comment');
12347        }
12348    };
12349
12350    var deleteBlock = function (login_member_code, review_code, block_member_code, type) {
12351        var block_review_list_code = [];
12352
12353        block_review_list_code = get_block_review_list_code(block_member_code);
12354
12355        $.ajax({
12356            type: 'POST',
12357            data: {
12358                'login_member_code': login_member_code,
12359                'review_code': review_code,
12360                'block_review_list_code': block_review_list_code,
12361                'type': type
12362            },
12363            url: ('/shop/delete_review_block.cm'),
12364            dataType: 'json',
12365            success: function (res) {
12366                if (res.msg === 'SUCCESS') {
12367                    switchToBlockButton(login_member_code, review_code, res.review_member_code, t
12367ype);
12368                    switchToBlockBody(res.review_member_code, block_review_list_code, res.block_review_data);
12369
12370                    $('._block_review_command_' + res.review_member_code).css("display", "block");
12371
12372                    if (type === 'post') {
12373                        SHOP_REVIEW_COMMENT.getReviewCommentHtml(review_code, 'accordion', $(this));
12374                    }
12375                } else {
12376                    alert(res.msg);
12377                }
12378            }
12379        });
12380    };
12381
12382    var switchToBlockButton = function (login_member_code, review_code, review_member_code, type) {
12383        $('._review_button._block_' + review_member_code)
12384            .text(getLocalizeString("설명_차단", "", "차단"))
12385            .removeAttr('onclick')
12386            .off('click')
12387            .on('click', function (e) {
12388                e.stopPropagation();
12389                if (type === 'comment') {
12390                    SITE_SHOP_REVIEW.addBlockComment(login_member_code, review_code);
12391                } else {
12392                    SITE_SHOP_REVIEW.addBlockPost(login_member_code, review_code);
12393                }
12394            });
12395    };
12396
12397    var switchToBlockBody = function (review_member_code, block_review_list_code, block_review_data) {
12398        //일반 이미지
12399        $('._review_img._block_' + review_member_code).removeAttr('style');
12400
12401        block_review_list_code.forEach(block_review_code => {
12402            var review_body_text = '';
12403            if (block_review_data && block_review_data[block_review_code] && block_review_data[block_review_code]['body']) {
12404                review_body_text = block_review_data[block_review_code]['body'];
12405            }
12406            var safe_review_body = $('<div>').text(review_body_text).html().replace(/(\n|\r\n)/g, '<br>');
12407            $('._review_body._review_code_' + block_review_code)
12408                .html(safe_review_body)
12409                .attr('block', 'false');
12410        });
12411        //모달 리뷰 이미지
12412        $('._block_no_img._block_' + review_member_code)
12413            .css('display', 'none');
12414    };
12415
12416    var get_block_review_list_code = function (block_member_code) {
12417        //차단 리스트 review code 가져오기
12418        var block_review_list_code = [];
12419
12420        $("._review_body._block_" + block_member_code + "[block='true']").each(function () {
12421            block_review_list_code.push($(this).attr('block-review-code'));
12422        });
12423
12424        return block_review_list_code;
12425    };
12426
12427    return {
12428        init: function (code) {
12429            init(code);
12430        },
12431        deleteReviewImage: function (code) {
12432            deleteReviewImage(code);
12433        },
12434        openAddReview: function (idx, prod_code, prod_order_code) {
12435            return openAddReview(idx, prod_code, prod_order_code);
12436        },
12437        convertReviewImage: function (image_list) {
12438            return convertReviewImage(image_list);
12439        },
12440        initMobileReview: function () {
12441            initMobileReview();
12442        },
12443        submit: function () {
12444            submit();
12445        },
12446        reviewCompleted: function (received_point, check_received_point) {
12447            reviewCompleted(received_point, check_received_point);
12448        },
12449        reviewCancel: function () {
12450            reviewCancel();
12451        },
12452        changeRating: function (t, n) {
12453            changeRating(t, n);
12454        },
12455        FormShow: function (t) {
12456            reviewFormShow(t);
12457        },
12458        Delete: function (t, c, r_p, only_photo, buyer_permission, secret_pass) {
12459            reviewDelete(t, c, r_p, only_photo, buyer_permission, secret_pass);
12460        },
12461        EditShow: function (t) {
12462            reviewEditShow(t);
12463        },
12464        EditReviewShow: function (t, c, idx) {
12465            EditReviewShow(t, c, idx);
12466        },
12467        DeleteShow: function (t, c, only_photo) {
12468            reviewDeleteShow(t, c, only_photo);
12469        },
12470        Hide: function (t, c, r_p) {
12471            reviewHide(t, c, r_p);
12472        },
12473        EditHide: function (t) {
12474            reviewEditHide(t);
12475        },
12476        imageUploadInit: function (n) {
12477            imageUploadInit(n);
12478        },
12479        createHtml: function (prod_idx, review_page, qna_page, paging_on, only_photo) {
12480            createHtml(prod_idx, review_page, qna_page, paging_on, only_photo);
12481        },
12482        checkReviewData: function () {
12483            checkReviewData();
12484        },
12485        addBlockPost: function (login_member_code, review_code) {
12486            addBlockPost(login_member_code, review_code);
12487        },
12488        addBlockComment: function (login_member_code, review_code) {
12489            addBlockComment(login_member_code, review_code);
12490        },
12491        deleteBlockPost: function (login_member_code, review_code, block_member_code) {
12492            deleteBlockPost(login_member_code, review_code, block_member_code);
12493        },
12494        deleteBlockComment: function (login_member_code, review_code, block_member_code) {
12495            deleteBlockComment(login_member_code, review_code, block_member_code);
12496        },
12497		C3_openAddReview : function(idx,prod_code,prod_order_code){
12498			return openAddReview(idx,prod_code,prod_order_code, true);
12499		},
12500    };
12501}();
12502
12503var SHOP_REVIEW_COMMENT = function () {
12504    var $review_comment_wrap;
12505    var $form;
12506    var $secret;
12507    var review_code;
12508    var device;
12509    var load_type; // 아코디언인지, 모달인지 체크
12510
12511    var init = function (code, type, device_type) {
12512        device = device_type;
12513        load_type = type;
12514        review_code = code;
12515        $form = $('.categorize-mobile #' + device + "_" + load_type + '_review_form_' + code + ', .categorize.review-box #' + device + "_" + load_type + '_review_form_' + code);
12516        $secret = $('._secret_btn');
12517        $secret.on('click', function () {
12518            if ($secret.hasClass('active')) {
12519                $secret.removeClass('active');
12520                $('#use_secret_review').val('N');
12521            } else {
12522                $secret.addClass('active');
12523                $('#use_secret_review').val('Y');
12524            }
12525        });
12526
12527        $form.each(function(){
12528            const $this = $(this);
12529            $this.find('#review_image_upload_btn_' + code).setUploadImage({
12530                url : '/shop/upload_image.cm',
12531                dropZone : 'icon_img_upload_wrap',
12532                singleFileUploads : true,
12533                formData : {temp : 'Y'}
12534            }, function(res, data){
12535                $this.find("#review_comment_image_box_" + code).show();
12536                $.each(data, function(e, tmp){
12537                    if(tmp.error == "" || tmp.error == null){
12538                        var url = CDN_UPLOAD_URL + tmp.url;
12539                        var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.url + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12540                        $this.find("#review_comment_image_box_" + code).append(html);
12541                    }else{
12542                        alert(tmp.error);
12543                    }
12544                });
12545            })
12546        });
12547    };
12548
12549    var imageUploadInit = function (idx) {
12550
12551        const $wrapper = $('.categorize-mobile, .categorize.review-box');
12552
12553        $wrapper.each(function(){
12554           const $this = $(this);
12555
12556            $this.find("#sub_review_image_box_" + idx).hide();
12557
12558            $this.find('#sub_review_image_upload_btn_' + idx).setUploadImage({
12559                url: '/shop/upload_image.cm',
12560                dropZone: 'icon_img_upload_wrap',
12561                singleFileUploads: true,
12562                formData: { temp: 'Y' }
12563            }, function (res, data) {
12564                $(".categorize #sub_review_image_box_" + idx).show();
12565                $(".categorize-mobile #sub_review_image_box_" + idx).show();
12566                $.each(data, function (e, tmp) {
12567                    if (tmp.error == "" || tmp.error == null) {
12568                        var url = CDN_UPLOAD_URL + tmp.url;
12569                        var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.url + '"><img src="' + url + '"><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12570                        $this.find("#sub_review_image_box_" + idx).append(html);
12571                    } else {
12572                        alert(tmp.error);
12573                    }
12574                });
12575            });
12576
12577            $this.find('#editor_review_image_upload_btn_' + idx).setUploadImage({
12578                url: '/shop/upload_image.cm',
12579                dropZone: 'icon_img_upload_wrap',
12580                singleFileUploads: true,
12581                formData: { temp: 'Y' }
12582            }, function (res, data) {
12583                $(".categorize #editor_review_image_box_" + idx).show();
12584                $(".categorize-mobile #editor_review_image_box_" + idx).show();
12585                $.each(data, function (e, tmp) {
12586                    if (tmp.error == "" || tmp.error == null) {
12587                        var url = CDN_UPLOAD_URL + tmp.url;
12588                        var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.url + '"><img src="' + url + '"><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12589                        $this.find("#editor_review_image_box_" + idx).append(html);
12590                    } else {
12591                        alert(tmp.error);
12592                    }
12593                });
12594            });
12595
12596        });
12597    };
12598
12599    var getReviewCommentHtml = function (code, type, object) {
12600        if (type == 'accordion') {
12601            $review_comment_wrap = $('.review_comment_wrap_' + code);
12602            if (!object.hasClass('active')) {
12603                $.ajax({
12604                    type: 'POST',
12605                    data: { 'code': code, 'type': type },
12606                    url: ('/shop/get_review_comment_list.cm'),
12607                    dataType: 'json',
12608                    async: false,
12609                    cache: false,
12610                    success: function (result) {
12611                        if (result.msg === 'SUCCESS') {
12612                            $review_comment_wrap.html(result.html);
12613                            $review_comment_wrap.show();
12614                        } else {
12615                            alert(result.msg);
12616                        }
12617                    }
12618                });
12619            } else {
12620                $review_comment_wrap.hide();
12621            }
12622        } else {
12623            $review_comment_wrap = $('#review_comment_section_' + code);
12624            $.ajax({
12625                type: 'POST',
12626                data: { 'code': code, 'type': type },
12627                url: ('/shop/get_review_comment_list.cm'),
12628                dataType: 'json',
12629                async: false,
12630                cache: false,
12631                success: function (result) {
12632                    if (result.msg === 'SUCCESS') {
12633                        $review_comment_wrap.html(result.html);
12634                    } else {
12635                        alert(result.msg);
12636                    }
12637                }
12638            });
12639        }
12640    };
12641    var submit = function (t, type, i) {
12642		// mobile 및 pc 에서 같은 id 값 공유 해당 부분 수정 필요...
12643		const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
12644		const isModal = document.querySelector('#cocoaModal').classList.contains('review');
12645        let data = {};
12646		switch (type) {
12647            case 'main': // pc 리뷰 댓글
12648	            if (isModal) {
12649					data = $('#_modal_review_form_' + review_code).serializeObject();
12650					break;
12651	            }
12652	            if (isMobile_width) {
12653		            data = $('.categorize-mobile #' + $form.attr('id')).serializeObject();
12654	            } else {
12655		            data = $('.categorize.review-box #' + $form.attr('id')).serializeObject();
12656	            }
12657                break;
12658            case 'sub_form': //pc 리뷰 대댓글
12659	            if (isModal) {
12660		            data = $('#sub_review_form_' + i).serializeObject();
12661		            break;
12662	            }
12663	            if (isMobile_width) {
12664					data = $('.categorize-mobile #sub_review_form_' + i).serializeObject();
12665	            } else {
12666					data = $('.categorize.review-box #sub_review_form_' + i).serializeObject();
12667	            }
12668                break;
12669            case 'editor': // pc 리뷰 댓글 수정
12670	            if (isModal) {
12671					data = $('#sub_review_editor_form_' + i).serializeObject();
12672					break;
12673	            }
12674                if (isMobile_width) {
12675		            data = $('.categorize-mobile #sub_review_editor_form_' + i).serializeObject();
12676	            } else {
12677		            data = $('.categorize.review-box #sub_review_editor_form_' + i).serializeObject();
12678	            }
12679                break;
12680            case 'mobile': // Mobile 리뷰 댓글
12681                data = $('#mobile_review_form').serializeObject();
12682                break;
12683            case 'mobile_sub_form': // Mobile 리뷰 대댓글
12684                data = $('#mobile_sub_review_form_' + i).serializeObject();
12685                break;
12686            case 'mobile_editor': // Mobile 리뷰 댓글 수정
12687                data = $('#mobile_sub_review_editor_form_' + i).serializeObject();
12688                break;
12689        }
12690        if (!t.hasClass("btn-writing")) {
12691            t.addClass("btn-writing");
12692        }
12693        $.ajax({
12694            type: 'POST',
12695            data: { data: data, review_code: review_code },
12696            url: ('/shop/add_review_comment.cm'),
12697            dataType: 'json',
12698            async: false,
12699            cache: false,
12700            success: function (result) {
12701                if (t.hasClass("btn-writing")) {
12702                    t.removeClass("btn-writing");
12703                }
12704                if (result.msg == 'SUCCESS') {
12705                    if (load_type == 'modal') {
12706                        $('#comment_count').text(" " + result.comment_count);
12707                        getReviewCommentHtml(review_code, 'modal', t);
12708                    } else {
12709                        $('.categorize #comment_count_' + review_code).text(" " + result.comment_count);
12710                        $('.categorize #comment_count_inner_' + review_code).text(" " + result.comment_count);
12711                        $('.categorize-mobile #comment_count_' + review_code).text(result.comment_count);
12712                        $('.categorize-mobile #comment_count_inner_' + review_code).text(" " + result.comment_count);
12713                        getReviewCommentHtml(review_code, 'accordion', t);
12714                    }
12715                    $("div[id^='sub_review_image_box_']").hide();
12716                } else
12717                    alert(result.msg);
12718            }
12719        });
12720    };
12721    var EditHide = function (idx) {
12722        var editor_form = $("._sub_form_editor_" + idx);
12723        editor_form.hide();
12724        $('.tools').show();
12725        editor_form.siblings('._comment_body').show();
12726        editor_form.closest('.comment_area').siblings().show();
12727        editor_form.closest('.comment_area').css('padding-top', '');
12728        editor_form.css('margin-top', '');
12729    };
12730
12731    var EditShow = function (idx) {
12732        var editor_form = $("._sub_form_editor_" + idx);
12733        editor_form.siblings().hide();
12734        editor_form.closest('.comment_area').siblings().hide();
12735        editor_form.closest('.comment_area').css('padding-top', 0);
12736        editor_form.css('margin-top', 0);
12737        editor_form.data('show', 'Y');
12738        editor_form.show();
12739        autosize.update(editor_form.find('textarea'));
12740    };
12741
12742    var reviewFormHide = function () {
12743        $("._sub_review_form").hide();
12744    };
12745
12746    var FormShow = function (idx) {
12747        var sub_form = $("._sub_form_" + idx);
12748
12749        sub_form.data('show', 'Y');
12750        sub_form.show();
12751        var comment_add_body = sub_form.find('._comment_add_body_' + idx);
12752
12753        $('body').off('mouseup.sub_comment')
12754            .on('mouseup.sub_comment', function (e) {
12755                var $c_target = $(e.target);
12756                var $s_form = $c_target.closest('._sub_form_' + idx + ', ._show_sub_form_btn_' + idx);
12757                if ($s_form.length == 0) {
12758
12759                    var text = comment_add_body.val();
12760                    sub_form.data('show', 'N');
12761                    if (text == '') {
12762                        $('body').off('mouseup.sub_comment');
12763                        reviewFormHide();
12764                    }
12765                }
12766            });
12767    };
12768
12769    var reviewCommentDelete = function (code) {
12770        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
12771            $.ajax({
12772                type: 'POST',
12773                data: { code: code, review_code: review_code },
12774                url: ('/shop/delete_review_comment.cm'),
12775                async: false,
12776                dataType: 'json',
12777                success: function (result) {
12778                    if (result.msg == 'SUCCESS') {
12779                        if (load_type == 'modal') {
12780                            $('#comment_count').text(result.comment_count);
12781                            getReviewCommentHtml(review_code, 'modal', $(this));
12782                        } else {
12783                            $('.categorize #comment_count_' + review_code).text(result.comment_count);
12784                            $('.categorize #comment_count_inner_' + review_code).text(result.comment_count);
12785                            $('.categorize-mobile #comment_count_' + review_code).text(result.comment_count);
12786                            $('.categorize-mobile #comment_count_inner_' + review_code).text(result.comment_count);
12787                            getReviewCommentHtml(review_code, 'accordion', $(this));
12788                        }
12789
12790
12791                    } else
12792                        alert(result.msg);
12793                }
12794            });
12795        }
12796    };
12797    return {
12798        init: function (code, type, device_type) {
12799            init(code, type, device_type);
12800        },
12801        imageUploadInit: function (idx) {
12802            imageUploadInit(idx);
12803        },
12804        getReviewCommentHtml: function (code, type, object) {
12805            getReviewCommentHtml(code, type, object);
12806        },
12807        submit: function (t, type, i) {
12808            submit(t, type, i);
12809        },
12810        Delete: function (code) {
12811            reviewCommentDelete(code);
12812        },
12813        EditShow: function (idx) {
12814            EditShow(idx);
12815        },
12816        EditHide: function (idx) {
12817            EditHide(idx);
12818        },
12819        FormShow: function (idx) {
12820            FormShow(idx);
12821        }
12822    };
12823}();
12824
12825var SITE_QNA_COMMENT = function () {
12826    var $form;
12827    var $secret;
12828    var $qna_comment_section;
12829    var qna_code;
12830
12831    var init = function (code) {
12832        qna_code = code;
12833        var $comment_area = $('.comment_textarea');
12834        $secret = $('._secret_btn');
12835        $secret.on('click', function () {
12836            if ($secret.hasClass('active')) {
12837                $secret.removeClass('active');
12838                $('#use_secret_qna').val('N');
12839            } else {
12840                $secret.addClass('active');
12841                $('#use_secret_qna').val('Y');
12842            }
12843        });
12844
12845        $form = $('#qna_form');
12846        $form.find('#qna_image_upload_btn').setUploadImage({
12847            url: '/shop/upload_image.cm',
12848            dropZone: 'icon_img_upload_wrap',
12849            singleFileUploads: true,
12850            formData: { temp: 'Y' }
12851        }, function (res, data) {
12852            $("#qna_image_box").show();
12853            $.each(data, function (e, tmp) {
12854                if (tmp.error == "" || tmp.error == null) {
12855                    var url = CDN_UPLOAD_URL + tmp.url;
12856                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12857                    $("#qna_image_box").append(html);
12858                } else {
12859                    alert(tmp.error);
12860                }
12861            });
12862        });
12863
12864    };
12865
12866    var QnaCommentDelete = function (code, is_visible) {
12867        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
12868            $.ajax({
12869                type: 'POST',
12870                data: { code: code, is_visible: is_visible },
12871                url: ('/shop/delete_qna_comment.cm'),
12872                async: false,
12873                dataType: 'json',
12874                success: function (result) {
12875                    if (result.msg == 'SUCCESS') {
12876                        getQnaCommentHtml(qna_code);
12877                    } else
12878                        alert(result.msg);
12879                }
12880            });
12881        }
12882    };
12883
12884    var imageUploadInit = function (idx) {
12885        $form = $('#qna_form');
12886        $form.find('#qna_image_upload_btn').setUploadImage({
12887            url: '/shop/upload_image.cm',
12888            dropZone: 'icon_img_upload_wrap',
12889            singleFileUploads: true,
12890            formData: { temp: 'Y' }
12891        }, function (res, data) {
12892            $("#qna_image_box").show();
12893            $.each(data, function (e, tmp) {
12894                if (tmp.error == "" || tmp.error == null) {
12895                    var url = CDN_UPLOAD_URL + tmp.url;
12896                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12897                    $("#qna_image_box").append(html);
12898                } else {
12899                    alert(tmp.error);
12900                }
12901            });
12902        });
12903
12904        $("#sub_qna_image_box_" + idx).hide();
12905
12906        $('#sub_qna_image_upload_btn_' + idx).setUploadImage({
12907            url: '/shop/upload_image.cm',
12908            dropZone: 'icon_img_upload_wrap',
12909            singleFileUploads: true,
12910            formData: { temp: 'Y' }
12911        }, function (res, data) {
12912            $("#sub_qna_image_box_" + idx).show();
12913            $.each(data, function (e, tmp) {
12914                if (tmp.error == "" || tmp.error == null) {
12915                    var url = CDN_UPLOAD_URL + tmp.url;
12916                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12917                    $("#sub_qna_image_box_" + idx).append(html);
12918                } else {
12919                    alert(tmp.error);
12920                }
12921            });
12922        });
12923
12924        $('#editor_qna_image_upload_btn_' + idx).setUploadImage({
12925            url: '/shop/upload_image.cm',
12926            dropZone: 'icon_img_upload_wrap',
12927            singleFileUploads: true,
12928            formData: { temp: 'Y' }
12929        }, function (res, data) {
12930            $("#editor_qna_image_box_" + idx).show();
12931            $.each(data, function (e, tmp) {
12932                if (tmp.error == "" || tmp.error == null) {
12933                    var url = CDN_UPLOAD_URL + tmp.url;
12934                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
12935                    $("#editor_qna_image_box_" + idx).append(html);
12936                } else {
12937                    alert(tmp.error);
12938                }
12939            });
12940        });
12941    };
12942
12943
12944    var getQnaCommentHtml = function (qna_code) {
12945        $qna_comment_section = $('#qna_comment_section');
12946        $.ajax({
12947            type: 'POST',
12948            data: { qna_code: qna_code },
12949            url: ('/ajax/get_qna_comment_list.cm'),
12950            dataType: 'html',
12951            cache: false,
12952            success: function (result) {
12953                $qna_comment_section.html(result);
12954            }
12955        });
12956    };
12957    var submit = function (t, type, i) {
12958        switch (type) {
12959            case 'main': // 1:1 문의 페이지에서 바로 작성하는 폼
12960                var data = $form.serializeObject();
12961                break;
12962            case 'sub_form': // 등록되어 있는 qna에 답변을 다는 폼
12963                var data = $('#sub_qna_form_' + i).serializeObject();
12964                break;
12965            case 'editor': // 등록되어 있는 qna를 수정하는 폼
12966                var data = $('#sub_qna_editor_form_' + i).serializeObject();
12967                break;
12968        }
12969        if (!t.hasClass("btn-writing")) {
12970            t.addClass("btn-writing");
12971        }
12972        $.ajax({
12973            type: 'POST',
12974            data: { data: data, type: type },
12975            url: ('/shop/add_qna_comment.cm'),
12976            dataType: 'json',
12977            cache: false,
12978            success: function (result) {
12979                if (t.hasClass("btn-writing")) {
12980                    t.removeClass("btn-writing");
12981                }
12982                if (result.msg == 'SUCCESS') {
12983                    getQnaCommentHtml(qna_code);
12984                    $("div[id^='sub_qna_image_box_']").hide();
12985                } else
12986                    alert(result.msg);
12987            }
12988        });
12989    };
12990    var EditShow = function (idx) {
12991        var editor_form = $("._sub_form_editor_" + idx);
12992        editor_form.siblings().hide();
12993        editor_form.closest('.comment_area').siblings().hide();
12994        editor_form.closest('.comment_area').css('padding-top', 0);
12995        editor_form.css('margin-top', 0);
12996        editor_form.data('show', 'Y');
12997        editor_form.show();
12998        autosize.update(editor_form.find('textarea'));
12999
13000    };
13001
13002    var EditHide = function (idx) {
13003        var editor_form = $("._sub_form_editor_" + idx);
13004        editor_form.hide();
13005        $('.tools').show();
13006        editor_form.siblings('._comment_body').show();
13007        editor_form.closest('.comment_area').siblings().show();
13008        editor_form.closest('.comment_area').css('padding-top', '');
13009        editor_form.css('margin-top', '');
13010    };
13011
13012    var qnaFormHide = function () {
13013        $("._sub_qna_form").hide();
13014    };
13015
13016    var FormShow = function (idx) {
13017        var sub_form = $("._sub_form_" + idx);
13018
13019        sub_form.data('show', 'Y');
13020        sub_form.show();
13021        var comment_add_body = sub_form.find('._comment_add_body_' + idx);
13022
13023        $('body').off('mouseup.sub_comment')
13024            .on('mouseup.sub_comment', function (e) {
13025                var $c_target = $(e.target);
13026                var $s_form = $c_target.closest('._sub_form_' + idx + ', ._show_sub_form_btn_' + idx);
13027                if ($s_form.length == 0) {
13028
13029                    var text = comment_add_body.val();
13030                    sub_form.data('show', 'N');
13031                    if (text == '') {
13032                        $('body').off('mouseup.sub_comment');
13033                        qnaFormHide();
13034                    }
13035                }
13036            });
13037    };
13038    return {
13039        init: function (code) {
13040            init(code);
13041        },
13042        imageUploadInit: function (idx) {
13043            imageUploadInit(idx);
13044        },
13045        getQnaCommentHtml: function (qna_code) {
13046            getQnaCommentHtml(qna_code);
13047        },
13048        submit: function (t, type, i) {
13049            submit(t, type, i);
13050        },
13051        Delete: function (code, is_visible) {
13052            QnaCommentDelete(code, is_visible);
13053        },
13054        EditShow: function (idx) {
13055            EditShow(idx);
13056        },
13057        EditHide: function (idx) {
13058            EditHide(idx);
13059        },
13060        FormShow: function (idx) {
13061            FormShow(idx);
13062        }
13063    };
13064}();
13065
13066var SITE_SHOP_QNA = function () {
13067    var $qna_wrap;
13068    var $mobile_qna_wrap;
13069    var $form;
13070    var $mobile_form;
13071    var $comment_body;
13072    var $qna_image_box;
13073    var $comment_area;
13074    var $secret;
13075    var $m_secret;
13076    var qna_body;
13077    var $qna_container;
13078    var $qna_form;
13079    var body_input;
13080    var $show_secret_password;
13081    var isIOS, isSafari, $fr_m_custom, $write_header, m_sticky_container_trigger_top, $toolbarContainer;
13082
13083    var init = function (code, qna_secret_type) {
13084        $qna_wrap = $('._qna_wrap');
13085        $qna_form = $('#qna_form');
13086        qna_body = $('#qna_body');
13087        $qna_container = $('#qna_container');
13088        $secret = $('._secret_btn');
13089        $qna_form.find("#qna_image_box").hide();
13090        body_input = $('#body_input');
13091        if (qna_secret_type == 'secret') {
13092            $secret.addClass('active');
13093            $('._secret').val('Y');
13094        } else {
13095            $secret.on('click', function () {
13096                if ($secret.hasClass('active')) {
13097                    $secret.removeClass('active');
13098                    $('._secret').val('N');
13099                } else {
13100                    $secret.addClass('active');
13101                    $('._secret').val('Y');
13102                }
13103            });
13104        }
13105        if ($('._secret').val() != '') {//수정일 경우 비밀글 체크
13106            if ($('._secret').val() == 'Y') {
13107                $secret.addClass('active');
13108                $('._secret').val('Y');
13109            } else {
13110                $secret.removeClass('active');
13111                $('._secret').val('N');
13112            }
13113        }
13114
13115
13116        if (IE_VERSION < 10) {
13117            CKEDITOR.replace('qna_body', {
13118                filebrowserImageUploadUrl: '/ajax/post_image_upload.cm?board_code=' + code
13119            });
13120        } else {
13121            if (android_version() == 4) {
13122                qna_body.addClass('legacy_webview');
13123            }
13124            var image_insert_key2 = 'image_insert_key2';
13125            setFroala('#qna_body', {
13126                code: '',
13127                placeholderText: getLocalizeString('설명_문의작성시개인정보입력주의', '', '개인정보(휴대폰번호, 계좌번호 등)는 입력하지 않도록 주의해주세요.'),
13128                // 파일 첨부, 비디오, 테이블 제외
13129                toolbarButtons: {
13130                    'moreText': {
13131                        'buttons': ['bold', 'italic', 'underline', 'fontSize', 'textColor', 'strikeThrough', 'backgroundColor', 'clearFormatting', '|', 'align', 'formatOL', 'formatUL', '|', image_insert_key2, 'insertLink', '|', 'undo', 'redo', 'html', 'emoticons'],
13132                        'buttonsVisible': 50
13133                    }
13134                },
13135                toolbarButtonsMobile: {
13136                    'moreText': {
13137                        'buttons': [image_insert_key2, 'insertLink', '|', "fontSize", "bold", "italic", "underline", "strikeThrough", "align", "textColor", "clearFormatting", '|', "formatOL", "formatUL"],
13138                        'buttonsVisible': 50
13139                    }
13140                },
13141                image_upload_url: "/ajax/post_image_upload.cm",
13142                image_insert_key: image_insert_key2,
13143                image_align: 'center',
13144                image_display: 'block',
13145                toolbarStickyOffset: 38,
13146                heightMin: 200,
13147                heightMax: 600,
13148                mobile_custom: true,
13149                linkAlwaysBlank: true
13150            });
13151        }
13152
13153        isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
13154        isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
13155        $fr_m_custom = $qna_container.find('._fr-m-custom');
13156        $write_header = $qna_container.find('._write_header');
13157        m_sticky_container_trigger_top = $fr_m_custom.offset().top - $fr_m_custom.height();
13158        $toolbarContainer = $fr_m_custom.find('#toolbarContainer');
13159        if (isIOS && isSafari) {
13160            $write_header.css('position', 'absolute');
13161        }
13162        var timeoutTime = isIOS && isSafari ? 100 : 10;
13163        var resize_time;
13164        resizeStickyContainer();
13165        $(window).off('scroll.mobile_write resize.mobile_write').on('scroll.mobile_write resize.mobile_write', function () {
13166            var s_top = $(this).scrollTop();
13167            if (isIOS && isSafari) {
13168                $write_header.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 0 });
13169                if (s_top > m_sticky_container_trigger_top) {
13170                    $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 0 });
13171                }
13172            }
13173            if (resize_time) {
13174                clearTimeout(resize_time);
13175            }
13176            resize_time = setTimeout(function () {
13177                resizeStickyContainer();
13178            }, timeoutTime);
13179        });
13180        autosize($('.textarea_block textarea'));
13181    };
13182
13183    function resizeStickyContainer() {
13184        var s_top = $(this).scrollTop();
13185        if (isIOS && isSafari) {
13186            $write_header.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
13187            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
13188            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
13189            if (s_top > m_sticky_container_trigger_top) {
13190                $toolbarContainer.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
13191                qna_body.find('.fr-view').css('padding-top', '90px');
13192            } else {
13193                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
13194                qna_body.find('.fr-view').css('padding-top', '');
13195            }
13196        } else {
13197            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
13198        }
13199        $toolbarContainer.find('.fr-toolbar').css('width', qna_body.find('.fr-view').width());
13200        if ($(window).width() >= 768) {
13201            if ($qna_container.hasClass('bg_on'))
13202                $qna_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 487);
13203            else
13204                $qna_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 180);
13205        }
13206    }
13207
13208    var initMobileQna = function () {
13209        $mobile_qna_wrap = $('#prod_detail_content_mobile');
13210        $mobile_form = $mobile_qna_wrap.find('#mobile_qna_form');
13211        $m_secret = $mobile_form.find('._secret_btn');
13212        $mobile_form.find("#mobile_qna_image_box").hide();
13213
13214        $m_secret.on('click', function () {
13215            if ($m_secret.hasClass('active')) {
13216                $m_secret.removeClass('active');
13217                $mobile_form.find('._secret').val('N');
13218            } else {
13219                $m_secret.addClass('active');
13220                $mobile_form.find('._secret').val('Y');
13221            }
13222        });
13223
13224        $mobile_form.find('#mobile_qna_image_upload_btn').setUploadImage({
13225            url: '/shop/upload_image.cm',
13226            dropZone: 'icon_img_upload_wrap',
13227            singleFileUploads: true,
13228            formData: { temp: 'Y' }
13229        }, function (res, data) {
13230            $("#mobile_qna_image_box").show();
13231            $.each(data, function (e, tmp) {
13232                if (tmp.error == "" || tmp.error == null) {
13233                    var url = CDN_UPLOAD_URL + tmp.url;
13234                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
13235                    $("#mobile_qna_image_box").append(html);
13236                } else {
13237                    alert(tmp.error);
13238                }
13239            });
13240        });
13241        autosize($('.textarea_block textarea'));
13242    };
13243
13244    var qnaFormShow = function (t) {
13245        var sub_form = $("._sub_form_" + t);
13246
13247        sub_form.data('show', 'Y');
13248        sub_form.show();
13249        var comment_add_body = sub_form.find('._comment_add_body_' + t);
13250
13251        $('body').off('mouseup.sub_comment')
13252            .on('mouseup.sub_comment', function (e) {
13253                var $c_target = $(e.target);
13254                var $s_form = $c_target.closest('._sub_form_' + t + ', ._show_sub_form_btn_' + t);
13255                if ($s_form.length == 0) {
13256
13257                    var text = comment_add_body.val();
13258                    sub_form.data('show', 'N');
13259                    if (text == '') {
13260                        $('body').off('mouseup.sub_comment');
13261                        qnaFormHide();
13262                    }
13263                }
13264            });
13265    };
13266
13267    var EditQnaShow = function (t, c, idx) {
13268        $show_secret_password = $('#show_secret_password');
13269        var $show_link = $(event.target);
13270        if ($show_secret_password.length == 0) {
13271            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13272            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" tabindex="0" style="position:absolute;left:' + left + 'px;top:80px;z-index:99999;"><p>' + LOCALIZE.설명_작성시등록하신비밀번호를입력해주세요() + '</p><div class="input_area"><input type="password" placeholder="' + LOCALIZE.설명_비밀번호() + '"><button class="btn btn-primary _confirm">' + LOCALIZE.버튼_확인닫기() + '</button></div></div>').hide();
13273            $show_link.after($show_secret_password);
13274        }
13275        $show_secret_password.find('input').val('');
13276        $show_secret_password.show();
13277        $show_secret_password.off('click', '._confirm')
13278            .on('click', '._confirm', function () {
13279                var secret_pass = $show_secret_password.find('input').val();
13280                CheckSecret(t, secret_pass, function () {
13281                    window.location.href = "?prod_code=" + c + "&qmode=write&back_url=&idx=" + idx;
13282                });
13283                $show_secret_password.hide();
13284            });
13285        $('body').off('mousedown.show_secret')
13286            .on('mousedown.show_secret', function (e) {
13287                var $tmp = $(e.target).closest('#show_secret_password');
13288                if ($tmp.length == 0) {
13289                    $show_secret_password.hide();
13290                    $('body').off('click.show_secret');
13291                }
13292            });
13293    };
13294
13295    var ViewQnaShow = function (t, c, idx, qna_page) {
13296        $show_secret_password = $('#show_secret_password');
13297        var $show_link = $(event.target);
13298        if ($show_secret_password.length == 0) {
13299            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13300            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" tabindex="0" style="position:absolute;
13300left:' + left + 'px;top:80px;z-index:99999;"><p>' + LOCALIZE.설명_작성시등록하신비밀번호를입력해주세요() + '</p><div class="input_area"><input type="password" placeholder="' + LOCALIZE.설명_비밀번호() + '"><button class="btn btn-primary _confirm">' + LOCALIZE.버튼_확인닫기() + '</button></div></div>').hide();
13301            $show_link.after($show_secret_password);
13302        }
13303        $show_secret_password.find('input').val('');
13304        $show_secret_password.show();
13305        $show_secret_password.off('click', '._confirm')
13306            .on('click', '._confirm', function () {
13307                var secret_pass = $show_secret_password.find('input').val();
13308                CheckSecret(t, secret_pass, function () {
13309                    SITE_SHOP_DETAIL.viewQnaDetail(idx, qna_page);
13310                    $show_secret_password.hide();
13311                });
13312                $show_secret_password.hide();
13313            });
13314        $('body').off('mousedown.show_secret')
13315            .on('mousedown.show_secret', function (e) {
13316                var $tmp = $(e.target).closest('#show_secret_password');
13317                if ($tmp.length == 0) {
13318                    $show_secret_password.hide();
13319                    $('body').off('click.show_secret');
13320                }
13321            });
13322    };
13323
13324    var CheckSecret = function (code, secret_pass, callback) {
13325        $.ajax({
13326            type: 'post',
13327            data: { code: code, secret_pass: secret_pass, type: 'qna' },
13328            url: '/ajax/check_review_pass.cm',
13329            dataType: 'json',
13330            success: function (result) {
13331                if (result.msg == 'SUCCESS') {
13332                    if (typeof callback == 'function')
13333                        callback();
13334                } else {
13335                    alert(result.msg);
13336                }
13337            }
13338        });
13339    };
13340
13341    var qnaEditShow = function (t) {
13342        var editor_form = $("._sub_form_editor_" + t);
13343        editor_form.siblings().hide();
13344
13345        editor_form.data('show', 'Y');
13346        editor_form.show();
13347        autosize.update(editor_form.find('textarea'));
13348
13349    };
13350
13351    var qnaEditHide = function (t) {
13352        var editor_form = $("._sub_form_editor_" + t);
13353        editor_form.hide();
13354    };
13355
13356    var qnaFormHide = function () {
13357        $("._sub_qna_form").hide();
13358    };
13359
13360    var qnaDelete = function (code, prod_code, secret_pass, q_p) {
13361        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
13362            $.ajax({
13363                type: 'POST',
13364                data: { code: code, prod_code: prod_code, secret_pass: secret_pass, qna_page: q_p },
13365                url: ('/shop/delete_qna.cm'),
13366                dataType: 'json',
13367                success: function (result) {
13368                    if (result.msg == 'SUCCESS') {
13369                        if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab('qna', 0, q_p);
13370                        else SITE_SHOP_DETAIL.changeContentPCTab('qna', 0, q_p);
13371                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.addCountUserProfileAttr('qnaCount', -1);
13372
13373                        SITE_SHOP_DETAIL.getReviewQnaCount(prod_code);
13374
13375                        $.cocoaDialog.close();
13376                    } else
13377                        alert(result.msg);
13378                }
13379            });
13380        }
13381    };
13382
13383    var qnaModify = function (idx, prod_code, secret_pass, is_book, code) {
13384        $.ajax({
13385            type: 'POST',
13386            data: { idx: idx, prod_code: prod_code, secret_pass: secret_pass, is_book: is_book, code: code },
13387            url: ('/shop/show_secret_qna.cm'),
13388            dataType: 'json',
13389            success: function (result) {
13390                if (result.msg == 'SUCCESS') {
13391                    qnaEditShow(idx);
13392                } else
13393                    alert(result.msg);
13394            }
13395        });
13396    };
13397
13398    var qnaShow = function (idx, prod_code, secret_pass, is_book, code) {
13399        $.ajax({
13400            type: 'POST',
13401            data: { idx: idx, prod_code: prod_code, secret_pass: secret_pass, is_book: is_book, code: code },
13402            url: ('/shop/show_secret_qna.cm'),
13403            dataType: 'json',
13404            success: function (result) {
13405                if (result.msg == 'SUCCESS') {
13406                    $("._comment_body_" + idx).html(result.html);
13407                    $('._comment_body_' + idx).closest('.comment_area').find('#_show_content_btn').hide();		// 보기 버튼 제거
13408                    if (result.isSubComment) {
13409                        for (var i in result.sub_comment) {
13410                            var sub_data = result.sub_comment[i];
13411                            $("._comment_child_" + idx + "_" + sub_data.idx).html(sub_data.html);
13412                        }
13413                    }
13414                } else
13415                    alert(result.msg);
13416            }
13417        });
13418    };
13419
13420    var qnaConfirmShow = function (e, idx, prod_code, type, code) {
13421        $show_secret_password = $('#show_secret_password');
13422        var $show_link = $(event.target);
13423        if ($show_secret_password.length == 0) {
13424            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13425            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" tabindex="0" style="position:absolute;
13425left:' + left + 'px;top:80px;z-index:99999;"><p>' + LOCALIZE.설명_작성시등록하신비밀번호를입력해주세요() + '</p><div class="input_area"><input type="password" placeholder="' + LOCALIZE.설명_비밀번호() + '"><button class="btn btn-primary _confirm">' + LOCALIZE.버튼_확인닫기() + '</button></div></div>').hide();
13426            $show_link.after($show_secret_password);
13427        }
13428
13429        $show_secret_password.find('input').val('');
13430        $show_secret_password.show();
13431        $show_secret_password.off('click', '._confirm')
13432            .on('click', '._confirm', function () {
13433                var secret_pass = $show_secret_password.find('input').val();
13434                $show_secret_password.hide();
13435                switch (type) {
13436                    case 'show':
13437                        qnaShow(idx, prod_code, secret_pass, 'N', code);
13438                        break;
13439                    case 'modify':
13440                        qnaModify(idx, prod_code, secret_pass, 'N', code);
13441                        break;
13442                    case 'delete':
13443                        qnaDelete(code, prod_code, secret_pass);
13444                        break;
13445
13446                }
13447            });
13448        $('body').off('mousedown.show_secret')
13449            .on('mousedown.show_secret', function (e) {
13450                var $tmp = $(e.target).closest('#show_secret_password');
13451                if ($tmp.length == 0) {
13452                    $show_secret_password.hide();
13453                    $('body').off('click.show_secret');
13454                }
13455            });
13456    };
13457
13458    var imageUploadInit = function (n) {
13459        $("#sub_qna_image_box_" + n).hide();
13460
13461        $('#sub_qna_image_upload_btn_' + n).setUploadImage({
13462            url: '/shop/upload_image.cm',
13463            dropZone: 'icon_img_upload_wrap',
13464            singleFileUploads: true,
13465            formData: { temp: 'Y' }
13466        }, function (res, data) {
13467            $("#sub_qna_image_box_" + n).show();
13468            $.each(data, function (e, tmp) {
13469                if (tmp.error == "" || tmp.error == null) {
13470                    var url = CDN_UPLOAD_URL + tmp.url;
13471                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
13472                    $("#sub_qna_image_box_" + n).append(html);
13473                } else {
13474                    alert(tmp.error);
13475                }
13476            });
13477        });
13478
13479        $('#editor_qna_image_upload_btn_' + n).setUploadImage({
13480            url: '/shop/upload_image.cm',
13481            dropZone: 'icon_img_upload_wrap',
13482            singleFileUploads: true,
13483            formData: { temp: 'Y' }
13484        }, function (res, data) {
13485            $("#editor_qna_image_box_" + n).show();
13486            $.each(data, function (e, tmp) {
13487                if (tmp.error == "" || tmp.error == null) {
13488                    var url = CDN_UPLOAD_URL + tmp.url;
13489                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
13490                    $("#editor_qna_image_box_" + n).append(html);
13491                } else {
13492                    alert(tmp.error);
13493                }
13494            });
13495        });
13496    };
13497
13498    var submit = function () {
13499        if (IE_VERSION < 10) {
13500            var body = CKEDITOR.instances.qna_body.getData();
13501            body_input.val(body);
13502            $qna_form.submit();
13503        } else {
13504            if (qna_body.hasClass('fr-code-view'))
13505                FroalaEditor('#qna_body').codeView.toggle();
13506            var body = FroalaEditor('#qna_body').html.get(true);
13507            body_input.val(body);
13508            $qna_form.submit();
13509        }
13510    };
13511
13512    var qnaCancel = function () {
13513        if (isIOS && isSafari) {
13514            var s_top = $(this).scrollTop();
13515            $write_header.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': 0 });
13516            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
13517            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
13518            if (s_top > m_sticky_container_trigger_top) {
13519                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': $write_header.height() + 'px' });
13520            } else {
13521                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
13522            }
13523        }
13524        history.go(-1);
13525    };
13526
13527    var createHtml = function (prod_idx, review_page, qna_page, paging_on) {
13528        const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
13529        const review_url = isMobile_width ? "/shop/prod_qna_mobile_html.cm" : "/shop/prod_qna_pc_html.cm";
13530
13531        $qna_wrap = $('._qna_wrap');
13532        if ($qna_wrap.length) {
13533            var is_cache = false;
13534            var callback = function (result) {
13535                $qna_wrap.html(result);
13536                if (!is_cache) IMWEB_SESSIONSTORAGE.set(`${isMobile_width ? "PROD_QNA_MOBILE_" : "PROD_QNA_PC_"}` + prod_idx + "_" + review_page + '_' + qna_page, result.replace(/\t+/g, '').trim(), 60);
13537            };
13538
13539            var html = IMWEB_SESSIONSTORAGE.get(`${isMobile_width ? "PROD_QNA_MOBILE_" : "PROD_QNA_PC_"}` + prod_idx + "_" + review_page + '_' + qna_page);
13540            if (html) {
13541                is_cache = true;
13542                callback(html);
13543            } else {
13544                $.ajax({
13545                    type: 'POST',
13546                    data: { prod_idx: prod_idx, review_page: review_page, qna_page: qna_page },
13547                    url: review_url,
13548                    dataType: 'html',
13549                    cache: false,
13550                    success: callback
13551                });
13552            }
13553        }
13554    };
13555
13556    var qnaHide = function (t, c, q_p) {
13557        if (confirm(LOCALIZE.설명_숨기시겠습니까())) {
13558            $.ajax({
13559                type: 'POST',
13560                data: { code: t, prod_code: c, is_visible: false },
13561                url: ('/shop/delete_qna.cm'),
13562                dataType: 'json',
13563                cache: false,
13564                success: function (result) {
13565                    if (result.msg == 'SUCCESS') {
13566                        if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab('qna', q_p);
13567                        else SITE_SHOP_DETAIL.changeContentPCTab('qna', q_p);
13568                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.addCountUserProfileAttr('qnaCount', -1);
13569                        $.cocoaDialog.close();
13570                    } else
13571                        alert(result.msg);
13572                }
13573            });
13574        }
13575    };
13576
13577    var qnaDeleteShow = function (qna_code, prod_code) {
13578        var $show_secret_password = $('#show_secret_password');
13579        var $show_link = $(event.target);
13580        if ($show_secret_password.length == 0) {
13581            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13582            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" tabindex="0" style="position:absolute;left:' + left + 'px;top:80px;z-index:99999;"><p>' + LOCALIZE.설명_작성시등록하신비밀번호를입력해주세요() + '</p><div class="input_area"><input type="password" placeholder="' + LOCALIZE.설명_비밀번호() + '"><button class="btn btn-primary _confirm">' + LOCALIZE.버튼_확인닫기() + '</button></div></div>').hide();
13583            $show_link.after($show_secret_password);
13584        }
13585        $show_secret_password.find('input').val('');
13586        $show_secret_password.show();
13587        $show_secret_password.off('click', '._confirm')
13588            .on('click', '._confirm', function () {
13589                var secret_pass = $show_secret_password.find('input').val();
13590                CheckSecret(qna_code, secret_pass, function () {
13591                    qnaDelete(qna_code, prod_code, secret_pass);
13592                });
13593                $show_secret_password.hide();
13594            });
13595        $('body').off('mousedown.show_secret')
13596            .on('mousedown.show_secret', function (e) {
13597                var $tmp = $(e.target).closest('#show_secret_password');
13598                if ($tmp.length == 0) {
13599                    $show_secret_password.hide();
13600                    $('body').off('click.show_secret');
13601                }
13602            });
13603    };
13604
13605
13606    return {
13607        init: function (code, qna_secret_type) {
13608            init(code, qna_secret_type);
13609        },
13610        initMobileQna: function () {
13611            initMobileQna();
13612        },
13613        submit: function () {
13614            submit();
13615        },
13616        qnaCancel: function () {
13617            qnaCancel();
13618        },
13619        FormShow: function (t) {
13620            qnaFormShow(t);
13621        },
13622        EditQnaShow: function (t, c, idx) {
13623            EditQnaShow(t, c, idx);
13624        },
13625        ViewQnaShow: function (t, c, idx, qna_page) {
13626            ViewQnaShow(t, c, idx, qna_page);
13627        },
13628        Delete: function (code, prod_code, q_p) {
13629            qnaDelete(code, prod_code, q_p);
13630        },
13631        EditShow: function (t) {
13632            qnaEditShow(t);
13633        },
13634        EditHide: function (t) {
13635            qnaEditHide(t);
13636        },
13637        imageUploadInit: function (n) {
13638            imageUploadInit(n);
13639        },
13640        createHtml: function (prod_idx, review_page, qna_page, paging_on) {
13641            createHtml(prod_idx, review_page, qna_page, paging_on);
13642        },
13643        confirmShow: function (e, idx, prod_code, type, code) {
13644            qnaConfirmShow(e, idx, prod_code, type, code);
13645        },
13646        Hide: function (t, c, q_p) {
13647            qnaHide(t, c, q_p);
13648        },
13649        DeleteShow: function (qna_code, prod_code) {
13650            qnaDeleteShow(qna_code, prod_code);
13651        },
13652    };
13653}();
13654
13655var SITE_PERSONAL_QNA = function () {
13656    var $personal_qna_wrap;
13657    var $mobile_qna_wrap;
13658    var $form;
13659    var $mobile_form;
13660    var $comment_body;
13661    var $qna_image_box;
13662
13663
13664    var $comment_area;
13665    var $secret;
13666
13667    var init = function () {
13668        $form = $('#qna_form');
13669        $secret = $form.find('._secret');
13670
13671        $secret.on('click', function () {
13672            if ($secret.hasClass('active')) {
13673                $secret.removeClass('active');
13674                $form.find('#secret').val('N');
13675            } else {
13676                $secret.addClass('active');
13677                $form.find('#secret').val('Y');
13678            }
13679        });
13680
13681        $('#qna_image_upload_btn').setUploadImage({
13682            url: '/shop/upload_image.cm',
13683            dropZone: 'icon_img_upload_wrap',
13684            singleFileUploads: true,
13685            formData: { temp: 'Y' }
13686        }, function (res, data) {
13687            $("#qna_image_box").show();
13688            $.each(data, function (e, tmp) {
13689                if (tmp.error == "" || tmp.error == null) {
13690                    var url = CDN_UPLOAD_URL + tmp.url;
13691                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
13692                    $("#qna_image_box").append(html);
13693                } else {
13694                    alert(tmp.error);
13695                }
13696            });
13697        });
13698        autosize($('.textarea_block textarea'));
13699    };
13700
13701    var submit = function (t, type, i) {
13702        switch (type) {
13703            case 'main': // 1:1 문의 페이지에서 바로 작성하는 폼
13704                var data = $form.serializeObject();
13705                break;
13706            case 'sub_form': // 등록되어 있는 qna에 답변을 다는 폼
13707                var data = $('#sub_qna_form_' + i).serializeObject();
13708                break;
13709            case 'editor': // 등록되어 있는 qna를 수정하는 폼
13710                var data = $('#sub_qna_editor_form_' + i).serializeObject();
13711                break;
13712        }
13713        if (!t.hasClass("btn-writing")) {
13714            t.addClass("btn-writing");
13715        }
13716        $.ajax({
13717            type: 'POST',
13718            data: { data: data, type: type, personal_qna: 'Y' },
13719            url: ('/shop/add_qna.cm'),
13720            dataType: 'json',
13721            cache: false,
13722            success: function (result) {
13723                if (t.hasClass("btn-writing")) {
13724                    t.removeClass("btn-writing");
13725                }
13726                if (result.msg == 'SUCCESS') {
13727                    createHtml(result.page);
13728                } else
13729                    alert(result.msg);
13730            }
13731        });
13732    };
13733
13734    var Delete = function (code, prod_code) {
13735        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
13736            $.ajax({
13737                type: 'POST',
13738                data: { code: code, prod_code: prod_code },
13739                url: ('/shop/delete_qna.cm'),
13740                dataType: 'json',
13741                success: function (result) {
13742                    if (result.msg == 'SUCCESS') {
13743                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.addCountUserProfileAttr('qnaCount', -1);
13744                        createHtml();
13745                    } else
13746                        alert(result.msg);
13747                }
13748            });
13749        }
13750    };
13751
13752    var FormShow = function (t) {
13753        var sub_form = $("._sub_form_" + t);
13754
13755        sub_form.data('show', 'Y');
13756        sub_form.show();
13757        var comment_add_body = sub_form.find('._comment_add_body_' + t);
13758
13759        $('body').off('mouseup.sub_comment')
13760            .on('mouseup.sub_comment', function (e) {
13761                var $c_target = $(e.target);
13762                var $s_form = $c_target.closest('._sub_form_' + t + ', ._show_sub_form_btn_' + t);
13763                if ($s_form.length == 0) {
13764
13765                    var text = comment_add_body.val();
13766                    sub_form.data('show', 'N');
13767                    if (text == '') {
13768                        $('body').off('mouseup.sub_comment');
13769                        FormHide();
13770                    }
13771                }
13772            });
13773    };
13774
13775    var FormHide = function () {
13776        $("._sub_qna_form").hide();
13777    };
13778
13779    var EditShow = function (t) {
13780        var editor_form = $("._sub_form_editor_" + t);
13781        editor_form.siblings('.block-postmeta').find('.write').hide();
13782        editor_form.siblings('.block-postmeta').find('.comment_area').hide();
13783        editor_form.data('show', 'Y');
13784        editor_form.show();
13785    };
13786
13787    var EditHide = function (t) {
13788        var editor_form = $("._sub_form_editor_" + t);
13789        editor_form.hide();
13790        editor_form.siblings('.block-postmeta').find('.write').show();
13791        editor_form.siblings('.block-postmeta').find('.comment_area').show();
13792    };
13793
13794    var imageUploadInit = function (n) {
13795        $("#sub_image_box_" + n).hide();
13796
13797        $('#sub_image_upload_btn_' + n).setUploadImage({
13798            url: '/shop/upload_image.cm',
13799            dropZone: 'icon_img_upload_wrap',
13800            singleFileUploads: true,
13801            formData: { temp: 'Y' }
13802        }, function (res, data) {
13803            $("#sub_image_box_" + n).show();
13804            $.each(data, function (e, tmp) {
13805                if (tmp.error == "" || tmp.error == null) {
13806                    var url = CDN_UPLOAD_URL + tmp.url;
13807                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
13808                    $("#sub_image_box_" + n).append(html);
13809                } else {
13810                    alert(tmp.error);
13811                }
13812            });
13813        });
13814
13815        $('#editor_image_upload_btn_' + n).setUploadImage({
13816            url: '/shop/upload_image.cm',
13817            dropZone: 'icon_img_upload_wrap',
13818            singleFileUploads: true,
13819            formData: { temp: 'Y' }
13820        }, function (res, data) {
13821            $("#editor_image_box_" + n).show();
13822            $.each(data, function (e, tmp) {
13823                if (tmp.error == "" || tmp.error == null) {
13824                    var url = CDN_UPLOAD_URL + tmp.url;
13825                    var html = '<span class="file-add"><input type="hidden" name="img" value="' + tmp.name + '"><div class="file-add-bg" style="background: url(' + url + ') no-repeat center center;"></div><em class="del" onclick="POST_COMMENT.removeCommentImg($(this))"></em></span>';
13826                    $("#editor_image_box_" + n).append(html);
13827                } else {
13828                    alert(tmp.error);
13829                }
13830            });
13831        });
13832    };
13833
13834    var createHtml = function (page) {
13835        $personal_qna_wrap = $('._personal_qna_wrap');
13836        $.ajax({
13837            type: 'POST',
13838            data: { page: page },
13839            url: ('/shop/personal_qna_list.cm'),
13840            dataType: 'html',
13841            cache: false,
13842            success: function (result) {
13843                $personal_qna_wrap.html(result);
13844            }
13845        });
13846    };
13847
13848    return {
13849        init: function () {
13850            init();
13851        },
13852        submit: function (t, type, i) {
13853            submit(t, type, i);
13854        },
13855        Delete: function (code, prod_code) {
13856            Delete(code, prod_code);
13857        },
13858        FormShow: function (t) {
13859            FormShow(t);
13860        },
13861        EditShow: function (t) {
13862            EditShow(t);
13863        },
13864        EditHide: function (t) {
13865            EditHide(t);
13866        },
13867        imageUploadInit: function (n) {
13868            imageUploadInit(n);
13869        },
13870        createHtml: function (page) {
13871            createHtml(page);
13872        }
13873    };
13874}();
13875
13876/**
13877 * 배송 서비스 관련 기능
13878 */
13879const SHIPPING_SERVICE = function () {
13880    const isActive = function () {
13881        const activeShippingTab = document.querySelector('.shipping-service-tab.active');
13882        return activeShippingTab !== null && activeShippingTab.getAttribute('data-service-code') !== '';
13883    };
13884
13885    const isZipcodeAvailable = function () {
13886        const activeShippingTab = document.querySelector('.shipping-service-tab.active');
13887        return activeShippingTab && activeShippingTab.getAttribute('data-zipcode-available') === 'Y';
13888    };
13889
13890    /**
13891     * 현재 배송지가 설정되어 있는지 확인
13892     * @returns {boolean} - 배송지가 설정되어 있으면 true, 아니면 false
13893     */
13894    const hasAddress = function () {
13895        const addressLink = document.querySelector('.shipping-service-address-link');
13896        if (!addressLink) {
13897            return false;
13898        }
13899        const addressCode = addressLink.getAttribute('data-address-code');
13900        return addressCode !== null && addressCode !== '';
13901    };
13902
13903    /**
13904     * 비회원 여부 확인
13905     * @returns {boolean} - 비회원이면 true, 회원이면 false
13906     */
13907    const isGuest = function () {
13908        return IS_GUEST === true;
13909    };
13910
13911    /**
13912     * 비회원 일반배송 확인 모달
13913     * @returns {Promise<boolean>} - 일반배송 구매 시 true, 배송 다시 선택 시 false
13914     */
13915    const confirmGuest = async function () {
13916        // 회원인 경우 바로 true 반환
13917        if (!isGuest()) {
13918            return true;
13919        }
13920
13921        const confirmed = await confirmModal({
13922            title: '일반배송으로 구매가 진행돼요',
13923            description: [
13924                '비회원으로 구매 시 일반배송으로 자동 변경돼요.',
13925                '로그인하면 다른 배송 방법으로 주문할 수 있어요.'
13926            ],
13927            confirmText: '일반배송 구매',
13928            cancelText: '배송 다시 선택하기'
13929        });
13930
13931        return confirmed;
13932    };
13933
13934    /**
13935     * 외부채널 구매 시 일반배송 확인 모달
13936     * @returns {Promise<boolean>} - 일반배송 구매 시 true, 배송 다시 선택 시 false
13937     */
13938    const confirmExternalChannel = async function () {
13939        const confirmed = await confirmModal({
13940            title: '일반배송으로 구매가 진행돼요',
13941            description: '선택한 배송은 톡체크아웃·네이버페이와 같은 간편구매에서는 이용할 수 없어요. 구매는 일반배송으로 자동 변경돼요.',
13942            confirmText: '일반배송 구매',
13943            cancelText: '배송 다시 선택하기'
13944        });
13945        return confirmed;
13946    };
13947
13948    const validateAddress = async function () {
13949        if (hasAddress()) {
13950            return true;
13951        }
13952
13953        const confirmed = await confirmModal({
13954            title: '배송지를 등록해야 해당 배송방법을 이용할 수 있어요',
13955            description: '등록된 배송지가 없어서 다른 배송 방법을 선택할 수 없어요. 배송지를 추가하면 이용할 수 있어요.',
13956            confirmText: '배송지 추가',
13957            cancelText: '취소'
13958        });
13959        // 취소 버튼 클릭 시 - 아무것도 하지 않음 (사용자가 배송 다시 선택)
13960        if (confirmed) {
13961            // 모바일 체크 (화면 너비 768px 이하 )
13962            const isMobile = window.innerWidth <= 768;
13963            const redirectUrl = '/shop_mypage/address';
13964
13965            if (isMobile) {
13966                window.location.href = redirectUrl;
13967                return;
13968            }
13969
13970            // PC 환경: 팝업 열기
13971            const popupWidth = 500;
13972            const popupHeight = 800;
13973            const left = (screen.width - popupWidth) / 2;
13974            const top = (screen.height - popupHeight) / 2;
13975            const features = `width=${popupWidth},height=${popupHeight},left=${left},top=${top},scrollbars=yes,resizable=yes`;
13976            window.open(redirectUrl, 'addressPopup', features);
13977            return;
13978        }
13979    }
13980
13981    const confirmZipcode = async function () {
13982        if (isZipcodeAvailable()) {
13983            return true;
13984        }
13985        const confirmed = await confirmModal({
13986            title: '일반배송으로 구매가 진행돼요',
13987            description: [
13988                '선택한 배송은 배송 불가 지역이라 이용할 수 없어요.',
13989                '구매는 일반배송으로 자동 변경돼요.'
13990            ],
13991            confirmText: '일반배송 구매',
13992            cancelText: '배송 다시 선택하기'
13993        });
13994        // 취소 버튼 클릭 시 - 아무것도 하지 않음 (사용자가 배송 다시 선택)
13995        if (!confirmed) {
13996            return false;
13997        }
13998
13999        return true;
14000    };
14001    /**
14002     * 모달 닫기
14003     */
14004    const closeModal = function () {
14005        const $modal = $('#shipping_general_modal');
14006        if ($modal.length) {
14007            $modal.fadeOut(200, function() {
14008                $(this).remove();
14009            });
14010        }
14011    };
14012
14013    /**
14014     * 확인 모달을 표시하고 사용자의 선택을 Promise로 반환
14015     * @param {Object} options - 모달 옵션
14016     * @param {string} options.title - 모달 제목
14017     * @param {string|string[]} options.description - 모달 설명 (문자열 또는 문자열 배열)
14018     * @param {string} options.confirmText - 확인 버튼 텍스트
14019     * @param {string} options.cancelText - 취소 버튼 텍스트
14020     * @returns {Promise<boolean>} - 확인 시 true, 취소 시 false
14021     */
14022    const confirmModal = function (options) {
14023        if (options === undefined) {
14024            return Promise.resolve(true);
14025        }
14026        // description이 문자열이면 배열로 변환
14027        const descriptionArray = Array.isArray(options.description) ? options.description : [options.description];
14028
14029        // description HTML 생성
14030        const descriptionHtml = descriptionArray.map(function(text, index) {
14031            const marginBottom = index < descriptionArray.length - 1 ? '4px' : '0';
14032            return `<p style="margin: 0 0 ${marginBottom} 0;">${text}</p>`;
14033        }).join('');
14034
14035        return new Promise(function (resolve, reject) {
14036            // 기존 모달이 있으면 제거
14037            closeModal();
14038
14039            // 모달 HTML 생성
14040            const modalHtml = `\
14041                <div id="shipping_general_modal" style="\
14042                    position: fixed;\
14043                    top: 0;\
14044                    left: 0;\
14045                    width: 100%;\
14046                    height: 100%;\
14047                    z-index: 17000;\
14048                    display: none;\
14049                ">\
14050                    <!-- Overlay (ë°°ê²½) -->\
14051                    <div class="modal-overlay" style="\
14052                        position: absolute;\
14053                        top: 0;\
14054                        left: 0;\
14055                        width: 100%;\
14056                        height: 100%;\
14057                        background-color: rgba(0, 0, 0, 0.5);\
14058                        z-index: 1;\
14059                    "></div>\
14060                    <!-- Modal Container -->\
14061                    <div style="\
14062                        position: relative;\
14063                        width: 100%;\
14064                        height: 100%;\
14065                        display: flex;\
14066                        align-items: center;\
14067                        justify-content: center;\
14068                        z-index: 2;\
14069                    ">\
14070                        <!-- Modal Content -->\
14071                        <div style="\
14072                            background: #ffffff;\
14073                            border-radius: 4px;\
14074                            box-shadow: 0px 0px 1px 0px rgba(75,81,91,0.2), 0px 20px 40px 0px rgba(75,81,91,0.2);\
14075                            overflow: hidden;\
14076                            max-width: 448px;\
14077                            width: calc(100% - 40px);\
14078                            margin: 0 20px;\
14079                        ">\
14080                            <!-- Modal Header -->\
14081                            <div style="\
14082                                background: white;\
14083                                padding: 20px;\
14084                                display: flex;\
14085                                flex-direction: column;\
14086                                gap: 4px;\
14087                            ">\
14088                                <div style="width: 100%;">\
14089                                    <p style="\
14090                                        font-weight: 700;\
14091                                        font-size: 20px;\
14092                                        line-height: 1.6;\
14093                                        color: #15181e;\
14094                                        margin: 0;\
14095                                    ">${options.title}</p>\
14096                                </div>\
14097                                <div style="width: 100%; margin-top: 4px;">\
14098                                    <div style="\
14099                                        font-weight: 400;\
14100                                        font-size: 14px;\
14101                                        line-height: 1.6;\
14102                                        color: #717680;\
14103                                    ">${descriptionHtml}</div>\
14104                                </div>\
14105                            </div>\
14106                            <!-- Modal Footer -->\
14107                            <div style="\
14108                                padding: 0 20px 20px 20px;\
14109                                display: flex;\
14110                                flex-direction: column;\
14111                                gap: 8px;\
14112                            ">\
14113                                <!-- 확인 버튼 -->\
14114                                <button class="btn-confirm" style="\
14115                                    width: 100%;\
14116                                    height: 48px;\
14117                                    background-color: var(--unit-style-brand_color, #006fff);\
14118                                    border: none;\
14119                                    border-radius: 8px;\
14120                                    font-weight: 700;\
14121                                    font-size: 16px;\
14122                                    line-height: 1.6;\
14123                                    color: #ffffff;\
14124                                    cursor: pointer;\
14125                                    transition: background-color 0.2s;\
14126                                ">${options.confirmText}</button>\
14127                                <!-- 취소 버튼 -->\
14128                                <button class="btn-cancel" style="\
14129                                    width: 100%;\
14130                                    height: 48px;\
14131                                    background-color: #ffffff;\
14132                                    border: 1px solid #d2d7e0;\
14133                                    border-radius: 8px;\
14134                                    font-weight: 700;\
14135                                    font-size: 16px;\
14136                                    line-height: 1.6;\
14137                                    color: #15181e;\
14138                                    cursor: pointer;\
14139                                    transition: background-color 0.2s;\
14140                                ">${options.cancelText}</button>\
14141                            </div>\
14142                        </div>\
14143                    </div>\
14144                </div>`;
14145
14146            // DOM에 모달 추가
14147            $('body').append(modalHtml);
14148            const $modal = $('#shipping_general_modal');
14149
14150            // 확인 버튼 이벤트
14151            $modal.find('.btn-confirm').on('click', function() {
14152                closeModal();
14153                resolve(true);
14154            });
14155
14156            // 취소 버튼 이벤트
14157            $modal.find('.btn-cancel').on('click', function() {
14158                closeModal();
14159                resolve(false);
14160            });
14161
14162            // 배경 클릭 시 모달 닫기
14163            $modal.find('.modal-overlay').on('click', function() {
14164                closeModal();
14165                resolve(false);
14166            });
14167
14168            // ESC 키로 모달 닫기
14169            $(document).on('keydown.shipping_modal', function(e) {
14170                if (e.keyCode === 27) { // ESC
14171                    closeModal();
14172                    resolve(false);
14173                    $(document).off('keydown.shipping_modal');
14174                }
14175            });
14176
14177            // 모달 표시 (페이드인 효과)
14178            $modal.fadeIn(200);
14179        });
14180    };
14181
14182    /**
14183     * 선물하기 빠른배송 불가 확인 모달
14184     * 빠른배송이 활성화된 상태에서 선물하기 시도 시 일반배송으로 변경 안내
14185     * @returns {Promise<boolean>} - 일반배송 구매 시 true, 배송 다시 선택 시 false
14186     */
14187    const confirmGift = async function () {
14188        // 빠른배송이 활성화되어 있지 않으면 바로 true 반환
14189        if (!isActive()) {
14190            return true;
14191        }
14192
14193        const confirmed = await confirmModal({
14194            title: '일반배송으로 선물하기가 진행돼요',
14195            description: [
14196                '선택한 배송은 선물하기에서 사용할 수 없어요.',
14197                '선물하기는 일반배송으로 자동 변경돼요.'
14198            ],
14199            confirmText: '일반배송으로 선물하기',
14200            cancelText: '배송 다시 선택하기'
14201        });
14202
14203        return confirmed;
14204    };
14205
14206    const verify = async function ({ type } = {}) {
14207        const isShippingServiceActive = typeof SHIPPING_SERVICE !== 'undefined' && SHIPPING_SERVICE.isActive();
14208        if (!isShippingServiceActive) {
14209            return true;
14210        }
14211        if (isShippingServiceActive) {
14212            // 외부채널 구매 시 일반배송 확인 모달
14213            if (type === 'npay' || type === 'talkpay') {
14214                const confirmed = await SHIPPING_SERVICE.confirmExternalChannel();
14215                if (!confirmed) {
14216                    return false;
14217                }
14218                SITE_SHOP_DETAIL.setShippingTemplateCode('');
14219                return true;
14220            }
14221
14222
14223            // 비회원인 경우 일반배송 확인 모달
14224            const confirmedGuest = await SHIPPING_SERVICE.confirmGuest();
14225            if (confirmedGuest && SHIPPING_SERVICE.isGuest()) {
14226                // 비회원이 일반배송 구매 선택 시 일반배송으로 변경
14227                SITE_SHOP_DETAIL.setShippingTemplateCode('');
14228                return true;
14229            } else if (!confirmedGuest) {
14230                return false;
14231            }
14232
14233            // 회원인 경우 배송지 확인
14234            if (!SHIPPING_SERVICE.isGuest()) {
14235                const isValidAddress = await SHIPPING_SERVICE.validateAddress();
14236                if (!isValidAddress) {
14237                    return false;
14238                }
14239
14240                const isZipcodeAvailable = SHIPPING_SERVICE.isZipcodeAvailable();
14241                if (!isZipcodeAvailable) {
14242                    const confirmedZipcode = await SHIPPING_SERVICE.confirmZipcode();
14243                    // 일반배송으로 구매 시 일반배송 템플릿 코드 설정
14244                    if (confirmedZipcode) {
14245                        SITE_SHOP_DETAIL.setShippingTemplateCode('');
14246                        return true;
14247                    } else {
14248                        return false;
14249                    }
14250                }
14251            }
14252        }
14253        return true;
14254    };
14255
14256    return {
14257        isActive,
14258        isZipcodeAvailable,
14259        isGuest,
14260        validateAddress,
14261        confirmGuest,
14262        confirmZipcode,
14263        confirmGift,
14264        confirmExternalChannel,
14265        verify,
14266    };
14267}();
14268
14269/**
14270 * 크리에이터 혜택 종료일 카운트다운 배너
14271 *
14272 * 모바일은 기존 sticky 정책을 유지하고, 사이트 헤더 영역 하단에서 16px
14273 * 아래에 위치하도록 --creator-banner-top CSS 변수를 주입한다.
14274 * PC는 상품 이미지 영역(#prod_image_list) 내부로 이동해 absolute 배치한다.
14275 *
14276 * 측정 방식:
14277 *   #doz_header_wrap 의 getBoundingClientRect().bottom (viewport 기준 좌표) 사용.
14278 *   - 스크롤 전: wrap 의 실제 끝점 (자연 흐름)
14279 *   - 스크롤 후: wrap 이 viewport 위로 사라지면 wrap 안의 ._new_fixed_header
14280 *               (viewport 상단에 박힌 fixed 헤더) 의 bottom 으로 fallback
14281 *   - 운영자 추가 top_banner / fixed_header_section 도 동일 방식으로 max 누적
14282 *
14283 *   getBoundingClientRect 는 element 가 자연 흐름이든 fixed 든 동일하게
14284 *   viewport 기준 실시간 좌표를 반환하므로 body class 분기가 불필요.
14285 */
14286var CREATOR_BENEFIT_BANNER = (function () {
14287    var $banner = null;
14288    var $countdown = null;
14289    var $slides = null;
14290    var $placeholder = null;
14291    var slideIndex = 0;
14292    var endTimestamp = 0;
14293    var rafScheduled = false;
14294    var countdownTimer = null;
14295    var slideTimer = null;
14296    var BANNER_GAP_PX_MOBILE = 16; // 모바일:   헤더 하단으로부터 16px
14297    var MOBILE_BREAKPOINT_PX = (typeof SHOP_CONST !== 'undefined' && SHOP_CONST.WIDTH_MOBILE) ? SHOP_CONST.WIDTH_MOBILE : 768;
14298    var SLIDE_INTERVAL_MS = 3000;  // 텍스트 슬라이드 롤링 간격
14299
14300    function isMobileViewport() {
14301        return window.innerWidth < MOBILE_BREAKPOINT_PX;
14302    }
14303
14304    function getHeaderOffsetTop() {
14305        var maxBottom = 0;
14306
14307        // #doz_header_wrap — 사이트 빌더 헤더 본체
14308        var $wrap = $('#doz_header_wrap');
14309        if ($wrap.length) {
14310            var wrapRect = $wrap[0].getBoundingClientRect();
14311            if (wrapRect.bottom > maxBottom) {
14312                maxBottom = wrapRect.bottom;
14313            }
14314            // wrap 자체가 viewport 위로 사라진 경우, 안에 박힌 fixed 헤더의 bottom 으로 fallback
14315            var $fixed = $wrap.find('._new_fixed_header').filter(':visible');
14316            if ($fixed.length) {
14317                var fixedRect = $fixed[0].getBoundingClientRect();
14318                if (fixedRect.bottom > maxBottom) {
14319                    maxBottom = fixedRect.bottom;
14320                }
14321            }
14322        }
14323
14324        // 운영자 추가 상단 배너 (reseller_banner / 일반 banner-container)
14325        $('.top_banner_wrap:visible, .banner-container:visible').each(function () {
14326            var r = this.getBoundingClientRect();
14327            if (r.bottom > maxBottom) maxBottom = r.bottom;
14328        });
14329
14330        // 페이지 내 추가 fixed 섹션
14331        $('._fixed_header_section:visible').each(function () {
14332            var r = this.getBoundingClientRect();
14333            if (r.bottom > maxBottom) maxBottom = r.bottom;
14334        });
14335
14336        return Math.max(0, Math.ceil(maxBottom));
14337    }
14338
14339    function applyTop() {
14340        if (!$banner || !$banner.length) return;
14341        if (!isMobileViewport()) {
14342            document.documentElement.style.removeProperty('--creator-banner-top');
14343            return;
14344        }
14345        var topPx = getHeaderOffsetTop() + BANNER_GAP_PX_MOBILE;
14346        document.documentElement.style.setProperty('--creator-banner-top', topPx + 'px');
14347    }
14348
14349    function applyPlacement() {
14350        if (!$banner || !$banner.length) return;
14351
14352        if (!$placeholder) {
14353            $placeholder = $('<span class="_creator_benefit_banner_placeholder" style="display:none;"></span>');
14354            $banner.before($placeholder);
14355        }
14356
14357        if (isMobileViewport()) {
14358            if ($placeholder && $placeholder.length && !$banner.prev().is($placeholder)) {
14359                $placeholder.after($banner);
14360            }
14361            return;
14362        }
14363
14364        var $imageWrap = $('#prod_detail #prod_image_list:visible').first();
14365        if (!$imageWrap.length) {
14366            $imageWrap = $('#prod_image_list:visible').first();
14367        }
14368        if ($imageWrap.length && !$banner.parent().is($imageWrap)) {
14369            $imageWrap.append($banner);
14370        }
14371    }
14372
14373    function syncLayout() {
14374        applyPlacement();
14375        applyTop();
14376    }
14377
14378    function scheduleLayout() {
14379        if (rafScheduled) return;
14380        rafScheduled = true;
14381        window.requestAnimationFrame(function () {
14382            rafScheduled = false;
14383            syncLayout();
14384        });
14385    }
14386
14387    function pad2(n) {
14388        return n < 10 ? '0' + n : '' + n;
14389    }
14390
14391    function formatRemaining(ms) {
14392        if (ms <= 0) return '00:00:00';
14393        var total = Math.floor(ms / 1000);
14394        var days  = Math.floor(total / 86400);
14395        var hours = Math.floor((total % 86400) / 3600);
14396        var mins  = Math.floor((total % 3600) / 60);
14397        var secs  = total % 60;
14398        var time  = pad2(hours) + ':' + pad2(mins) + ':' + pad2(secs);
14399        return days > 0 ? (days + '일 ' + time) : time;
14400    }
14401
14402    function tickCountdown() {
14403        if (!$countdown || !$countdown.length) return;
14404        var remain = endTimestamp - Date.now();
14405        if (remain <= 0) {
14406            $countdown.text('00:00:00');
14407            destroy();
14408            return;
14409        }
14410        $countdown.text(formatRemaining(remain));
14411    }
14412
14413    // 두 슬라이드 vertical roll: 현재 → is-above (위로 빠짐), 다음 → is-active (아래에서 올라옴).
14414    // transition(0.4s) 종료 후 prev 를 transition off 한 채 is-below 로 reset → 다음 cycle 에 다시 아래에서 올라올 준비.
14415    function tickSlide() {
14416        if (!$slides || $slides.length < 2) return;
14417        var prevIndex = slideIndex;
14418        slideIndex = (slideIndex + 1) % $slides.length;
14419        var $prev = $slides.eq(prevIndex);
14420        var $next = $slides.eq(slideIndex);
14421
14422        $prev.removeClass('is-active is-below').addClass('is-above');
14423        $next.removeClass('is-below is-above').addClass('is-active');
14424
14425        setTimeout(function () {
14426            if (!$prev || !$prev.length) return;
14427            $prev.addClass('no-transition').removeClass('is-above').addClass('is-below');
14428            if ($prev[0]) {
14429                /* eslint-disable-next-line no-unused-expressions */
14430                $prev[0].offsetHeight;
14431            }
14432            $prev.removeClass('no-transition');
14433        }, 450);
14434    }
14435
14436    function clearTimers() {
14437        if (countdownTimer !== null) {
14438            clearInterval(countdownTimer);
14439            countdownTimer = null;
14440        }
14441        if (slideTimer !== null) {
14442            clearInterval(slideTimer);
14443            slideTimer = null;
14444        }
14445    }
14446
14447    function destroy() {
14448        clearTimers();
14449        $(window).off('scroll.creatorBanner resize.creatorBanner load.creatorBanner');
14450        $('#doz_header_wrap').off('transitionend.creatorBanner');
14451        if ($banner && $banner.length) {
14452            $banner.remove();
14453        }
14454        if ($placeholder && $placeholder.length) {
14455            $placeholder.remove();
14456        }
14457        $banner = null;
14458        $countdown = null;
14459        $slides = null;
14460        $placeholder = null;
14461        document.documentElement.style.removeProperty('--creator-banner-top');
14462    }
14463
14464    function init() {
14465        $banner = $('._creator_benefit_banner');
14466        if (!$banner.length) return;
14467
14468        // 카운트다운 영역은 end_date 가 유효한 미래일 때만 가동.
14469        // 시간 제한 없는 혜택(end_date 부재/과거)은 카운트다운만 비활성, 배너 자체는 그대로 표시.
14470        var endStr = $banner.attr('data-creator-end') || '';
14471        var parsed = endStr ? Date.parse(endStr.replace(' ', 'T')) : NaN;
14472        var hasCountdown = !!(parsed && !isNaN(parsed) && parsed > Date.now());
14473
14474        if (hasCountdown) {
14475            endTimestamp = parsed;
14476            $countdown = $banner.find('._creator_benefit_banner__countdown');
14477            tickCountdown();
14478            countdownTimer = setInterval(tickCountdown, 1000);
14479        }
14480
14481        $slides = $banner.find('._creator_benefit_banner__slide');
14482        slideIndex = Math.max(0, $slides.index($slides.filter('.is-active').eq(0)));
14483
14484        syncLayout();
14485        $banner.addClass('is-ready');
14486
14487        // 슬라이드가 2개 이상일 때만 롤링
14488        if ($slides.length >= 2) {
14489            slideTimer = setInterval(tickSlide, SLIDE_INTERVAL_MS);
14490        }
14491
14492        // 헤더 height 변동 및 PC/모바일 전환 트리거
14493        $(window).on('scroll.creatorBanner resize.creatorBanner', scheduleLayout);
14494        $(window).on('load.creatorBanner', scheduleLayout);
14495        $('#doz_header_wrap').on('transitionend.creatorBanner', scheduleLayout);
14496    }
14497
14498    return {
14499        init: init,
14500        destroy: destroy
14501    };
14502})();
14503
14504$(function () {
14505    CREATOR_BENEFIT_BANNER.init();
14506});

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.