PageSourceSearch

https://en.meerkat.day/js/site_shop/site_shop.js?1791369969

js meerkat.day collected 2026-10-10 00:01:27 UTC 672,605 bytes, 14,656 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        var skipContentTabInit = options.skip_content_tab_init === true;
753
754	    setTimeout(() => {
755            if (skipContentTabInit) return;
756		    switch(hash_temp[0]){
757			    case '#prod_detail_detail':
758				    is_init_detail = true;
759				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.DETAIL);
760				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.DETAIL);
761				    break;
762			    case '#prod_detail_review':
763				    is_init_detail = true;
764				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.REVIEW);
765				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW);
766				    if(hash_temp[1]){
767					    SITE_SHOP_DETAIL.viewReviewDetail(hash_temp[1], 1, 'N', 'Y');
768				    }
769				    break;
770			    case '#prod_detail_qna':
771				    is_init_detail = true;
772				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.QNA);
773				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.QNA);
774				    if(hash_temp[1]){
775					    SITE_SHOP_DETAIL.viewQnaDetail(hash_temp[1], 1, 'Y');
776				    }
777				    break;
778			    case '#prod_detail_return':
779				    is_init_detail = true;
780				    if(is_mobile_width) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.RETURN);
781				    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.RETURN);
782				    break;
783			    default:
784
785				    let target_tab = options.first_tab === "prod_detail"
786						    ? SHOP_CONST.TAB_TYPE.DETAIL
787						    : options.first_tab === "prod_review"
788								    ? SHOP_CONST.TAB_TYPE.REVIEW
789								    : options.first_tab === "prod_qna"
790										    ? SHOP_CONST.TAB_TYPE.QNA
791										    : options.first_tab === "prod_return"
792												    ? SHOP_CONST.TAB_TYPE.RETURN
793												    : "";
794
795				    if(options.shop_view_tab_display === "N"){
796					    if(is_mobile_width) {
797						    SITE_SHOP_DETAIL.changeContentTab(target_tab, review_page, qna_page, false, false);
798					    } else {
799						    SITE_SHOP_DETAIL.changeContentPCTab(target_tab, review_page, qna_page, false, false);
800					    }
801				    }
802
803				    checkSeemore(target_tab);
804				    is_init_detail = true;
805				    break;
806		    }
807
808		    /* 크리마 리뷰 사용 중일 시 위젯 호출 */
809		    if(document.querySelector(".crema-product-reviews")){
810			    if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
811				    crema.run();
812			    }
813		    }
814	    }, 200)
815
816        if (cm_data.shop_view_buy_item_tooltip === 'Y') {
817            $('.btn_tooltip[data-toggle="tooltip"]').tooltip({
818                delay: { show: 500, hide: 1000000 }
819            });
820            var toggle_regularly = $("input[name='add_cart_type']");
821            var $tooltip = $('.btn_tooltip[data-toggle="tooltip"]');
822            var window_height = $(window).height();
823            toggle_regularly.off('change').on('change', function () {
824                $tooltip.css({ 'opacity': '0' });
825                if ($(this).prop('checked')) {
826                    if ($tooltip.length > 0) {
827                        var window_height = $(window).height();
828                        $.each($tooltip, function (k, v) {
829                            var tooltip_y = v.getBoundingClientRect().y;
830                            if (tooltip_y >= 0 && tooltip_y < window_height) {
831                                $tooltip.css({ 'opacity': '1' });
832                                $(v).tooltip('show');
833                            }
834                        });
835                    }
836                }
837            });
838            if ($tooltip.length > 0) {
839                $.each($tooltip, function (k, v) {
840                    var tooltip_y = v.getBoundingClientRect().y;
841                    if (tooltip_y >= 0 && tooltip_y < window_height) {
842                        // 툴팁이 화면 내에 보이지 않을 때 show하면 위치가 어긋나게 됨
843                        setTimeout(function () {
844                            $(v).tooltip('show');
845                        }, 100);
846                    }
847
848                    $(window).scroll(function () {
849                        if ($(v).next('.tooltip:visible').length === 0) {
850                            var tooltip_y = v.getBoundingClientRect().y;
851                            if (tooltip_y > 0 && tooltip_y < (window_height - $(v).height())) {
852                                $(v).tooltip('show');
853                            }
854                        }
855                    });
856                });
857            }
858        }
859
860        setTimesale();
861        if (!is_prod_detail_page) {
862            setImgZoom(30, 800, section_code);
863
864            if ($first_photo_review_wrap.length > 0) {
865                getFirstPhotoReview(1);
866            }
867        }
868
869        /* resize 시 window_width 재계산 */
870        let is_resizing = false;
871        window.addEventListener('resize', function () {
872            if (!is_resizing) {
873                is_resizing = true;
874                setTimeout(function () {
875                    window_width = window.innerWidth;
876					let was_mobile_width = is_mobile_width;
877                    is_mobile_width = (window_width < SHOP_CONST.WIDTH_MOBILE);
878					if(is_mobile_width != was_mobile_width){
879						if(is_mobile_width){
880							changeContentTab(current_content_tab);
881						}else{
882							/* 동영상 전체화면 시 PC 너비로 인지되는데 탭을 새로 그리면서 동영상이 다시 불러와지는 문제가 있어 예외처리 */
883							if (!is_shop_view_tab_display && current_content_tab === SHOP_CONST.TAB_TYPE.REVIEW) {
884								changeContentPCTab(current_content_tab);
885							}
886						}
887						// 상단 조치로 인해 lazyload가 다시 실행되지 않아서 추가
888						setTabHistory(current_content_tab);
889						runLazyload();
890					}
891                    is_resizing = false;
892                }, 100);
893            }
894        });
895
896        /* debounce로 변경 */
897		// const handleResize = _.debounce(function () {
898		// 	window_width = window.innerWidth;
899		// 	let was_mobile_width = is_mobile_width;
900		// 	is_mobile_width = (window_width < SHOP_CONST.WIDTH_MOBILE /* 768w */);
901		// 	if(is_mobile_width != was_mobile_width){
902		// 		if(is_mobile_width){
903		// 			changeContentTab(current_content_tab);
904		// 		}else{
905		// 			/* 동영상 전체화면 시 PC 너비로 인지되는데 탭을 새로 그리면서 동영상이 다시 불러와지는 문제가 있어 예외처리 */
906		// 			if (!is_shop_view_tab_display && current_content_tab === SHOP_CONST.TAB_TYPE.REVIEW) {
907		// 				changeContentPCTab(current_content_tab);
908		// 			}
909		// 		}
910		// 		// 상단 조치로 인해 lazyload가 다시 실행되지 않아서 추가
911		// 		setTabHistory(current_content_tab);
912		// 		// runLazyload();
913		// 	}
914        // }, 100)
915
916        // window.addEventListener('resize', handleResize);
917
918        _hasFreeShipNotifyInlineMagnet = !!document.querySelector('.foFreeShipNotifyMagnet');
919
920        _initDetailDone = true;
921        if (isInSalesToolPreview && typeof _handleSalesToolPreviewMessage === 'function') {
922            while (_pendingSalesToolPreviewMessages.length > 0) {
923                _handleSalesToolPreviewMessage(_pendingSalesToolPreviewMessages.shift());
924            }
925        }
926        notifySalesToolPreviewReady();
927    };
928
929    var initProdStock = function (stock_use, stock, stock_un_limit) {
930        prod_stock_use = stock_use;
931        prod_stock = stock;
932        prod_stock_unlimit = stock_un_limit;
933    };
934
935    var runLazyload = function () {
936        if (!use_lazy_load)
937            return false;
938
939        var $seemore_wrap = $body.find('._seemore_wrap');
940        // [TFR-1179] 한 페이지에 전체 노출 + 순차적 로드 + 상세정보 펼쳐보기 미사용 시 순차적 로드에 의해 다른 탭 스크롤이 밀리는 현상 방지를 위해 마지막 이미지는 레이지 로드 제외
941        let is_except_last_lazy_load = options.shop_view_tab_display === 'Y' && $seemore_wrap.length == 0;
942		if(is_mobile_width){
943            let lazy_load_selector = 'img';
944            if(is_except_last_lazy_load){
945                let last_img = $prod_detail_content_mobile.find('img:last');
946                last_img.attr('src', last_img.attr('data-original')).removeAttr('height');
947                lazy_load_selector = 'img:not(:last)';
948            }
949			$prod_detail_content_mobile.find(lazy_load_selector).lazyload({		/* 상품 상세페이지 lazy load 적용, 기본 /img/no-image.png 는 한번만 불러옴 */
950				placeholder: NO_IMAGE_URL,
951				threshold: 100,
952				effect: "fadeIn",
953				effect_speed: 50, // fadeIn 시간 단축(기본 400ms→50ms)
954				load: function () {
955					$(this).removeAttr('height');
956					if ($seemore_wrap.length > 0) {
957						// 레이지로드 이후 no-image보다 로딩된 이미지가 작아 펼쳐보기 높이 제한에 걸리지 않게 되면 펼쳐보기 해제
958						setTimeout(function () {
959							if ($prod_detail_content_mobile.outerHeight() < SHOP_CONST.SEEMORE_HEIGHT.MOBILE) {
960								$prod_detail_content_mobile.toggleClass('hide_seemore', true);
961								checkSeemore(current_content_tab);
962							}
963						safeScheduleProdDetailBodyExpandedHeightUpdate();
964						}, 100);
965					}
966				}
967			});
968		}else{
969            let lazy_load_selector = 'img';
970            if(is_except_last_lazy_load){
971                let last_img = $prod_detail_content_pc.find('img:last');
972                last_img.attr('src', last_img.attr('data-original')).removeAttr('height');
973                lazy_load_selector = 'img:not(:last)';
974            }
975			$prod_detail_content_pc.find(lazy_load_selector).lazyload({		/* 상품 상세페이지 lazy load 적용, 기본 /img/no-image.png 는 한번만 불러옴 */
976				placeholder: NO_IMAGE_URL,
977				threshold: 100,
978				effect: "fadeIn",
979				effect_speed: 50, // fadeIn 시간 단축(기본 400ms→50ms)
980				load: function () {
981					$(this).removeAttr('height');
982					if ($seemore_wrap.length > 0) {
983						// 레이지로드 이후 no-image보다 로딩된 이미지가 작아 펼쳐보기 높이 제한에 걸리지 않게 되면 펼쳐보기 해제
984						setTimeout(function () {
985							if ($prod_detail_content_pc.outerHeight() < SHOP_CONST.SEEMORE_HEIGHT.PC) {
986								$prod_detail_content_pc.toggleClass('hide_seemore', true);
987								checkSeemore(current_content_tab);
988							}
989						safeScheduleProdDetailBodyExpandedHeightUpdate();
990						}, 100);
991					}
992				}
993			});
994		}
995    };
996
997    var setImgZoom = function (margin, max_width, section_code) {
998        if ($('.shop_view .xzoom').length > 0) {
999            // 메인 이미지에 object-fit 크롭이 적용된 경우(원본 경로 서빙) xzoom 전환 페이드 고스트가
1000            // 크롭 없이 fill 로 복제되어 왜곡되므로, 이 경우에만 fadeTrans 를 비활성화한다.
1001            // 기본값 fill(기존 사이트)에서는 fadeTrans: true 로 기존 크로스페이드 동작 유지.
1002            var is_cover_fit_main = ($('.shop_view .xzoom').css('object-fit') || 'fill') !== 'fill';
1003            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 });
1004            if (typeof shop_view_xzoom != 'undefined') {
1005                // 연관상품 장바구니 확인 후 닫을 시 재실행되면서 오류나는 문제 방지
1006                shop_view_xzoom.eventmove = function (element) {
1007                    var $xzoom_preview = $('.shop_view .xzoom-preview');
1008                    $xzoom_preview.addClass(section_code);
1009                    var $xzoom_preview_img = $xzoom_preview.find('img');
1010                    var $xzoom = $('.shop_view .xzoom');
1011                    var xzoom_width = $(window).width() - ($xzoom.offset().left + $xzoom.outerWidth() + margin * 2);
1012                    if (xzoom_width > max_width) {
1013                        xzoom_width = max_width;
1014                    }
1015                    var xzoom_height = xzoom_width;
1016                    var xzoom_preview_img_width = $xzoom_preview_img.outerWidth();
1017                    var xzoom_preview_img_height = $xzoom_preview_img.outerHeight();
1018                    if (xzoom_preview_img_width < xzoom_width || xzoom_preview_img_height < xzoom_height) {
1019                        shop_view_xzoom.closezoom();
1020                    } else {
1021                        $xzoom_preview.outerWidth(xzoom_width);
1022                        $xzoom_preview.outerHeight(xzoom_height);
1023                        element.bind('mousemove', shop_view_xzoom.movezoom);
1024                    }
1025                    $xzoom_preview.mouseover(shop_view_xzoom.closezoom);
1026                };
1027            }
1028        }
1029    };
1030
1031    var setTimesale = function () {
1032        var $doz_timesale_wrap = $('#prod_goods_form ._doz_timesale_wrap');
1033        if ($doz_timesale_wrap.length > 0) {
1034            $doz_timesale_wrap.each(function () {
1035                var $that = $(this);
1036                var $doz_timesale = $that.find('._doz_timesale');
1037                var start_time = ($that.find('._doz_timesale').attr('data-start-time') * 1000);
1038                var timesale_interval = setInterval(function () {
1039                    var remain_ms = ($doz_timesale.attr('data-end-time') * 1000) - start_time;
1040                    if (remain_ms > 0) {
1041                        var remain_d = Math.floor(remain_ms / 86400000);
1042                        var remain_h = Math.floor((remain_ms % 86400000) / 3600000);
1043                        var remain_m = Math.floor((remain_ms % 3600000) / 60000);
1044                        var remain_s = Math.floor((remain_ms % 60000) / 1000);
1045
1046                        var remain_hh = remain_h < 10 ? '0' + remain_h : '' + remain_h;
1047                        var remain_mm = remain_m < 10 ? '0' + remain_m : '' + remain_m;
1048                        var remain_ss = remain_s < 10 ? '0' + remain_s : '' + remain_s;
1049
1050                        if (remain_d >= 1) {
1051	                        $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> 남음"));
1052                        } else {
1053                            $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> 남음"));
1054                        }
1055                        start_time = start_time + 1000;
1056                    } else {
1057                        /* 타임세일 종료 */
1058                        clearInterval(timesale_interval);
1059                        $that.remove();
1060                    }
1061                }, 1000);
1062            });
1063        }
1064    };
1065
1066    /**
1067     * 다국어코드를 백엔드에서 호출하여 자바스크립트에 저장하는 함수
1068     * initDetail 피라미터 개수가 다른 히스토리를 모르겠어서 일단 따로 만들었음
1069     * @param code
1070     */
1071    var initLocalize = function (code) {
1072        total_price_localize_text = code;
1073    };
1074
1075    /**
1076     * 특정상품 위시리스트 추가 처리
1077     * @param prod_code
1078     */
1079    var addProdWish = function (prod_code, back_url) {
1080        var back_url = typeof back_url == 'undefined' ? '' : back_url;
1081        $.ajax({
1082            type: 'POST',
1083            data: { 'prod_code': prod_code },
1084            url: ('/shop/add_prod_wish.cm'),
1085            dataType: 'json',
1086            success: function (res) {
1087                if (res.msg == 'SUCCESS') {
1088                    if (res.res == 'add') {
1089                        if (typeof FB_PIXEL != 'undefined') FB_PIXEL.AddToWishlist();
1090                        if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.AddToWishlist();
1091	                    if (typeof NP_NDA != 'undefined') NP_NDA.AddToWishList(prod_code);
1092                        if (typeof TIKTOK_PIXEL != 'undefined') {
1093                            // TIKTOK_PIXEL.track('AddToWishlist', {
1094                            //     contents: [
1095                            //         {
1096                            //             content_id: options.prod_idx,
1097                            //             content_name: res.prod_name,
1098                            //             quantity: res.wish_cnt,
1099                            //             price: options.prod_price
1100                            //         },
1101                            //     ],
1102                            //     content_type: 'product',
1103                            //     value: options.prod_price,
1104                            //     currency: res.currency,
1105                            // });
1106                        }
1107
1108                        if ($wish_buttons) {
1109                            toggleWishButtons($wish_buttons, res.wish_cnt, options.is_show_wish_count, true);
1110                        }
1111                        if ($(".wish-icon-" + prod_code).length > 0) {
1112                            $(".wish-icon-" + prod_code).removeClass('im-ico-like');
1113                            $(".wish-icon-" + prod_code).addClass('im-ico-liked');
1114                        }
1115                    } else if (res.res == 'delete') {
1116                        if ($wish_buttons) {
1117                            toggleWishButtons($wish_buttons, res.wish_cnt, options.is_show_wish_count, false);
1118                        }
1119                        if ($(".wish-icon-" + prod_code).length > 0) {
1120                            $(".wish-icon-" + prod_code).removeClass('im-ico-liked');
1121                            $(".wish-icon-" + prod_code).addClass('im-ico-like');
1122                        }
1123                        if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.addCountUserProfileAttr('wishCount', -1);
1124                    }
1125                    if ($(".wish-text-" + prod_code).length > 0) {
1126                        $(".wish-text-" + prod_code).text(res.wish_cnt);
1127                    }
1128                } else if (res.msg === 'NON_MEMBERS') {
1129                    SITE_MEMBER.openLogin(back_url);
1130                } else {
1131                    alert(res.msg);
1132                }
1133            }
1134        });
1135    };
1136    /**
1137     * 특정상품 위시정보 가져오기 (내 위시여부,위시갯수)
1138     * @param prod_code
1139     */
1140    var getProdWish = function (prod_code) {
1141        $.ajax({
1142            type: 'POST',
1143            data: { 'prod_code': prod_code },
1144            url: ('/shop/get_prod_wish.cm'),
1145            dataType: 'json',
1146            success: function (res) {
1147                if (res.msg == 'SUCCESS') {
1148                } else
1149                    alert(res.msg);
1150            }
1151        });
1152    };
1153    /**
1154     * 두개의 옵션 데이터가 같은지 확인 (순서 무관)
1155     * @param options1 [ {option_code: option_value} ]
1156     * @param options2 [ {option_code: option_value} ]
1157     */
1158    var isSameOptionList = function (options1, options2) {
1159        if (options1.length == options2.length) {
1160            var isSame = true;
1161            var exist = false;
1162            $.each(options1, function (no, data) {
1163                exist = false;
1164                $.each(options2, function (no2, data2) {
1165                    if (data.option_code == data2.option_code) {
1166                        if (data.value_code != '') {
1167                            if (data.value_code == data2.value_code) {
1168                                exist = true;
1169                                return false;
1170                            }
1171                        } else {
1172                            if (data.value_name == data2.value_name) {
1173                                exist = true;
1174                                return false;
1175                            }
1176                        }
1177                    }
1178                });
1179                if (!exist) {
1180                    isSame = false;
1181                    return false;
1182                }
1183            });
1184            return isSame;
1185        } else {
1186            return false;
1187        }
1188    };
1189
1190    /**
1191     * 상품 이미지 롤링 시작
1192     */
1193    var startProdImageRolling = function (autoWidth) {
1194
1195        var items = $prod_image_list.find('._item');
1196        var is_dots = false;
1197        is_dots = items.length > 1 ? true : false;
1198
1199        if (is_dots) {
1200            switch (cm_data.paging_style_type) {
1201                case 'st00':
1202                    switch (cm_data.paging_default_style_type) {
1203                        case 'st00':
1204                            $prod_image_list_rolling.toggleClass('paging_type_dot paging_type_dot01', true);
1205                            break;
1206                        case 'st01':
1207                            $prod_image_list_rolling.toggleClass('paging_type_dot paging_type_dot02', true);
1208                            break;
1209                    }
1210                    break;
1211                case 'st01':
1212                    $prod_image_list_rolling.toggleClass('paging_type_big_dot', true);
1213                    break;
1214                case 'st02':
1215                    $prod_image_list_rolling.toggleClass('paging_type_line', true);
1216                    break;
1217                case 'st03':
1218                    switch (cm_data.paging_active_style_type) {
1219                        case 'st00':
1220                            $prod_image_list_rolling.toggleClass('paging_type_count paging_type_count01', true);
1221                            break;
1222                        case 'st01':
1223                            $prod_image_list_rolling.toggleClass('paging_type_count paging_type_count02', true);
1224                            break;
1225                        case 'st02':
1226                            $prod_image_list_rolling.toggleClass('paging_type_count paging_type_count03', true);
1227                            break;
1228                    }
1229                    break;
1230            }
1231        }
1232
1233        if (autoWidth) {
1234            $prod_image_list_rolling.owlCarousel({
1235                dots: is_dots,
1236                navigation: true,
1237                slideSpeed: 300,
1238                paginationSpeed: 400,
1239                singleItem: true,
1240                animateOut: 'fadeOut',
1241                items: 1,
1242                autoHeight: true,
1243                autoWidth: true,
1244                onInitialized: function () {
1245                    var owl_item_list = $prod_image_list.find('.owl-item');
1246                    var first_url = $(items[0]).find('img').attr('src');
1247                    var max_width = 750;
1248                    // TODO: is_prod_detail_page가 명확하지 않아 장바구니 임시조치
1249                    // shopping_list에서 들어오면 400으로 잡아야 함
1250                    if ($('.modal_prod_detail_from_shopping_list').length > 0) {
1251                        var max_width = 400;
1252                    } else if ($prod_detail.width() < max_width) {
1253                        max_width = $prod_detail.width();
1254                    }
1255                    $.each(owl_item_list, function (key, value) {
1256                        var $value = $(value);
1257                        var image_url = $value.find('img').attr('src');
1258                        var image = new Image();
1259                        image.onload = function () {
1260                            var that = this;
1261                            // owl initialized 이후 내부적으로 resized를 하면서 크기가 어긋나는 경우가 있어 timeout을 둠
1262                            setTimeout(function () {
1263                                var org_width = that.width;
1264                                var org_height = that.height;
1265                                var width = org_width > max_width ? max_width : org_width;
1266                                var height = width * (org_height / org_width);
1267                                const new_width = getComputedStyle($prod_image_list[0]).margin == '0px -15px' ? width + 30 : width;
1268                                const new_height = getComputedStyle($prod_image_list[0]).margin == '0px -15px' ? height - 30 : height;
1269                                $value.css({ 'width': new_width + 'px', 'height': new_height + 'px' });
1270                                if (that.src === first_url) {
1271                                    // 첫 이미지 크기 세팅
1272                                    $prod_image_list.find('.owl-carousel').css({ 'width': `${new_width}px`, 'height': `${new_height}px` });
1273                                    $prod_image_list.find('.owl-height').css({ 'height': `${new_height}px` });
1274                                }
1275                            }, 5);
1276                        };
1277                        image.src = image_url;
1278                    });
1279                },
1280                onChanged: function () {
1281                    var owl = $prod_image_list_rolling.data('owlCarousel');
1282                    var current = 0;
1283                    if (typeof owl !== "undefined") current = owl._current;
1284                    var li_list = $prod_image_list.find('li.owl-dot');
1285                    li_list.find('a').removeClass('active');
1286                    li_list.eq(current).find('a').addClass('active');
1287                    if (items.length > 1) {
1288                        $prod_goods_form.find('header').css('margin-top', 0);
1289                    }
1290                    // 현재 이미지 너비로 슬라이드 너비 수정
1291                    var current_width = $(items[current]).find('img').css('width');
1292                    $prod_image_list.find('.owl-carousel').css({ 'width': current_width, 'height': '' });
1293                }
1294            });
1295        } else {
1296            $prod_image_list_rolling.owlCarousel({
1297                dots: true,
1298                navigation: true,
1299                slideSpeed: 300,
1300                paginationSpeed: 400,
1301                singleItem: true,
1302                animateOut: 'fadeOut',
1303                items: 1,
1304                autoHeight: false,
1305                onInitialized: function () {
1306                    var owl_item_list = $prod_image_list.find('.owl-item');
1307                    var total = owl_item_list.length;
1308                    var html = '';
1309                    html += '<span class="_numbering_variable">1</span> <span class="_numbering_total">/ ' + total + '</span>';
1310                    if (total <= 1) {
1311                        $("._carousel_count_numbering").hide();
1312                    }
1313                    else {
1314                        $("._carousel_count_numbering").append(html);
1315                    }
1316                },
1317                onChanged: function () {
1318                    var owl = $prod_image_list_rolling.data('owlCarousel');
1319                    var current = 0;
1320                    if (typeof owl !== "undefined") current = owl._current;
1321                    var now = parseInt(current);
1322                    var html = '';
1323                    html += '<span class="_numbering_variable">' + (now + 1) + '</span>';
1324                    $("._numbering_variable").html(html);
1325                }
1326            });
1327        }
1328    };
1329
1330    /**
1331     * 상품 이미지 롤링 특정 위치 이동
1332     * @param no
1333     */
1334    function changeProdImageRolling(no) {
1335        var owl = $prod_image_list_rolling.data('owlCarousel');
1336        if (typeof owl !== "undefined") {
1337            owl.to(no);
1338
1339            var option_img_list = $prod_image_list.find('._color_option_img');
1340            if (option_img_list.length) {
1341                $prod_image_list.attr('data-preview-type', 'prod');
1342                option_img_list.attr('data-visible', 'false');
1343            }
1344        }
1345    }
1346
1347    const loadProductDetail = function (code, idx, menu_code, current_url) {
1348        $.ajax({
1349            type: 'POST',
1350            data: { 'idx': idx, 'menu_code': menu_code, current_url },
1351            url: ('/shop/load_product_detail.cm'),
1352            dataType: 'json',
1353            cache: false,
1354            success: function (result) {
1355                if (result.msg === 'SUCCESS') {
1356                    $('.shop_detail_wrap_csr_' + code).html(result.html);
1357
1358		                function injectHTMLWithScripts(html, target = document.body) {
1359			                const wrapper = document.createElement('div');
1360			                wrapper.innerHTML = html;
1361
1362			                // 먼저 script를 제외한 나머지 노드 삽입
1363			                const scripts = wrapper.querySelectorAll('script');
1364			                scripts.forEach(script => script.remove()); // 미리 제거
1365
1366			                // 나머지 HTML 삽입
1367			                Array.from(wrapper.childNodes).forEach(node => target.appendChild(node));
1368
1369			                // script 수동 실행
1370			                scripts.forEach(oldScript => {
1371				                const newScript = document.createElement('script');
1372
1373				                // 복사: src / type / defer / async 등
1374				                for (const attr of oldScript.attributes) {
1375					                newScript.setAttribute(attr.name, attr.value);
1376				                }
1377
1378				                // inline script일 경우 textContent 복사
1379				                if (!oldScript.src) {
1380					                newScript.textContent = oldScript.textContent;
1381				                }
1382
1383				                // 실행
1384				                document.body.appendChild(newScript);
1385			                });
1386		                }
1387
1388										if (result.script){
1389											injectHTMLWithScripts(result.script);
1390										}
1391
1392
1393	                // 최근 본 상품, refresh
1394										if (window.RECENT_PRODUCT && typeof window.RECENT_PRODUCT.refresh === 'function') {
1395											window.RECENT_PRODUCT.refresh();
1396										}
1397
1398										// 상세페이지 CSR 로드 완료
1399										document.dispatchEvent(new CustomEvent('productDetailLoaded', {detail: {idx}}));
1400								}
1401            },
1402            error: function (req, status, error) {
1403                document.write(req.responseText);
1404            }
1405        });
1406    };
1407
1408    const loadBookingDetail = function (code, idx, day, endDay, current_url) {
1409        $.ajax({
1410            type: 'POST',
1411            data: { 'idx': idx, 'day': day, 'endDay': endDay, current_url, 'widgetCode': code },
1412            url: ('/shop/load_booking_detail.cm'),
1413            dataType: 'json',
1414            cache: false,
1415            success: function (result) {
1416                if (result.msg === 'SUCCESS') {
1417                    $('.book_detail_wrap_csr_' + code).html(result.output);
1418                }
1419            }
1420        });
1421    };
1422
1423    const loadCode = function (code) {
1424        $.ajax({
1425            type: 'POST',
1426            data: { 'code': code },
1427            url: ('/shop/load_code.cm'),
1428            dataType: 'json',
1429            cache: false,
1430            success: function (result) {
1431                if (result.msg === 'SUCCESS') {
1432                    // DOM에 콘텐츠를 추가하는 함수
1433                    const appendContentToDOM = function() {
1434                        $('.code_wrap_csr_' + code).html(result.output);
1435
1436                        // script 태그를 찾아서 실행
1437                        const $container = $('.code_wrap_csr_' + code);
1438                        $container.find('script').each(function() {
1439                            const scriptContent = $(this).html();
1440                            if (scriptContent) {
1441                                try {
1442                                    // script 내용 실행
1443                                    setTimeout(function() {
1444                                        eval(scriptContent);
1445                                    }, 0);
1446                                } catch (e) {
1447                                    console.error('Script execution error:', e);
1448                                }
1449                            }
1450                        });
1451                    };
1452
1453                    // 다른 CSR 위젯이 로드되었는지 확인
1454                    const checkInterval = setInterval(function () {
1455                        const target_now = $('[class^="shop_detail_wrap_csr_"], [class^="book_detail_wrap_csr_"]');
1456                        const target = target_now.length > 0 ? target_now.first() : null;
1457
1458
1459                        if (!target) {
1460                            clearInterval(checkInterval); // 감지 종료
1461                            appendContentToDOM();
1462                            return;
1463                        }
1464
1465
1466                        if (target.is(':empty')) {
1467                            return;
1468                        }
1469
1470                        // prod_detail 요소가 있는지 확인
1471                        if (!target.find('#prod_detail').length) {
1472														return;
1473                        }
1474
1475                        clearInterval(checkInterval); // 감지 종료
1476                        appendContentToDOM();
1477                    }, 100); // 100ms 간격으로 검사
1478                }
1479            }
1480        });
1481    };
1482
1483    /**
1484     * 무료배송 안내 모달의 추가옵션 영역(#prod_free_ship_notify_additional_options)이
1485     * 현재 활성 상태인지 확인.
1486     * - PC cocoaDialog: 모달이 열려있어야(`.modal.in`) 활성으로 인정
1487     * - 모바일 imSheet: 닫힐 때 DOM에서 element 자체가 제거되므로 존재만으로 활성으로 간주
1488     * 모달이 닫혔지만 DOM에 잔존한 경우(PC에서 `in` 클래스만 빠진 상태) 일반 옵션 흐름과
1489     * 섞이지 않도록 분기 조건으로 사용한다.
1490     */
1491    var isFreeShipNotifyAdditionalActive = function () {
1492        var $el = $('#prod_free_ship_notify_additional_options');
1493        if (!$el.length) return false;
1494        var $modal = $el.closest('.modal');
1495        return !$modal.length || $modal.hasClass('in');
1496    };
1497
1498    /**
1499     * 장바구니 무료배송 안내 모달 전용 옵션 로더.
1500     * 기본 #prod_options 컨테이너가 없는 환경(cart 페이지 모달)에서 load_prod_additional_option.cm을 호출해
1501     * 모달 전용 컨테이너(#prod_free_ship_notify_additional_options)의 HTML을 갱신한다.
1502     * 최초 렌더와 동일한 템플릿을 재사용하여 스타일 일관성 유지 (loadProdAdditionalOptions 패턴 참조).
1503     */
1504    var loadOptionForFreeShipNotify = function (prod_idx) {
1505        if (_isFreeShipNotifyTemplatePreview) return;
1506        var $freeShipContainer = $('#prod_free_ship_notify_additional_options');
1507        if (!$freeShipContainer.length) return;
1508
1509        $.ajax({
1510            type: 'POST',
1511            data: {
1512                'prod_idx': prod_idx,
1513                'selected_require_options': selected_require_options,
1514                '__': prod_edit_time,
1515                'is_additional': true,
1516                'context': 'cart'
1517            },
1518            url: '/shop/load_prod_additional_option.cm',
1519            dataType: 'json',
1520            cache: false,
1521            success: function (result) {
1522                if (result.msg === 'SUCCESS') {
1523                    $freeShipContainer.html(result.option_html);
1524                } else {
1525                    alert(result.msg);
1526                }
1527            }
1528        });
1529    };
1530
1531    var loadOption = function (type, prod_idx) {
1532        if (_isFreeShipNotifyTemplatePreview) return;
1533        // 장바구니 무료배송 안내 모달이 열려있는 cart 컨텍스트에서는 모달 전용 로더로 위임
1534        if (type === 'cart' && isFreeShipNotifyAdditionalActive()) {
1535            return loadOptionForFreeShipNotify(prod_idx);
1536        }
1537
1538        $.ajax({
1539            type: 'POST',
1540            data: { 'type': type, 'prod_idx': prod_idx, 'selected_require_options': selected_require_options, '__': prod_edit_time },
1541            url: ('/shop/load_option.cm'),
1542            dataType: 'json',
1543            cache: false,
1544            success: function (result) {
1545                if (result.msg == 'SUCCESS') {
1546                    max_prod_quantity = result.max_prod_quantity;
1547                    max_member_quantity = result.max_member_quantity;
1548                    maximum_purchase_quantity_type = result.maximum_purchase_quantity_type;
1549                    optional_limit = result.optional_limit;
1550                    optional_limit_type = result.optional_limit_type;
1551                    if (result.prod_name) prod_name = result.prod_name;
1552                    $options.html(result.option_html);
1553
1554                    // 색상 옵션이 있고 + 색상 옵션의 이미지까지 있다면
1555                    if (options.exist_color_option && options.is_exist_color_option_images) {
1556                        if ($prod_image_list.find('.xzoom').length) {
1557                            // xzoom 사용
1558                            $options.find('label[data-opttype]').hover(function () {
1559                                var $_that = $(this);
1560                                var $_target_wrap = $("#btn_owl_" + $_that.attr('data-optcode'));
1561                                if (!$_target_wrap.length) {
1562                                    $_target_wrap = $("#btn_owl_0");
1563                                }
1564                                $_target_wrap.find('a').trigger('click');
1565                            });
1566                        } else {
1567                            // xzoom 미사용2
1568                            var $_color_option_img = $prod_image_list.find('._color_option_img');
1569                            if ($_color_option_img.length) {
1570                                (function () {
1571                                    var hover_evnets = (IS_MOBILE ? 'touchstart touchend' : 'mouseover');
1572                                    $options.find('label[data-opttype] ._bg').off(hover_evnets).on(hover_evnets, function (e) {
1573                                        // mouse over
1574                                        var $_that = $(this);
1575                                        var opt_code = $_that.attr('data-optcode');
1576                                        var $_target = $_color_option_img.filter('[data-optcode="' + opt_code + '"]');
1577
1578                                        $_color_option_img.attr('data-visible', 'false');
1579
1580                                        if ($_target.length) {
1581                                            if ($_target.attr('src').length == 0) {
1582                                                $_target.attr('src', $_target.attr('data-src'));
1583                                                // 이미지 load 이벤트
1584                                                $_target.on('load', function () {
1585                                                    var _width = this.naturalWi
1585dth;
1586                                                    var _height = this.naturalHeight;
1587
1588                                                    if (_width == _height) {
1589                                                        var _wrap_width = $prod_image_list.width();
1590                                                        var _wrap_height = $prod_image_list.height();
1591
1592                                                        if (_wrap_width == _wrap_height) {
1593                                                            $(this).attr('data-size-type', 'A');
1594                                                        } else if (_wrap_width > _wrap_height) {
1595                                                            $(this).attr('data-size-type', 'B');
1596                                                        } else {
1597                                                            $(this).attr('data-size-type', 'C');
1598                                                        }
1599                                                    } else if (_width > _height) {
1600                                                        $(this).attr('data-size-type', 'B');
1601                                                    } else {
1602                                                        $(this).attr('data-size-type', 'C');
1603                                                    }
1604                                                });
1605                                            }
1606                                            $prod_image_list.attr('data-preview-type', 'option');
1607                                            $_target.attr('data-visible', 'true');
1608                                        }
1609
1610                                        switch (e.type) {
1611                                            case 'touchstart':
1612                                                prod_option_touching = true;
1613                                                break;
1614                                            case 'touchend':
1615                                                prod_option_touching = false;
1616                                                break;
1617                                        }
1618                                    });
1619
1620                                    $options.find('label[data-opttype] ._bg').off('mouseout').on('mouseout', function (e) {
1621                                        // mouse out
1622                                        var $_that = $(this);
1623                                        var opt_code = $_that.attr('data-optcode');
1624                                        var $_target = $_color_option_img.filter('[data-optcode="' + opt_code + '"]');
1625
1626                                        $prod_image_list.attr('data-preview-type', 'prod');
1627
1628                                        if ($_target.length) {
1629                                            $_target.attr('data-visible', 'false');
1630                                        }
1631                                    });
1632
1633                                    if (IS_MOBILE) {
1634                                        // 모바일일 경우 터치이벤트 추가
1635                                        var $_body = $("#doz_body");
1636                                        if (!$_body.hasClass('_bind_touchstart_evt')) {
1637                                            $_body.on('touchstart', function (e) {
1638                                                if (!prod_option_touching) {
1639                                                    $prod_image_list.attr('data-preview-type', 'prod');
1640                                                    if ($_color_option_img.length) {
1641                                                        $_color_option_img.attr('data-visible', 'false');
1642                                                    }
1643                                                }
1644                                            }).addClass('_bind_touchstart_evt');
1645                                        }
1646                                    }
1647                                })();
1648                            }
1649                        }
1650                    }
1651
1652                    if (IS_MOBILE) {
1653                        addEventMobileOptionInput();
1654                    }
1655
1656                } else {
1657                    alert(result.msg);
1658                }
1659            }
1660        });
1661
1662				if($prod_additional.length > 0){
1663					loadProdAdditional(prod_idx);
1664				}
1665    };
1666
1667		var loadProdAdditional = function (prod_idx) {
1668			if (_isFreeShipNotifyTemplatePreview) return;
1669			if(is_mobile_width){
1670				$.ajax({
1671					type : 'POST',
1672					data : {'prod_idx' : prod_idx, 'selected_additional_prod_idx' : current_select_additional_prod_idx, '__' : prod_edit_time},
1673					url : (
1674							'/shop/load_prod_additional_mobile.cm'
1675					),
1676					dataType : 'json',
1677					cache : false,
1678					success : function(result){
1679						if(result.msg == 'SUCCESS'){
1680							// 추가 상품
1681							if(!result.prod_additional.prod_list.length) return;
1682							$prod_additional.html(result.prod_additional_sheet_contents);
1683							// $prod_additional.html(result.prod_additional.html);
1684							// $prod_additional.find('#btnProdAdditionalSelect').off('click').on('click', function(e){
1685							// 	e.stopPropagation();
1686							// 	imSheet.open({
1687							// 		id: 'prod_additional_sheet',
1688							// 		html: result.prod_additional_sheet_contents,
1689							// 		backdrop: 'rgba(0, 0, 0, 0.15)',
1690							// 		zIndex: 17001
1691							// 	});
1692							// });
1693						}else{
1694							alert(result.msg);
1695						}
1696					}
1697				});
1698			}else{
1699				$.ajax({
1700					type: 'POST',
1701					data: {'prod_idx': prod_idx, 'selected_additional_prod_idx': current_select_additional_prod_idx, '__': prod_edit_time },
1702					url: ('/shop/load_prod_additional.cm'),
1703					dataType: 'json',
1704					cache: false,
1705					success: function (result) {
1706						if (result.msg == 'SUCCESS') {
1707							// 추가 상품
1708							if(!result.prod_additional.prod_list.length) return;
1709							$prod_additional.html(result.prod_additional.html);
1710						} else {
1711							alert(result.msg);
1712						}
1713					}
1714				});
1715			}
1716
1717    };
1718
1719		var loadProdAdditionalOptions = function (product_idx, cb) {
1720			if (_isFreeShipNotifyTemplatePreview) return;
1721			if(is_mobile_width){
1722				$.ajax({
1723					type: 'POST',
1724					data: {'prod_idx': product_idx, 'selected_require_options': selected_prod_additional_require_options, '__': prod_edit_time, 'is_additional': true },
1725					url: ('/shop/load_prod_additional_option_mobile.cm'),
1726					dataType: 'json',
1727					cache: false,
1728					success: function (result) {
1729						if (result.msg == 'SUCCESS') {
1730							prod_additional_require_option_count = {
1731								...prod_additional_require_option_count,
1732								...result.require_option_count};
1733							prod_additional_require_input_option_count = {
1734								...prod_additional_require_input_option_count,
1735								...result.require_input_option_count
1736							};
1737							if(isFreeShipNotifyAdditionalActive()){
1738								$('#prod_free_ship_notify_additional_options').html(result.option_html);
1739							}else{
1740								// 모바일에서는 드롭다운이 동작하지 않도록 클릭 이벤트 대치
1741								$prod_additional_options.html(result.option_html).find('._form_parent:not(.disabled) ._form_select_wrap > a, ._requireInputOption').each(function(e){
1742									let $that = $(this);
1743									$that.on('click', function(e){
1744										e.stopPropagation();
1745										loadProdAdditionalOptionRequire(product_idx, $that.attr('data-option-code'));
1746									});
1747								});
1748
1749								var full_input = false;
1750								var $form = $prod_additional_options.find('._form_parent');
1751								// 첫번째 선택 후 창을 닫았을때
1752								$form.each(function (index) {
1753
1754									var is_input = $(this).children().hasClass('option_box_wrap');
1755
1756									if (is_input) {
1757										var input_items = $(this).find('._requireInputOption');
1758										input_items.each(function (index) {
1759											if ($(this).val() != '') {
1760												if ((index + 1) == input_items.length) {
1761													full_input = true;
1762												}
1763											} else {
1764												return false;
1765											}
1766										});
1767
1768									} else {
1769										var prev_is_selected = $(this).prev().find('.dropdown-item').hasClass('selected');
1770										var is_selected = $(this).find('.dropdown-item').hasClass('selected');
1771										// 이전 필수 드롭다운이 선택 되어 있을 경위
1772										if (prev_is_selected || full_input) {
1773											$(this).toggleClass('disabled', false);
1774
1775											full_input = false;
1776
1777											/* 드롭다운 활성화는 loadProdAdditionalOptionRequire의 시트 호출로 대체 */
1778										} else if (index === 0) {
1779											// 첫 드롭다운은 일단 true로 표시
1780											$(this).toggleClass('disabled', false);
1781										} else {
1782											$(this).toggleClass('disabled', true);
1783										}
1784									}
1785								});
1786
1787								if (typeof prod_additional_require_option_count[product_idx] == 'undefined' || selected_prod_additional_require_options.length == prod_additional_require_option_count[product_idx]){
1788									/* 필수 옵션이 모두 선택되었을 경우 시트를 닫음 */
1789									imSheet.close('prod_additional_sheet');
1790								}else{
1791									loadProdAdditionalOptionRequire(product_idx);
1792								}
1793							}
1794
1795							updateSelectedOptions('prod');
1796						} else {
1797							alert(result.msg);
1798						}
1799					}
1800				});
1801			}else{
1802				$.ajax({
1803					type: 'POST',
1804					data: {'prod_idx': product_idx, 'selected_require_options': selected_prod_additional_require_options, '__': prod_edit_time},
1805					url: ('/shop/load_prod_additional_option.cm'),
1806					dataType: 'json',
1807					cache: false,
1808					success: function (result) {
1809						if (result.msg == 'SUCCESS') {
1810							prod_additional_require_option_count = {
1811								...prod_additional_require_option_count,
1812								...result.require_option_count};
1813							prod_additional_require_input_option_count = {
1814								...prod_additional_require_input_option_count,
1815								...result.require_input_option_count
1816							};
1817							if(isFreeShipNotifyAdditionalActive()){
1818								$('#prod_free_ship_notify_additional_options').html(result.option_html);
1819							}else{
1820								$prod_additional_options.html(result.option_html);
1821							}
1822							// PC 인라인 컨테이너 사용 후, 다른 상품 선택 시 기존 인라인 영역 정리
1823							try{
1824								if($prod_additional_options.hasClass('_prod_additional_inline_options')){
1825									$('._prod_additional_inline_options').not($prod_additional_options).remove();
1826								}
1827							}catch(e){}
1828
1829							updateSelectedOptions('prod');
1830						} else {
1831							alert(result.msg);
1832						}
1833					}
1834				});
1835			}
1836		}
1837
1838	/**
1839	 * 모바일 추가 상품 선택 시 다음 필수 옵션에 대한 html을 시트에 출력
1840	 * @param product_idx
1841	 * @param option_code
1842	 */
1843		var loadProdAdditionalOptionRequire = function(product_idx, option_code = null) {
1844			if (_isFreeShipNotifyTemplatePreview) return;
1845			$.ajax({
1846				type: 'POST',
1847				data: {'prod_idx': product_idx, 'option_code': option_code, 'selected_require_options': selected_prod_additional_require_options, '__': prod_edit_time, 'is_additional': true },
1848				url: ('/shop/load_prod_additional_option_require.cm'),
1849				dataType: 'json',
1850				cache: false,
1851				success: function (result) {
1852					if (result.msg == 'SUCCESS') {
1853						prod_additional_require_option_count = {
1854							...prod_additional_require_option_count,
1855							...result.require_option_count};
1856						prod_additional_require_input_option_count = {
1857							...prod_additional_require_input_option_count,
1858							...result.require_input_option_count
1859						};
1860
1861						// 현재 시트의 내용을 받아온 옵션(html)으로 변경(시트를 닫고 새로 열어줌)
1862						imSheet.close('prod_additional_sheet', () => {
1863							imSheet.open({
1864								id: 'prod_additional_sheet',
1865								html: result.prod_additional_sheet_contents,
1866								backdrop: 'rgba(0, 0, 0, 0.15)',
1867								zIndex: 17001
1868							});
1869						});
1870					} else {
1871						alert(result.msg);
1872					}
1873				}
1874			});
1875		}
1876
1877		/**
1878		 * 모바일 추가 상품 선택 옵션에 대한 html을 시트에 출력
1879		 * @param product_idx
1880		 * @param option_code
1881		 */
1882		var loadProdAdditionalOptionOptional = function(product_idx, option_code) {
1883			if (_isFreeShipNotifyTemplatePreview) return;
1884			$.ajax({
1885				type: 'POST',
1886				data: {'prod_idx': product_idx, 'option_code': option_code, '__': prod_edit_time, 'is_additional': true },
1887				url: ('/shop/load_prod_additional_option_optional.cm'),
1888				dataType: 'json',
1889				cache: false,
1890				success: function (result) {
1891					if (result.msg == 'SUCCESS') {
1892						// 현재 시트의 내용을 받아온 옵션(html)으로 변경(시트를 닫고 새로 열어줌)
1893						imSheet.close('prod_additional_sheet', () => {
1894							imSheet.open({
1895								id: 'prod_additional_sheet',
1896								html: result.prod_additional_sheet_contents,
1897								backdrop: 'rgba(0, 0, 0, 0.15)',
1898								zIndex: 17001
1899							});
1900						});
1901					} else {
1902						alert(result.msg);
1903					}
1904				}
1905			});
1906		}
1907
1908		/**
1909		 * 추가 상품 선택 시 선택 옵션에 대한 html을 반환
1910		 * @param product_idx
1911		 * @param option_code
1912		 */
1913		var loadProdAdditionalOptionalList = function() {
1914			if (_isFreeShipNotifyTemplatePreview) return;
1915			$('._prod_additional_option_optional').each((k, v) => {
1916				let $that = $(v);
1917				let product_idx = $that.attr('data-prod-idx');
1918				if($that.attr('data-prod-idx') > 0){
1919					$.ajax({
1920						type: 'POST',
1921						data: {'prod_idx': $that.attr('data-prod-idx'), '__': prod_edit_time, 'is_additional': true },
1922						url: ('/shop/load_prod_additional_optional_list.cm'),
1923						dataType: 'json',
1924						cache: false,
1925						success: function (result) {
1926							if (result.msg == 'SUCCESS') {
1927								if(is_mobile_width){
1928									$that.html(result.option_html).find('._form_select_wrap > a').each(function(e){
1929										let $form = $(this);
1930										$form.on('click', function(e){
1931											e.stopPropagation();
1932											loadProdAdditionalOptionOptional(product_idx, $form.attr('data-option-code'));
1933										});
1934									});
1935								}else{
1936									$that.html(result.option_html);
1937								}
1938							}
1939						}
1940					});
1941				}
1942			});
1943		}
1944
1945    var loadDelivSetting = _.debounce(function (prod_idx, change_country, deliv_type, deliv_pay_type) {
1946        $deliv_country = change_country;
1947        $deliv_pay_type = deliv_pay_type;
1948            var is_design_mode = (location.pathname.indexOf('/admin/design') != -1);
1949            $.ajax({
1950                type: 'GET',
1951                data: {
1952                    'prod_idx': prod_idx,
1953                    'change_country': change_country,
1954                    'deliv_type': deliv_type,
1955                    'deliv_pay_type': deliv_pay_type,
1956                    '__': MEMBER_HASH
1957                },
1958                url: (is_mobile_width ? '/shop/load_deliv_setting.cm' : '/shop/load_deliv_setting_pc.cm'),
1959                dataType: 'json',
1960                cache: !is_design_mode,
1961                async: false,
1962                success: function (result) {
1963                    if (result.msg == 'SUCCESS') {
1964                        $prod_deliv_setting.html(result.deliv_html);
1965                        if (window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK && typeof window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate === 'function') {
1966                            window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($prod_deliv_setting.get(0));
1967                        }
1968                        if (is_mobile_width) {
1969                            $prod_detail.find('._today_arrival_wrap').html(result.today_arrival_html);
1970                        }
1971
1972                        /* 환불 정보 입력 */
1973                        last_refund_data = result.refund;
1974                        setDetailReturnHtml();
1975
1976                        // 표시항목 주입: sanitize 후 주입 (IMOPS-13023)
1977                        if ($prod_detail && $prod_detail.find('._item_detail_wrap').length > 0) {
1978                            $prod_detail.find('._item_detail_wrap').html(_sanitizeHtml(result.item_detail_html));
1979                            // sanitize로 script가 제거되어 popover 초기화가 누락되므로 재초기화 (IMOPS-13118)
1980                            $prod_detail.find('._item_detail_wrap .btn-popover').popover();
1981                            $prod_detail.find('._item_detail_wrap .html-popover').popover({html: true});
1982                        }
1983
1984                        if (IS_MOBILE) {
1985                            addEventMobileOptionInput();
1986                        }
1987
1988                        if (typeof makeNaverPayBtn !== 'undefined') {
1989														try{
1990															// v2 는 show/hide idempotent 호출이라 항상 재호출(국가 노출 갱신). v1 은 기존대로 최초 1회만.
1991															if(window.__npayButtonV2 || !document.getElementById('naverPayWrap').innerHTML){
1992																makeNaverPayBtn('naverPayWrap', (
1993																		change_country == 'KR' || change_country == 'none' || change_country == ''
1994																));
1995															}
1996														} catch(e) {
1997
1998														}
1999                        }
2000
2001                        if (typeof kakaoCheckout != 'undefined' && typeof makeTalkPayButton != 'undefined') {
2002                            var _target_container_id = 'create-kakao-checkout-button';
2003                            _target_container_id = document.getElementById(_target_container_id) ? _target_container_id : 'create-kakao-checkout-button-mobile';
2004                            if (!document.getElementById(_target_container_id).innerHTML) {
2005                                makeTalkPayButton(_target_container_id, (change_country == 'KR' || change_country == 'none' || change_country == ''));
2006                            }
2007                        }
2008
2009                    } else {
2010                        alert(result.msg);
2011                    }
2012                }
2013            });
2014    }, 100, { leading: true, trailing: false});
2015
2016    /**
2017     * 통합 상품정보 AJAX 호출 (item_detail_html + deliv_html 분리 응답)
2018     * Feature flag ON 시 DetailItemMake/loadDelivSetting 대신 사용
2019     */
2020    const loadItemInfo = ({ prod_idx, change_country, deliv_type, deliv_pay_type } = {}) => {
2021        const is_design_mode = location.pathname.includes('/admin/design');
2022        $deliv_country = change_country;
2023        $deliv_pay_type = deliv_pay_type;
2024        const isKrOrEmpty = change_country === 'KR' || change_country === 'none' || change_country === '';
2025        $.ajax({
2026            type: 'GET',
2027            data: {
2028                prod_idx,
2029                change_country,
2030                deliv_type,
2031                deliv_pay_type,
2032                '__': MEMBER_HASH
2033            },
2034            url: '/shop/prod_detail_item_info.cm',
2035            dataType: 'json',
2036            cache: !is_design_mode,
2037            success(result) {
2038                if (result.msg !== 'SUCCESS') return;
2039
2040                if (shop_view_style === 'b' && !IS_MOBILE) {
2041                    // style_b PC: 상품정보 → _item_detail_wrap, 배송정보 → 바텀시트(#prod_deliv_setting)
2042                    var $item_detail_wrap = $prod_detail && $prod_detail.find('._item_detail_wrap');
2043                    if ($item_detail_wrap && $item_detail_wrap.length > 0) {
2044                        $item_detail_wrap.html(result.item_detail_html);
2045                    }
2046                    $prod_deliv_setting.html(result.deliv_html);
2047                    if (window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK && typeof window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate === 'function') {
2048                        if ($item_detail_wrap && $item_detail_wrap.length > 0) {
2049                            window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($item_detail_wrap.get(0));
2050                        }
2051                        window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($prod_deliv_setting.get(0));
2052                    }
2053                } else {
2054                    // style_a + style_b 모바일: 상품정보 + 배송정보 합쳐서 _item_detail_wrap에 주입
2055                    if ($prod_detail && $prod_detail.find('._item_detail_wrap').length > 0) {
2056                        var $item_detail_wrap = $prod_detail.find('._item_detail_wrap');
2057                        $item_detail_wrap.html(result.item_detail_html + result.deliv_html);
2058                        if (window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK && typeof window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate === 'function') {
2059                            window.IMWEB_SHOP_PAYMENT_METHOD_BENEFIT_SDK.hydrate($item_detail_wrap.get(0));
2060                        }
2061                    }
2062                }
2063
2064                // 배송 타입 업데이트
2065                if (result.deliv_type) SITE_SHOP_DETAIL.addDelivType(result.deliv_type);
2066                if (result.deliv_pay_type) SITE_SHOP_DETAIL.addDelivPayType(result.deliv_pay_type);
2067
2068                var hasFreeShipNotifyTargetResult = typeof result.free_ship_notify_delivery_target_product === 'boolean';
2069                var isFreeShipNotifyDeliveryTarget = hasFreeShipNotifyTargetResult
2070                    ? result.free_ship_notify_delivery_target_product
2071                    : change_country === 'KR';
2072                $('._free_ship_notify_detail_magnet_wrap').toggleClass(
2073                    'tw-hidden',
2074                    hasFreeShipNotifyTargetResult ? !isFreeShipNotifyDeliveryTarget : ($('._deliv_country_selector').length > 0 && change_country !== 'KR')
2075                );
2076
2077                if (typeof result.deliv_price_flexable_key === 'number') {
2078                    var newThreshold = isFreeShipNotifyDeliveryTarget ? result.deliv_price_flexable_key : 0;
2079                    var newThresholdStr = String(newThreshold);
2080                    $('#foFreeShipNotifyProdDetailMagnet').attr('data-init-deliv-price-flexable-key', newThresholdStr);
2081                    $('#shop_detail_add_cart_alarm_with_free_ship_notify').attr('data-init-deliv-price-flexable-key', newThresholdStr);
2082                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:freeShippingThreshold:update', {
2083                        detail: { threshold: newThreshold }
2084                    }));
2085                }
2086
2087                // 환불 정보
2088                last_refund_data = result.refund;
2089                setDetailReturnHtml();
2090
2091                // popover 초기화
2092                $('.btn-popover').popover();
2093                $('.html-popover').popover({html: true});
2094
2095                // 네이버페이 버튼 — v2 에서만 재렌더(show/hide). v1 은 기존 no-op 유지(플래그 미설정).
2096                if (window.__npayButtonV2 && typeof makeNaverPayBtn !== 'undefined') {
2097                    try { makeNaverPayBtn('naverPayWrap', isKrOrEmpty); } catch(e) {}
2098                }
2099
2100                // 카카오페이 버튼
2101                if (typeof kakaoCheckout !== 'undefined' && typeof makeTalkPayButton !== 'undefined') {
2102                    try { makeTalkPayButton('create-kakao-checkout-button', isKrOrEmpty); } catch(e) {}
2103                }
2104            }
2105        });
2106    };
2107
2108    /**
2109     * 선택형 필수 옵션 선택
2110     * @param type cart (장바구니변경), prod(상품상세)
2111     * @param option_code
2112     * @param value_code
2113     */
2114    var selectRequireOption = function (type, prod_idx, option_code, value_code, value_name, success) {
2115        var data = { 'value_type': 'SELECT', 'option_code': option_code, 'value_code': value_code, 'value_name': value_name };
2116        var no = findSelectedRequireOption(option_code);
2117        if (no == -1) {
2118            if (value_code == '') return;
2119            selected_require_options.push(data);
2120            /** 처음 선택된 옵션인 경우 새로 추가*/
2121        } else {
2122            if (value_code == '') {
2123                /** 옵션 삭제 */
2124                selected_require_options.splice(no, (s
2124elected_require_options.length - no));
2125            } else {
2126                selected_require_options[no] = data;
2127                /** 이미 선택된 옵션인 경우 기존 값 교체 */
2128                if (no < selected_require_options.length - 1) {
2129                    selected_require_options.splice(no + 1, (selected_require_options.length - (no + 1)));
2130                }
2131            }
2132        }
2133        if (selected_require_options.length == require_option_count) {
2134            if (isFreeShipNotifyAdditionalActive()) {
2135                // 무료 배송 안내 모달에서는 추가하기 버튼을 눌러야 반영되므로
2136                // selectOption/selected_require_options reset을 생략하고 현재 선택 상태를 유지한 채 화면만 갱신한다.
2137                // (selectProdAdditionalRequireOption의 무료배송 모달 분기와 동일 패턴)
2138                loadOption(type, prod_idx);
2139                if (typeof success === 'function') success();
2140            } else {
2141                /** 필수 옵션이 모두 선택되었을 경우*/
2142                selectOption(
2143                    prod_idx,
2144                    {
2145                        options: selected_require_options,
2146                        require: true,
2147                        count: 1
2148                    },
2149                    function () {
2150                        selected_require_options = [];
2151                        loadOption(type, prod_idx);
2152                        success();
2153                    },
2154                    function (msg) {
2155                        alert(msg);
2156                    }
2157                );
2158            }
2159        } else {
2160            /** 필수옵션 선택이 아직 끝나지 않았을 경우 옵션 재로드 */
2161            loadOption(type, prod_idx);
2162        }
2163    };
2164
2165    /**
2166     * 입력형 필수 옵션 변경시 처리
2167     * @param type cart (장바구니변경), prod(상품상세)
2168     */
2169    var changeRequireInputOption = function (type, prod_idx, option_code, msg, success) {
2170        var data = { 'value_type': 'INPUT', 'option_code': option_code, 'value_code': '', 'value_name': msg };
2171        var no = findSelectedRequireOption(option_code);
2172        if (no == -1) {
2173            if (msg == '') return;
2174            selected_require_options.push(data);
2175            /** 처음 입력한 옵션인 경우 새로 추가*/
2176        } else {
2177            if (msg == '') {
2178                /** 입력형 옵션 삭제 */
2179                selected_require_options.splice(no, (selected_require_options.length - no));
2180            } else {
2181                /** 기존값 교체 */
2182                selected_require_options[no] = data;
2183            }
2184        }
2185        if (selected_require_options.length == require_option_count) {
2186            if (isFreeShipNotifyAdditionalActive()) {
2187                // 무료 배송 안내 모달에서는 추가하기 버튼을 눌러야 반영되므로
2188                // selectOption/selected_require_options reset을 생략하고 현재 선택 상태를 유지한 채 화면만 갱신한다.
2189                loadOption(type, prod_idx);
2190                if (typeof success === 'function') success();
2191            } else {
2192                /** 필수 옵션이 모두 선택되었을 경우*/
2193                selectOption(
2194                    prod_idx,
2195                    {
2196                        options: selected_require_options,
2197                        require: true,
2198                        count: 1
2199                    },
2200                    function () {
2201                        selected_require_options = [];
2202                        loadOption(type, prod_idx);
2203                        success();
2204                    },
2205                    function (msg) {
2206                        alert(msg);
2207                    }
2208                );
2209            }
2210        } else {
2211            // 웹가속기A(edge HTML cache) 사이트는 서버 IS_MOBILE이 항상 false라(UA가 CloudFront로 대체됨)
2212            // 모바일 바텀시트의 순차 필수옵션 unlock 재렌더가 죽는다. 뷰포트 기준 판정을 함께 사용한다.
2213            if ((IS_MOBILE || is_mobile_width) && selected_require_options.length == require_input_option_count) loadOption(type, prod_idx);
2214        }
2215    };
2216
2217		/**
2218     * 입력형 필수 옵션 변경시 처리
2219     * @param type cart (장바구니변경), prod(상품상세)
2220     */
2221    var changeProdAdditionalRequireInputOption = function (type, prod_idx, option_code, msg, success) {
2222        var data = { 'value_type': 'INPUT', 'option_code': option_code, 'value_code': '', 'value_name': msg };
2223        var no = findSelectedRequireOption(option_code);
2224        if (no == -1) {
2225            if (msg == '') return;
2226            selected_prod_additional_require_options.push(data);
2227            /** 처음 입력한 옵션인 경우 새로 추가*/
2228        } else {
2229            if (msg == '') {
2230                /** 입력형 옵션 삭제 */
2231                selected_prod_additional_require_options.splice(no, (selected_prod_additional_require_options.length - no));
2232            } else {
2233                /** 기존값 교체 */
2234                selected_prod_additional_require_options[no] = data;
2235            }
2236        }
2237        if (selected_prod_additional_require_options.length == prod_additional_require_option_count[prod_idx]) {
2238            if (isFreeShipNotifyAdditionalActive()) {
2239                loadProdAdditionalOptions(prod_idx);
2240                if (typeof success === 'function') success();
2241            } else {
2242                /** 필수 옵션이 없거나 모두 선택되었을 경우*/
2243                selectProdAdditionalOption(prod_idx, selected_prod_additional_require_options, true, 1, function () {
2244		                if(is_mobile_width){
2245			                imSheet.close('prod_additional_sheet');
2246		                }
2247	                  current_select_additional_prod_idx = 0;
2248                    selected_prod_additional_require_options = [];
2249	                  $prod_additional_options.html('');
2250                        $('#prod_additional_drop_menu_content').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // PC
2251                        // 인라인 컨테이너 초기화 및 원래 위치로 복구
2252                        try{ $('._prod_additional_inline_options').remove(); $prod_additional_options = $prod_additional_options_default; }catch(e){}
2253	                  $('#btnProdAdditionalSelect').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // 모바일
2254                    success();
2255                }, function (msg) {
2256                    alert(msg);
2257                });
2258            }
2259        } else {
2260	        loadProdAdditionalOptions(prod_idx);
2261        }
2262    };
2263
2264    /**
2265     * 옵션 선택
2266     * @param options [ {value_type: SELECT/INPUT, option_code:, value_code:, value_name:} ]
2267     */
2268    var selectOption = function (prod_idx, {options, require, count, idx = 0, skip_quantity_validation = false}, success, failed) {
2269        var btn_buy_onclick_temp = $('._btn_buy').attr('onclick');
2270        var btn_regularly_onclick_temp = $('._btn_regularly').attr('onclick');
2271        var btn_cart_onclick_temp = $('._btn_cart').attr('onclick');
2272        var btn_regularly_cart_onclick_temp = $('._btn_cart.im-regularly').attr('onclick');
2273        $('._btn_buy').attr('onclick', 'event.cancelBubble=true');
2274        $('._btn_cart').attr('onclick', 'event.cancelBubble=true');
2275        $.ajax({
2276            type: 'POST',
2277            data: {
2278                'prod_idx': prod_idx,
2279                'options': options,
2280                'require': (require ? 'Y' : 'N'),
2281                'count': count,
2282                'idx' : idx,
2283                'skip_quantity_validation': skip_quantity_validation ? true : false
2284            },
2285            url: ('/shop/select_option.cm'),
2286            dataType: 'json',
2287            cache: false,
2288            async: false,
2289            success: function (result) {
2290                if (result.msg == 'SUCCESS') {
2291                    prod_price = result.prod_price;
2292
2293                    var no = findSelectedOption(options);
2294                    if (no == -1) {
2295                        /* 상품 구매수량 체크 (장바구니 로드 시 검증 건너뛰기) */
2296                        if (skip_quantity_validation) {
2297                            selected_options.push(result.selected_option);
2298                            success();
2299                        } else {
2300                            var total_selected_count = getProdTotalQuantity(maximum_purchase_quantity_type);
2301
2302                            var new_total = total_selected_count + count;
2303                            if (max_prod_quantity == 0 || new_total <= max_prod_quantity) {
2304                                selected_options.push(result.selected_option);
2305                                /** 처음 선택된 옵션인 경우 새로 추가*/
2306                                success();
2307                            } else {
2308                                failed(LOCALIZE.설명_최대N개만구매가능한상품입니다(max_prod_quantity));
2309                            }
2310                        }
2311                    } else {
2312                        failed(LOCALIZE.설명_이미선택된옵션입니다());
2313                    }
2314                } else {
2315                    failed(result.msg);
2316                }
2317                $('._btn_buy').attr('onclick', btn_buy_onclick_temp);
2318                $('._btn_regularly').attr('onclick', btn_regularly_onclick_temp);
2319                $('._btn_cart').attr('onclick', btn_cart_onclick_temp);
2320                $('._btn_cart.im-regularly').attr('onclick', btn_regularly_cart_onclick_temp);
2321            }
2322        });
2323    };
2324
2325		/**
2326     * 옵션 선택
2327     * @param options [ {value_type: SELECT/INPUT, option_code:, value_code:, value_name:} ]
2328     */
2329    var selectProdAdditionalOption = function (prod_idx, options, require, count, success, failed) {
2330        //$('.npay_btn_pay').attr('onclick', 'event.cancelBubble=true');
2331        var btn_buy_onclick_temp = $('._btn_buy').attr('onclick');
2332        var btn_regularly_onclick_temp = $('._btn_regularly').attr('onclick');
2333        var btn_cart_onclick_temp = $('._btn_cart').attr('onclick');
2334        var btn_regularly_cart_onclick_temp = $('._btn_cart.im-regularly').attr('onclick');
2335        $('._btn_buy').attr('onclick', 'event.cancelBubble=true');
2336        $('._btn_cart').attr('onclick', 'event.cancelBubble=true');
2337        $.ajax({
2338            type: 'POST',
2339            data: { 'prod_idx': prod_idx, 'options': options, 'require': (require ? 'Y' : 'N'), 'count': count },
2340            url: ('/shop/select_option.cm'),
2341            dataType: 'json',
2342            cache: false,
2343            async: false,
2344            success: function (result) {
2345                if (result.msg == 'SUCCESS') {
2346                    var no = findSelectedProdAdditionalOption(options);
2347										var prod_name = result.prod_name;
2348                    if (no == -1) {
2349                        /* 상품 구매수량 체크 */
2350	                    var selected_prod_data = selected_prod_additionals.find(v=>v.idx===prod_idx);
2351                        var can_add = true;
2352                        var error_msg = '';
2353
2354                        // 추가상품 자체의 제한만 확인한다.
2355                        var total_selected_count = getProdAdditionalTotalQuantity(selected_prod_data.maximum_purchase_quantity_type, prod_idx);
2356                        var new_total = total_selected_count + count;
2357
2358                        if (selected_prod_data.maximum_purchase_quantity > 0 && new_total > selected_prod_data.maximum_purchase_quantity) {
2359                            can_add = false;
2360                            error_msg = LOCALIZE.설명_최대N개만구매가능한상품입니다(selected_prod_data.maximum_purchase_quantity);
2361                        }
2362
2363                        if (can_add) {
2364	                        selected_prod_additional_options.push({prod_idx, prod_name, ...result.selected_option});
2365                            /** 처음 선택된 옵션인 경우 새로 추가*/
2366                            success();
2367                        } else {
2368                            failed(error_msg);
2369                        }
2370                    } else {
2371                        failed(LOCALIZE.설명_이미선택된옵션입니다());
2372                    }
2373                } else {
2374                    failed(result.msg);
2375                }
2376                $('._btn_buy').attr('onclick', btn_buy_onclick_temp);
2377                $('._btn_regularly').attr('onclick', btn_regularly_onclick_temp);
2378                $('._btn_cart').attr('onclick', btn_cart_onclick_temp);
2379                $('._btn_cart.im-regularly').attr('onclick', btn_regularly_cart_onclick_temp);
2380            }
2381        });
2382    };
2383
2384		var checkRequireOption = function() {
2385			var main_require_option_count = parseInt(require_option_count) || 0;
2386			if (main_require_option_count <= 0) return true;
2387
2388			var has_selected_require_option = false;
2389			$.each(selected_options, function (no, data) {
2390				if (data.require == true) {
2391					has_selected_require_option = true;
2392					return false;
2393				}
2394			});
2395
2396			return has_selected_require_option;
2397		};
2398
2399    var getProdTotalQuantity = function (maximum_purchase_quantity_type) {
2400        /* 본상품 구매수량 체크: 추가상품은 별도 상품이므로 포함하지 않는다. */
2401        var total_selected_count = 0;
2402        if (maximum_purchase_quantity_type === 'order') {
2403            // 옵션이 없는 경우의 본품 수량 포함
2404            total_selected_count += order_count;
2405        }
2406        $.each(selected_options, function (idx, obj) {
2407            if (maximum_purchase_quantity_type === 'order') {
2408                total_selected_count += obj.count;
2409            }
2410        });
2411        return total_selected_count;
2412    };
2413
2414    var getProdRequireTotalQuantity = function () {
2415        /* 필수 옵션 구매수량 체크 */
2416        var total_selected_count = 0;
2417        $.each(selected_options, function (idx, obj) {
2418            if (obj.require) total_selected_count += obj.count;
2419        });
2420        return total_selected_count;
2421    };
2422
2423		var getProdAdditionalTotalQuantity = function (maximum_purchase_quantity_type, prod_idx) {
2424        /* 추가상품 구매수량 체크: 같은 추가상품의 옵션만 합산한다. */
2425        var total_selected_count = 0;
2426        $.each(selected_prod_additional_options, function (idx, obj) {
2427            if (maximum_purchase_quantity_type === 'order' && obj.prod_idx === prod_idx) {
2428                total_selected_count += obj.count;
2429            }
2430        });
2431        return total_selected_count;
2432    };
2433
2434    var getProdAdditionalRequireTotalQuantity = function () {
2435        /* 필수 옵션 구매수량 체크 */
2436        var total_selected_count = 0;
2437        $.each(selected_prod_additional_options, function (idx, obj) {
2438            if (obj.require) total_selected_count += obj.count;
2439        });
2440        return total_selected_count;
2441    };
2442
2443    /**
2444     * 선택된 옵션 삭제
2445     * @param optName
2446     * @param success
2447     */
2448    var removeSelectedOption = function (optNo, success) {
2449        if (optNo <= (selected_options.length - 1)) {
2450            selected_options.splice(optNo, 1);
2451            success();
2452        }
2453    };
2454
2455	/**
2456	 * 선택된 추가 상품 삭제
2457	 * @param optNo
2458	 * @param success
2459	 */
2460	var removeSelectedProdAdditional = function (prodIdx, success) {
2461		if (selected_prod_additionals.length) {
2462			selected_prod_additionals = selected_prod_additionals.filter(data => data.idx !== prodIdx);
2463			/* 추가 상품의 하위 옵션도 같이 삭제 */
2464			selected_prod_additional_options = selected_prod_additional_options.filter(data => data.prod_idx !== prodIdx);
2465
2466			success();
2467		}
2468	};
2469
2470	/**
2471     * 선택된 추가 상품 옵션 삭제
2472     * @param optNo
2473     * @param success
2474     */
2475    var removeSelectedProdAdditionalOption = function (optNo, success) {
2476        if (optNo <= (selected_prod_additional_options.length - 1)) {
2477            var deleted = selected_prod_additional_options.splice(optNo, 1)[0];
2478            if (deleted.require) {
2479                var hasRemainingRequiredOption = selected_prod_additional_options.some(function (option) {
2480                    return option.prod_idx === deleted.prod_idx && option.require;
2481                });
2482                if (!hasRemainingRequiredOption) {
2483                    removeSelectedProdAdditional(deleted.prod_idx, function () {});
2484                }
2485            }
2486            success();
2487        }
2488    };
2489
2490    /**
2491     * 해당 옵션이 현재 선택되어있는지 확인
2492     * @param options [{option_code:, value_code:, value_name:}]
2493     * */
2494    var findSelectedOption = function (options) {
2495        var found_no = -1;
2496        $.each(selected_options, function (no, data) {
2497            if (isSameOptionList(data.options, options)) {
2498                found_no = no;
2499                return false;
2500            }
2501        });
2502        return found_no;
2503    };
2504
2505		/**
2506     * 해당 옵션이 현재 선택되어있는지 확인
2507     * @param options [{option_code:, value_code:, value_name:}]
2508     * */
2509    var findSelectedProdAdditionalOption = function (options) {
2510        var found_no = -1;
2511        $.each(selected_prod_additional_options, function (no, data) {
2512	        if(data.prod_idx != current_select_additional_prod_idx){
2513		        return true; // 현재 선택 중인 추가 상품이 아닌 상품의 옵션은 체크하지 않고 continue 처리
2514	        }
2515	        if (isSameOptionList(data.options, options)) {
2516		        found_no = no;
2517		        return false;
2518	        }
2519        });
2520        return found_no;
2521    };
2522
2523    /**
2524     * 해당 필수 옵션이 현재 선택되어있는지 확인
2525     * @param optName
2526     */
2527    var findSelectedRequireOption = function (option_code) {
2528        var foundNo = -1;
2529        $.each(selected_require_options, function (no, data) {
2530            if (data.option_code == option_code) {
2531                foundNo = no;
2532                return false;
2533            }
2534        });
2535        return foundNo;
2536    };
2537
2538	/**
2539	 * 해당 필수 옵션이 현재 선택되어있는지 확인
2540	 * @param optName
2541	 */
2542	var findSelectedProdAdditionalRequireOption = function (option_code) {
2543		var foundNo = -1;
2544		$.each(selected_prod_additional_require_options, function (no, data) {
2545			if (data.option_code == option_code) {
2546				foundNo = no;
2547				return false;
2548			}
2549		});
2550		return foundNo;
2551	};
2552
2553    /**
2554     * 옵션 수량 및 재고 체크하는 기능
2555     * @param optNo
2556     * @param cnt
2557     * @returns {*}
2558     */
2559    var checkOptionCount = function (optNo, cnt) {
2560        // 그냥 재고 체크
2561        if (selected_options[optNo].use_stock && !selected_options[optNo].stock_un_limit) {
2562            if (cnt > selected_options[optNo].stock) {
2563                selected_options[optNo].count = selected_options[optNo].stock;
2564                return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(selected_options[optNo].stock + 1);
2565            }
2566        }
2567
2568
2569
2570        if (selected_options[optNo].require) { // 필수 옵션
2571            if (prod_type === SHOP_CONST.PROD_TYPE.DIGITAL) {
2572                // 만약 디지털 상품일 경우 수량변경이 제한된다.
2573                selected_options[optNo].count = 1;
2574                return LOCALIZE.설명_디지털상품은수량을변경하실수없습니다();
2575            }
2576        } else { // 선택 옵션
2577            if (parseInt(selected_options[optNo].price) == 0) {
2578                // 가격이 0원일 경우... 0원 선택옵션 구매 시 최대 수량 제한 체크를 한다.
2579                switch (optional_limit_type) {
2580                    case 'limit':
2581                        if (cnt > optional_limit) {
2582                            selected_options[optNo].count = optional_limit;
2583                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(optional_limit);
2584                        }
2585                        break;
2586                    case 'unique':
2587                        if (cnt > 1) {
2588                            selected_options[optNo].count = 1;
2589                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(1);
2590                        }
2591                        break;
2592                    case 'relative':
2593                        var _order_cnt = order_count;
2594                        if (_order_cnt == 0) {
2595                            $.each(selected_options, function (no, data) { if (data.require) { _order_cnt += data.count; } });
2596                        }
2597
2598                        if (cnt > _order_cnt) {
2599                            selected_options[optNo].count = _order_cnt;
2600                            return LOCALIZE.설명_0원상품갯수제한();
2601                        }
2602                        break;
2603                }
2604            }
2605        }
2606
2607        return true;
2608    };
2609
2610		/**
2611     * 옵션 수량 및 재고 체크하는 기능
2612     * @param optNo
2613     * @param cnt
2614     * @returns {*}
2615     */
2616    var checkProdAdditionalOptionCount = function (optNo, cnt) {
2617			var prod_idx = selected_prod_additional_options[optNo].prod_idx;
2618			var selected_prod_data = selected_prod_additionals.find(v=>v.idx===prod_idx);
2619        // 그냥 재고 체크
2620        if (selected_prod_additional_options[optNo].use_stock && !selected_prod_additional_options[optNo].stock_un_limit) {
2621            if (cnt > selected_prod_additional_options[optNo].stock) {
2622                selected_prod_additional_options[optNo].count = selected_prod_additional_options[optNo].stock;
2623                return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(selected_prod_additional_options[optNo].stock + 1);
2624            }
2625        }
2626
2627
2628
2629        if (selected_prod_additional_options[optNo].require) { // 필수 옵션
2630            if (prod_type === SHOP_CONST.PROD_TYPE.DIGITAL) {
2631                // 만약 디지털 상품일 경우 수량변경이 제한된다.
2632                selected_prod_additional_options[optNo].count = 1;
2633                return LOCALIZE.설명_디지털상품은수량을변경하실수없습니다();
2634            }
2635        } else { // 선택 옵션
2636            if (parseInt(selected_prod_additional_options[optNo].price) == 0) {
2637                // 가격이 0원일 경우... 0원 선택옵션 구매 시 최대 수량 제한 체크를 한다.
2638                switch (selected_prod_data.optional_limit_type) {
2639                    case 'limit':
2640                        if (cnt > selected_prod_data.optional_limit) {
2641                            selected_prod_additional_options[optNo].count = selected_prod_data.optional_limit;
2642                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(selected_prod_data.optional_limit);
2643                        }
2644                        break;
2645                    case 'unique':
2646                        if (cnt > 1) {
2647                            selected_prod_additional_options[optNo].count = 1;
2648                            return LOCALIZE.설명_해당선택옵션은최대N개까지구매가능합니다(1);
2649                        }
2650                        break;
2651                    case 'relative':
2652                        var _order_cnt = selected_prod_data.count;
2653                        if (_order_cnt == 0) {
2654                            $.each(selected_prod_additional_options, function (no, data) { if (data.require) { _order_cnt += data.count; } });
2655                        }
2656
2657                        if (cnt > _order_cnt) {
2658                            selected_prod_additional_options[optNo].count = _order_cnt;
2659                            return LOCALIZE.설명_0원상품갯수제한();
2660                        }
2661                        break;
2662                }
2663            }
2664        }
2665
2666        return true;
2667    };
2668
2669    var increaseOptionCount = function (optNo, success) {
2670        var $curCount = $('#prdOption' + optNo).find('input._count');
2671        var curCount = $curCount.val();
2672        if (isNaN(curCount))
2673            curCount = 1;
2674        else
2675            curCount = parseInt(curCount) + 1;
2676
2677        var optional_result = checkOptionCount(optNo, curCount);
2678        if (optional_result === true) {
2679            selected_options[optNo].count = curCount;
2680        } else {
2681            alert(optional_result);
2682        }
2683
2684
2685        var is_success = true;
2686        //1회당 구매 가능한 수량 체크
2687        if (max_prod_quantity > 0) {
2688            var _selected_count = 0;
2689            if (maximum_purchase_quantity_type === 'order') {//주문단위 기준인경우
2690                // 본품 수량 포함
2691                _selected_count += order_count;
2692                // 필수/선택 옵션 수량 포함
2693                $.each(selected_options, function (idx, obj) {
2694                    _selected_count += obj.count;
2695                });
2696            } else if (maximum_purchase_quantity_type !== 'order') { // 옵션별 수량 체크인 경우
2697                switch (maximum_purchase_quantity_type) {
2698                    // required_option 타입인 경우 필수옵션만 카운트
2699                    case 'required_option':
2700                        var $option = $('#prdOption' + optNo);
2701                        if ($option.hasClass('_selected_require_option')) {
2702                            _selected_count = selected_options[optNo].count;
2703                        }
2704                        break;
2705                    case 'optional_option':
2706                        var $option = $('#prdOption' + optNo);
2707                        if (!$option.hasClass('_selected_require_option')) {
2708                            _selected_count = selected_options[optNo].count;
2709                        }
2710                        break;
2711                    case 'prod':
2712                    default:
2713                        _selected_count = selected_options[optNo].count;
2714                        break;
2715                }
2716            }
2717            if (_selected_count > max_prod_quantity) {
2718                selected_options[optNo].count -= 1;
2719                is_success = false;
2720                alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
2721            }
2722        }
2723
2724        // 회원당 최대 구매수량 검증 (1회 구매 제한이 옵션별 타입이면 1인 제한 미적용 - 스펙)
2725        if (is_success && max_member_quantity > 0 && maximum_purchase_quantity_type === 'order') {
2726            var _member_count = order_count;
2727            $.each(selected_options, function (idx, obj) {
2728                _member_count += obj.count;
2729            });
2730            if (_member_count > max_member_quantity) {
2731                selected_options[optNo].count -= 1;
2732                is_success = false;
2733                alert(LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity));
2734            }
2735        }
2736
2737        if (is_success) success();
2738    };
2739
2740    var decreaseOptionCount = function (optNo, success) {
2741        var $curCount = $('#prdOption' + optNo).find('input._count');
2742        var curCount = $curCount.val();
2743        if (isNaN(curCount))
2744            curCount = 1;
2745        else
2746            curCount = parseInt(curCount) - 1;
2747        if (curCount < 1) curCount = 1;
2748
2749        var optional_result = checkOptionCount(optNo, curCount);
2750        if (optional_result === true) {
2751            selected_options[optNo].count = curCount;
2752        } else {
2753            alert(optional_result);
2754        }
2755        success();
2756    };
2757
2758    var increaseProdAdditionalOptionCount = function (optNo, success) {
2759        var $curCount = $('#prdAdditionalOption' + optNo).find('input._count');
2760        var curCount = $curCount.val();
2761				var prod_idx = selected_prod_additional_options[optNo].prod_idx;
2762				var selected_prod_data = selected_prod_additionals.find(v=>v.idx===prod_idx);
2763        if (isNaN(curCount))
2764            curCount = 1;
2765        else
2766            curCount = parseInt(curCount) + 1;
2767
2768        var optional_result = checkProdAdditionalOptionCount(optNo, curCount);
2769        if (optional_result === true) {
2770            selected_prod_additional_options[optNo].count = curCount;
2771        } else {
2772            alert(optional_result);
2773        }
2774
2775        var is_success = true;
2776        //1회당 구매 가능한 수량 체크
2777        if (selected_prod_data.maximum_purchase_quantity > 0) {
2778            var _selected_count = 0;
2779            if (selected_prod_data.maximum_purchase_quantity_type === 'order') {//주문단위 기준인경우
2780                _selected_count = getProdAdditionalTotalQuantity(selected_prod_data.maximum_purchase_quantity_type, prod_idx);
2781            } else if (selected_prod_data.maximum_purchase_quantity_type !== 'order') {
2782                switch (selected_prod_data.maximum_purchase_quantity_type) {
2783                    case 'required_option':
2784                        var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2785                        if ($prodAddtionalOption.hasClass('_selected_require_option')) {
2786                            _selected_count = selected_prod_additional_options[optNo].count;
2787                        }
2788                        break;
2789                    case 'optional_option':
2790                        var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2791                        if (!$prodAddtionalOption.hasClass('_selected_require_option')) {
2792                            _selected_count = selected_prod_additional_options[optNo].count;
2793                        }
2794                        break;
2795                    case 'prod':
2796                    default:
2797                        _selected_count = selected_prod_additional_options[optNo].count;
2798                        break;
2799                }
2800            }
2801            if (_selected_count > selected_prod_data.maximum_purchase_quantity) {
2802                selected_prod_additional_options[optNo].count -= 1;
2803                is_success = false;
2804                alert(LOCALIZE.설명_최대구매수량(selected_prod_data.maximum_purchase_quantity));
2805            }
2806        }
2807
2808        if (is_success) success();
2809    };
2810
2811    var decreaseProdAdditionalOptionCount = function (optNo, success) {
2812        var $curCount = $('#prdAdditionalOption' + optNo).find('input._count');
2813        var curCount = $curCount.val();
2814        if (isNaN(curCount))
2815            curCount = 1;
2816        else
2817            curCount = parseInt(curCount) - 1;
2818        if (curCount < 1) curCount = 1;
2819
2820        var optional_result = checkProdAdditionalOptionCount(optNo, curCount);
2821        if (optional_result === true) {
2822            selected_prod_additional_options[optNo].count = curCount;
2823        } else {
2824            alert(optional_result);
2825        }
2826        success();
2827    };
2828
2829    const changeProdAdditionalOptionCount = function (optNo, toBeCount, success) {
2830        toBeCount = parseInt(toBeCount, 10);
2831        if (isNaN(toBeCount) || toBeCount < 1) toBeCount = 1;
2832
2833        const optionalResult = checkProdAdditionalOptionCount(optNo, toBeCount);
2834        if (optionalResult === true) {
2835            selected_prod_additional_options[optNo].count = toBeCount;
2836        } else {
2837            alert(optionalResult);
2838        }
2839
2840        const prodIdx = selected_prod_additional_options[optNo].prod_idx;
2841        const selectedProdData = selected_prod_additionals.find(v => v.idx === prodIdx);
2842
2843        //1회당 구매 가능한 수량 체크
2844        if (!selectedProdData || selectedProdData?.maximum_purchase_quantity === undefined) {
2845            return;
2846        }
2847
2848        if (selectedProdData.maximum_purchase_quantity > 0) {
2849            var shouldCheck = false;
2850            switch (selectedProdData.maximum_purchase_quantity_type) {
2851                case 'required_option':
2852                    var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2853                    shouldCheck = $prodAddtionalOption.hasClass('_selected_require_option');
2854                    break;
2855                case 'optional_option':
2856                    var $prodAddtionalOption = $('#prdAdditionalOption' + optNo);
2857                    shouldCheck = !$prodAddtionalOption.hasClass('_selected_require_option');
2858                    break;
2859                case 'order':
2860                    // order 타입은 전체 합산 로직 필요
2861                    break;
2862                case 'prod':
2863                default:
2864                    shouldCheck = true;
2865                    break;
2866            }
2867
2868            if (shouldCheck && toBeCount > selectedProdData.maximum_purchase_quantity) {
2869                selected_prod_additional_options[optNo].count = selectedProdData.maximum_purchase_quantity;
2870                alert(LOCALIZE.설명_최대구매수량(selectedProdData.maximum_purchase_quantity));
2871            }
2872        }
2873
2874        if (typeof success === 'function') success();
2875    };
2876
2877    var changeOptionCount = function (optNo, optCount, success) {
2878        var prevCount = selected_options[optNo].count;
2879        optCount = parseInt(optCount);
2880        if (isNaN(optCount)) optCount = 1;
2881        if (optCount < 1) optCount = 1;
2882
2883        var optional_result = checkOptionCount(optNo, optCount);
2884        if (optional_result === true) {
2885            selected_options[optNo].count = optCount;
2886        } else {
2887            alert(optional_result);
2888        }
2889
2890        var is_success = true;
2891        //1회당 구매 가능한 수량 체크
2892        if (max_prod_quantity > 0) {
2893            var _selected_count = 0;
2894            if (maximum_purchase_quantity_type === 'order') {//주문단위 기준인경우
2895                // 본품 수량 포함
2896                _selected_count += order_count;
2897                // 필수/선택 옵션 수량 포함
2898                $.each(selected_options, function (idx, obj) {
2899                    _selected_count += obj.count;
2900                });
2901            } else if (maximum_purchase_quantity_type !== 'order') { // 옵션별 수량 체크인 경우
2902                switch (maximum_purchase_quantity_type) {
2903                    // required_option 타입인 경우 필수옵션만 카운트
2904                    case 'required_option':
2905                        var $option = $('#prdOption' + optNo);
2906                        if ($option.hasClass('_selected_require_option')) {
2907                            _selected_count = selected_options[optNo].count;
2908                        }
2909                        break;
2910                    case 'optional_option':
2911                        var $option = $('#prdOption' + optNo);
2912                        if (!$option.hasClass('_selected_require_option')) {
2913                            _selected_count = selected_options[optNo].count;
2914                        }
2915                        break;
2916                    case 'prod':
2917                    default:
2918                        _selected_count = selected_options[optNo].count;
2919                        break;
2920                }
2921            }
2922            if (_selected_count > max_prod_quantity) {
2923                selected_options[optNo].count = prevCount;
2924                is_success = false;
2925                alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
2926            }
2927        }
2928
2929        // 회원당 최대 구매수량 검증 (1회 구매 제한이 옵션별 타입이면 1인 제한 미적용 - 스펙)
2930        if (is_success && max_member_quantity > 0 && maximum_purchase_quantity_type === 'order') {
2931            var _member_count = order_count;
2932            $.each(selected_options, function (idx, obj) {
2933                _member_count += obj.count;
2934            });
2935            if (_member_count > max_member_quantity) {
2936                selected_options[optNo].count = prevCount;
2937                is_success = false;
2938                alert(LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity));
2939            }
2940        }
2941
2942        if (is_success) success();
2943    };
2944
2945    var checkProdStock = function (cnt) {
2946        // 그냥 재고 체크
2947        if (prod_stock_use && !prod_stock_unlimit) {
2948            if (cnt > prod_stock) {
2949                order_count = prod_stock;
2950                return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(prod_stock + 1);
2951            }
2952        }
2953
2954        return true;
2955    };
2956
2957    var increaseOrderCount = function (type, success) {
2958        var o = $prod_detail.find('input._order_count_' + type);
2959        var curCount = o.val();
2960        if (isNaN(curCount))
2961            curCount = 1;
2962        else
2963            curCount = parseInt(curCount) + 1;
2964
2965        var checkProdStockResult = checkProdStock(curCount);
2966        if (checkProdStockResult === true) {
2967            order_count = curCount;
2968        } else {
2969            alert(checkProdStockResult);
2970        }
2971
2972        var is_success = true;
2973        //1회당 구매 가능한 수량 체크
2974        if (max_prod_quantity > 0 && maximum_purchase_quantity_type !== 'optional_option') {
2975            var _selected_count = order_count;
2976
2977            // order 타입이면 본상품의 필수/선택 옵션도 포함하여 검증
2978            if (maximum_purchase_quantity_type === 'order') {
2979                $.each(selected_options, function (idx, obj) {
2980                    _selected_count += obj.count;
2981                });
2982            }
2983
2984            if (_selected_count > max_prod_quantity) {
2985                order_count -= 1;
2986                is_success = false;
2987                alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
2988            }
2989        }
2990        o.val(order_count);
2991
2992        if (is_success) success();
2993    };
2994
2995    var decreaseOrderCount = function (type, success) {
2996        var o = $prod_detail.find('input._order_count_' + type);
2997        var curCount = o.val();
2998        if (isNaN(curCount))
2999            curCount = 1;
3000        else
3001            curCount = parseInt(curCount) - 1;
3002
3003        if (curCount < 1) curCount = 1;
3004
3005        var checkProdStockResult = checkProdStock(curCount);
3006        if (checkProdStockResult === true) {
3007            order_count = curCount;
3008        } else {
3009            alert(checkProdStockResult);
3010        }
3011
3012        o.val(order_count);
3013        success();
3014    };
3015
3016    var changeOrderCount = function (type, count, success, is_alert) {
3017        if (is_alert == void 0) is_alert = true;
3018        var prevCount = order_count;
3019        if (isNaN(count))
3020            count = 1;
3021        else
3022            count = parseInt(count);
3023        if (count < 1) count = 1;
3024
3025        var checkProdStockResult = checkProdStock(count);
3026        if (checkProdStockResult === true) {
3027            order_count = count;
3028        } else {
3029            if (is_alert) alert(checkProdStockResult);
3030        }
3031
3032        var is_success = true;
3033        //1회당 구매 가능한 수량 체크
3034        if (max_prod_quantity > 0 && maximum_purchase_quantity_type !== 'optional_option') {
3035            var _selected_count = order_count;
3036
3037            // order 타입이면 본상품의 필수/선택 옵션도 포함하여 검증
3038            if (maximum_purchase_quantity_type === 'order') {
3039                $.each(selected_options, function (idx, obj) {
3040                    _selected_count += obj.count;
3041                });
3042            }
3043
3044            if (_selected_count > max_prod_quantity) {
3045                order_count = prevCount;
3046                is_success = false;
3047                if (is_alert) alert(LOCALIZE.설명_최대구매수량(max_prod_quantity));
3048            }
3049        }
3050
3051        $prod_detail.find("input._order_count_" + type).val(order_count);
3052        if (is_success) success();
3053    };
3054
3055	var increaseProdAdditionalOrderCount = function (no, type, success) {
3056		var selected_prod_data = selected_prod_additionals[no];
3057		var o = $prod_detail.find(`._area_count[data-no=${no}] input._prod_additional_order_count_${type}`);
3058		var curCount = o.val();
3059		if (isNaN(curCount))
3060			curCount = 1;
3061		else
3062			curCount = parseInt(curCount) + 1;
3063
3064		var checkProdStockResult = checkProdAdditionalStock(no, curCount);
3065		if (checkProdStockResult === true) {
3066			selected_prod_data.count = curCount;
3067		} else {
3068			alert(checkProdStockResult);
3069		}
3070
3071		var is_success = true;
3072
3073		// 추가상품 자체의 제한을 확인한다.
3074		if (is_success && selected_prod_data.maximum_purchase_quantity > 0 && selected_prod_data.maximum_purchase_quantity_type !== 'optional_option') {
3075			if (selected_prod_data.count > selected_prod_data.maximum_purchase_quantity) {
3076				selected_prod_data.count -= 1;
3077				is_success = false;
3078				alert(LOCALIZE.설명_최대구매수량(selected_prod_data.maximum_purchase_quantity));
3079			}
3080		}
3081		o.val(selected_prod_data.count);
3082
3083		if (is_success) success();
3084	};
3085
3086	var decreaseProdAdditionalOrderCount = function (no, type, success) {
3087		var selected_prod_data = selected_prod_additionals[no];
3088		var o = $prod_detail.find(`._area_count[data-no=${no}] input._prod_additional_order_count_${type}`);
3089		var curCount = o.val();
3090		if (isNaN(curCount))
3091			curCount = 1;
3092		else
3093			curCount = parseInt(curCount) - 1;
3094
3095		if (curCount < 1) curCount = 1;
3096
3097		var checkProdStockResult = checkProdAdditionalStock(no, curCount);
3098		if (checkProdStockResult === true) {
3099			selected_prod_data.count = curCount;
3100		} else {
3101			alert(checkProdStockResult);
3102		}
3103
3104		o.val(selected_prod_data.count);
3105		success();
3106	};
3107
3108	var changeProdAdditionalOrderCount = function (no, type, count, success, is_alert) {
3109		var selected_prod_data = selected_prod_additionals[no];
3110		var prevCount = selected_prod_data.count;
3111		if (is_alert == void 0) is_alert = true;
3112		if (isNaN(count))
3113			count = 1;
3114		else
3115			count = parseInt(count);
3116		if (count < 1) count = 1;
3117
3118		var checkProdStockResult = checkProdAdditionalStock(no, count);
3119		if (checkProdStockResult === true) {
3120			selected_prod_data.count = count;
3121		} else {
3122			if (is_alert) alert(checkProdStockResult);
3123		}
3124
3125		var is_success = true;
3126
3127		// 추가상품 자체의 제한을 확인한다.
3128		if (is_success && selected_prod_data.maximum_purchase_quantity > 0 && selected_prod_data.maximum_purchase_quantity_type !== 'optional_option') {
3129			if (selected_prod_data.count > selected_prod_data.maximum_purchase_quantity) {
3130				selected_prod_data.count = prevCount;
3131				is_success = false;
3132				if (is_alert) alert(LOCALIZE.설명_최대구매수량(selected_prod_data.maximum_purchase_quantity));
3133			}
3134		}
3135
3136		$prod_detail.find(`._area_count[data-no=${no}] input._prod_additional_order_count_${type}`).val(selected_prod_data.count);
3137		if (is_success) success();
3138	};
3139
3140	var checkProdAdditionalStock = function (no, cnt) {
3141		var selected_prod_data = selected_prod_additionals[no];
3142		// 그냥 재고 체크
3143		if (selected_prod_data.stock_use && !selected_prod_data.stock_unlimit) {
3144			if (cnt > selected_prod_data.stock) {
3145				selected_prod_data.count = selected_prod_data.stock;
3146				return LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(selected_prod_data.stock + 1);
3147			}
3148		}
3149
3150		return true;
3151	};
3152
3153    var selectCartType = function (type) {
3154        if (type === 'regularly') {
3155            $prod_detail.addClass('detail-regularly');
3156            // 정기구독 선택 시 수량별 할인 배너/혜택 숨김 (IMIO-7132)
3157            $prod_detail.find('._quantity_discount_banner_wrap').hide();
3158            $prod_detail.find('._quantity_discount_banner_mobile').hide();
3159            $prod_detail.find('._quantity_discount_benefits').hide();
3160        } else {
3161            $prod_detail.removeClass('detail-regularly');
3162            // 1회구매 복귀 시 수량별 할인 배너/혜택 복원
3163            $prod_detail.find('._quantity_discount_banner_mobile').show();
3164            $prod_detail.find('._quantity_discount_benefits').show();
3165            // 배너는 옵션 선택 여부에 따라 updateSelectedOptions 에서 toggle 됨
3166        }
3167        updateSelectedOptions('prod');
3168    };
3169
3170    var getCreatorDiscountMaxToastTarget = function () {
3171        var $scope = options.is_prod_detail_page ? $('.modal_prod_detail_from_shopping_list') : $(document);
3172        var $target = $scope.find('.buy_btns.mobile:visible .cart_btn:visible').first();
3173        if (!$target.length) {
3174            $target = $scope.find('.buy_btns.mobile .cart_btn').first();
3175        }
3176        if (!$target.length) {
3177            $target = $scope.find('._mobile_action_btn_modal_wrap:visible, ._mobile_action_btn_wrap:visible, .buy_btns.mobile:visible').first();
3178        }
3179        if (!$target.length) {
3180            $target = $scope.find('._mobile_action_btn_modal_wrap, ._mobile_action_btn_wrap, .buy_btns.mobile').first();
3181        }
3182        return $target;
3183    };
3184
3185    var getCreatorDiscountMaxToast = function () {
3186        var $toast = $('body').children('._creator_max_discount_toast');
3187        if (!$toast.length) {
3188            $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>');
3189            $('body').append($toast);
3190        }
3191        return $toast;
3192    };
3193
3194    var positionCreatorDiscountMaxToast = function ($toast, $target) {
3195        if (!$toast || !$toast.length || !$target || !$target.length) return;
3196
3197        var rect = $target.get(0).getBoundingClientRect();
3198        var bottom = Math.max(window.innerHeight - rect.top + 16, 16);
3199        var center = rect.left + (rect.width / 2);
3200
3201        $toast.css({
3202            bottom: bottom + 'px',
3203            left: center + 'px'
3204        });
3205    };
3206
3207    var hideCreatorDiscountMaxToast = function () {
3208        if (creator_discount_max_toast_timer) {
3209            clearTimeout(creator_discount_max_toast_timer);
3210            creator_discount_max_toast_timer = null;
3211        }
3212        $('body').children('._creator_max_discount_toast').removeClass('is-visible');
3213    };
3214
3215    var showCreatorDiscountMaxToast = function () {
3216        var $target = getCreatorDiscountMaxToastTarget();
3217        if (!$target.length) return;
3218
3219        var $toast = getCreatorDiscountMaxToast();
3220        positionCreatorDiscountMaxToast($toast, $target);
3221
3222        $toast.find('span').text(creator_discount_max_toast_text);
3223        $toast.removeClass('is-visible');
3224        var nextFrame = window.requestAnimationFrame || function (callback) { return setTimeout(callback, 0); };
3225        nextFrame(function () {
3226            positionCreatorDiscountMaxToast($toast, $target);
3227            $toast.addClass('is-visible');
3228        });
3229
3230        if (creator_discount_max_toast_timer) {
3231            clearTimeout(creator_discount_max_toast_timer);
3232        }
3233        creator_discount_max_toast_timer = setTimeout(function () {
3234            $toast.removeClass('is-visible');
3235            creator_discount_max_toast_timer = null;
3236        }, 2200);
3237    };
3238
3239    var parseFreeShipNotifyPrice = function (price) {
3240        var parsed = parseFloat(price);
3241        return isNaN(parsed) ? 0 : parsed;
3242    };
3243
3244    var getFreeShipNotifyRegularPrice = function (regular_price, sale_price) {
3245        var regularPrice = parseFreeShipNotifyPrice(regular_price);
3246        var salePrice = parseFreeShipNotifyPrice(sale_price);
3247        return regularPrice > 0 ? regularPrice : salePrice;
3248    };
3249
3250    var getFreeShipNotifyMainRegularPrice = function () {
3251        return getFreeShipNotifyRegularPrice(free_ship_notify_prod_regular_price, prod_price);
3252    };
3253
3254    var setFreeShipNotifyProdAdditionalRegularPrice = function (prod_idx, regular_price) {
3255        if (!prod_idx) return;
3256        free_ship_notify_prod_additional_regular_price_map[prod_idx] = parseFreeShipNotifyPrice(regular_price);
3257    };
3258
3259    var getFreeShipNotifyProdAdditionalRegularPrice = function (prod_idx, sale_price) {
3260        return getFreeShipNotifyRegularPrice(free_ship_notify_prod_additional_regular_price_map[prod_idx], sale_price);
3261    };
3262
3263    var isFreeShipNotifyAfterSale = function () {
3264        return free_ship_notify_deliv_price_cost_type === FREE_SHIP_NOTIFY_DELIV_PRICE_COST_TYPE_AFTER;
3265    };
3266
3267    var calcFreeShipNotifyRequiredOptionPrice = function (option_price) {
3268        return parseFreeShipNotifyPrice(option_price);
3269    };
3270
3271    var calcFreeShipNotifyCreatorDiscountRaw = function (total_base, total_count) {
3272        if (!creator_discount_data) return 0;
3273        var raw = 0;
3274        if (creator_discount_data.type === 'PDT01') {
3275            raw = (parseInt(total_count, 10) || 0) * (parseInt(creator_discount_data.discount_amount, 10) || 0);
3276        } else if (creator_discount_data.type === 'PDT02') {
3277            var pct = parseFloat(creator_discount_data.percent) || 0;
3278            if (pct <= 0) return 0;
3279            raw = Math.floor((parseFreeShipNotifyPrice(total_base) * pct) / 100);
3280        }
3281        return raw;
3282    };
3283
3284    var calcFreeShipNotifyCreatorDiscount = function (total_base, total_count) {
3285        if (!creator_discount_data) return 0;
3286        var raw = calcFreeShipNotifyCreatorDiscountRaw(total_base, total_count);
3287        var cap = parseFreeShipNotifyPrice(creator_discount_data.max_price);
3288        return cap > 0 ? Math.min(raw, cap) : raw;
3289    };
3290
3291    var calcFreeShipNotifyQuantityDiscount = function (base_amount, total_count) {
3292        if (creator_discount_data || !$selected_options || !$selected_options.length) return 0;
3293
3294        var raw = $selected_options.attr('data-qd-tiers');
3295        if (!raw) return 0;
3296
3297        var tiers = null;
3298        try {
3299            tiers = JSON.parse(raw);
3300        } catch (e) {
3301            return 0;
3302        }
3303        if (!tiers || !tiers.length) return 0;
3304
3305        var matched = tiers
3306            .filter(function (tier) { return total_count >= tier.min_quantity; })
3307            .sort(function (a, b) { return b.min_quantity - a.min_quantity; })[0];
3308        if (!matched) return 0;
3309
3310        var cap = Math.max(parseFreeShipNotifyPrice(base_amount), 0);
3311        if (matched.method === 'percentage') {
3312            return Math.min(Math.ceil(cap * matched.value / 100), cap);
3313        }
3314        return Math.min(parseInt(matched.value, 10) || 0, cap);
3315    };
3316
3317    var getFreeShipNotifyInitialTotal = function () {
3318        if (!isFreeShipNotifyAfterSale()) {
3319            return getFreeShipNotifyMainRegularPrice();
3320        }
3321
3322        var totalPrice = parseFreeShipNotifyPrice(prod_price);
3323        var periodDiscountPrice = 0;
3324        var creatorUsesPeriodDiscountTarget = creator_discount_data && creator_discount_data.reference_source === 'period_discount';
3325
3326        if (!creator_discount_data || creatorUsesPeriodDiscountTarget) {
3327            periodDiscountPrice = calcPeriodDiscount(prod_price, 1);
3328            if (periodDiscountPrice > totalPrice) periodDiscountPrice = totalPrice;
3329            totalPrice -= periodDiscountPrice;
3330        }
3331
3332        var isRegularlyProd = $prod_detail && $prod_detail.hasClass('detail-regularly');
3333        var quantityDiscountPrice = isRegularlyProd ? 0 : calcFreeShipNotifyQuantityDiscount(totalPrice, 1);
3334        totalPrice -= quantityDiscountPrice;
3335
3336        if (is_main_product_membership_discount) {
3337            var membershipDiscountPrice = calcMembershipDiscount(totalPrice);
3338            if (membershipDiscountPrice > totalPrice) membershipDiscountPrice = totalPrice;
3339            totalPrice -= membershipDiscountPrice;
3340        }
3341
3342        if (creator_discount_data) {
3343            var creatorDiscountBase = creatorUsesPeriodDiscountTarget ? totalPrice : parseFreeShipNotifyPrice(prod_price);
3344            var creatorDiscountPrice = calcFreeShipNotifyCreatorDiscount(creatorDiscountBase, 1);
3345            if (creatorDiscountPrice > totalPrice) creatorDiscountPrice = totalPrice;
3346            totalPrice -= creatorDiscountPrice;
3347        }
3348
3349        return Math.max(totalPrice, 0);
3350    };
3351
3352    var refreshFreeShipNotifyInitialTotal = function () {
3353        if (require_option_count == 0 && selected_options.length == 0 && selected_prod_additionals.length == 0 && selected_prod_additional_options.length == 0 && order_count == 0) {
3354            notifyFreeShipNotifySdk(getFreeShipNotifyInitialTotal());
3355        }
3356    };
3357
3358    /**
3359     * 무료배송 안내 SDK 에 현재 합계와 필수옵션 완료 상태를 전달.
3360     * updateSelectedOptions 의 SDK dispatch 부분만 분리하여, DOM 갱신이 필요 없는
3361     * 진입 경로(예: initDetail 의 옵션 없는 상품 초기 가격 반영)에서도 재사용한다.
3362     * SDK 미로드 환경에서는 no-op.
3363     * @param {number} total_price - SDK 에 전달할 무료배송 기준 합계
3364     */
3365    var notifyFreeShipNotifySdk = function (total_price) {
3366        if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK === 'undefined') return;
3367        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.updateSelectedOptionsTotal(total_price);
3368        // REQUIRE_OPTION 스타일에서 인라인 마그넷 표시 토글.
3369        // selectRequireOption은 필수 옵션이 모두 채워지면 selectOption 호출 후 selected_require_options를 []로 리셋하고
3370        // 확정된 옵션 조합을 selected_options에 push하므로, 진짜 "완료" 신호는 selected_options.length > 0이다.
3371        // 필수 옵션이 없는 상품(require_option_count == 0)은 항상 true.
3372        var isRequireOptionCompleted = (require_option_count == 0) || (selected_options.length > 0);
3373        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setRequireOptionCompleted(isRequireOptionCompleted);
3374    };
3375
3376    /**
3377     * 옵션선택화면갱신
3378     * type : prod (상품상세화면용), cart (장바구니 수량 변경용)
3379     */
3380    var updateSelectedOptions = function (type) {
3381        var html = '';
3382        var total_price_html = '';
3383
3384        var total_price = 0;
3385        var total_count = 0;
3386        var period_discount_price = 0;
3387        var show_period_discount = false;
3388				var membership_discount_not_involved_price = 0;  // 등급할인을 사용하지 않는 상품의 가격(등급할인 계산 과정에서 제외됨)
3389        var free_ship_notify_is_after_sale = isFreeShipNotifyAfterSale();
3390        var free_ship_notify_total_price = 0;
3391
3392        // 수량별 할인 데이터 (data-attribute에서 읽기)
3393        const _quantityDiscountTiers = (() => {
3394            const raw = $selected_options.attr('data-qd-tiers');
3395            if (!raw) return null;
3396            try { return JSON.parse(raw); } catch { return null; }
3397        })();
3398        const _quantityDiscountExcludeOptional = $selected_options.attr('data-qd-exclude-optional') === 'true';
3399        let _quantityDiscountRequireOptionCount = 0; // 필수 옵션 수량 합계
3400        let _quantityDiscountBaseRaw = 0; // 본 상품 raw 매출 합계 (기간할인 미적용, 추가상품 제외) - 수량별 할인 base
3401        // 크리에이터 전용 혜택가 — 본품 + 필수 옵션(require) 합산 가격에 한 번 할인 적용
3402        // 정책 B(필수옵션 포함, 선택옵션 제외) / Y(N건 모두 적용) / M(cap 합산 한 번)
3403        //   - PDT01: discount_amount × totalCount (Y), cap 합산 (M)
3404        //   - PDT02: floor(totalBase × percent / 100) (B+Y 자동 누적), cap 합산 (M)
3405        let _creator_discount_base = 0;  // 본품 + 필수옵션 타겟 가격 합산
3406        let _creator_discount_count = 0; // 본품 + 필수옵션 수량 합산
3407        let _prod_additional_creator_discount_price = 0; // 추가상품별 크리에이터 할인 row 누적액
3408        const _creator_uses_period_discount_target = creator_discount_data && creator_discount_data.reference_source === 'period_discount';
3409        const calcCreatorDiscountRaw = (totalBase, totalCount) => {
3410            if (!creator_discount_data) return 0;
3411            var raw = 0;
3412            if (creator_discount_data.type === 'PDT01') {
3413                raw = (parseInt(totalCount, 10) || 0) * (parseInt(creator_discount_data.discount_amount, 10) || 0);
3414            } else if (creator_discount_data.type === 'PDT02') {
3415                var pct = parseFloat(creator_discount_data.percent) || 0;
3416                if (pct <= 0) return 0;
3417                raw = Math.floor((parseInt(totalBase, 10) || 0) * pct / 100);
3418            }
3419            return raw;
3420        };
3421        const calcCreatorDiscount = (totalBase, totalCount) => {
3422            if (!creator_discount_data) return 0;
3423            var raw = calcCreatorDiscountRaw(totalBase, totalCount);
3424            var cap = parseInt(creator_discount_data.max_price, 10) || 0;
3425            return (cap > 0) ? Math.min(raw, cap) : raw;
3426        };
3427        const calcAdditionalCreatorDiscount = (additionalCreatorData, totalBase, totalCount) => {
3428            if (!additionalCreatorData) return 0;
3429            var raw = 0;
3430            if (additionalCreatorData.type === 'PDT01') {
3431                raw = (parseInt(totalCount, 10) || 0) * (parseInt(additionalCreatorData.discount_amount, 10) || 0);
3432            } else if (additionalCreatorData.type === 'PDT02') {
3433                var pct = parseFloat(additionalCreatorData.percent) || 0;
3434                if (pct <= 0) return 0;
3435                raw = Math.floor((parseInt(totalBase, 10) || 0) * pct / 100);
3436            }
3437            var cap = parseInt(additionalCreatorData.max_price, 10) || 0;
3438            return (cap > 0) ? Math.min(raw, cap) : raw;
3439        };
3440
3441        // 크리에이터 혜택가 — DOM insert 후 본품/옵션 행 가격을 합산 cap 비례 분배해 갱신
3442        // [data-creator-row] 셀렉터로 본품(book)/필수옵션(option) 가격 노드 모두 수집 → 비례 분배
3443        const _applyCreatorDiscountDistribution = ($container) => {
3444            if (!creator_discount_data) return;
3445            if (!$container || !$container.length) return;
3446            var $rows = $container.find('[data-creator-row]');
3447            if (!$rows.length) return;
3448            var totalRaw = 0;
3449            $rows.each(function () {
3450                totalRaw += parseInt($(this).attr('data-creator-raw'), 10) || 0;
3451            });
3452            if (totalRaw <= 0) return;
3453            var totalDiscount = calcCreatorDiscount(_creator_discount_base, _creator_discount_count);
3454            if (totalDiscount <= 0) return;
3455            $rows.each(function () {
3456                var $row = $(this);
3457                var raw = parseInt($row.attr('data-creator-raw'), 10) || 0;
3458                if (raw <= 0) return;
3459                var share = Math.floor(totalDiscount * raw / totalRaw);
3460                var finalPrice = raw - share;
3461                $row.text(LOCALIZE.getCurrencyFormat(finalPrice));
3462            });
3463        };
3464
3465        const _calcQuantityDiscount = (baseAmount, totalCount) => {
3466            // 크리에이터 전용 혜택가 활성 시 MSA 수량별 할인(MFE 프로모션) 무시 (정책 #2)
3467            if (creator_discount_data) return 0;
3468            if (!_quantityDiscountTiers) return 0;
3469            const matched = _quantityDiscountTiers
3470                .filter(t => totalCount >= t.min_quantity)
3471                .sort((a, b) => b.min_quantity - a.min_quantity)[0];
3472            if (!matched) return 0;
3473            const cap = Math.max(baseAmount, 0);
3474            if (matched.method === 'percentage') {
3475                // 서버 OMS 식과 동일: ceil((row 매출 - 기간할인) × 비율)
3476                return Math.min(Math.ceil(cap * matched.value / 100), cap);
3477            }
3478            // fixed_amount, fixed_price: value는 총 할인금액 (수량 무관)
3479            // 품목 금액 초과 방지 (결제페이지/OMS 와 동일한 클램핑, IMIO-7184)
3480            return Math.min(parseInt(matched.value, 10) || 0, cap);
3481        };
3482	      var is_use_prod_additional = selected_prod_additionals.length > 0 || selected_prod_additional_options.length > 0;
3483				/* 본 상품 계산 */
3484        if (require_option_count == 0) {
3485					let section_total_price = 0;
3486
3487            // 본품 행 표시 = 타겟 기준가 × 수량. 크리에이터 할인은 합계(total_price)에서만 차감.
3488            if (typeof USE_OMS !== 'undefined' && USE_OMS === true){
3489                if (creator_discount_data && !_creator_uses_period_discount_target) {
3490                    period_discount_price = 0;
3491                } else {
3492                    period_discount_price = calcPeriodDiscount(prod_price, 1);
3493							if(period_discount_price > prod_price){
3494								period_discount_price = prod_price;
3495							}
3496							period_discount_price *= order_count;
3497						}
3498                section_total_price = order_count * prod_price;
3499                section_total_price = section_total_price - period_discount_price;
3500                show_period_discount = true;
3501            } else {
3502                section_total_price = order_count * prod_price;
3503                if (creator_discount_data && !_creator_uses_period_discount_target) {
3504                    period_discount_price = 0;
3505                } else {
3506							period_discount_price = calcPeriodDiscount(prod_price, order_count);
3507							if (period_discount_price > section_total_price) period_discount_price = section_total_price;
3508						}
3509						section_total_price = section_total_price - period_discount_price;
3510						show_period_discount = true;
3511					}
3512
3513            // 필수옵션이 없을 때 기본 상품 수량 조절 html 출력
3514            if (prod_type === SHOP_CONST.PROD_TYPE.NORMAL) {
3515                // 디지털, 이용권 상품은 수량 변경 할 필요가 없으므로 출력하지 않는다
3516                switch (type) {
3517                    case 'prod':
3518                        if (!is_view_price) break;
3519                        // 상품 상세페이지
3520                        html += '<div class="opt_block no-border order_quantity_area" style="height: auto; "> ';
3521                        html += '	<div class="area_tit holder">';
3522                        html += '		<span class="option_title inline-blocked" style="margin-bottom: 7px">' + LOCALIZE.타이틀_수량() + '</span> ';
3523                        html += '	</div> ';
3524                        html += '	<div class="area_count holder">';
3525                        html += '		<div class="option_btn_wrap" style="top:0;"> ';
3526                        html += '			<div class="option_btn_tools" style="float: none;"> ';
3527                        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>';
3528                        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 + '\')})"> ';
3529                        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>';
3530                        html += '			</div> ';
3531                        // 본품 가격은 위에서 _bookCreatorTotal 로 자체 cap 처리됨 → data-creator-row 미부착 (옵션만 비례 분배)
3532                        html += '			<div class="area_price absolute absolute_right absolute_middle"><span>' + LOCALIZE.getCurrencyFormat(section_total_price) + '</span></div>';
3533                        html += '		</div> ';
3534                        html += '	</div> ';
3535                        html += '</div> ';
3536                        break;
3537                    case 'cart':
3538                        // 장바구니
3539                        html += '<div class="opt_block no-border" style="height: auto; "> ';
3540                        html += '	<div class="col-control row"> ';
3541                        html += '		<div class="col-xs-12"><span class="option_title text-bold inline-blocked" style="margin-bottom: 7px">' + LOCALIZE.타이틀_수량() + '</span></div> ';
3542                        html += '		<div class="col-xs-12 option_btn_wrap" style="top:0;"> ';
3543                        html += '			<div class="option_btn_tools" style="float: none;"> ';
3544                        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>';
3545                        html += '				<input type="text" title="number" value="' + order_count + '" class="form-control _order_count_mobile" onchange="SITE_SHOP_CART.changeCartOrderCount(\'mobile\', $(this).val())"> ';
3546                        html += '				<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartIncrease(\'mobile\')"><i class="btl bt-plus"></i><span class="sr-only">plus</span></a>';
3547                        html += '			</div> ';
3548                        html += '		</div> ';
3549                        html += '	</div> ';
3550                        html += '</div> ';
3551                        break;
3552                }
3553            }
3554						/* 본 상품 합계에 반영 */
3555	          total_count += order_count;
3556            total_price += section_total_price;
3557            if (!free_ship_notify_is_after_sale) {
3558                free_ship_notify_total_price += order_count * getFreeShipNotifyMainRegularPrice();
3559            }
3560            // 수량별 할인 base에 본품 raw 매출 누적
3561            _quantityDiscountBaseRaw += order_count * prod_price;
3562            // 본품 타겟 가격도 합산 cap base 에 누적 (creator 활성 시 합계에서 한 번 차감)
3563            if (creator_discount_data) {
3564                _creator_discount_base += _creator_uses_period_discount_target ? section_total_price : order_count * prod_price;
3565                _creator_discount_count += order_count;
3566            }
3567	          if(!is_main_product_membership_discount){
3568		          membership_discount_not_involved_price += section_total_price;
3569	          }
3570        }
3571
3572				/* 본 상품 옵션 계산 */
3573		    var require_class = '';
3574		    var quantity_changeable = false;	// 상품 옵션부분 디지털, 이용권 상품 및 선택옵션 구분 - 수량 변경 가능
3575	      let option_total_count = 0;
3576	      var option_total_price = 0;
3577		    var option_price = 0;
3578
3579        var prod_option_html = { "Y": "", "N": "" };
3580        var option_html = '';
3581        $.each(selected_options, function (no, data) {
3582            option_price = parseFloat(data.price || 0) * parseFloat(data.count);
3583            // 크리에이터 활성 시 옵션 단가는 타겟 기준가 그대로 — 합산 cap 후 비례 분배 후처리에서 갱신
3584							let option_period_discount_price = 0;
3585            if (data.require == true && (!creator_discount_data || _creator_uses_period_discount_target)){
3586
3587							if(typeof USE_OMS !== 'undefined' && USE_OMS === true){
3588		            option_period_discount_price = calcPeriodDiscount(data.price, 1);
3589		            if(option_period_discount_price > data.price){
3590			            option_period_discount_price = data.price;
3591		            }
3592		            option_period_discount_price *= data.count;
3593	            }else{
3594		            option_period_discount_price = calcPeriodDiscount(data.price, data.count);
3595		            if(option_period_discount_price > option_price){
3596			            option_period_discount_price = option_price;
3597		            }
3598	            }
3599							period_discount_price += option_period_discount_price;
3600							if (data.option_mix_type == 'MIX' || period_dc_type == 'percent') {
3601									show_period_discount = true;
3602									option_price = option_price - option_period_discount_price;
3603							}
3604            }
3605            option_total_count += parseInt(data.count);
3606            if (data.require) _quantityDiscountRequireOptionCount += parseInt(data.count);
3607            // 수량별 할인 base에 옵션 raw 매출 누적 (선택옵션 제외 설정 시 필수 옵션만)
3608            if (!_quantityDiscountExcludeOptional || data.require) {
3609                _quantityDiscountBaseRaw += parseFloat(data.price || 0) * parseFloat(data.count);
3610            }
3611            // 크리에이터 할인 base 에 필수 옵션 타겟 매출만 누적
3612            if (creator_discount_data && data.require) {
3613                _creator_discount_base += _creator_uses_period_discount_target ? option_price : parseFloat(data.price || 0) * parseFloat(data.count);
3614                _creator_discount_count += parseInt(data.count);
3615            }
3616            option_total_price += option_price;
3617            if (!free_ship_notify_is_after_sale) {
3618                var free_ship_notify_option_unit_price = parseFreeShipNotifyPrice(data.price);
3619                if (data.require) {
3620                    free_ship_notify_option_unit_price = calcFreeShipNotifyRequiredOptionPrice(free_ship_notify_option_unit_price, prod_price, free_ship_notify_prod_regular_price);
3621                }
3622                free_ship_notify_total_price += free_ship_notify_option_unit_price * parseFreeShipNotifyPrice(data.count);
3623            }
3624
3625            var option_data_html = [];
3626            $.each(data.options, function (no2, data2) {
3627                option_data_html.push(RemoveTag(data2.option_name + ': ' + data2.value_name));
3628            });
3629            quantity_changeable = !(prod_type !== SHOP_CONST.PROD_TYPE.NORMAL && data.require == true);
3630            require_class = (data.require ? '_selected_require_option ' : '');
3631
3632
3633            // 한 개마다 초기화
3634            option_html = '';
3635            if (type == 'prod') {
3636                // 상품 상세페이지
3637                option_html += '<div class="' + require_class + (no == 0 ? '' : ' middle ') + 'opt_block" id="prdOption' + no + '">';
3638                option_html += '	<div class="full-width opt_product_area">';
3639                option_html += '		<div class="area_tit holder">';
3640                option_html += '		 	<span class="body_font_color_80">' + option_data_html.join(' / ') + '</span>';
3641                option_html += '			<a class="text-18 absolute absolute_right absolute_middle" href="javascript:;
3641" onclick="SITE_SHOP_DETAIL.removeSelectedOption(' + no + ',\'prod\')"><i class="btl bt-times-circle"></i></a>';
3642                option_html += '		</div>';
3643                option_html += '		<div class="area_count holder">';
3644                option_html += '			<div class="option_btn_wrap">';
3645                if (quantity_changeable) {
3646                    option_html += '			<div class="option_btn_tools">';
3647                    option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.decreaseOptionCount(' + no + ',\'prod\')"> <i class="btl bt-minus" aria-hidden="true"></i> </a>';
3648                    option_html += '				<input type="text" value="' + data.count + '" class="form-control count _count" onchange="SITE_SHOP_DETAIL.changeOptionCount(' + no + ', $(this).val(),\'prod\')" />';
3649                    option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.increaseOptionCount(' + no + ',\'prod\')"> <i class="btl bt-plus"></i> </a>';
3650                    option_html += '			</div>';
3651                }
3652                option_html += '			</div>';
3653                // 크리에이터 활성 + 필수 옵션이면 타겟 가격 정보를 data attr 로 노출 (비례 분배 후처리용)
3654                var _optionCreatorRaw = _creator_uses_period_discount_target ? option_price : parseFloat(data.price || 0) * parseFloat(data.count);
3655                var _optionCreatorAttr = (creator_discount_data && data.require) ? ' data-creator-row="option" data-creator-raw="' + _optionCreatorRaw + '"' : '';
3656                if (quantity_changeable) {
3657									option_html += '			<div class="area_price absolute absolute_right absolute_middle"><span' + _optionCreatorAttr + '>' + LOCALIZE.getCurrencyFormat(option_price) + '</span></div>';
3658                } else {
3659									option_html += '<div class="tw-flex tw-justify-between"><div>' + LOCALIZE.타이틀_수량() + ' ' + LOCALIZE.설명_n개띄어쓰기없음(1) + '</div><div' + _optionCreatorAttr + '>' + LOCALIZE.getCurrencyFormat(option_price) + '</div></div>';
3660                }
3661                option_html += '		</div>';
3662                option_html += '	</div>';
3663                option_html += '</div>';
3664            } else {
3665                // 장바구니
3666                option_html += '<div class="' + require_class + (no == 0 ? '' : ' middle ') + 'opt_block" id="prdOption' + no + '">';
3667                option_html += '	<div class="full-width opt_product_area">';
3668                option_html += '		<div class="area_tit holder">';
3669                option_html += '			<span class="body_font_color_80">' + option_data_html.join(' / ') + '</span>';
3670                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>';
3671                option_html += '		</div>';
3672                option_html += '		<div class="area_count holder">';
3673                option_html += '			<div class="option_btn_wrap">';
3674                if (quantity_changeable) {
3675                    option_html += '				<div class="option_btn_tools">';
3676                    option_html += '					<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartItemDecrease(' + no + ')"> <i class="btl bt-minus" aria-hidden="true"></i> </a>';
3677                    option_html += '					<input type="text" value="' + data.count + '" class="form-control count _count" onchange="SITE_SHOP_CART.changeCartItemCount(' + no + ', $(this).val())" />';
3678                    option_html += '					<a href="javascript:;" onclick="SITE_SHOP_CART.changeCartItemIncrease(' + no + ')"> <i class="btl bt-plus"></i> </a>';
3679                    option_html += '				</div>';
3680                }
3681                option_html += '			</div>';
3682                option_html += '			<div class="area_price absolute absolute_right absolute_middle">';
3683                // 크리에이터 활성 + 필수 옵션이면 raw 정보를 data attr 로 노출 (cart 분기에서도 동일)
3684                var _optionCreatorAttr = (creator_discount_data && data.require) ? ' data-creator-row="option" data-creator-raw="' + (parseFloat(data.price || 0) * parseFloat(data.count)) + '"' : '';
3685                option_html += '				<span' + _optionCreatorAttr + '>' + LOCALIZE.getCurrencyFormat(option_price) + '</span>';
3686                option_html += '			</div>';
3687                option_html += '		</div>';
3688                option_html += '	</div>';
3689                option_html += '</div>';
3690            }
3691
3692            // 기존처럼 옵션 구분 없이 선택 순 출력으로 사용하려면 prod_option_html 사용하는 부분 지우고 html += option_html 하면 됨
3693            prod_option_html[((data.require) ? "Y" : "N")] += option_html;
3694        });
3695
3696        html += prod_option_html["Y"];
3697        html += prod_option_html["N"];
3698
3699	     /* 본 상품 옵션 합계에 반영 */
3700	      total_count += option_total_count;
3701        total_price += option_total_price;
3702		    if(!is_main_product_membership_discount){
3703			    membership_discount_not_involved_price += option_total_price;
3704		    }
3705
3706				/* 추가상품 계산 */
3707				if(is_use_prod_additional){
3708
3709					$.each(selected_prod_additionals, function (no, data) {
3710						let prod_additional_total_count = 0;
3711						var prod_additional_total_price = 0;
3712						var prod_additional_require_option_count_by_data_idx = prod_additional_require_option_count[data.idx] || 0;
3713						if (prod_additional_require_option_count_by_data_idx == 0) {
3714
3715								prod_additional_total_count = parseInt(data.count);
3716								prod_additional_total_price = parseInt(data.count) * parseFloat(data.price);
3717								var prod_additional_creator_data = data.creator_discount_data || null;
3718								var prod_additional_creator_uses_period = prod_additional_creator_data && prod_additional_creator_data.reference_source === 'period_discount';
3719								var prod_additional_period_discount_price = 0;
3720								if (!prod_additional_creator_data || prod_additional_creator_uses_period) {
3721									prod_additional_period_discount_price = calcProdAdditionalPeriodDiscount(data.idx, data.price, data.count);
3722									if (prod_additional_period_discount_price > prod_additional_total_price) prod_additional_period_discount_price = prod_additional_total_price;
3723									// 추가상품의 경우 상품 할인 금액에 별도로 할인가를 포함하지 않고 항상 옵션가에 포함시킴
3724									prod_additional_total_price = prod_additional_total_price - prod_additional_period_discount_price;
3725								}
3726								if (prod_additional_creator_data) {
3727									var prod_additional_creator_discount_price = calcAdditionalCreatorDiscount(prod_additional_creator_data, prod_additional_total_price, data.count);
3728									if (prod_additional_creator_discount_price > prod_additional_total_price) prod_additional_creator_discount_price = prod_additional_total_price;
3729									// 추가상품 행에는 판매가/기간할인가 기준 금액을 담고, 크리에이터 할인은 하단 할인 row 에 누적한다.
3730									_prod_additional_creator_discount_price += prod_additional_creator_discount_price;
3731								}
3732								if (!free_ship_notify_is_after_sale) {
3733									free_ship_notify_total_price += parseInt(data.count) * getFreeShipNotifyProdAdditionalRegularPrice(data.idx, data.price);
3734								}
3735
3736							if(is_view_price){ // @TODO 본상품과 추가상품 가격없는 상품 분기 처리 필요
3737								// 상품 상세페이지
3738								html += '<div class="opt_block no-border order_quantity_area" style="height: auto; "> ';
3739								html += '	<div class="area_tit holder">';
3740								html += '		<div class="tw-flex tw-flex-col tw-gap-[12px]">';
3741								html += '     <span class="body_font_color_80 tw-font-bold">' + getLocalizeString('타이틀_추가상품', '', '추가 상품') + '</span>';
3742								html += '		 	<span class="body_font_color_80">' + data.name + '</span>';
3743								html += `		 	<div class="_prod_additional_option_optional goods-select-prod-additional" data-prod-idx="${data.idx}"></div>`;
3744								html += '		</div>';
3745								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> ';
3746								html += '	</div> ';
3747								html += '	<div class="area_count holder _area_count" data-no="' + no + '">';
3748								html += '		<div class="option_btn_wrap" style="top:0;"> ';
3749								if(data.prod_type === SHOP_CONST.PROD_TYPE.NORMAL){
3750									html += '			<div class="option_btn_tools" style="float: none;"> ';
3751									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>';
3752									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 + '\')})"> ';
3753									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>';
3754									html += '			</div> ';
3755								}
3756								html += ' </div>';
3757								if(data.prod_type === SHOP_CONST.PROD_TYPE.NORMAL){
3758									html += '			<div class="area_price absolute absolute_right absolute_middle"><span>' + LOCALIZE.getCurrencyFormat(prod_additional_total_price) + '</span></div>';
3759								}else{
3760									html += '<div class="tw-flex tw-justify-between"><div>' + LOCALIZE.타이틀_수량() + ' ' + LOCALIZE.설명_n개띄어쓰기없음(1) + '</div><div>' + LOCALIZE.getCurrencyFormat(prod_additional_total_price) + '</div></div>';
3761								}
3762								html += '	</div> ';
3763								html += '</div> ';
3764							}
3765
3766								if(!data.membership_discount || prod_additional_creator_data){
3767									membership_discount_not_involved_price += prod_additional_total_price;
3768								}
3769						}
3770
3771						/* 추가상품 합계에 반영 */
3772						total_count += prod_additional_total_count;
3773						total_price += prod_additional_total_price;
3774					});
3775
3776
3777					/* 추가상품 옵션 계산 */
3778					require_class = '';
3779					quantity_changeable = false;	// 상품 옵션부분 디지털, 이용권 상품 및 선택옵션 구분 - 수량 변경 가능
3780					option_total_count = 0;
3781					option_total_price = 0;
3782
3783					prod_option_html = { "Y": "", "N": "" };
3784					option_html = '';
3785					$.each(selected_prod_additional_options, function (no, data) {
3786						let prod_data = selected_prod_additionals.find(v=>v.idx===data.prod_idx);
3787						if(typeof prod_data === 'undefined') return true; // prod_data가 확인되지 않을 경우 continue 처리
3788							let option_price = parseFloat(data.price || 0) * parseFloat(data.count);
3789							var option_period_discount_price = 0;
3790							var prod_additional_option_creator_data = prod_data.creator_discount_data || null;
3791							var prod_additional_option_creator_uses_period = prod_additional_option_creator_data && prod_additional_option_creator_data.reference_source === 'period_discount';
3792							if (data.require && (!prod_additional_option_creator_data || prod_additional_option_creator_uses_period)) {
3793								option_period_discount_price = calcProdAdditionalPeriodDiscount(data.prod_idx, data.price, data.count);
3794								if (option_period_discount_price > option_price) {
3795									option_period_discount_price = option_price;
3796								}
3797
3798								// 추가상품 옵션의 경우 상품 할인 금액에 별도로 할인가를 포함하지 않고 항상 옵션가에 포함시킴
3799								option_price = option_price - option_period_discount_price;
3800							}
3801							if (data.require && prod_additional_option_creator_data) {
3802								var prod_additional_option_creator_discount_price = calcAdditionalCreatorDiscount(prod_additional_option_creator_data, option_price, data.count);
3803								if (prod_additional_option_creator_discount_price > option_price) prod_additional_option_creator_discount_price = option_price;
3804								// 추가상품 옵션 행에는 판매가/기간할인가 기준 금액을 담고, 크리에이터 할인은 하단 할인 row 에 누적한다.
3805								_prod_additional_creator_discount_price += prod_additional_option_creator_discount_price;
3806							}
3807							option_total_count += parseInt(data.count);
3808							option_total_price += option_price;
3809							if (!free_ship_notify_is_after_sale) {
3810								var prod_additional_regular_price = getFreeShipNotifyProdAdditionalRegularPrice(prod_data.idx, prod_data.price);
3811								var free_ship_notify_prod_additional_option_unit_price = parseFreeShipNotifyPrice(data.price);
3812								if (data.require) {
3813									free_ship_notify_prod_additional_option_unit_price = calcFreeShipNotifyRequiredOptionPrice(free_ship_notify_prod_additional_option_unit_price, prod_data.price, prod_additional_regular_price);
3814								}
3815								free_ship_notify_total_price += free_ship_notify_prod_additional_option_unit_price * parseFreeShipNotifyPrice(data.count);
3816							}
3817
3818							if(!prod_data.membership_discount || (data.require && prod_additional_option_creator_data)){
3819								membership_discount_not_involved_price += option_price;
3820							}
3821
3822						var option_data_html = [];
3823						$.each(data.options, function (no2, data2) {
3824							option_data_html.push(RemoveTag(data2.option_name + ': ' + data2.value_name));
3825						});
3826						quantity_changeable = !(prod_data.prod_type !== SHOP_CONST.PROD_TYPE.NORMAL && data.require == true);
3827						require_class = (data.require ? '_selected_require_option ' : '');
3828
3829						// 한 개마다 초기화
3830						option_html = '';
3831						// 상품 상세페이지
3832						option_html += '<div class="' + require_class + (no == 0 ? '' : ' middle ') + 'opt_block" id="prdAdditionalOption' + no + '">';
3833						option_html += '	<div class="full-width opt_product_area">';
3834						option_html += '		<div class="area_tit holder">';
3835						option_html += '		 	<div class="tw-flex tw-flex-col tw-gap-[12px]">';
3836						option_html += '        <span class="body_font_color_80 tw-font-bold">' + getLocalizeString('타이틀_추가상품', '', '추가 상품') + '</span>';
3837						option_html += '		 	  <span class="body_font_color_80">' + prod_data.name + ' ' + option_data_html.join(' / ') + '</span>';
3838						if (data.require){
3839							option_html += `		 		<div class="_prod_additional_option_optional goods-select-prod-additional" data-prod-idx="${data.prod_idx}"></div>`;
3840						}
3841						option_html += '		 	</div>';
3842						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>';
3843						option_html += '		</div>';
3844						option_html += '		<div class="area_count holder">';
3845						option_html += '			<div class="option_btn_wrap">';
3846						if (quantity_changeable) {
3847							option_html += '			<div class="option_btn_tools">';
3848							option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.decreaseProdAdditionalOptionCount(' + no + ')"> <i class="btl bt-minus" aria-hidden="true"></i> </a>';
3849							option_html += '				<input type="text" value="' + data.count + '" class="form-control count _count" onchange="SITE_SHOP_DETAIL.changeProdAdditionalOptionCount(' + no + ', $(this).val())" />';
3850							option_html += '				<a href="javascript:;" onclick="SITE_SHOP_DETAIL.increaseProdAdditionalOptionCount(' + no + ')"> <i class="btl bt-plus"></i> </a>';
3851							option_html += '			</div>';
3852						}
3853						option_html += '			</div>';
3854						if (quantity_changeable) {
3855							option_html += '			<div class="area_price absolute absolute_right absolute_middle"><span>' + LOCALIZE.getCurrencyFormat(option_price) + '</span></div>';
3856						} else {
3857							option_html += '<div class="tw-flex tw-justify-between"><div>' + LOCALIZE.타이틀_수량() + ' ' + LOCALIZE.설명_n개띄어쓰기없음(1) + '</div><div>' + LOCALIZE.getCurrencyFormat(option_price) + '</div></div>';
3858						}
3859						option_html += '		</div>';
3860						option_html += '	</div>';
3861						option_html += '</div>';
3862
3863						// 기존처럼 옵션 구분 없이 선택 순 출력으로 사용하려면 prod_option_html 사용하는 부분 지우고 html += option_html 하면 됨
3864						prod_option_html[((data.require) ? "Y" : "N")] += option_html;
3865					});
3866
3867					html += prod_option_html["Y"];
3868					html += prod_option_html["N"];
3869
3870					total_count += option_total_count;
3871					total_price += option_total_price;
3872				}
3873
3874				/* 전체 내역 표시 */
3875
3876        // 선택된 옵션이 없으면 출력하지 않음
3877        if (total_count > 0) {
3878            // 하단에 결제 예상 총 금액 표기
3879            switch (type) {
3880                case 'prod':
3881                    // 상품 상세페이지
3882                    if (!is_view_price) break;
3883
3884                    const is_mobile_view = $(window).width() < 768;
3885                    const left_text_class = is_mobile_view ? '' : 'text-left';
3886                    var total_price_class = is_mobile_view ? 'non_member ' : '';
3887                    const discount_item_style = is_mobile_view ? 'padding-bottom: 4px;' : 'padding: 0 0 16px 0';
3888
3889                    // 할인 항목 HTML 생성 헬퍼
3890                    const discountRowHtml = (label, price, hide_row = false) => {
3891                        if (price <= 0) return '';
3892                        total_price -= price;
3893                        // 행을 숨겨도 결제 예상 금액의 할인 차감은 유지한다.
3894                        if (hide_row) return '';
3895                        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>`;
3896                    };
3897
3898                    // 수량별 할인 계산 (정기구독 시 미적용: IMIO-7132)
3899                    const _isRegularlyProd = $prod_detail.hasClass('detail-regularly');
3900                    const _quantityDiscountCount = _quantityDiscountExcludeOptional ? (order_count + _quantityDiscountRequireOptionCount) : total_count;
3901                    const _quantityDiscountBase = _quantityDiscountBaseRaw - period_discount_price;
3902                    const quantity_discount_price = _isRegularlyProd ? 0 : _calcQuantityDiscount(_quantityDiscountBase, _quantityDiscountCount);
3903
3904                    // 등급 할인 계산 (기간할인 + 수량별 할인 차감 후 금액 기준)
3905                    let membership_discount_target_price = total_price;
3906                    if (membership_discount_not_involved_price > 0) {
3907                        membership_discount_target_price -= membership_discount_not_involved_price;
3908                    }
3909                    if (period_discount_price > 0 && show_period_discount === false) {
3910                        membership_discount_target_price -= period_discount_price;
3911                    }
3912                    membership_discount_target_price -= quantity_discount_price;
3913                    const membership_discount_price = calcMembershipDiscount(membership_discount_target_price);
3914
3915                    // 크리에이터 전용 혜택가 — 본품/옵션 단가는 정상가로 누적되어 있음.
3916                    // 합산 raw(_creator_discount_base) 에 정책 M(합산 cap) 정확 적용 후 합계에서 차감.
3917                    // 본품/옵션 행 표시는 DOM insert 후 _applyCreatorDiscountDistribution() 후처리에서 비례 분배 갱신.
3918                    let creator_discount_price = 0;
3919                    let creator_discount_raw_price = 0;
3920                    let is_creator_discount_max_reached_now = false;
3921                    if (creator_discount_data) {
3922                        creator_discount_raw_price = calcCreatorDiscountRaw(_creator_discount_base, _creator_discount_count);
3923                        creator_discount_price = calcCreatorDiscount(_creator_discount_base, _creator_discount_count);
3924                        if (creator_discount_price > total_price) creator_discount_price = total_price;
3925                        const creator_discount_max_price = parseInt(creator_discount_data.max_price, 10) || 0;
3926                        is_creator_discount_max_reached_now = creator_discount_max_price > 0 && creator_discount_raw_price >= creator_discount_max_price;
3927                    }
3928                    creator_discount_price += _prod_additional_creator_discount_price;
3929                    if (creator_discount_price > total_price) creator_discount_price = total_price;
3930
3931                    if (is_mobile_view) {
3932                        if (is_creator_discount_max_reached_now && !creator_discount_max_reached) {
3933                            showCreatorDiscountMaxToast();
3934                        } else if (!is_creator_discount_max_reached_now) {
3935                            hideCreatorDiscountMaxToast();
3936                        }
3937                        creator_discount_max_reached = is_creator_discount_max_reached_now;
3938                    } else {
3939                        creator_discount_max_reached = is_creator_discount_max_reached_now;
3940                        hideCreatorDiscountMaxToast();
3941                    }
3942
3943                    // 취소선 합계는 할인 전 판매가에 본품/필수옵션 수량의 정상가 차액을 더한다.
3944                    const creator_footer_original_total = total_price
3945                        + (show_period_discount ? period_discount_price : 0)
3946                        + (creator_discount_data && creator_discount_data.has_base_price
3947                            ? Math.max(0, Number(creator_discount_data.base_price) - Number(creator_discount_data.sale_price)) * _creator_discount_count
3948                            : 0);
3949
3950                    // 표기 순서: 상품 할인 → 수량별 할인 → 등급 할인 → 크리에이터 할인
3951                    const period_discount_html = (period_discount_price > 0 && show_period_discount === false)
3952                        ? discountRowHtml(LOCALIZE.설명_상품할인금액(), period_discount_price) : '';
3953                    const quantity_discount_html = discountRowHtml('수량별 할인', quantity_discount_price);
3954                    const membership_discount_html = discountRowHtml(LOCALIZE.설명_회원등급할인(), membership_discount_price);
3955                    const hide_creator_discount_row = creator_discount_data
3956                        && creator_discount_data.hide_calculator_row === true;
3957                    const creator_discount_html = discountRowHtml(
3958                        '크리에이터 할인',
3959                        creator_discount_price,
3960                        hide_creator_discount_row
3961                    );
3962
3963                    const discount_style = is_mobile_view ? 'padding-bottom: 4px;' : 'padding: 16px 0';
3964                    const has_visible_creator_discount = creator_discount_price > 0 && !hide_creator_discount_row;
3965                    const hasDiscount = period_discount_price > 0
3966                        || membership_discount_price > 0
3967                        || quantity_discount_price > 0
3968                        || has_visible_creator_discount;
3969
3970                    total_price = Math.max(total_price, 0);
3971                    total_price_html += `<div class="opt_block total bottom">`;
3972                    total_price_html += period_discount_html + quantity_discount_html + membership_discount_html + creator_discount_html;
3973                    if (!is_mobile_view && hasDiscount) {
3974                        total_price_html += `<p style="border-bottom: 1px solid rgba(0, 0, 0, 0.1); position: absolute; width: 100%; display: block;"></p>`;
3975                    }
3976                    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>`;
3977
3978                    const $footer_price_wrap = $prod_detail.find('._footer_price_wrap');
3979                    if ($footer_price_wrap.length) {
3980                        const $creator_footer_price = $footer_price_wrap.filter('._creator_footer_price');
3981                        // 신규 템플릿이 없으면 기존 합계 표시를 유지한다(파일별 혼합 배포).
3982                        $footer_price_wrap.not('._creator_footer_price').find('._pay_label').text(LOCALIZE.설명_결제예상금액임시(total_count)).show();
3983                        $footer_price_wrap.not('._creator_footer_price').find('._pay_number').text(LOCALIZE.getCurrencyFormat(total_price)).show();
3984                        if ($creator_footer_price.length) {
3985                            const creator_footer_percent = creator_footer_original_total > 0
3986                                ? Math.round((creator_footer_original_total - total_price) * 100 / creator_footer_original_total)
3987                                : 0;
3988                            $creator_footer_price.find('._creator_footer_total_label').text(LOCALIZE.설명_결제예상금액임시(total_count));
3989                            $creator_footer_price.find('._creator_footer_original_total').text(LOCALIZE.getCurrencyFormat(creator_footer_original_total));
3990                            $creator_footer_price.find('._creator_footer_total_percent').text(creator_footer_percent > 0 ? creator_footer_percent + '%' : '').toggle(creator_footer_percent > 0);
3991                            $creator_footer_price.find('._creator_footer_total_price').text(LOCALIZE.getCurrencyFormat(total_price));
3992                            $creator_footer_price.find('._pay_label, ._pay_number').show();
3993                        }
3994                        $footer_price_wrap.find('._pay_org_label').hide();
3995                        $footer_price_wrap.find('._pay_org_number').hide();
3996                    }
3997                    break;
3998                case 'cart':
3999                    // 장바구니
4000                    if (period_discount_price > 0 && show_period_discount == false) {
4001                        // 상품 할인금액
4002                        total_price_html += '<div class="opt_block total bottom">';
4003                        total_price_html += '	<div class="col-xs-4 vertical-middle"> <span class="body_font_color_70 text-13">' + LOCALIZE.설명_상품할인금액() + '</span> </div>';
4004                        total_price_html += '	<div class="col-xs-8 vertical-middle">';
4005                        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>';
4006                        total_price_html += '	</div>';
4007                        total_price_html += '</div>';
4008                        total_price = (total_price - period_discount_price);
4009                    }
4010
4011                    total_price_html += '<div class="opt_block total bottom">';
4012                    total_price_html += '	<div class="col-xs-4 vertical-middle"> <span class="body_font_color_70 text-13">' + LOCALIZE.타이틀_총수량(total_count) + '</span> </div>';
4013                    total_price_html += '	<div class="col-xs-8 vertical-middle">';
4014                    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>';
4015                    total_price_html += '	</div>';
4016                    total_price_html += '</div>';
4017                    break;
4018            }
4019        } else {
4020            if (type === 'prod') {
4021                creator_discount_max_reached = false;
4022                hideCreatorDiscountMaxToast();
4023                if ($prod_detail.find('._footer_price_wrap').length > 0) {
4024                    // 선택된 옵션이 없을 경우 총 상품금액이 아닌 판매가 출력
4025                    var $footer_price_wrap = $prod_detail.find('._footer_price_wrap');
4026                    $footer_price_wrap.find('._pay_org_label').show();
4027                    $footer_price_wrap.find('._pay_org_number').show();
4028                    $footer_price_wrap.find('._pay_label').hide();
4029                    $footer_price_wrap.find('._pay_number').hide();
4030                }
4031            }
4032        }
4033
4034				// 무료배송 안내 SDK 에 무료배송 기준 합계 / 필수옵션 완료 상태 전달
4035				if (free_ship_notify_is_after_sale) {
4036					free_ship_notify_total_price = total_price;
4037				}
4038				notifyFreeShipNotifySdk(Math.max(free_ship_notify_total_price, 0));
4039				//
4040
4041        html += total_price_html;
4042        $selected_options.html(html);
4043
4044        // 크리에이터 혜택가 — 본품/옵션 행은 정상가 표시. 할인은 합계(total_price) 에서만 차감 완료.
4045        // (이전 비례 분배 후처리는 폐기. _applyCreatorDiscountDistribution 미호출)
4046
4047        // 수량별 할인 배너: 옵션 선택 시 표시, 선택 해제 시 숨김 (정기구독 시 미노출: IMIO-7132)
4048        if (type === 'prod') {
4049            var _isRegularly = $prod_detail.hasClass('detail-regularly');
4050            $prod_detail.find('._quantity_discount_banner_wrap').toggle(total_count > 0 && !_isRegularly);
4051
4052            if (_hasFreeShipNotifyInlineMagnet
4053                && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
4054                && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.syncInlineMagnetItemCountsFromAdditionals === 'function') {
4055                var inlineMagnetSyncList = selected_prod_additionals.map(function (additional) {
4056                    var requireOptionCount = prod_additional_require_option_count[additional.idx] || 0;
4057                    var optionLines = selected_prod_additional_options.filter(function (o) {
4058                        return o.prod_idx === additional.idx;
4059                    });
4060                    var optionLinesCount = optionLines.reduce(function (sum, o) { return sum + (o.count || 0); }, 0);
4061                    var totalCount = requireOptionCount > 0
4062                        ? optionLinesCount
4063                        : (optionLines.length > 0 ? optionLinesCount : (additional.count || 0));
4064                    return { idx: additional.idx, count: totalCount };
4065                });
4066                window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.syncInlineMagnetItemCountsFromAdditionals(inlineMagnetSyncList);
4067            }
4068        }
4069
4070				loadProdAdditionalOptionalList();
4071    };
4072
4073    /**
4074     * 옵션 글자 제한 체크 (네이버페이 주문형이 활성화되어 있는 상품의 경우 글자 제한)
4075     * @param target
4076     * @param limit
4077     */
4078    const checkOptionLength = function (target, limit) {
4079        const input_option_length_tooltip = target.parentNode.querySelector('._input_option_length_tooltip');
4080        if (input_option_length_tooltip) {
4081            if (target.value.length >= limit) {
4082                input_option_length_tooltip.classList.remove('tw-hidden');
4083            } else {
4084                input_option_length_tooltip.classList.add('tw-hidden');
4085            }
4086        }
4087    };
4088
4089    var membership_discount_data = {};
4090		var is_main_product_membership_discount = false;
4091    var setMembershipSaleData = function (data, _is_main_product_membership_discount) {
4092        if (typeof data != 'undefined') {
4093            membership_discount_data = data;
4094	          is_main_product_membership_discount = _is_main_product_membership_discount;
4095            refreshFreeShipNotifyInitialTotal();
4096        }
4097    };
4098
4099    var calcMembershipDiscount = function (price) {
4100        var sale_price = 0;
4101
4102        // 크리에이터 전용 혜택가 활성 시 회원그룹(등급) 할인 무시 (정책 #1)
4103        if (creator_discount_data) {
4104            return sale_price;
4105        }
4106
4107        if ($.isEmptyObject(membership_discount_data)) {
4108            return sale_price;
4109        }
4110
4111        if (price >= membership_discount_data['minimum']) {
4112            sale_price = membership_discount_data['amount'];
4113            if (membership_discount_data['type'] == 'percent') {
4114                sale_price = (price * membership_discount_data['amount']) / 100;
4115                if (sale_price > membership_discount_data['maximum']) {
4116                    sale_price = membership_discount_data['maximum'];
4117                }
4118            }
4119        }
4120
4121        if (typeof USE_OMS !== 'undefined' && USE_OMS === true) {
4122            const decimalCount = LOCALIZE.getCurrencyDecimalCount();
4123            if (decimalCount === 0) {
4124                return Math.ceil(sale_price > price ? price : sale_price);
4125            }
4126
4127            const pow = Math.pow(10, decimalCount);
4128            return Math.ceil((sale_price > price ? price : sale_price) * pow) / pow;
4129        } else {
4130            return sale_price > price ? price : sale_price;
4131        }
4132    };
4133
4134    var period_discount_data = {};
4135    var setPeriodDiscountData = function (flag, data, group_list) {
4136        if (typeof data != 'undefined' && data != null) {
4137            period_discount_data = data;
4138            period_discount_data['switch'] = flag;
4139            period_discount_data['group_list'] = group_list;
4140            refreshFreeShipNotifyInitialTotal();
4141        }
4142    };
4143
4144    var period_dc_type;
4145    var calcPeriodDiscount = function (price, count) {
4146        var sale_price = 0;
4147        var dc_price;
4148        if ($.isEmptyObject(period_discount_data)) {
4149            return sale_price;
4150        }
4151        if (period_discount_data['switch'] === false || period_discount_data['switch'] == 'false') return sale_price;
4152        var group_list = period_discount_data['group_list'];
4153
4154        for (var target in period_discount_data['target']) {
4155            var discount_data = period_discount_data['target'][target];
4156
4157            // dc_amount 검증 및 정규화
4158            var dcAmount;
4159            if (typeof discount_data['dc_amount'] === 'string') {
4160                var amountStr = discount_data['dc_amount'];
4161                // EUR 통화는 점을 천단위 구분자로, 쉼표를 소수점으로 사용
4162                if (typeof LOCALIZE !== 'undefined' && LOCALIZE.getCurrency() === 'EUR') {
4163                    amountStr = amountStr.replace(/\./g, '').replace(',', '.');
4164                } else {
4165                    amountStr = amountStr.replace(/,/g, '');
4166                }
4167                dcAmount = parseFloat(amountStr);
4168            } else if (typeof discount_data['dc_amount'] === 'number') {
4169                dcAmount = discount_data['dc_amount'];
4170            } else {
4171                continue; // 유효하지 않은 타입인 경우 다음 항목으로
4172            }
4173
4174            if (Number.isNaN(dcAmount) || dcAmount < 0) {
4175                continue; // NaN이거나 음수인 경우 다음 항목으로
4176            }
4177
4178            // 정규화된 값을 다시 할당
4179            discount_data['dc_amount'] = dcAmount;
4180
4181            if (IS_GUEST) {
4182                if (discount_data['group_type'] != 'guest') continue;
4183            } else {
4184                if (discount_data['group_type'] == 'group' && group_list.indexOf(discount_data['group_code']) == -1) continue;
4185            }
4186            if (discount_data['dc_type'] == 'percent') {
4187                if (discount_data['dc_amount'] > 100) discount_data['dc_amount'] = 100;
4188
4189                if (typeof USE_OMS !== 'undefined' && USE_OMS === true){
4190                  const decimalCount = LOCALIZE.getCurrencyDecimalCount();
4191	                if (decimalCount === 0) {
4192                      dc_price = Math.ceil(( price * discount_data['dc_amount'] ) / 100 * count );
4193                  } else {
4194										const pow = Math.pow(10, decimalCount);
4195                    dc_price = Math.ceil(( ( price * discount_data['dc_amount'] ) / 100 * count ) * pow) / pow;
4196	                }
4197                } else {
4198	                dc_price = (price * discount_data['dc_amount']) / 100 * count;
4199                }
4200            } else {
4201                dc_price = discount_data['dc_amount'] * count;
4202            }
4203            if (sale_price < dc_price) {
4204                sale_price = dc_price;
4205                period_dc_type = discount_data['dc_type'];
4206            }
4207        }
4208
4209        return sale_price;
4210    };
4211
4212	var prod_additional_period_discount_data = {};
4213	var setProdAdditionalPeriodDiscountData = function (prod_idx, flag, data, group_list) {
4214		if (typeof data != 'undefined' && data != null) {
4215			prod_additional_period_discount_data[prod_idx] = data;
4216			prod_additional_period_discount_data[prod_idx]['switch'] = flag;
4217			prod_additional_period_discount_data[prod_idx]['group_list'] = group_list;
4218		}
4219	};
4220
4221	var prod_additional_period_dc_type = {};
4222	var calcProdAdditionalPeriodDiscount = function (prod_idx, price, count) {
4223		var sale_price = 0;
4224		var dc_price;
4225		if ($.isEmptyObject(prod_additional_period_discount_data[prod_idx])) {
4226			return sale_price;
4227		}
4228		if (prod_additional_period_discount_data[prod_idx]['switch'] === false || prod_additional_period_discount_data[prod_idx]['switch'] == 'false') return sale_price;
4229		var group_list = prod_additional_period_discount_data[prod_idx]['group_list'];
4230
4231		for (var target in prod_additional_period_discount_data[prod_idx]['target']) {
4232			var discount_data = prod_additional_period_discount_data[prod_idx]['target'][target];
4233
4234            // dc_amount 검증 및 정규화
4235            var dcAmount;
4236            if (typeof discount_data['dc_amount'] === 'string') {
4237                var amountStr = discount_data['dc_amount'];
4238                // EUR 통화는 점을 천단위 구분자로, 쉼표를 소수점으로 사용
4239                if (typeof LOCALIZE !== 'undefined' && LOCALIZE.getCurrency() === 'EUR') {
4240                    amountStr = amountStr.replace(/\./g, '').replace(',', '.');
4241                } else {
4242                    amountStr = amountStr.replace(/,/g, '');
4243                }
4244                dcAmount = parseFloat(amountStr);
4245            } else if (typeof discount_data['dc_amount'] === 'number') {
4246                dcAmount = discount_data['dc_amount'];
4247            } else {
4248                continue; // 유효하지 않은 타입인 경우 다음 항목으로
4249            }
4250
4251            if (Number.isNaN(dcAmount) || dcAmount < 0) {
4252                continue; // NaN이거나 음수인 경우 다음 항목으로
4253            }
4254
4255            // 정규화된 값을 다시 할당
4256            discount_data['dc_amount'] = dcAmount;
4257
4258			if (IS_GUEST) {
4259				if (discount_data['group_type'] != 'guest') continue;
4260			} else {
4261				if (discount_data['group_type'] == 'group' && group_list.indexOf(discount_data['group_code']) == -1) continue;
4262			}
4263			if (discount_data['dc_type'] == 'percent') {
4264				if (discount_data['dc_amount'] > 100) discount_data['dc_amount'] = 100;
4265				dc_price = (price * discount_data['dc_amount']) / 100 * count;
4266			} else {
4267				dc_price = discount_data['dc_amount'] * count;
4268			}
4269			if (sale_price < dc_price) {
4270				sale_price = dc_price;
4271				prod_additional_period_dc_type[prod_idx] = discount_data['dc_type'];
4272			}
4273		}
4274
4275		return sale_price;
4276	};
4277
4278    /**
4279     * 장바구니에 추가
4280     */
4281    var addCart = async function (callback, options = {}) {
4282        // 배송 국가 선택 유효성 검사
4283        if ($('._deliv_country_selector').length > 0 && (!$deliv_country || $deliv_country === 'none')) {
4284            $('._deliv_country_selector').addClass('warning_select');
4285            alert(typeof LOCALIZE.설명_배송받을국가를선택해주세요 === 'function' ? LOCALIZE.설명_배송받을국가를선택해주세요() : '배송받을 국가를 선택해주세요.');
4286            return;
4287        }
4288
4289        const skipShippingValidation = options.skipShippingValidation || false;
4290        if (!skipShippingValidation) {
4291            const verified = await SHIPPING_SERVICE.verify();
4292            if (!verified) {
4293                return;
4294            }
4295        }
4296
4297        cart_type = $('input[name=add_cart_type]:checked').val();
4298        $.ajax({
4299            type: 'POST',
4300            data: {
4301                'prodIdx': current_prod_idx,
4302                'options': selected_options,
4303                'orderCount': order_count,
4304                'deliv_type': $deliv_type,
4305                'deliv_pay_type': $deliv_pay_type,
4306                'deliv_country': $deliv_country,
4307                'cart_type': cart_type,
4308                'prod_additional_list': selected_prod_additionals,
4309                'prod_additional_option_list': selected_prod_additional_options,
4310                'shipping_template_code': selected_shipping_template_code
4311            },
4312            url: ('/shop/add_cart.cm'),
4313            dataType: 'json',
4314            success: function (result) {
4315                if (result.msg == 'SUCCESS') {
4316                    if (typeof NP_LOG != 'undefined') NP_LOG.AddToCart();
4317                    if (typeof NP_NDA != 'undefined') NP_NDA.AddToCart(result.prod_code);
4318                    if (typeof CRITEO != 'undefined') CRITEO.AddToCart(result.prod_id, result.total_price);
4319                    if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4320                        // 구 provider에는 송신을 위임하지 않는다. 대상 응답이 잘못되어도 미송신을 유지한다.
4321                        try {
4322                            if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.AddToCartDryRun === 'function') {
4323                                FB_PIXEL.AddToCartDryRun(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, result.meta_compatibility);
4324                            } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4325                                console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'AddToCart', disposition: 'provider_unsupported'});
4326                            }
4327                        } catch (e) { /* 관측 오류와 무관하게 후속 UI 및 다른 광고 채널을 계속 실행한다. */ }
4328                    } else {
4329                        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);
4330                    }
4331                    if (typeof ACE_COUNTER != 'undefined') ACE_COUNTER.AddToCart(result.prod_id, result.prod_name, result.prod_count);
4332                    if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddToCart(result.prod_id, result.prod_count, result.total_price, result.currency);
4333                    if (typeof ACE_COUNTER_PARTNER != 'undefined') ACE_COUNTER_PARTNER.AddToCart(result.prod_id, result.prod_count, result.check_quantity);
4334                    if (typeof AW_PRODUCT != 'undefined') AW_PRODUCT(result.prod_count);
4335                    if (typeof AM_PRODUCT != 'undefined') AM_PRODUCT(result.prod_count);
4336                    if (typeof TIKTOK_PIXEL != 'undefined') {
4337                        TIKTOK_PIXEL.track('AddToCart', {
4338                            contents: [
4339                                {
4340                                    content_id: result.prod_id,
4341                                    content_name: result.prod_name,
4342                                    quantity: result.prod_count,
4343                                    price: result.cart_price
4344                                },
4345                            ],
4346                            event_id: result.tiktok_event_id,
4347                            content_type: 'product',
4348                            value: result.total_price,
4349                            currency: result.currency,
4350                        });
4351                    }
4352                    if (typeof CRM_ONSITE != 'undefined') {
4353		                    CRM_ONSITE.track({
4354			                    name: 'ADD_TO_CART',
4355			                    properties: {
4356				                    productCode: result.prod_code,
4357				                    categoryCodes: result.category_codes || []
4358			                    }
4359		                    });
4360                    }
4361                    // BO(마케팅 > 고급 전환추적)에서 설정한 커스텀 추적 코드 주입.
4362                    // append 시 인라인 스크립트가 동기 실행되므로, 잘못된 코드가 저장된 경우
4363                    // 예외가 밖으로 전파되어 뱃지 갱신·담기 완료 알림 등 후속 처리가 막히지 않도록 영역별로 격리한다.
4364                    if (result.advanced_trace_data != null) {
4365                        var appendAdvancedTraceCode = function (selector, code) {
4366                            if (code == '') return;
4367                            try {
4368                                $(selector).append(code);
4369                            } catch (e) {
4370                                if (window.console && window.console.error) {
4371                                    window.console.error('[ADVANCED_TRACE] add_cart 추적 코드 실행 오류 (' + selector + ')', e);
4372                                }
4373                            }
4374                        };
4375                        appendAdvancedTraceCode('head', result.advanced_trace_data.header);
4376                        appendAdvancedTraceCode('body', result.advanced_trace_data.body);
4377                        appendAdvancedTraceCode('footer', result.advanced_trace_data.footer);
4378                    }
4379
4380                    // 같은 상품이 장바구니에 존재하지 않을 경우 장바구니 카운트 증가
4381                    if (!result.prod_found) {
4382                        var $shop_cart_badge_cnt_wrap = $('._shop_cart_badge_cnt_wrap');
4383                        var org_cnt = 0;
4384                        if ($shop_cart_badge_cnt_wrap.eq(0).text() != '') org_cnt = parseInt($shop_cart_badge_cnt_wrap.eq(0).text());
4385                        $shop_cart_badge_cnt_wrap.text(org_cnt + 1);
4386
4387                        // 카운트가 증가할때 기존에 카운트가 0이어서 ui가 안보이던 것을 다시 보이게 활성화
4388                        $shop_cart_badge_cnt_wrap.removeAttr('disabled');
4389                    }
4390
4391                    // 무료배송 안내 모달의 게이지 누적 베이스라인 - 메인 상품 담기 시점의 가격으로 시작.
4392                    // 이후 SITE_SHOP_CART.addCart로 추가상품을 담을 때마다 이 누적값에 더해 imweb:freeShipNotify:cartTotal:update 이벤트를 dispatch한다.
4393                    // 메인 상품 담기 직후 CART_POPUP 모달이 뜨는 경로에서 dispatch 가 누락되면
4394                    // SDK 캐시/mfe store 가 갱신되지 않아 게이지가 0% 로 보이므로 베이스라인 설정과 함께 dispatch.
4395                    var freeShipNotifyCartPrice = (typeof result.free_shipping_price === 'number' ? result.free_shipping_price : (typeof result.total_price === 'number' ? result.total_price : 0));
4396                    var freeShipNotifySdk = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK;
4397                    if (freeShipNotifySdk && typeof freeShipNotifySdk.getCachedSelectedOptionsTotal === 'function') {
4398                        var selectedOptionsTotal = freeShipNotifySdk.getCachedSelectedOptionsTotal();
4399                        if (typeof selectedOptionsTotal === 'number' && selectedOptionsTotal >= 0) {
4400                            freeShipNotifyCartPrice = selectedOptionsTotal;
4401                        }
4402                    }
4403                    window.__imwebFreeShipCartCumulative = freeShipNotifyCartPrice;
4404                    window.__imwebFreeShipCartAdditionalsSum = 0;
4405                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartTotal:update', {
4406                        detail: { totalPrice: window.__imwebFreeShipCartCumulative }
4407                    }));
4408
4409                    if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
4410                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.resetInlineMagnetItemCounts === 'function') {
4411                        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.resetInlineMagnetItemCounts();
4412                    }
4413
4414                    window.__imwebAddToCartProdCodeList = [];
4415                    window.__imwebAddToCartHasFreeShipUpsell = false;
4416                    if (typeof result.prod_code === 'string' && result.prod_code !== '') {
4417                        window.__imwebAddToCartProdCodeList.push(result.prod_code);
4418                    }
4419                    if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
4420                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.getInlineMagnetCodeByIdx === 'function'
4421                        && Array.isArray(selected_prod_additionals)) {
4422                        var codeByIdx = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.getInlineMagnetCodeByIdx();
4423                        selected_prod_additionals.forEach(function (additional) {
4424                            var code = codeByIdx[additional.idx];
4425                            if (typeof code === 'string' && code !== '') {
4426                                window.__imwebAddToCartProdCodeList.push(code);
4427                                window.__imwebAddToCartHasFreeShipUpsell = true;
4428                            }
4429                        });
4430                    }
4431
4432                    callback();
4433                } else {
4434                    // 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4435                    if (result.code === 7) {
4436                        redirectToLoginPage();
4437                    } else {
4438                        alert(result.msg);
4439                        if (result.code === 3) {
4440                            location.reload();
4441                        }
4442                    }
4443                }
4444            }
4445        });
4446    };
4447
4448    /**
4449     * 바로 주문하기  추가, C3 분기를 위해 is_c3 파라미터 추가
4450     */
4451    var _addOrder = function (type, backurl, params) {
4452			let is_oms = params && params.is_oms ? params.is_oms : false;
4453			let callback = params && params.callback ? params.callback : function () {};
4454			let is_gift_buy = params && params.is_gift_buy ? params.is_gift_buy : false;
4455			let selected_freebies = params && params.selected_freebies ? params.selected_freebies : [];
4456
4457
4458			add_order_progress_check = true;
4459			cart_type = $('input[name=add_cart_type]:checked').val();
4460
4461			var _data = {
4462				'backurl': backurl,
4463				'prodIdx': current_prod_idx,
4464				'optDataList': selected_options,
4465				'orderCount': order_count,
4466				'type': type,
4467				'deliv_type': $deliv_type,
4468				'deliv_pay_type': $deliv_pay_type,
4469				'deliv_country': $deliv_country,
4470				'order_type': cart_type,
4471				'is_gift_buy': is_gift_buy,
4472				'prod_additional_list': selected_prod_additionals,
4473				'prod_additional_option_list': selected_prod_additional_options,
4474				'freebie_list': cart_type !== 'regularly' ? selected_freebies : undefined,
4475				'shipping_template_code': selected_shipping_template_code
4476			};
4477
4478			if(is_oms){
4479				if (localStorage.getItem("selectedDeliveryCountry") === "undefined") {
4480					localStorage.removeItem("selectedDeliveryCountry");
4481				}
4482				if (typeof $deliv_country !== 'undefined') {
4483					localStorage.setItem("selectedDeliveryCountry", $deliv_country);
4484				}
4485				_data.infoUrl = location.origin;
4486			}
4487
4488			_data.ace_pid = window._AcePID || window._AceMID;
4489
4490			const [buyBtn, buyBtnMobile, buyBtnDialog, buyBtnMobileDialog, buyBtnRegularly, buyBtnRegularlyMobile] = [$('._btn_buy'), $('._btn_mobile_buy'), $('._btn_dialog_buy'), $('._btn_mobile_dialog_buy'), $('._btn_regularly'), $('._buy_regularly')];
4491			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()];
4492
4493			const setSpinner = function ($buttons) {
4494				const spinner = '<div class="loading-spinner-container"><div class="loading-spinner"></div></div>';
4495
4496				if (Array.isArray($buttons)) {
4497					for (const $button of $buttons) {
4498						if ($button.length > 0) {
4499							$button.html(spinner);
4500						}
4501					}
4502				} else {
4503					if ($buttons.length > 0) {
4504						$buttons.html(spinner);
4505					}
4506				}
4507			}
4508
4509			const unsetSpinner = function ($btn, text) {
4510				if ($btn.length > 0) {
4511					$btn.html(text);
4512				}
4513			};
4514
4515			const landingUrlHeaders = {};
4516			const _landingUrlValue = getBsLandingUrlHeaderValue();
4517			if (_landingUrlValue) {
4518				landingUrlHeaders['imweb-landing-url'] = _landingUrlValue;
4519			}
4520
4521			$.ajax({
4522				type: 'POST',
4523				headers: landingUrlHeaders,
4524				data: _data,
4525				// OMS path 분기
4526				url: is_oms ? '/shop/oms/OMS_add_order.cm' : '/shop/add_order.cm',
4527				dataType: 'json',
4528				cache: false,
4529				beforeSend: function () {
4530					// TODO 선물하기 버튼에 스피너 적용
4531					// 간편결제 버튼은 PG사 정책 우려로 인해 스피너 적용하지 않음
4532					if (is_gift_buy || type === 'npay' || type === 'talkpay') {
4533						add_order_progress_check = false;
4534
4535						return;
4536					}
4537
4538					// 배송 국가 선택 유효성 검사
4539					if ($('._deliv_country_selector').length > 0 && (!$deliv_country || $deliv_country === 'none')) {
4540						$('._deliv_country_selector').addClass('warning_select');
4541						alert(typeof LOCALIZE.설명_배송받을국가를선택해주세요 === 'function' ? LOCALIZE.설명_배송받을국가를선택해주세요() : '배송받을 국가를 선택해주세요.');
4542
4543						add_order_progress_check = false;
4544
4545						return false;
4546					}
4547
4548					// 필수옵션 선택 유효성 검사 실행
4549					if (!checkRequireOption()) {
4550						alert(LOCALIZE.설명_필수옵션이모두선택되어있지않습니다());
4551
4552						add_order_progress_check = false;
4553
4554						return false;
4555					}
4556
4557					// 구매하기, 모바일 구매하기, 다이얼로그 구매하기, 모바일 다이얼로그 구매하기, 정기구독 신청, 모바일 정기구독 신청 버튼에 로딩 스피너 추가
4558					setSpinner([buyBtn, buyBtnMobile, buyBtnDialog, buyBtnMobileDialog, buyBtnRegularly, buyBtnRegularlyMobile]);
4559				},
4560				error: function (result) {
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					add_order_progress_check = false;
4575				},
4576				success: function (result) {
4577					if (result.msg == 'SUCCESS') {
4578								// 페이지가 이동하는 동작의 시각적 연결성을 위해 성공 시에는 스피너를 초기화 하지 않는다.
4579
4580								if (typeof AW_PRODUCT != 'undefined' && typeof AW_F_D != 'undefined') {
4581										AW_F_D(current_prod_idx, 'i', order_count);
4582										AW_PRODUCT(order_count);
4583								}
4584
4585								if (result.advanced_kakao_trace_data != null) {
4586										$('body').append(result.advanced_kakao_trace_data);
4587								}
4588								fetch('/ajax/oms/OMS_auth.cm')
4589									.then((res) => res.json())
4590									.then((res) => {
4591										if (res.msg === 'SUCCESS') {
4592											window.sessionStorage.setItem('oms_token', res.token);
4593										}
4594									})
4595									.finally(() => callback(result))
4596						} else {
4597								add_order_progress_check = false;
4598
4599								// 구매하기 PC 버튼 스피너 초기화
4600								unsetSpinner(buyBtn, buyBtnText);
4601								// 구매하기 모바일 버튼 스피너 초기화
4602								unsetSpinner(buyBtnMobile, buyBtnMobileText);
4603								// 구매하기 다이얼로그 버튼 스피너 초기화
4604								unsetSpinner(buyBtnDialog, buyBtnDialogText);
4605								// 구매하기 모바일 다이얼로그 버튼 스피너 초기화
4606								unsetSpinner(buyBtnMobileDialog, buyBtnMobileDialogText);
4607								// 정기구독 신청 버튼 스피너 초기화
4608								unsetSpinner(buyBtnRegularly, buyBtnRegularlyText);
4609								// 정기구독 신청 모바일 버튼 스피너 초기화
4610								unsetSpinner(buyBtnRegularlyMobile, buyBtnRegularlyMobileText);
4611
4612								if (type == 'talkpay') {
4613										callback(result);
4614								} else {
4615										// 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4616										if (result.code === 7) {
4617												redirectToLoginPage();
4618										} else {
4619												alert(result.msg.replace(/\\n/g, '\n'));
4620												if (result.code === 11) {
4621														callback(result);
4622												}
4623												if (result.code === 6) {
4624														location.reload();
4625												}
4626										}
4627								}
4628
4629								if(result.code === 2){
4630									$('._deliv_country_selector').toggleClass('warning_select');
4631								}
4632						}
4633
4634						add_order_progress_check = false;
4635				}
4636			});
4637    };
4638
4639    var addOrder = function (type, backurl, params) {
4640        if (add_order_progress_check) return false;
4641        if (!ensureAddOrderPermission()) return false;
4642        if (typeof AM_PRODUCT != 'undefined') AM_PRODUCT(order_count);
4643
4644            //type 이 guest_login 으로 넘어오는경우 비회원주문+로그인페이지로이동후주문 방식임 type 을 normal 로처리해야 다른 로직에 영향이 없어 이렇게 처리함
4645            // NOTE: 현재 addOrder 함수를 guest_login 값을 type인자로 호출하는 부분을 php코드베이스 내에서 찾을 수 없음.
4646            var is_guest_login = false;
4647            if (type == 'guest_login') {
4648                is_guest_login = true;
4649                type = 'normal';
4650            }
4651
4652            _addOrder(type, backurl, {
4653							...params,
4654							is_oms: false,
4655							callback: function(result){
4656                switch (type) {
4657                    case 'npay':
4658                        if (result.npay_url == '') {
4659                            if (result.errmsg) {
4660                                alert(escape_javascript(result.errmsg));
4661                            } else {
4662                                alert(LOCALIZE.설명_네이버페이상품구매실패(escape_javascript(result.errmsg)));
4663                            }
4664                            add_order_progress_check = false;
4665                        } else {
4666                            if (!!result.shopping_additional_price_msg) {
4667                                if (!confirm(result.shopping_additional_price_msg + '\n\n' + LOCALIZE.설명_네이버페이를계속해서진행하시겠습니까())) {
4668                                    add_order_progress_check = false;
4669                                    return false;
4670                                }
4671                            }
4672
4673                            if (result.auth_type == 2) {
4674                                // 성인 인증이 필요할 경우
4675                                window.location.href = '/?mode=adult_auth_npay&data=' + result.data;
4676                            } else {
4677                                // 성인 인증이 필요하지 않는 경우
4678                                var npay_order_info = result['npay_order_info'];
4679
4680                                if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder();
4681                                if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4682                                    try {
4683                                        if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
4684                                            FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
4685                                        } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4686                                            console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
4687                                        }
4688                                    } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
4689                                } else {
4690                                    if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4691                                }
4692                                if (typeof FB_PIXEL != 'undefined' && FB_PIXEL) {
4693                                    if (result.fb_npay_switch === 'Y') FB_PIXEL.addNpayOrder(npay_order_info);
4694                                }
4695
4696                                if (typeof TIKTOK_PIXEL != 'undefined') {
4697                                    TIKTOK_PIXEL.track('InitiateCheckout', {
4698                                        event_id: result.order_code,
4699                                        contents: result.prod_list.map(prod => ({
4700                                            content_id: prod.id,
4701                                            content_name: prod.name,
4702                                            price: prod.price,
4703                                        })),
4704                                        content_type: 'product',
4705                                        value: result.total_price,
4706                                        currency: result.currency,
4707                                    });
4708                                }
4709
4710                                if (result.google_analytics_type == 'G' && result.is_ga_api_secret === false) {
4711                                    if (typeof GOOGLE_ANAUYTICS != 'undefined') GOOGLE_ANAUYTICS.addNpayOrder(npay_order_info);
4712                                }
4713                                if (typeof GOOGLE_ADWORDS_TRACE != 'undefined' && result.google_ads_include_npay === 'Y') GOOGLE_ADWORDS_TRACE.addNpayOrder(npay_order_info);
4714                                if (typeof CRITEO != 'undefined') CRITEO.npayTrackTransaction(npay_order_info);
4715	                            if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4716															window.location.href = result.npay_url;
4717                            }
4718                        }
4719                        break;
4720                    case 'talkpay':
4721                        switch (params.type) {
4722                            case 'onOrder':
4723                                if (result.msg == 'SUCCESS') {
4724                                    if (result.order_sheet_id) {
4725                                        params.onSuccess(result.order_sheet_id);
4726                                    } else {
4727                                        params.onFailure({ message: result.msg });
4728                                    }
4729                                } else {
4730                                    params.onFailure({ message: result.msg });
4731                                }
4732                                break;
4733                            case 'onPayOrder':
4734                                if (result.msg == 'SUCCESS') {
4735                                    if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4736                                        try {
4737                                            if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
4738                                                FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
4739                                            } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4740                                                console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
4741                                            }
4742                                        } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
4743                                    } else {
4744                                        if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4745                                    }
4746                                    if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder(result.order_code);
4747	                                if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4748
4749                                    if (typeof TIKTOK_PIXEL != 'undefined') {
4750                                        TIKTOK_PIXEL.track('InitiateCheckout', {
4751                                            event_id: result.order_code,
4752                                            contents: result.prod_list.map(prod => ({
4753                                                content_id: prod.id,
4754                                                content_name: prod.name,
4755                                                price: prod.price,
4756                                            })),
4757                                            content_type: 'product',
4758                                            value: result.total_price,
4759                                            currency: result.currency,
4760                                        });
4761                                    }
4762
4763                                    window.location.href = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code);
4764                                }
4765                                break;
4766                        };
4767
4768                        add_order_progress_check = false;
4769                        break;
4770                    default:
4771                        if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4772                            try {
4773                                if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
4774                                    FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
4775                                } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4776                                    console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
4777                                }
4778                            } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
4779                        } else {
4780                            if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4781                        }
4782                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder(result.order_code);
4783	                    if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4784
4785                        if (typeof TIKTOK_PIXEL != 'undefined') {
4786                            TIKTOK_PIXEL.track('InitiateCheckout', {
4787                                event_id: result.order_code,
4788                                contents: result.prod_list.map(prod => ({
4789                                    content_id: prod.id,
4790                                    content_name: prod.name,
4791                                    price: prod.price,
4792                                })),
4793                                content_type: 'product',
4794                                value: result.total_price,
4795                                currency: result.currency,
4796                            });
4797                        }
4798
4799                        if (is_guest_login && cm_data.use_login_popup === 'N' && !options.is_using_nomember_order_login_after) {	//비회원주문시 로그인 페이지로 이동처리
4800                            //비회원 구매 불가 조건의 경우 로그인 페이지로 보냄. (주문 생성 x)
4801                            if (result.code === 11) {
4802                                var back_url_base64 = window.location.pathname;
4803                                var url_param = document.location.href.split("?");
4804                                if (url_param.length > 1) {
4805                                    back_url_base64 += "?" + url_param[1];
4806                                }
4807                                window.location.href = '/login?back_url=' + encodeURIComponent(btoa(back_url_base64));
4808                            } else {
4809                                window.location.href = '/login?shopping_order_code=' + result.order_code + '&back_url=' + encodeURIComponent(result.back_url_base64);
4810                            }
4811                        } else if (result.code === 7) {
4812                            // 코드 7: 비회원 이용권 구매 시 로그인 페이지로
4813                            redirectToLoginPage();
4814                        } else if(result.msg === 'SUCCESS'){
4815                            //일반주문이며 주문서 생성에 성공 한 경우 주문서로 이동
4816                            window.location.href = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code);
4817                        }
4818                        break;
4819                }
4820            }
4821					});
4822    }
4823
4824    var OMS_addOrder = async function (type, backurl, params) {
4825			if(add_order_progress_check) return false;
4826			if (!ensureAddOrderPermission()) return false;
4827			if ( typeof AM_PRODUCT != 'undefined' ) AM_PRODUCT(order_count);
4828
4829            if (params.shippingVerification) {
4830                const verified = await SHIPPING_SERVICE.verify({ type });
4831                if (!verified) {
4832                    return false;
4833                }
4834            }
4835
4836			//type 이 guest_login 으로 넘어오는경우 비회원주문+로그인페이지로이동후주문 방식임 type 을 normal 로처리해야 다른 로직에 영향이 없어 이렇게 처리함
4837			var is_guest_login=false;
4838			if (type=='guest_login'){
4839				is_guest_login=true;
4840				type='normal';
4841			}
4842
4843			_addOrder(type, backurl, {
4844				...params,
4845				is_oms: true,
4846				callback: function(result){
4847					switch( type ) {
4848						case 'npay':
4849							if(result.npay_url == '') {
4850								if ( result.errmsg ) {
4851									alert(escape_javascript(result.errmsg));
4852								} else {
4853									alert(LOCALIZE.설명_네이버페이상품구매실패(escape_javascript(result.errmsg)));
4854								}
4855								add_order_progress_check = false;
4856							} else {
4857								if ( !! result.shopping_additional_price_msg ) {
4858									if ( ! confirm(result.shopping_additional_price_msg + '\n\n' + LOCALIZE.설명_네이버페이를계속해서진행하시겠습니까()) ) {
4859										add_order_progress_check = false;
4860										return false;
4861									}
4862								}
4863
4864								if ( result.auth_type == 2 ) {
4865									// 성인 인증이 필요할 경우
4866									window.location.href = '/?mode=adult_auth_npay&data=' + result.data;
4867								} else {
4868									// 성인 인증이 필요하지 않는 경우
4869									var npay_order_info = result['npay_order_info'];
4870
4871									if ( typeof CHANNEL_PLUGIN != 'undefined' ) CHANNEL_PLUGIN.AddOrder();
4872									if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4873									    try {
4874									        if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
4875									            FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
4876									        } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4877									            console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
4878									        }
4879									    } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
4880									} else {
4881									    if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4882									}
4883									if (typeof FB_PIXEL != 'undefined' && FB_PIXEL) {
4884										if(result.fb_npay_switch === 'Y') FB_PIXEL.addNpayOrder(npay_order_info);
4885									}
4886
4887									if ( result.google_analytics_type == 'G' && result.is_ga_api_secret === false ) {
4888										if ( typeof GOOGLE_ANAUYTICS != 'undefined') GOOGLE_ANAUYTICS.addNpayOrder(npay_order_info);
4889									}
4890									if ( typeof GOOGLE_ADWORDS_TRACE != 'undefined' && result.google_ads_include_npay === 'Y') GOOGLE_ADWORDS_TRACE.addNpayOrder(npay_order_info);
4891									if ( typeof CRITEO != 'undefined') CRITEO.npayTrackTransaction(npay_order_info);
4892									if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4893
4894                                    if (typeof TIKTOK_PIXEL != 'undefined') {
4895                                        TIKTOK_PIXEL.track('InitiateCheckout', {
4896                                            event_id: result.order_code,
4897                                            contents: result.prod_list.map(prod => ({
4898                                                content_id: prod.id,
4899                                                content_name: prod.name,
4900                                                price: prod.price,
4901                                            })),
4902                                            content_type: 'product',
4903                                            value: result.total_price,
4904                                            currency: result.currency,
4905                                        });
4906                                    }
4907
4908									if (typeof result.npay_url !== 'string' || result.npay_url.indexOf('pay.naver.com') < 0) return false;
4909									window.location.href = result.npay_url;
4910								}
4911							}
4912							break;
4913						case 'talkpay':
4914							switch ( params.type ) {
4915								case 'onOrder':
4916									if ( result.msg == 'SUCCESS' ) {
4917										if ( result.order_sheet_id ) {
4918											params.onSuccess(result.order_sheet_id);
4919										} else {
4920											params.onFailure({message: result.msg});
4921										}
4922									} else {
4923										params.onFailure({message: result.msg});
4924									}
4925									break;
4926								case 'onPayOrder':
4927									if ( result.msg == 'SUCCESS' ) {
4928										if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4929										    try {
4930										        if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
4931										            FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
4932										        } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4933										            console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
4934										        }
4935										    } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
4936										} else {
4937										    if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4938										}
4939										if ( typeof CHANNEL_PLUGIN != 'undefined' ) CHANNEL_PLUGIN.AddOrder(result.order_code);
4940										if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4941
4942                                        if (typeof TIKTOK_PIXEL != 'undefined') {
4943                                            TIKTOK_PIXEL.track('InitiateCheckout', {
4944                                                event_id: result.order_code,
4945                                                contents: result.prod_list.map(prod => ({
4946                                                    content_id: prod.id,
4947                                                    content_name: prod.name,
4948                                                    price: prod.price,
4949                                                })),
4950                                                content_type: 'product',
4951                                                value: result.total_price,
4952                                                currency: result.currency,
4953                                            });
4954                                        }
4955
4956										var shop_payment_url = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code) + '&order_no=' + encodeURIComponent(result.order_no);
4957										if (result.member_code) {
4958											shop_payment_url += "&order_member=" + encodeURIComponent(result.member_code);
4959										}
4960										if ( result.kakaopay_only === 'Y' ) {
4961											shop_payment_url += "&kakaopay_only=Y";
4962										}
4963										window.location.href = shop_payment_url;
4964									}
4965									break;
4966							};
4967
4968							add_order_progress_check = false;
4969							break;
4970						default:
4971							if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
4972							    try {
4973							        if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
4974							            FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
4975							        } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
4976							            console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
4977							        }
4978							    } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
4979							} else {
4980							    if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
4981							}
4982							if ( typeof CHANNEL_PLUGIN != 'undefined' ) CHANNEL_PLUGIN.AddOrder(result.order_code);
4983							if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
4984
4985                            if (typeof TIKTOK_PIXEL != 'undefined') {
4986                                TIKTOK_PIXEL.track('InitiateCheckout', {
4987                                    event_id: result.order_code,
4988                                    contents: result.prod_list.map(prod => ({
4989                                        content_id: prod.id,
4990                                        content_name: prod.name,
4991                                        price: prod.price,
4992                                    })),
4993                                    content_type: 'product',
4994                                    value: result.total_price,
4995                                    currency: result.currency,
4996                                });
4997                            }
4998
4999							if (is_guest_login && cm_data.use_login_popup === 'N' && !options.is_using_nomember_order_login_after){	//비회원주문시 로그인 페이지로 이동처리
5000															//비회원 구매 불가 조건의 경우 로그인 페이지로 보냄. (주문 생성 x)
5001															if(result.code === 11){
5002																	var back_url_base64 = window.location.pathname;
5003																	var url_param = document.location.href.split("?");
5004																	if(url_param.length > 1){
5005																			back_url_base64 += "?"+url_param[1];
5006																	}
5007																	window.location.href = '/login?back_url='+encodeURIComponent(btoa(back_url_base64));
5008															}else{
5009																	window.location.href = '/login?shopping_order_code=' + result.order_code + '&back_url=' + encodeURIComponent(result.back_url_base64);
5010															}
5011							} else if (result.code === 7) {
5012															// 코드 7: 비회원 이용권 구매 시 로그인 페이지로
5013															redirectToLoginPage();
5014							}else{  //일반주문인경우 결제화면으로 이동
5015								if (typeof result.c3_result === "undefined" || typeof result.c3_result.orderCode === "undefined" || typeof result.c3_result.orderNo === "undefined") return
5016								window.location.href
5017									= "/shop_payment/?order_code="
5018									+ encodeURIComponent(result.c3_result.orderCode)
5019									+ "&order_no="
5020									+  encodeURIComponent(result.c3_result.orderNo)
5021									+ (result.c3_result.memberCode ? `&order_member=${encodeURIComponent(result.c3_result.memberCode)}` : '');
5022							}
5023							break;
5024					}
5025				}
5026			});
5027    }
5028
5029    /**
5030     * 네이버페이지 찜하기  등록
5031     */
5032    var addNPayWish = function () {
5033        $.ajax({
5034            type: 'POST',
5035            data: { 'prod_idx_list': [current_prod_idx] },
5036            url: ('/shop/add_npay_wish.cm'),
5037            dataType: 'json',
5038            cache: false,
5039            success: function (result) {
5040                if (result.msg == 'SUCCESS') {
5041                    if (result.mobile == 'Y')
5042                        window.location.href = result.npay_url;
5043                    else
5044                        window.open(result.npay_url, "", "scrollbars=yes,width=400,height=267");
5045                } else {
5046                    alert(LOCALIZE.설명_네이버페이찜등록실패(escape_javascript(result.msg)));
5047                }
5048            }
5049        });
5050    };
5051
5052
5053    var digitalFileDownload = function (order_no) {
5054        if (prod_type !== SHOP_CONST.PROD_TYPE.DIGITAL) return false;
5055        $.ajax({
5056            "type": "POST",
5057            "data": { "prod_no": current_prod_idx, "order_no": order_no, "mode": "detail" },
5058            "url": "/ajax/shop_digital_prod_download.cm",
5059            "dataType": "JSON",
5060            "success": function (res) {
5061                if (res['msg'] == 'SUCCESS') {
5062                    if (res['download_info_msg'].trim() == '' || confirm(res['download_info_msg'])) {
5063                        location.href = res['file_url'];
5064                    }
5065                } else {
5066                    alert(res['msg']);
5067                    if (typeof res['reload'] != 'undefined') {
5068                        location.reload();
5069                    }
5070                }
5071            }
5072        });
5073    };
5074
5075    /**
5076     * OMS 디지털 상품 다운로드 (쓰로틀링 적용)
5077     */
5078    var lastOmsDownloadClick = 0;
5079    var handleOmsDigitalDownload = function (e, href) {
5080        if (e) e.preventDefault();
5081
5082        var now = new Date().getTime();
5083        if (now - lastOmsDownloadClick < 1000) {
5084            return;
5085        }
5086
5087        lastOmsDownloadClick = now;
5088        window.location.href = href;
5089    };
5090    /**
5091     * 모바일에서 구매하기 클릭시 옵션 표시 처리
5092     * @param type	buy 구매하기, regularly 정기구독, gift 선물하기
5093     */
5094    var showMobileOptions = function (type) {
5095        if (!options.is_price_view_permission && window.IS_GUEST) {
5096            const backUrl = encodeURIComponent(base64Encode(window.location.href));
5097            if (cm_data.use_login_popup === 'Y') {
5098                window.SITE_MEMBER.openLogin(
5099                    backUrl,
5100                    'payment',
5101                    function () {
5102											if (window.USE_OMS === true) {
5103												OMS_addOrder(
5104													'normal',
5105													encodeURIComponent(base64Encode(window.location.href))
5106												)
5107											} else {
5108												addOrder(
5109													'normal',
5110													encodeURIComponent(base64Encode(window.location.href))
5111												);
5112											}
5113										},
5114                    'N',
5115                    'payment',
5116                );
5117            } else {
5118                window.location.href = '/login?back_url=' + backUrl;
5119            }
5120            return;
5121        }
5122        if (type === 'regularly') {
5123            $('input[name=add_cart_type]:checked').val('regularly');
5124            // 모바일 구매/옵션 바텀시트 여는 코드
5125            $prod_detail.addClass('open detail-regularly');
5126        } else {
5127            $('input[name=add_cart_type]:checked').val('normal');
5128            // 모바일 구매/옵션 바텀시트 여는 코드
5129            $prod_detail.addClass('open');
5130			$prod_detail.find('._btn_restock').css('display', 'flex');
5131        }
5132        showMobileFirstSelect();
5133        var full_input = false;
5134        var $form = $options.find('._form_parent');
5135        // 첫번째 선택 후 창을 닫았을때
5136        $form.each(function (index) {
5137
5138            var is_input = $(this).children().hasClass('option_box_wrap');
5139
5140            if (is_input) {
5141                var input_items = $(this).find('._requireInputOption');
5142                input_items.each(function (index) {
5143                    if ($(this).val() != '') {
5144                        if ((index + 1) == input_items.length) {
5145                            full_input = true;
5146                        }
5147                    } else {
5148                        return false;
5149                    }
5150                });
5151
5152            } else {
5153                var prev_is_selected = $(this).prev().find('.dropdown-item').hasClass('selected');
5154                var is_selected = $(this).find('.dropdown-item').hasClass('selected');
5155                // 이전 필수 드롭다운이 선택 되어 있을 경위
5156                if (prev_is_selected || full_input) {
5157                    $(this).toggleClass('disabled', false);
5158
5159                    full_input = false;
5160
5161                    if (!is_selected) {
5162                        var current = 0;
5163                        var $current_form_select = $(this).eq(current);
5164                        var $current_form_dropdown_toggle = $current_form_select.find('.dropdown-toggle');
5165
5166                        $current_form_dropdown_toggle.attr('aria-expanded', true);
5167                        $current_form_dropdown_toggle.toggleClass('active', true);
5168                        $current_form_select.attr('data-val', true);
5169
5170                        setTimeout(function () {
5171                            $current_form_dropdown_toggle.dropdown('toggle');
5172                        }, 10);
5173
5174                        $current_form_dropdown_toggle.off('click').on('click', function () {
5175                            $current_form_dropdown_toggle.attr('aria-expanded', false);
5176                            $current_form_dropdown_toggle.toggleClass('active', false);
5177                            $current_form_select.attr('data-val', false);
5178                        });
5179                    }
5180                } else if (index === 0) {
5181                    // 첫 드롭다운은 일단 true로 표시
5182                    $(this).toggleClass('disabled', false);
5183                } else {
5184                    $(this).toggleClass('disabled', true);
5185                }
5186            }
5187        });
5188        // 모달 또는 바텀시트 표시될 때 전체스크롤 등의 동작 막는 코드
5189        $('html').addClass('mobile-shop-open');
5190        $('body').addClass('mobile-nav-on modal-open');
5191        $('header#doz_header_wrap').addClass('bg-back');
5192        $('.categorize-mobile .site_prod_nav_wrap').addClass('bg-back');
5193
5194        // 실시간 상담 부분
5195        $('#ch-plugin, #kakao-talk-channel-chat-button, .talk_banner_wrap, #fb-root').hide();
5196
5197	    if (options.is_possible_use_gift) {
5198		    handleMobileGiftFooterUI(true, type);
5199	    }
5200    };
5201    const changeValueChecked = function () {
5202        $("input:radio[name='add_cart_type']:radio[value='normal']").prop('checked', false);
5203        $("input:radio[name='add_cart_type']:radio[value='regularly']").prop('checked', true);
5204    };
5205    /**
5206     * 조합형 + 필수 옵션일 때 첫번째 셀렉트 박스는 show 처리
5207     */
5208    var showMobileFirstSelect = function () {
5209        var $form_select = $options.find('._first_form_select');
5210        if ($form_select.length) {
5211            var current = 0;
5212            var $current_form_select = $form_select.eq(current);
5213            var $current_form_dropdown_toggle = $current_form_select.find('.dropdown-toggle');
5214
5215            $current_form_dropdown_toggle.attr('aria-expanded', true);
5216            $current_form_dropdown_toggle.toggleClass('active', true);
5217            $current_form_select.attr('data-val', true);
5218
5219            setTimeout(function () {
5220                $current_form_dropdown_toggle.dropdown('toggle');
5221            }, 50);
5222
5223            $current_form_dropdown_toggle.off('click').on('click', function () {
5224                $current_form_dropdown_toggle.attr('aria-expanded', false);
5225                $current_form_dropdown_toggle.toggleClass('active', false);
5226                $current_form_select.attr('data-val', false);
5227            });
5228            $form_select.parent('._form_parent').nextAll().toggleClass('disabled', true);
5229        }
5230    };
5231
5232    /**
5233     * 모바일에서 구매하기 클릭시 옵션 숨기기 처리
5234     */
5235    var hideMobileOptions = function () {
5236        $prod_detail.removeClass('open detail-regularly');
5237        $('html').removeClass('mobile-shop-open');
5238        $('body').removeClass('mobile-nav-on modal-open');
5239        $('header#doz_header_wrap').removeClass('bg-back');
5240        $('.categorize-mobile .site_prod_nav_wrap').removeClass('bg-back');
5241
5242        // 실시간 상담 부분
5243        $('#ch-plugin, #kakao-talk-channel-chat-button, .talk_banner_wrap, #fb-root').show();
5244				$prod_detail.find('._btn_restock').hide();
5245
5246		if (options.is_possible_use_gift) {
5247			handleMobileGiftFooterUI(false);
5248		}
5249    };
5250
5251    /**
5252     * PC B타입에서 구매하기/장바구니 클릭 시 옵션 표시 처리, 옵션 표시되어있으면 구매하기/장바구니
5253     * @param type order: 구매하기, cart: 장바구니, gift: 선물하기
5254     * @param backurl 비회원 구매 시 로그인 등에서 사용될 back url
5255     */
5256    var showPCOptions = function (type, backurl = location.href, is_c3 = false){
5257        if (backurl == undefined) backurl = location.href;
5258        if ($prod_detail.hasClass('open')) {
5259					// 바텀시트가 열려 있으면
5260            if(type == 'cart'){
5261							if (typeof BrandScope !== "undefined") {
5262								try {
5263									BrandScope.track('click_add_to_cart_shop_view', {
5264										'action': 'click',
5265										'content': 'add_to_cart',
5266										'where': 'shop_view',
5267										'prod_code': current_prod_code,
5268										'is_regularly_prod': $('input[name=add_cart_type]:checked').val() === 'regularly' ? true : false,
5269										'prod_type': prod_type
5270									});
5271								} catch {
5272									// ignore
5273								}
5274							}
5275                SITE_SHOP_DETAIL.addCart();
5276            } else if (type == 'gift') {
5277							  if (is_c3) {
5278									SITE_SHOP_DETAIL.OMS_addGiftOrder('normal', backurl);
5279								} else {
5280									SITE_SHOP_DETAIL.addGiftOrder('normal', backurl);
5281								}
5282            } else {
5283                if (is_c3) {
5284                    SITE_SHOP_DETAIL.OMS_addOrder('normal', backurl);
5285                }else{
5286                    SITE_SHOP_DETAIL.addOrder('normal', backurl);
5287                }
5288            }
5289        } else {
5290					// 바텀시트가 안열려 있으면
5291            $prod_detail.find('._btn_npay').hide();
5292            $prod_detail.find('._btn_kakaopay').hide();
5293			$prod_detail.find('._btn_restock').show();
5294            $prod_detail.addClass('open');
5295            var $add_cart_type = $prod_detail.find('input[name=add_cart_type]:checked');
5296            if ($add_cart_type.length > 0) {
5297                if ($add_cart_type.val() === 'regularly') {
5298                    $prod_detail.addClass('detail-regularly');
5299                }
5300            }
5301
5302			if (options.is_possible_use_gift) {
5303				handlePCGiftFooterUI(true, type);
5304			}
5305        }
5306    };
5307
5308    var hidePCOptions = function () {
5309        $prod_detail.removeClass('open detail-regularly');
5310        $prod_detail.find('._btn_npay').show();
5311        $prod_detail.find('._btn_kakaopay').show();
5312		$prod_detail.find('._btn_restock').hide();
5313
5314	    if (options.is_possible_use_gift) {
5315		    handlePCGiftFooterUI(false);
5316	    }
5317    };
5318
5319	var handlePCGiftFooterUI = function (isOpen, type) {
5320		const $root = $('._buy_footer_fixed');
5321
5322		const $footerPriceWrap = $root.find('._footer_price_wrap');
5323		const $btnCart = $root.find('._btn_cart');
5324		const $btnGift = $root.find('._btn_gift');
5325		const $btnBuy = $root.find('._btn_buy');
5326		const $btnDialogSoldout = $root.find('._btn_dialog_soldout');
5327		const $btnDialogBuy = $root.find('._btn_dialog_buy');
5328		const $btnDialogGift = $root.find('._btn_dialog_gift');
5329
5330		const hideAll = () => {
5331			$footerPriceWrap.hide();
5332			$btnCart.hide();
5333			$btnGift.hide();
5334			$btnBuy.hide();
5335			$btnDialogSoldout.hide();
5336			$btnDialogBuy.hide();
5337			$btnDialogGift.hide();
5338		}
5339
5340		const isSoldout = options.is_soldout;
5341
5342		hideAll();
5343
5344		if (isOpen) {
5345			if (isSoldout) {
5346				$btnDialogSoldout.show();
5347			} else {
5348				$footerPriceWrap.show();
5349				$btnCart.show();
5350
5351				switch(type) {
5352					case 'order':
5353						$btnDialogBuy.show();
5354						break;
5355					case 'gift':
5356						$btnDialogGift.show();
5357						break;
5358					case 'cart':
5359						$btnGift.show();
5360						$btnBuy.show();
5361						break;
5362				}
5363			}
5364		} else {
5365			$footerPriceWrap.show();
5366			$btnCart.show();
5367			$btnGift.show();
5368			$btnBuy.show();
5369		}
5370	}
5371
5372	const handleMobileGiftFooterUI = (isOpen, type) => {
5373		const $root = $('._mobile-gift-footer');
5374		const $socialBtnRoot = $('._social_m_position');
5375
5376		const $btnGift = $root.find('._btn_mobile_gift');
5377		const $btnBuy = $root.find('._btn_mobile_buy');
5378		const $btnNPay = $root.find('._btn_mobile_npay');
5379		const $btnKakaoPay = $root.find('._btn_mobile_kakaopay');
5380		const $btnDialogSoldout = $root.find('._btn_mobile_dialog_soldout');
5381		const $btnDialogCart = $root.find('._btn_mobile_dialog_cart');
5382		const $btnDialogBuy = $root.find('._btn_mobile_dialog_buy');
5383		const $btnDialogGift = $root.find('._btn_mobile_dialog_gift');
5384
5385		const hideAll = () => {
5386			$btnGift.hide();
5387			$btnBuy.hide();
5388			$btnNPay.hide();
5389			$btnKakaoPay.hide();
5390			$btnDialogSoldout.hide();
5391			$btnDialogCart.hide();
5392			$btnDialogBuy.hide();
5393			$btnDialogGift.hide();
5394			$socialBtnRoot.hide();
5395		}
5396
5397		hideAll();
5398
5399		const isSoldout = options.is_soldout;
5400
5401        if (isOpen) {
5402            if (isSoldout) {
5403                $btnDialogSoldout.show();
5404            } else {
5405                switch(type) {
5406                    case 'buy':
5407                        $btnDialogCart.show();
5408                        $btnDialogBuy.show();
5409                        if ($socialBtnRoot.find('._social_pay').children().length > 0) $socialBtnRoot.show();
5410                        break;
5411                    case 'gift':
5412                        $btnDialogGift.show();
5413                        break;
5414                }
5415            }
5416        } else {
5417            $btnGift.show();
5418            $btnBuy.show();
5419            $btnNPay.show();
5420            $btnKakaoPay.show();
5421        }
5422    }
5423
5424    var socialBtnResize = function () {
5425        var $social_box = $('._social_pay');
5426        var $pc_position = $('._social_pc_position');
5427        var $m_position = $('._social_m_position');
5428        var w_width = $(window).width();
5429
5430        if (w_width > 787) {
5431            $pc_position.append($social_box);
5432        } else {
5433            $m_position.append($social_box);
5434        }
5435
5436        $(window).resize(function () {
5437            w_width = $(window).width();
5438            if (w_width > 787) {
5439                $pc_position.append($social_box);
5440                if ($('html').hasClass('mobile-shop-open')) {
5441                    hideMobileOptions();
5442                }
5443                if ($('._style_b_clse').length) {
5444                    $('._style_b_clse').removeAttr('onclick');
5445                    $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hidePCOptions()');
5446                }
5447            } else {
5448                $m_position.append($social_box);
5449                if ($('._style_b_clse').length) {
5450                    $('._style_b_clse').removeAttr('onclick');
5451                    $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hideMobileOptions()');
5452                }
5453            }
5454        });
5455    };
5456
5457	const externalwidgetWrapResize = function(move_target, pc_position, mo_position){
5458		const $target = $(`.${move_target}`);
5459		const $pc_position = $(`.${pc_position}`);
5460		const $m_position = $(`.${mo_position}`);
5461		let w_width = $(window).width();
5462
5463        if($('body').hasClass('device_type_m')) {
5464            w_width = 768;
5465        }
5466
5467		if(w_width > 768){
5468			$pc_position.append($target);
5469		}else{
5470			$m_position.append($target);
5471		}
5472
5473		$(window).resize(function(){
5474			w_width = $(window).width();
5475			if(w_width > 768){
5476                if(!($pc_position.find($target).length)) {
5477                    $pc_position.append($target);
5478                    if($('html').hasClass('mobile-shop-open')){
5479                        hideMobileOptions();
5480                    }
5481                    if($('._style_b_clse').length){
5482                        $('._style_b_clse').removeAttr('onclick');
5483                        $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hidePCOptions()');
5484                    }
5485                }
5486			}else{
5487                if(!($m_position.find($target).length)){
5488                    $m_position.append($target);
5489                    if($('._style_b_clse').length){
5490                        $('._style_b_clse').removeAttr('onclick');
5491                        $('._style_b_clse').attr('onclick', 'SITE_SHOP_DETAIL.hideMobileOptions()');
5492                    }
5493                }
5494			}
5495		});
5496	};
5497
5498	/**
5499	 * qna 상세페이지 모달
5500	 * @param idx
5501	 * @param qna_page
5502	 * @param is_hash		주소창에 직접 입력 또는 최신글 위젯 등 상세페이지 외부에서 바로 넘어왔는지 여부, Y/N
5503	 */
5504	var viewQnaDetail = function(idx,qna_page,is_hash){
5505		$(function(){
5506			$.ajax({
5507				type : 'POST',
5508				data : {idx : idx, qna_page : qna_page},
5509				url : ('/ajax/qna_detail_view.cm'),
5510				dataType : 'json',
5511				async : false,
5512				cache : false,
5513				success : function(res){
5514					if(res.msg === 'SUCCESS'){
5515						$.cocoaDialog.open({
5516							type : 'prod_detail', custom_popup : res.html, width : 800});
5517						if(checkUseHistory()){
5518							// 모달 히스토리 커스텀(IE 10 이상)
5519							var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
5520							var back_url = document.referrer.indexOf('#') === -1 ? document.referrer : document.referrer.substr(0, document.referrer.indexOf('#'));
5521							if(current_url !== back_url && is_hash !== 'Y'){
5522								history.pushState(null, null, current_url);
5523							}
5524							history.replaceState(null, null, current_url + "#prod_detail_qna!/" + res.idx);
5525						}else{
5526							location.hash = "prod_detail_qna!/" + res.idx;
5527						}
5528						$(window).off('hashchange').on('hashchange',function(){
5529							var hash_qna_spilt = location.hash.split('!/')[1];
5530							if(!hash_qna_spilt){
5531								$.cocoaDialog.close();
5532							}else{
5533								if(checkUseHistory()){
5534									var hash_spilt_tab = location.hash.split('!/')[0];
5535									if(hash_spilt_tab === '#prod_detail_review'){
5536										viewReviewDetail(hash_qna_spilt);
5537									}else if(hash_spilt_tab === '#prod_detail_qna'){
5538										viewQnaDetail(hash_qna_spilt);
5539									}
5540								}
5541							}
5542						});
5543						$('.modal_prod_detail').off('hidden.bs.modal').on('hidden.bs.modal', function (e) {
5544              $('html').toggleClass('modal-scroll-control', false);
5545							if(is_hash !== 'Y'){
5546								removeQnawHash();
5547							}
5548						});
5549					}else{
5550						alert(res.msg);
5551					}
5552				}
5553			});
5554		});
5555	};
5556    /**
5557     * qna 상세페이지 모달
5558     * @param idx
5559     * @param qna_page
5560     * @param is_hash		주소창에 직접 입력 또는 최신글 위젯 등 상세페이지 외부에서 바로 넘어왔는지 여부, Y/N
5561     */
5562    var viewQnaDetail = function (idx, qna_page, is_hash) {
5563        $(function () {
5564            $.ajax({
5565                type: 'POST',
5566                data: { idx: idx, qna_page: qna_page },
5567                url: ('/ajax/qna_detail_view.cm'),
5568                dataType: 'json',
5569                async: false,
5570                cache: false,
5571                success: function (res) {
5572                    if (res.msg === 'SUCCESS') {
5573                        $.cocoaDialog.open({
5574                            type: 'prod_detail', custom_popup: res.html, width: 800
5575                        });
5576                        if (checkUseHistory()) {
5577                            // 모달 히스토리 커스텀(IE 10 이상)
5578                            var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
5579                            var back_url = document.referrer.indexOf('#') === -1 ? document.referrer : document.referrer.substr(0, document.referrer.indexOf('#'));
5580                            if (current_url !== back_url && is_hash !== 'Y') {
5581                                history.pushState(null, null, current_url);
5582                            }
5583                            history.replaceState(null, null, current_url + "#prod_detail_qna!/" + res.idx);
5584                        } else {
5585                            location.hash = "prod_detail_qna!/" + res.idx;
5586                        }
5587                        $(window).off('hashchange').on('hashchange', function () {
5588                            var hash_qna_spilt = location.hash.split('!/')
5588[1];
5589                            if (!hash_qna_spilt) {
5590                                $.cocoaDialog.close();
5591                            } else {
5592                                if (checkUseHistory()) {
5593                                    var hash_spilt_tab = location.hash.split('!/')[0];
5594                                    if (hash_spilt_tab === '#prod_detail_review') {
5595                                        viewReviewDetail(hash_qna_spilt);
5596                                    } else if (hash_spilt_tab === '#prod_detail_qna') {
5597                                        viewQnaDetail(hash_qna_spilt);
5598                                    }
5599                                }
5600                            }
5601                        });
5602                        $('.modal_prod_detail').off('hidden.bs.modal').on('hidden.bs.modal', function (e) {
5603                            $('html').toggleClass('modal-scroll-control', false);
5604                            if (is_hash !== 'Y') {
5605                                removeQnawHash();
5606                            }
5607                        });
5608                    } else {
5609                        alert(res.msg);
5610                    }
5611                }
5612            });
5613        });
5614    };
5615
5616    var removeQnawHash = function () {
5617        $(window).off('hashchange');
5618        var hash_qna_spilt = location.hash.split('!/')[1];
5619        if (hash_qna_spilt) {
5620            if (checkUseHistory()) {
5621                history.back();
5622            } else {
5623                location.href = '#prod_detail_qna';
5624            }
5625        }
5626    };
5627
5628    /**
5629     *  리뷰 모달 html 변경 (앞 뒤 버튼으로 호출)
5630     * @param idx
5631     */
5632    var changeReviewDetail = function (idx) {
5633        $.ajax({
5634            type: 'POST',
5635            data: { idx: idx, review_page: 1, only_photo: "Y" },
5636            url: ('/ajax/review_detail_change.cm'),
5637            dataType: 'json',
5638            async: false,
5639            cache: false,
5640            success: function (res) {
5641                if (res.msg === 'SUCCESS') {
5642                    $('._review_modal_body').html(res.html);
5643                    if (IS_MOBILE) {
5644                        // setReviewSwipe(res.prev_idx, res.next_idx);
5645                        $body.find('.modal.review').after(res.btn_html);
5646                        $body.find('._btn_nav_wrap').off('click').on('click', function () {
5647                            $(this).remove();
5648                        });
5649                        $body.find('#review_detail_close').off('click').on('click', function () {
5650                            $('._btn_nav_wrap').remove();
5651                        });
5652                        setReviewBtnAnimation();
5653                        setReviewClass(idx);
5654                    }
5655                    setReviewCarousel();
5656                    $(window).resize(function () {
5657                        setReviewCarousel();
5658                    });
5659                    if (res.review_code != '') SHOP_REVIEW_COMMENT.getReviewCommentHtml(res.review_code, 'modal');
5660                } else {
5661                    alert(res.msg);
5662                }
5663            }
5664        });
5665    };
5666
5667    /**
5668     * 리뷰 상세페이지 모달
5669     * @param idx
5670     * @param review_page
5671     * @param only_photo
5672     * @param is_hash		주소창에 직접 입력 또는 최신글 위젯 등 상세페이지 외부에서 바로 넘어왔는지 여부, Y/N
5673     */
5674    var viewReviewDetail = function (idx, review_page, only_photo, is_hash) {
5675        $(function () {
5676            $.ajax({
5677                type: 'POST',
5678                data: { idx: idx, review_page: review_page, only_photo: only_photo },
5679                url: ('/ajax/review_detail_view.cm'),
5680                dataType: 'json',
5681                async: false,
5682                cache: false,
5683                success: function (res) {
5684                    if (res.msg === 'SUCCESS') {
5685                        $.cocoaDialog.open({ type: 'prod_detail review', custom_popup: res.html, width: 800 });
5686                        if (checkUseHistory()) {
5687                            // 모달 히스토리 커스텀(IE 10 이상)
5688                            var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
5689                            var back_url = document.referrer.indexOf('#') === -1 ? document.referrer : document.referrer.substr(0, document.referrer.indexOf('#'));
5690                            if (current_url !== back_url && is_hash !== 'Y') {
5691                                history.pushState(null, null, current_url);
5692                            }
5693                            history.replaceState(null, null, current_url + "#prod_detail_review!/" + res.idx);
5694                        } else {
5695                            location.hash = "prod_detail_review!/" + res.idx;
5696                        }
5697                        $(window).off('hashchange').on('hashchange', function () {
5698                            var hash_review_spilt = location.hash.split('!/')[1];
5699                            if (!hash_review_spilt) {
5700                                $.cocoaDialog.close();
5701                            } else {
5702                                if (checkUseHistory()) {
5703                                    var hash_spilt_tab = location.hash.split('!/')[0];
5704                                    if (hash_spilt_tab === '#prod_detail_qna') {
5705                                        viewQnaDetail(hash_review_spilt);
5706                                    } else if (hash_spilt_tab === '#prod_detail_review') {
5707                                        viewReviewDetail(hash_review_spilt);
5708                                    }
5709                                }
5710                            }
5711                        });
5712                        $('.modal_prod_detail').off('hidden.bs.modal').on('hidden.bs.modal', function (e) {
5713                            $('html').toggleClass('modal-scroll-control', false);
5714                            if (is_hash !== 'Y') {
5715                                removeReviewHash();
5716                            }
5717                        });
5718                    } else {
5719                        alert(res.msg);
5720                    }
5721                }
5722            });
5723        });
5724    };
5725
5726    var removeReviewHash = function () {
5727        $(window).off('hashchange');
5728        var hash_review_spilt = location.hash.split('!/')[1];
5729        if (hash_review_spilt) {
5730            if (checkUseHistory()) {
5731                history.back();
5732            } else {
5733                location.href = '#prod_detail_review';
5734            }
5735        }
5736    };
5737
5738
5739
5740    /**
5741     *
5742     */
5743    var getOnlyPhotoReview = function (only_photo_switch, is_mobile, is_one_page, rating) {
5744		    let $target_html;
5745        const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
5746        let isMobile = is_mobile === 'Y';
5747        if (isMobile !== isMobile_width) {
5748            isMobile = !isMobile;
5749        }
5750        const device_name = isMobile ? 'mobile' : 'pc';
5751        const one_page_mode = is_one_page === 'Y';
5752        const ajax_target = `/shop/prod_review_${device_name}_html.cm`;
5753
5754        if (one_page_mode) $target_html = $(`._detail_review_wrap${device_name === 'pc'? '' : '_' + device_name} ._review_wrap`);
5755		    else $target_html = $(`.product_review${device_name === 'pc'? '' : '_' + device_name}`);
5756
5757        if ($target_html.length) {
5758            var $icon_picture = $('.icon-picture');
5759            $.ajax({
5760                type: 'POST',
5761                data: {
5762                  'prod_idx': current_prod_idx,
5763                  'only_photo': only_photo_switch,
5764                  'rating': rating,
5765                },
5766                url: (ajax_target),
5767                dataType: 'html',
5768                cache: false,
5769                async: false,
5770                success: function (result) {
5771                    $target_html.html(result);
5772                    if ($icon_picture.hasClass('active')) {
5773                        $icon_picture.removeClass('active');
5774                    } else {
5775                        $icon_picture.addClass('active');
5776                    }
5777                }
5778            });
5779        }
5780    };
5781
5782    const getStarRatingReview = function (only_photo_switch, is_mobile, is_one_page, rating) {
5783      let $target_html;
5784      const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
5785      let isMobile = is_mobile === 'Y';
5786      if (isMobile !== isMobile_width) {
5787        isMobile = !isMobile;
5788      }
5789      const device_name = isMobile ? 'mobile' : 'pc';
5790      const one_page_mode = is_one_page === 'Y';
5791      const ajax_target = `/shop/prod_review_${device_name}_html.cm`;
5792
5793      if (one_page_mode) $target_html = $(`._detail_review_wrap${device_name === 'pc'? '' : '_' + device_name} ._review_wrap`);
5794      else $target_html = $(`.product_review${device_name === 'pc'? '' : '_' + device_name}`);
5795
5796      if ($target_html.length) {
5797        var $icon_picture = $('.icon-picture');
5798        $.ajax({
5799          type: 'POST',
5800          data: {
5801            'prod_idx': current_prod_idx,
5802            'only_photo': only_photo_switch,
5803            'rating' : rating,
5804          },
5805          url: (ajax_target),
5806          dataType: 'html',
5807          cache: false,
5808          async: false,
5809          success: function (result) {
5810            $target_html.html(result);
5811            const $rating = $('.icon-picture');
5812          }
5813        });
5814      }
5815    }
5816
5817    var getFirstPhotoReview = function (page) {
5818        if ($first_photo_review_wrap.length) {
5819            var is_cache = false;
5820            var callback = function (result) {
5821                $first_photo_review_wrap.html(result);
5822                if (!is_cache) IMWEB_SESSIONSTORAGE.set("PROD_REVIEW_PC_PHOTO_" + current_prod_idx + "_" + page, result.replace(/\t+/g, '').trim(), 60);
5823            };
5824
5825            var html = IMWEB_SESSIONSTORAGE.get("PROD_REVIEW_PC_PHOTO_" + current_prod_idx + "_" + page);
5826            if (html) {
5827                is_cache = true;
5828                callback(html);
5829            } else {
5830                $.ajax({
5831                    type: 'POST',
5832                    data: { prod_idx: current_prod_idx, page: page },
5833                    url: '/shop/prod_photo_review_pc_html.cm',
5834                    dataType: 'html',
5835                    cache: false,
5836                    async: false,
5837                    success: callback
5838                });
5839            }
5840        }
5841    };
5842
5843    var viewPhotoReviewMore = function (is_mobile, is_one_page) {
5844        if (is_mobile !== 'Y') {
5845            // 모바일에서는 구매평 내부에서만 호출하므로 탭 전환 필요 없음
5846            changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW, review_page, qna_page, false);
5847        }
5848        getOnlyPhotoReview('Y', is_mobile, is_one_page);
5849        if (is_mobile !== 'Y') {
5850            if (is_one_page === 'Y') {
5851                $('._detail_review_wrap')[0].scrollIntoView();
5852            } else {
5853                $('._prod_detail_detail_lazy_load')[0].scrollIntoView();
5854            }
5855        } else {
5856            if (is_one_page === 'Y') {
5857                $('._detail_review_wrap_mobile')[0].scrollIntoView();
5858            } else {
5859                $('._prod_detail_detail_lazy_load_mobile')[0].scrollIntoView();
5860            }
5861        }
5862    };
5863
5864    var getReviewSummary = function (target) {
5865        if ($review_summary_wrap_mobile.length) {
5866            $.ajax({
5867                type: 'POST',
5868                data: { 'prod_idx': current_prod_idx, 'type': 'mobile' },
5869                url: ('/shop/prod_review_summary_html.cm'),
5870                dataType: 'html',
5871	              cache: false,
5872                async: false,
5873                success: function (result) {
5874                    if(target) {
5875                        target.style.display = 'block';
5876                        target.innerHTML = result;
5877                    } else {
5878                        $review_summary_wrap_mobile.html(result);
5879                        $review_summary_wrap_mobile.show();
5880                    }
5881                    startReviewImageRolling('mobile');
5882                }
5883            });
5884        }
5885
5886        if (!IS_MOBILE) {
5887            if ($review_summary_wrap.length) {
5888                var pc_type = $prod_detail.width() > 991 ? 'pc' : 'tablet';
5889
5890                $.ajax({
5891                    type: 'POST',
5892                    data: { 'prod_idx': current_prod_idx, 'type': pc_type },
5893                    url: ('/shop/prod_review_summary_html.cm'),
5894                    dataType: 'html',
5895	                  cache: false,
5896                    async: false,
5897                    success: function (result) {
5898                        $review_summary_wrap.html(result);
5899                        if ($prod_detail.width() <= 991) {
5900                            $review_summary_wrap.removeClass('review_summary_wrap');
5901                            $review_summary_wrap.addClass('review_summary_wrap_tablet');
5902                        }
5903                        $review_summary_wrap.show();
5904                        startReviewImageRolling(pc_type);
5905                    }
5906                });
5907            }
5908        }
5909    };
5910
5911    var startReviewImageRolling = function (type) {
5912        if (type === 'mobile') {
5913            $review_image_list = $review_summary_wrap_mobile.find('.review_image_list');
5914        } else {
5915            $review_image_list = $review_summary_wrap.find('.review_image_list');
5916        }
5917        $review_image_list_rolling = $review_image_list.find('div.owl-carousel');
5918
5919        var navtext_left = $('<i class="btl bt-angle-left" style="position: absolute; font-size: 20px; left: -25px; top: 45px;"></i>');
5920        var navtext_right = $('<i class="btl bt-angle-right" style="position: absolute; font-size: 20px; right: -25px; top: 45px;"></i>');
5921
5922        var margin = type === 'mobile' ? 5 : 10;
5923        var items = 4;
5924
5925
5926        var img_width = Math.floor(($review_image_list.innerWidth() - (margin * (items - 1))) / items);
5927        if (type === 'tablet') {
5928            if (img_width > 150) {
5929                // img_width가 150보다 크면 개수 자체를 150에 맞춰서 재계산
5930                items = Math.ceil($review_image_list.innerWidth() / (150 + margin / 2));
5931                img_width = Math.floor(($review_image_list.innerWidth() - (margin * (items - 1))) / items);
5932            }
5933        }
5934        $('._review_carousel_image').each(function () {
5935            $(this).css('width', img_width);
5936            $(this).css('height', img_width);
5937        });
5938
5939        $review_image_list_rolling.owlCarousel({
5940            dots: false,
5941            nav: type === 'pc',
5942            navText: [navtext_left, navtext_right],
5943            slideSpeed: 300,
5944            paginationSpeed: 400,
5945            animateOut: 'fadeOut',
5946            autoWidth: true,
5947            items: items,
5948            margin: margin
5949        });
5950    };
5951
5952
5953    /**
5954     * 한페이지 전체 노출에서 리뷰 삭제 코드
5955     * @param prod_code
5956     * @param only_photo
5957     * @returns {boolean}
5958     */
5959
5960    const deleteReviewInTabDisplay = function (prod_code, only_photo) {
5961
5962        if (options.shop_view_tab_display !== "Y") return false;
5963
5964        $.ajax({
5965            type: 'POST',
5966            data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page, 'only_photo': only_photo },
5967            url: ('/shop/prod_review_pc_html.cm'),
5968            dataType: 'html',
5969            cache: false,
5970            async: true,
5971            success: function (result) {
5972                $('.categorize ._review_wrap').html(result);
5973            }
5974        });
5975
5976        $.ajax({
5977            type: 'POST',
5978            data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page, 'only_photo': only_photo },
5979            url: ('/shop/prod_review_mobile_html.cm'),
5980            dataType: 'html',
5981            cache: false,
5982            async: true,
5983            success: function (result) {
5984                $('.categorize-mobile ._review_wrap').html(result);
5985            }
5986        });
5987
5988    }
5989
5990    /**
5991     *  pc 상세페이지 탭 이벤트 처리
5992     * @param type 탭 type
5993     * @param r_p
5994     * @param q_p
5995     * @param paging_on 스크롤 조정 //deprecated
5996     * @param only_photo
5997     * @param shop_view_body_width
5998     * @param rating
5999     */
6000    var changeContentPCTab = function (type, r_p, q_p, paging_on, only_photo, rating = 0, shop_view_body_width) {
6001        var $site_prod_nav_wrap = $('.categorize .site_prod_nav_wrap');
6002
6003        $site_prod_nav_wrap.find(`a`).removeClass('active');
6004
6005        current_content_tab = type;
6006
6007        $site_prod_nav_wrap.find(`a._${type}`).addClass('active');
6008
6009		var review_paging = false;
6010		var qna_paging = false;
6011
6012		if(parseInt(r_p) > 0){
6013			if(review_page != parseInt(r_p) && type == SHOP_CONST.TAB_TYPE.REVIEW){
6014				review_paging = true;
6015			}
6016			review_page = parseInt(r_p);
6017		}else{
6018			r_p = review_page;
6019		}
6020		if(parseInt(q_p) > 0){
6021			if(qna_page != parseInt(q_p) && type == SHOP_CONST.TAB_TYPE.QNA){
6022				qna_paging = true;
6023			}
6024			qna_page = parseInt(q_p);
6025		}else{
6026			q_p = qna_page;
6027		}
6028
6029	    if(options.shop_view_tab_display !== "Y" || review_paging || qna_paging){
6030		    switch (true) {
6031			    case use_cdn_optimized && (type === SHOP_CONST.TAB_TYPE.DETAIL): {
6032				    if (options.shop_view_tab_display !== "Y"){
6033					    if($review_summary_wrap.html()){
6034						    $review_summary_wrap.empty();
6035						    $review_summary_wrap.hide();
6036					    }
6037				    }
6038					$prod_detail_content_pc.empty();
6039					Promise.resolve(loadTemplate('prodDetailPC', $prod_detail_content_pc, async(node) => {
6040						if(use_lazy_load){
6041							await setImageWidthHeightBeforeLoad(node);
6042						}
6043						return node;
6044					})).then(() => {
6045						// 크리마 리뷰 사용 중일 시 위젯 호출
6046						if(document.querySelector(".crema-product-reviews")){
6047							if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
6048								crema.run();
6049							}
6050						}
6051            setTimeout(()=>{
6052              document.querySelectorAll('#prod_detail table._table_responsive').forEach((table)=>{
6053                if (table.classList.contains('table')) return;
6054
6055                table.classList.add('table');
6056                table.outerHTML = `<div class="table-responsive">${table.outerHTML}</div>`;
6057              });
6058            }, 0);
6059					safeScheduleProdDetailBodyExpandedHeightUpdate();
6060					});
6061				    if (typeof shop_view_body_width != 'undefined') {
6062					    $prod_detail_content_pc.css({ 'width': shop_view_body_width + '%' });
6063				    }
6064
6065				    $prod_detail_content_pc.toggleClass('product_detail', true);
6066				    $prod_detail_content_pc.toggleClass('product_review', false);
6067				    $prod_detail_content_pc.toggleClass('product_qna', false);
6068				    $prod_detail_content_pc.toggleClass('product_return', false);
6069				    break;
6070				}
6071				case type === SHOP_CONST.TAB_TYPE.DETAIL: {
6072					if (options.shop_view_tab_display !== "Y"){
6073					    if($review_summary_wrap.html()){
6074						    $review_summary_wrap.empty();
6075						    $review_summary_wrap.hide();
6076					    }
6077				    }
6078				    var pc_detail_html = IMWEB_TEMPLATE.loadSimple("prodDetailPC");
6079				    $prod_detail_content_pc.html(pc_detail_html);
6080				    if (typeof shop_view_body_width != 'undefined') {
6081					    $prod_detail_content_pc.css({ 'width': shop_view_body_width + '%' });
6082				    }
6083				    if (use_lazy_load) {
6084					    runLazyload();
6085				    }
6086					// 크리마 리뷰 사용 중일 시 위젯 호출
6087					if(document.querySelector(".crema-product-reviews")){
6088						if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
6089							crema.run();
6090						}
6091					}
6092					safeScheduleProdDetailBodyExpandedHeightUpdate();
6093
6094				    $prod_detail_content_pc.toggleClass('product_detail', true);
6095				    $prod_detail_content_pc.toggleClass('product_review', false);
6096				    $prod_detail_content_pc.toggleClass('product_qna', false);
6097				    $prod_detail_content_pc.toggleClass('product_return', false);
6098				    break;
6099				}
6100			    case type === SHOP_CONST.TAB_TYPE.REVIEW: {
6101				    if (!$review_summary_wrap.html()) {
6102					    getReviewSummary();
6103				    }
6104				    $.ajax({
6105					    type: 'POST',
6106					    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 },
6107					    url: ('/shop/prod_review_pc_html.cm'),
6108					    dataType: 'html',
6109					    cache: false,
6110					    async: false,
6111					    success: function (result) {
6112
6113							if ($('._external_widget_review_mo_position')) {
6114								$('._external_widget_review_mo_position').remove();
6115							}
6116
6117							if (tab_type === 'Y') {
6118							    $('.categorize ._review_wrap').html(result);
6119						    } else {
6120                                // 구조 변경 이후 해당 부분 아닌데, 이부분에 코드 추가됨
6121							    $prod_detail_content_pc.html(result).css({ 'width': '100%' });
6122						    }
6123					    }
6124				    });
6125
6126				    $prod_detail_content_pc.toggleClass('product_detail', false);
6127				    $prod_detail_content_pc.toggleClass('product_review', true);
6128				    $prod_detail_content_pc.toggleClass('product_qna', false);
6129				    $prod_detail_content_pc.toggleClass('product_return', false);
6130				    break;
6131				}
6132			    case type === SHOP_CONST.TAB_TYPE.QNA: {
6133				    if (options.shop_view_tab_display !== "Y"){
6134					    if($review_summary_wrap.html()){
6135						    $review_summary_wrap.empty();
6136						    $review_summary_wrap.hide();
6137					    }
6138				    }
6139				    $.ajax({
6140					    type: 'POST',
6141					    data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page },
6142					    url: ('/shop/prod_qna_pc_html.cm'),
6143					    dataType: 'html',
6144					    cache: false,
6145					    async: false,
6146					    success: function (result) {
6147						    if (tab_type === 'Y') {
6148							    $('.categorize ._qna_wrap').html(result);
6149						    } else {
6150							    $prod_detail_content_pc.html(result).css({ 'width': '100%' });
6151						    }
6152					    }
6153				    });
6154
6155				    $prod_detail_content_pc.toggleClass('product_detail', false);
6156				    $prod_detail_content_pc.toggleClass('product_review', false);
6157				    $prod_detail_content_pc.toggleClass('product_qna', true);
6158				    $prod_detail_content_pc.toggleClass('product_return', false);
6159				    break;
6160				}
6161			    case type === SHOP_CONST.TAB_TYPE.RETURN: {
6162				    if (options.shop_view_tab_display !== "Y"){
6163					    if($review_summary_wrap.html()){
6164						    $review_summary_wrap.empty();
6165						    $review_summary_wrap.hide();
6166					    }
6167				    }
6168				    if (last_refund_data) {
6169					    const prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnPc", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6170					    if (tab_type === 'Y') {
6171						    var $_target = $('.categorize ._return_wrap');
6172					    }else{
6173						    var $_target = $prod_detail_content_pc;
6174					    }
6175					    $_target.empty();
6176					    $_target.html(prod_detail_return_html).css({ 'width': '100%' });
6177				    }
6178
6179				    $prod_detail_content_pc.toggleClass('product_detail', false);
6180				    $prod_detail_content_pc.toggleClass('product_review', false);
6181				    $prod_detail_content_pc.toggleClass('product_qna', false);
6182				    $prod_detail_content_pc.toggleClass('product_return', true);
6183				    break;
6184				}
6185		    }
6186	    }
6187
6188	    let margin_top = 0;
6189	    let prod_tab_target;
6190	    if(options.shop_view_tab_display !== "Y"){
6191		    prod_tab_target = document.querySelector('#prod_tab_target');
6192		    if(options.shop_tab_fixed !== "Y"){
6193			    margin_top = -1 * getFixedHeaderHeight() + 'px';
6194		    }else if(review_paging){
6195			    margin_top = (-1 * document.querySelector('#fixed_tab ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6196		    }
6197	    }else{
6198		    prod_tab_target = document.querySelector(`#prod_detail_${type}_target`);
6199		    margin_top = (-1 * document.querySelector('#fixed_tab ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6200	    }
6201	    if(review_paging){
6202		    prod_tab_target = document.querySelector('.categorize ._prod_detail_review_board_target');
6203	    }
6204
6205	    if(review_paging || qna_paging || is_init_detail){
6206			if(prod_tab_target){
6207				prod_tab_target.style.top = margin_top;
6208				prod_tab_target.scrollIntoView();
6209			}
6210		    if(is_init_detail){
6211			    setTabHistory(type);
6212			    setTimeout(() => {
6213				    checkSeemore(type);
6214			    }, 200);
6215		    }
6216	    }
6217
6218    };
6219
6220    /**
6221     * 상세정보 탭 변경 처리
6222     * @param type
6223     * @param r_p
6224     * @param q_p
6225     * @param paging_on 스크롤 조정 //deprecated
6226     * @param only_photo
6227     * @param rating
6228     */
6229    var changeContentTab = function (type, r_p, q_p, paging_on, only_photo, rating = 0) {
6230        if (current_content_tab != '') {
6231            $('.active').parent('li').removeClass('activeborder');
6232            $prod_detail_content_tab_mobile.find('a').removeClass('active');
6233        }
6234        current_content_tab = type;
6235        $prod_detail_content_tab_mobile.find('a._' + type).addClass('active');
6236        $('.table-cell > .active').parent('li').addClass('activeborder');
6237
6238        var $seemore_wrap = $body.find('._seemore_wrap');
6239
6240	    var review_paging = false;
6241	    var qna_paging = false;
6242
6243	    if(parseInt(r_p) > 0){
6244		    if(review_page != parseInt(r_p) && type == SHOP_CONST.TAB_TYPE.REVIEW){
6245			    review_paging = true;
6246		    }
6247		    review_page = parseInt(r_p);
6248	    }else{
6249		    r_p = review_page;
6250	    }
6251	    if(parseInt(q_p) > 0){
6252		    if(qna_page != parseInt(q_p) && type == SHOP_CONST.TAB_TYPE.QNA){
6253			    qna_paging = true;
6254		    }
6255		    qna_page = parseInt(q_p);
6256	    }else{
6257		    q_p = qna_page;
6258	    }
6259
6260	    if(options.shop_view_tab_display !== "Y" || review_paging || qna_paging){
6261		    switch (true) {
6262			    case use_cdn_optimized && (type === SHOP_CONST.TAB_TYPE.DETAIL): {
6263				    if (options.shop_view_tab_display !== "Y"){
6264					    if($review_summary_wrap_mobile.html()){
6265						    $review_summary_wrap_mobile.empty();
6266						    $review_summary_wrap_mobile.hide();
6267					    }
6268				    }
6269
6270					$prod_detail_content_mobile.empty();
6271					Promise.resolve(loadTemplate('prodDetailMobile', $prod_detail_content_mobile, async (node) => {
6272						if (use_lazy_load) {
6273							await setImageWidthHeightBeforeLoad(node);
6274						}
6275						if (node.children.length === 0) {
6276							$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>');
6277						}
6278						return node;
6279					})).then(() => {
6280						setMobileVideoRatio();
6281						// 크리마 리뷰 사용 중일 시 위젯 호출
6282						if(document.querySelector(".crema-product-reviews")){
6283							if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
6284								crema.run();
6285							}
6286						}
6287            setTimeout(()=>{
6288              document.querySelectorAll('#prod_detail table._table_responsive').forEach((table)=>{
6289                if (table.classList.contains('table')) return;
6290
6291                table.classList.add('table');
6292                table.outerHTML = `<div class="table-responsive">${table.outerHTML}</div>`;
6293              });
6294            }, 0);
6295					safeScheduleProdDetailBodyExpandedHeightUpdate();
6296					});
6297
6298				    $prod_detail_content_mobile.toggleClass('product_detail_mobile', true);
6299				    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6300				    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6301				    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6302				    break;
6303				}
6304			    case type === SHOP_CONST.TAB_TYPE.DETAIL: {
6305				    if (options.shop_view_tab_display !== "Y"){
6306					    if($review_summary_wrap_mobile.html()){
6307						    $review_summary_wrap_mobile.empty();
6308						    $review_summary_wrap_mobile.hide();
6309					    }
6310				    }
6311
6312				    var mobile_detail_html = IMWEB_TEMPLATE.loadSimple('prodDetailMobile');
6313				    if (mobile_detail_html) {
6314					    if (use_lazy_load) {
6315                            $prod_detail_content_mobile.html(mobile_detail_html);
6316                            runLazyload();
6317					    } else {
6318						    $prod_detail_content_mobile.html(mobile_detail_html);
6319					    }
6320				    } else {
6321					    $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>');
6322				    }
6323				    setMobileVideoRatio();
6324
6325					// 크리마 리뷰 사용 중일 시 위젯 호출
6326				    if(document.querySelector(".crema-product-reviews")){
6327					    if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
6328						    crema.run();
6329					    }
6330				    }
6331					safeScheduleProdDetailBodyExpandedHeightUpdate();
6332
6333				    $prod_detail_content_mobile.toggleClass('product_detail_mobile', true);
6334				    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6335				    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6336				    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6337				    break;
6338				}
6339			    case type === SHOP_CONST.TAB_TYPE.REVIEW: {
6340				    $.ajax({
6341					    type: review_paging ? 'POST' : 'GET',
6342					    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 },
6343					    url: ('/shop/prod_review_mobile_html.cm'),
6344					    dataType: 'html',
6345					    async: false,
6346					    success: function (result) {
6347						    if ($('._external_widget_review_pc_position')) {
6348							    $('._external_widget_review_pc_position').remove();
6349						    }
6350						    if (!$review_summary_wrap_mobile.html()) {
6351							    getReviewSummary();
6352						    }
6353						    if (tab_type === 'Y') {
6354							    $('.categorize-mobile ._review_wrap').html(result);
6355						    }else{
6356							    $prod_detail_content_mobile.html(result);
6357						    }
6358
6359						    $prod_detail_content_mobile.toggleClass('product_detail_mobile', false);
6360						    $prod_detail_content_mobile.toggleClass('product_review_mobile', true);
6361						    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6362						    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6363					    }
6364				    });
6365				    break;
6366				}
6367			    case type === SHOP_CONST.TAB_TYPE.QNA: {
6368				    $.ajax({
6369					    type: 'POST',
6370					    data: { 'prod_idx': current_prod_idx, 'review_page': review_page, 'qna_page': qna_page },
6371					    url: ('/shop/prod_qna_mobile_html.cm'),
6372					    dataType: 'html',
6373					    cache: false,
6374					    async: false,
6375					    success: function (result) {
6376						    if (options.shop_view_tab_display !== "Y"){
6377							    if($review_summary_wrap_mobile.html()){
6378								    $review_summary_wrap_mobile.empty();
6379								    $review_summary_wrap_mobile.hide();
6380							    }
6381						    }
6382						    if (tab_type === 'Y') {
6383							    $('.categorize-mobile ._qna_wrap').html(result);
6384						    } else {
6385							    $prod_detail_content_mobile.html(result).css({ 'width': '100%' });
6386						    }
6387						    $prod_detail_content_mobile.toggleClass('product_detail_mobile', false);
6388						    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6389						    $prod_detail_content_mobile.toggleClass('product_qna_mobile', true);
6390						    $prod_detail_content_mobile.toggleClass('product_return_mobile', false);
6391					    }
6392				    });
6393				    break;
6394				}
6395			    case type === SHOP_CONST.TAB_TYPE.RETURN: {
6396				    if (options.shop_view_tab_display !== "Y"){
6397					    if($review_summary_wrap_mobile.html()){
6398						    $review_summary_wrap_mobile.empty();
6399						    $review_summary_wrap_mobile.hide();
6400					    }
6401				    }
6402				    if (last_refund_data) {
6403					    const prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnMobile", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6404					    if (tab_type === 'Y') {
6405						    var $_target = $('.categorize-mobile ._return_wrap');
6406					    }else{
6407						    var $_target = $prod_detail_content_mobile;
6408					    }
6409					    $_target.empty();
6410					    $_target.html(prod_detail_return_html).css({ 'width': '100%' });
6411				    }
6412				    $prod_detail_content_mobile.toggleClass('product_detail_mobile', false);
6413				    $prod_detail_content_mobile.toggleClass('product_review_mobile', false);
6414				    $prod_detail_content_mobile.toggleClass('product_qna_mobile', false);
6415				    $prod_detail_content_mobile.toggleClass('product_return_mobile', true);
6416				    break;
6417				}
6418		    }
6419	    }
6420
6421		let margin_top = 0;
6422		let prod_tab_target;
6423		if(options.shop_view_tab_display !== "Y"){
6424			prod_tab_target = document.querySelector('#prod_tab_target_mobile');
6425			if(options.shop_tab_fixed !== "Y"){
6426				margin_top = -1 * getFixedHeaderHeight() + 'px';
6427			}else if(review_paging){
6428				margin_top = (-1 * document.querySelector('#fixed_tab_mobile ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6429			}
6430		}else{
6431			prod_tab_target = document.querySelector(`#prod_detail_${type}_target_mobile`);
6432			margin_top = (-1 * document.querySelector('#fixed_tab_mobile ._prod_detail_tab_fixed').getBoundingClientRect().height) + 'px';
6433		}
6434	    if(review_paging){
6435		    prod_tab_target = document.querySelector('.categorize-mobile ._prod_detail_review_board_target');
6436	    }
6437
6438		if(review_paging || qna_paging || is_init_detail){
6439			if(prod_tab_target){
6440				prod_tab_target.style.top = margin_top;
6441				prod_tab_target.scrollIntoView();
6442			}
6443			if(is_init_detail){
6444				setTabHistory(type);
6445				setTimeout(() => {
6446					checkSeemore(type);
6447				}, 200);
6448			}
6449		}
6450    };
6451
6452	var getFixedHeaderHeight = function(){
6453		let margin_top = 0;
6454		let $fixed_header_disable;
6455		if(document.querySelector('#inline_header_fixed')){
6456			if (!IS_MOBILE) {
6457				$fixed_header_disable = $('#inline_header_fixed');
6458			} else {
6459				$fixed_header_disable = $('#inline_header_mobile').find('._fixed_header_section');
6460			}
6461		}else{
6462			if (!IS_MOBILE) {
6463				$fixed_header_disable = $('#inline_header_normal').find('._fixed_header_section');
6464			} else {
6465				$fixed_header_disable = $('#inline_header_mobile').find('._fixed_header_section');
6466			}
6467		}
6468		for ( var i = 0; i < $fixed_header_disable.length; i++ ) {
6469			var target = $fixed_header_disable[i].getBoundingClientRect();
6470			margin_top += target.height;
6471		}
6472		return margin_top;
6473	};
6474
6475    /**
6476     * 상세정보 펼쳐보기 버튼 노출 처리
6477     */
6478    let checkSeemore = function (type) {
6479        const $seemore_wrap = $body.find('._seemore_wrap');
6480        if($seemore_wrap.length === 0) return;
6481
6482        // 탭 노출 방식이 한 페이지 노출 방식이거나 탭 노출 방식이 아니면서 상세정보 탭일 때만 펼쳐보기 노출
6483        if(options.shop_view_tab_display == "Y" || (options.shop_view_tab_display == "N" && type == SHOP_CONST.TAB_TYPE.DETAIL)) {
6484			// template 이 변환되는 시점이 비동기가 되어 당장 사이즈를 알 수가 없다.
6485            // 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)) {
6486                $prod_detail_content_pc.toggleClass('hide_seemore', false);
6487                $prod_detail_content_mobile.toggleClass('hide_seemore', false);
6488				$seemore_wrap.show();
6489				safeScheduleProdDetailBodyExpandedHeightUpdate();
6490				return;
6491            // }
6492        }
6493		$prod_detail_content_pc.toggleClass('hide_seemore', true);
6494		$prod_detail_content_mobile.toggleClass('hide_seemore', true);
6495		$seemore_wrap.hide();
6496		safeScheduleProdDetailBodyExpandedHeightUpdate();
6497	};
6498
6499    var setTabHistory = function (type) {
6500        if (!$('body').hasClass('admin')) {
6501            if (checkUseHistory()) {
6502                var current_url = location.href.indexOf('#') === -1 ? location.href : location.href.substr(0, location.href.indexOf('#'));
6503                switch (type) {
6504                    case SHOP_CONST.TAB_TYPE.DETAIL:
6505                        history.replaceState(null, null, current_url + "#prod_detail_detail");
6506                        break;
6507                    case SHOP_CONST.TAB_TYPE.REVIEW:
6508                        history.replaceState(null, null, current_url + "#prod_detail_review");
6509                        break;
6510                    case SHOP_CONST.TAB_TYPE.QNA:
6511                        history.replaceState(null, null, current_url + "#prod_detail_qna");
6512                        break;
6513                    case SHOP_CONST.TAB_TYPE.RETURN:
6514                        history.replaceState(null, null, current_url + "#prod_detail_return");
6515                        break;
6516                    default:
6517                        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 : "";
6518
6519                        history.replaceState(null, null, current_url + `#prod_detail_${target_tab}`);
6520                        break;
6521                }
6522            } else {
6523                switch (type) {
6524                    case SHOP_CONST.TAB_TYPE.DETAIL:
6525                        location.hash = "prod_detail_detail";
6526                        break;
6527                    case SHOP_CONST.TAB_TYPE.REVIEW:
6528                        location.hash = "prod_detail_review";
6529                        break;
6530                    case SHOP_CONST.TAB_TYPE.QNA:
6531                        location.hash = "prod_detail_qna";
6532                        break;
6533                    case SHOP_CONST.TAB_TYPE.RETURN:
6534                        location.hash = "prod_detail_return";
6535                        break;
6536                    default:
6537                        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 : "";
6538
6539                        location.hash = `prod_detail_${target_tab}`;
6540                        break;
6541                }
6542            }
6543        }
6544    };
6545
6546    var setMobileVideoRatio = function () {
6547        // 동영상 크기 값이 입력되어 있을 시 모바일에서도 입력된 크기 기준으로 비율 계산해서 반영
6548        var $fr_video = $prod_detail_content_mobile.find('.fr-video.fr-dvb');
6549		if ($fr_video.length > 0) {
6550            $fr_video.each(function (k, v) {
6551                var $fr_video_iframe = $(v).find('iframe');
6552                if ($fr_video_iframe.length > 0) {
6553					var origin_width = $fr_video_iframe[0].style.width;
6554                    var origin_height = $fr_video_iframe[0].style.height;
6555					if (origin_width != '' && origin_height != '') {
6556                        // px 단위일 때만 비율 보정
6557                        var origin_width_split = origin_width.split('px');
6558                        var origin_height_split = origin_height.split('px');
6559						if (origin_width_split.length === 2 && origin_height_split.length === 2) {
6560                            var video_ratio = (origin_height_split[0] / origin_width_split[0]) * 100;
6561                            $(v).css('padding-bottom', video_ratio + '%');
6562                        }
6563                    }
6564                }
6565            });
6566        }
6567    };
6568
6569    var prepareAddCartAlarmModal = function () {
6570        if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
6571            $.cocoaDialog.close();
6572        }
6573        if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
6574            imSheet.close('prod_additional_sheet');
6575        }
6576    };
6577
6578	    var openAddCartAlarmHtml = function (html) {
6579	        if (typeof html !== 'string' || html.trim() === '') {
6580	            $add_cart_alarm.show();
6581	            return;
6582	        }
6583
6584        if (is_mobile_width) {
6585            imSheet.close('shop_detail_add_cart_alarm_sheet', function () {
6586                imSheet.open({
6587                    id: 'shop_detail_add_cart_alarm_sheet',
6588                    html: html,
6589                    backdrop: 'rgba(0, 0, 0, 0.15)',
6590                    zIndex: 17001
6591                });
6592            });
6593        } else {
6594            if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
6595                $.cocoaDialog.close();
6596            }
6597            $.cocoaDialog.open({
6598                type: 'site_shop_free_ship_notify',
6599                custom_popup: html,
6600                pc_width: 440
6601            });
6602	        }
6603	    };
6604
6605	    var PRODUCT_RECOMMENDATION_ADD_CART_ALARM_SHOWN_SESSION_KEY_PREFIX = 'SHOP_PRODUCT_RECOMMEND:SHOWN:PROD:';
6606
6607	    var getProductRecommendationAddCartAlarmShownSessionKey = function (prodIdx) {
6608	        return PRODUCT_RECOMMENDATION_ADD_CART_ALARM_SHOWN_SESSION_KEY_PREFIX + prodIdx;
6609	    };
6610
6611	    var hasProductRecommendationAddCartAlarmShown = function (prodIdx) {
6612	        if (_isFreeShipNotifySalesToolPreview) {
6613	            return true;
6614	        }
6615	        if (!prodIdx) {
6616	            return false;
6617	        }
6618
6619	        try {
6620	            if (typeof window.sessionStorage === 'undefined') {
6621	                return false;
6622	            }
6623	            return window.sessionStorage.getItem(getProductRecommendationAddCartAlarmShownSessionKey(prodIdx)) === 'Y';
6624	        } catch (e) {
6625	            return false;
6626	        }
6627	    };
6628
6629	    var markProductRecommendationAddCartAlarmShown = function (prodIdx) {
6630	        if (_isFreeShipNotifySalesToolPreview) {
6631	            return;
6632	        }
6633	        if (!prodIdx) {
6634	            return;
6635	        }
6636
6637	        try {
6638	            if (typeof window.sessionStorage === 'undefined') {
6639	                return;
6640	            }
6641	            window.sessionStorage.setItem(getProductRecommendationAddCartAlarmShownSessionKey(prodIdx), 'Y');
6642	        } catch (e) {}
6643	    };
6644
6645    function getFreeShipNotifyPreviewMarker() {
6646        var markerEl = document.getElementById('shop_detail_add_cart_alarm_with_free_ship_notify');
6647        if (markerEl && document.documentElement.contains(markerEl)) {
6648            return markerEl;
6649        }
6650
6651        if (!_isFreeShipNotifySalesToolPreview) {
6652            return null;
6653        }
6654
6655        var inlineMarkerEl = document.querySelector('#foFreeShipNotifyProdDetailMagnet.foFreeShipNotifyMagnet, .foFreeShipNotifyMagnet[data-type="prod"]');
6656        var inlineThreshold = inlineMarkerEl
6657            ? parseFloat(inlineMarkerEl.getAttribute('data-init-deliv-price-flexable-key') || '0')
6658            : 0;
6659        return inlineMarkerEl && !isNaN(inlineThreshold) && inlineThreshold > 0 ? inlineMarkerEl : null;
6660    }
6661
6662    function isFreeShipNotifyPreviewReady() {
6663        return !!(
6664            window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK
6665            && getFreeShipNotifyPreviewMarker()
6666        );
6667    }
6668
6669    function flushFreeShipNotifyPreviewReadyCallbacks() {
6670        if (!isFreeShipNotifyPreviewReady()) {
6671            return;
6672        }
6673
6674        window.removeEventListener('imweb:freeShipNotify:sdk-ready', flushFreeShipNotifyPreviewReadyCallbacks);
6675        document.removeEventListener('DOMContentLoaded', flushFreeShipNotifyPreviewReadyCallbacks);
6676        window.removeEventListener('load', flushFreeShipNotifyPreviewReadyCallbacks);
6677        if (_freeShipNotifyPreviewReadyObserver) {
6678            _freeShipNotifyPreviewReadyObserver.disconnect();
6679            _freeShipNotifyPreviewReadyObserver = null;
6680        }
6681
6682        var callbacks = _freeShipNotifyPreviewReadyCallbacks.splice(0);
6683        callbacks.forEach(function (callback) {
6684            callback();
6685        });
6686    }
6687
6688    function waitForFreeShipNotifyPreviewReady(callback) {
6689        if (!_isFreeShipNotifySalesToolPreview) {
6690            callback();
6691            return;
6692        }
6693        if (isFreeShipNotifyPreviewReady()) {
6694            callback();
6695            return;
6696        }
6697
6698        _freeShipNotifyPreviewReadyCallbacks.push(callback);
6699
6700        window.addEventListener('imweb:freeShipNotify:sdk-ready', flushFreeShipNotifyPreviewReadyCallbacks);
6701        document.addEventListener('DOMContentLoaded', flushFreeShipNotifyPreviewReadyCallbacks);
6702        window.addEventListener('load', flushFreeShipNotifyPreviewReadyCallbacks);
6703
6704        if (!_freeShipNotifyPreviewReadyObserver && typeof MutationObserver !== 'undefined') {
6705            _freeShipNotifyPreviewReadyObserver = new MutationObserver(flushFreeShipNotifyPreviewReadyCallbacks);
6706            _freeShipNotifyPreviewReadyObserver.observe(document.documentElement, {
6707                attributes: true,
6708                childList: true,
6709                subtree: true
6710            });
6711        }
6712    }
6713
6714	    /**
6715	     * 장바구니 담기 완료 모달 표시.
6716	     * 무료 배송 안내 대상 상품이면 무료 배송 안내를 우선 노출하고, 이미 노출된 경우 기본 모달로 fallback 한다.
6717	     * 무료 배송 안내 대상 상품이 아닐 때만 구매 상품 추천 모달을 노출한다.
6718	     */
6719    var showAddCartAlarm = function () {
6720        var freeShipNotifySdk = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK;
6721        var freeShipMarkerEl = $add_cart_alarm_with_free_ship_notify && $add_cart_alarm_with_free_ship_notify.length > 0
6722            ? $add_cart_alarm_with_free_ship_notify.get(0)
6723            : null;
6724        if (!freeShipMarkerEl || !document.documentElement.contains(freeShipMarkerEl)) {
6725            freeShipMarkerEl = getFreeShipNotifyPreviewMarker();
6726        }
6727        if (freeShipMarkerEl) {
6728            $add_cart_alarm_with_free_ship_notify = $(freeShipMarkerEl);
6729        }
6730        var initDelivPriceFlexableKey = freeShipMarkerEl
6731            ? parseFloat(freeShipMarkerEl.getAttribute('data-init-deliv-price-flexable-key') || '0')
6732            : 0;
6733        var productRecommendationMarkerEl = $add_cart_alarm_with_product_recommendation && $add_cart_alarm_with_product_recommendation.length > 0
6734            ? $add_cart_alarm_with_product_recommendation.get(0)
6735            : null;
6736        if (!productRecommendationMarkerEl || !document.documentElement.contains(productRecommendationMarkerEl)) {
6737            productRecommendationMarkerEl = document.getElementById('shop_detail_add_cart_alarm_with_product_recommendation');
6738        }
6739        if (productRecommendationMarkerEl) {
6740            $add_cart_alarm_with_product_recommendation = $(productRecommendationMarkerEl);
6741        }
6742        var productRecommendationProdIdx = productRecommendationMarkerEl
6743            ? parseInt(productRecommendationMarkerEl.getAttribute('data-prod-idx') || '0', 10)
6744            : 0;
6745        var isFreeShipNotifyTargetProduct = !!freeShipMarkerEl;
6746        if (_isFreeShipNotifySalesToolPreview && (!isFreeShipNotifyTargetProduct || !freeShipNotifySdk)) {
6747            if (!_isWaitingFreeShipNotifySdkForAddCartAlarm) {
6748                _isWaitingFreeShipNotifySdkForAddCartAlarm = true;
6749                waitForFreeShipNotifyPreviewReady(function () {
6750                    _isWaitingFreeShipNotifySdkForAddCartAlarm = false;
6751                    showAddCartAlarm();
6752                });
6753            }
6754            return;
6755        }
6756        var hasFreeShipNotifyAlarmShown = !!(
6757            freeShipNotifySdk
6758            && freeShipNotifySdk.hasAddCartAlarmShown()
6759        );
6760
6761        var canShowFreeShipNotify = !!(
6762            isFreeShipNotifyTargetProduct
6763            && freeShipNotifySdk
6764            && !hasFreeShipNotifyAlarmShown
6765            && !($('._deliv_country_selector').length > 0 && $deliv_country !== 'KR')
6766            && !isNaN(initDelivPriceFlexableKey)
6767            && initDelivPriceFlexableKey > 0
6768        );
6769	        var canShowProductRecommendation = !!(
6770	            !isFreeShipNotifyTargetProduct
6771	            && productRecommendationMarkerEl
6772	            && !isNaN(productRecommendationProdIdx)
6773	            && productRecommendationProdIdx > 0
6774	            && !hasProductRecommendationAddCartAlarmShown(productRecommendationProdIdx)
6775	        );
6776
6777        if (isFreeShipNotifyTargetProduct && !canShowFreeShipNotify) {
6778            $add_cart_alarm.show();
6779            return;
6780        }
6781
6782        if (!canShowFreeShipNotify && !canShowProductRecommendation) {
6783            $add_cart_alarm.show();
6784            return;
6785        }
6786
6787        if (canShowFreeShipNotify) {
6788            window.__imwebFreeShipCartItemCounts = {};
6789            window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartItems:update', {
6790                detail: { counts: {} }
6791            }));
6792
6793            var prodImage = freeShipMarkerEl.getAttribute('data-prod-image') || '';
6794            var prodName = freeShipMarkerEl.getAttribute('data-prod-name') || '';
6795
6796            freeShipNotifySdk.markAddCartAlarmShown();
6797            prepareAddCartAlarmModal();
6798
6799            $.ajax({
6800                type: 'POST',
6801                url: '/shop/free_ship_notify/get_add_cart_alarm_modal.cm',
6802                data: {
6803                    prod_image: prodImage,
6804                    prod_name: prodName,
6805                    init_deliv_price_flexable_key: initDelivPriceFlexableKey
6806                },
6807                dataType: 'html',
6808                cache: false,
6809                success: openAddCartAlarmHtml,
6810                error: function () {
6811                    $add_cart_alarm.show();
6812                }
6813            });
6814            return;
6815        }
6816
6817        prepareAddCartAlarmModal();
6818
6819        $.ajax({
6820            type: 'POST',
6821            url: '/shop/product_recommendation/get_add_cart_alarm_modal.cm',
6822            data: {
6823                prod_idx: productRecommendationProdIdx,
6824                prod_code: productRecommendationMarkerEl.getAttribute('data-prod-code') || '',
6825                prod_image: productRecommendationMarkerEl.getAttribute('data-prod-image') || '',
6826                prod_name: productRecommendationMarkerEl.getAttribute('data-prod-name') || ''
6827            },
6828            dataType: 'html',
6829            cache: false,
6830            success: function (html) {
6831	                if (typeof html !== 'string' || html.trim() === '') {
6832	                    $add_cart_alarm.show();
6833	                    return;
6834	                }
6835	                markProductRecommendationAddCartAlarmShown(productRecommendationProdIdx);
6836	                openAddCartAlarmHtml(html);
6837	            },
6838            error: function () {
6839                $add_cart_alarm.show();
6840            }
6841        });
6842    };
6843
6844    var hideAddCartAlarm = function () {
6845        trackAddToCartPopupAction('click_continue_shopping_add_to_cart_popup', 'continue_shopping');
6846        $add_cart_alarm.hide();
6847        if (($add_cart_alarm_with_free_ship_notify && $add_cart_alarm_with_free_ship_notify.length > 0)
6848	        || ($add_cart_alarm_with_product_recommendation && $add_cart_alarm_with_product_recommendation.length > 0)) {
6849            // 매출 상승 도구 장바구니 알림 사용 사이트 - cocoaDialog/imSheet 양쪽 모두 닫기
6850            if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
6851                $.cocoaDialog.close();
6852            }
6853            if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
6854                imSheet.close('shop_detail_add_cart_alarm_sheet');
6855            }
6856            // imSheet.close 의 비동기 cleanup 이 다른 시트 진입과 race 가 되면 orphan sheet element 가 남아
6857            // 옵션 시트의 dim 위에 중첩되는 현상을 방어. 같은 id 의 element 를 즉시 DOM 에서 제거.
6858            document.querySelectorAll('#shop_detail_add_cart_alarm_sheet').forEach(function (el) {
6859                el.remove();
6860            });
6861
6862            if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
6863                && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.dispatchInlineMagnetItemCounts === 'function') {
6864                window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.dispatchInlineMagnetItemCounts();
6865            }
6866        }
6867    };
6868
6869    var moveCart = function () {
6870        trackAddToCartPopupAction('click_go_cart_add_to_cart_popup', 'go_cart');
6871        cart_type = $('input[name=add_cart_type]:checked').val();
6872        if (cart_type == 'regularly') {
6873            window.location.href = '/shop_cart?type=regularly';
6874        } else {
6875            window.location.href = '/shop_cart';
6876        }
6877    };
6878
6879    var trackAddToCartPopupAction = function (eventName, content, options) {
6880        if (typeof window.BrandScope === 'undefined' || typeof window.BrandScope.track !== 'function') return;
6881        var isFreeShippingUpsell = (options && typeof options.isFreeShippingUpsell === 'boolean')
6882            ? options.isFreeShippingUpsell
6883            : !!window.__imwebAddToCartHasFreeShipUpsell;
6884        window.BrandScope.track(eventName, {
6885            action: 'click',
6886            content: content,
6887            where: 'add_to_cart_popup',
6888            prod_code_list: Array.isArray(window.__imwebAddToCartProdCodeList)
6889                ? window.__imwebAddToCartProdCodeList.slice()
6890                : [],
6891            is_free_shipping_upsell: isFreeShippingUpsell
6892        });
6893    };
6894
6895    var changeInput = function () {
6896        $(document).on('keypress', 'input._requireInputOption', function (e) {
6897            if (e.which == 13) {
6898                e.preventDefault();
6899                var $next = $('[tabIndex=' + (+this.tabIndex + 1) + ']');
6900                if ($next.length != 0) {
6901                    $next.focus().click();
6902                } else {
6903                    e.target.blur();
6904                }
6905            }
6906        });
6907    };
6908
6909    var changeTab = function (type) {
6910        $('._detail_detail_wrap').hide();
6911        $('._detail_review_wrap').hide();
6912        $('._detail_qna_wrap').hide();
6913        $('._detail_' + type + '_wrap').show();
6914    };
6915
6916    var countryCodeChange = function (country) {
6917        $.ajax({
6918            type: 'POST',
6919            data: { 'country': country },
6920            url: ('/shop/country_code_change.cm'),
6921            dataType: 'json',
6922            cache: false,
6923            success: function (result) {
6924                if (result.msg != "SUCCESS") {
6925                    alert(result.msg);
6926                }
6927            }
6928        });
6929    };
6930
6931    var DetailItemMake = function (idx, change_country, deliv_type, deliv_pay_type) {
6932        var is_design_mode = (location.pathname.indexOf('/admin/design') != -1);
6933        $deliv_country = change_country;
6934        $deliv_pay_type = deliv_pay_type;
6935        $.ajax({
6936            type: 'GET',
6937            data: {
6938                'idx': idx,
6939                'change_country': change_country,
6940                'deliv_type': deliv_type,
6941                'deliv_pay_type': deliv_pay_type,
6942                '__': MEMBER_HASH
6943            },
6944            url: ('/shop/prod_detail_make.cm'),
6945            dataType: 'json',
6946            cache: !is_design_mode,
6947            success: function (result) {
6948                if (result.msg == 'SUCCESS') {
6949                    $prod_detail.find('._item_detail_wrap').html(result.html);
6950
6951                    /* 환불 정보 입력 */
6952                    last_refund_data = result.refund;
6953                    setDetailReturnHtml();
6954
6955                    if (window.__npayButtonV2 && typeof makeNaverPayBtn !== 'undefined') {
6956												try{
6957													makeNaverPayBtn('naverPayWrap', change_country == 'KR' || change_country == 'none' || change_country == '');
6958												}catch(e) {
6959
6960												}
6961                    }
6962
6963                    if (typeof kakaoCheckout != 'undefined' && typeof makeTalkPayButton != 'undefined') {
6964												try{
6965                          makeTalkPayButton('create-kakao-checkout-button', (change_country == 'KR' || change_country == 'none' || change_country == ''));
6966												}catch(e) {
6967
6968												}
6969                    }
6970                }
6971            }
6972        });
6973    };
6974    function setDetailReturnHtml() {
6975        if (!last_refund_data) return;
6976        if (options.shop_view_tab_display == 'Y') {
6977            let prod_detail_return_html = null;
6978            prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnPc", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6979            $prod_detail_return.html(prod_detail_return_html);
6980            prod_detail_return_html = last_refund_data.use_shop_return ? IMWEB_TEMPLATE.loadSimple("prodReturnMobile", last_refund_data) : IMWEB_TEMPLATE.loadSimple("prodReturnDisable", null);
6981            $prod_detail_return_mobile.html(prod_detail_return_html);
6982
6983            // 반품/교환 주소지 동적 업데이트 (템플릿에서 PHP 변수로 초기 렌더링된 부분)
6984            if (last_refund_data.return_address) {
6985                $prod_detail_return.find('._return_address_text').html(last_refund_data.return_address);
6986                $prod_detail_return_mobile.find('._return_address_text').html(last_refund_data.return_address);
6987            }
6988        } else {
6989            if (current_content_tab !== SHOP_CONST.TAB_TYPE.RETURN) return;
6990            SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.RETURN);
6991            SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.RETURN);
6992        }
6993    }
6994
6995    /**
6996     * 환불 정보 데이터 업데이트 및 템플릿 렌더링
6997     * @param {Object} refundData - 환불 정보 데이터
6998     */
6999    const updateRefundData = (refundData) => {
7000        if (!refundData) {
7001            return;
7002        }
7003        last_refund_data = refundData;
7004        setDetailReturnHtml();
7005    };
7006
7007    /**
7008     * 모바일 옵션 input클릭시 입력키보드 때문에 css예외처리가 필요하여 클래스 추가.
7009     */
7010    var addEventMobileOptionInput = function () {
7011        $options.find('input.form-control').focus(function () {
7012            if (!$('body').hasClass('mobile_focus_on')) {
7013                $('body').addClass('mobile_focus_on');
7014            }
7015        });
7016        $options.find('input').blur(function () {
7017            if ($('body').hasClass('mobile_focus_on')) {
7018                $('body').removeClass('mobile_focus_on');
7019            }
7020        });
7021    };
7022
7023    /**
7024     * 쇼핑 카테고리 목록에서 상품 상세페이지 레이어팝업 띄우는 함수
7025	 * @param idx
7026	 * @param back_url
7027	 * @param is_prod_detail_page: 상품상세페이지에서 레이어팝업 - 상품 상세 Dialog - 을 띄울 경우 'Y'
7028	 * @param is_mobile
7029	 * @param prod_idx_org
7030     */
7031    var openProdDetailFromShoppingList = function (idx, back_url, is_prod_detail_page, is_mobile, prod_idx_org) {
7032        $.ajax({
7033            type: 'GET',
7034            data: { 'idx': idx, 'is_prod_detail_page': is_prod_detail_page },
7035            url: ('/shop/prod_detail_from_shopping_list.cm'),
7036            dataType: 'html',
7037            cache: true,
7038            async: false,
7039            success: function (result) {
7040                $.cocoaDialog.open({ type: 'prod_detail_from_shopping_list', custom_popup: result, 'close_block': false });
7041                const currentUrl = location.href;
7042                history.replaceState(null, null, currentUrl + '?idx=' + idx);
7043                $('.modal_prod_detail_from_shopping_list').addClass('custom_tooltip');
7044
7045                if (is_prod_detail_page == 'Y' && is_mobile) {
7046                    var $mobile_action_btn_wrap = $('._mobile_action_btn_wrap');
7047                    $mobile_action_btn_wrap.hide();
7048                }
7049
7050				// 모달이 닫힐 때 실행되는 콜백 붙이는 부분
7051                $('.modal_prod_detail_from_shopping_list').on('hidden.bs.modal', function (e) {
7052                    $('.modal_prod_detail_from_shopping_list').find('.modal-content').html(''); // close 해도 html 이 남아있어서 스크립트가 재실행이 안 되므로 html 을 삭제시킴
7053                    $('html').removeClass('mobile-shop-open');
7054                    history.replaceState(null, null, currentUrl);
7055
7056                    if (is_prod_detail_page == 'Y') {
7057                        initDetail({
7058                            prod_idx: prod_idx_org,
7059														prod_code: prod_code_org,
7060                            prod_price: prod_price_org,
7061                            require_option_count: require_option_count_org,
7062							use_image_optimizer_on_product_detail_markup: use_lazy_load_org,
7063                            shop_use_full_load: use_lazy_load_org,
7064                            shop_view_tab_display: tab_type_org,
7065                            is_site_page: is_site_page_org,
7066                            prod_type: prod_type_org,
7067                            is_prod_detail_page: false,
7068                            is_price_view_permission: is_view_price,
7069                            cm_style: options.cm_style,
7070                            section_code: section_code
7071                        });
7072
7073                        if (is_mobile) $mobile_action_btn_wrap.show();
7074                    }
7075                });
7076
7077                if (checkUseHistory()) { // 뒤로가기 누를 경우 모달만 꺼지고 현재 페이지에 남아있도록
7078                    history.pushState(null, null, location.href);
7079                    window.onpopstate = function () {
7080                        if ($('.modal_prod_detail_from_shopping_list').hasClass('in')) {
7081                            $.cocoaDialog.close();
7082                        }
7083                    };
7084                }
7085
7086				// 최근 본 상품, refresh
7087				if (window.RECENT_PRODUCT && typeof window.RECENT_PRODUCT.refresh === 'function') {
7088					window.RECENT_PRODUCT.refresh();
7089				}
7090            }
7091        });
7092    };
7093    var getReviewCountFromShoppingList = function (prod_code_list) {
7094        $.ajax({
7095            type: 'POST',
7096            data: { 'prod_code_list': prod_code_list },
7097            url: ('/shop/get_review_count_from_shopping_list.cm'),
7098            dataType: 'json',
7099            cache: false,
7100            async: true,
7101            success: function (res) {
7102                // $('"#' +  + '"').find("._review_count_text").text(res.review_count);
7103                // $(prod_code).find("._wish_count_text").text(res.wish_count);
7104            }
7105        });
7106    };
7107
7108    /**
7109     * review, qna 권한이 buyer일때 체크 모달
7110     * @param prod_code
7111     * @param type
7112     */
7113    var openBuyerReview = function (prod_code) {
7114        $.ajax({
7115            type: 'POST',
7116            data: { 'prod_code': prod_code },
7117            url: ('/shop/open_buyer_review.cm'),
7118            dataType: 'json',
7119            cache: false,
7120            async: false,
7121            success: function (result) {
7122                if (result.msg === 'SUCCESS') {
7123                    $.cocoaDialog.open({
7124                        type: 'eduModal', custom_popup: result.html, width: 800, hide_event: function () {
7125                        }
7126                    });
7127                } else {
7128                    alert(result.msg);
7129                }
7130            }
7131        });
7132    };
7133
7134    var deleteReviewImage = function (obj) {
7135        var box_obj = obj.parent().parent();
7136        obj.parent().remove();
7137        if (box_obj.find('.file-add').length == 0) box_obj.hide();
7138    };
7139
7140    /**
7141     * 상품상세 페이지에 뿌려지는 review, qna 의 카운트 가져ì˜
7141¤ê¸°.
7142     * @param prod_code
7143     */
7144    var getReviewQnaCount = function (prod_code) {
7145        $.ajax({
7146            type: 'POST',
7147            data: { 'prod_code': prod_code },
7148            url: ('/shop/get_review_qna_count.cm'),
7149            dataType: 'json',
7150            cache: false,
7151            async: true,
7152            success: function (res) {
7153                setReviewQnaCountText($("._review_count_text"), res.review_count);
7154                setReviewQnaCountText($("._qna_count_text"), res.qna_count);
7155            }
7156        });
7157    };
7158
7159    var setReviewQnaCountText = function ($target, count) {
7160        $target.text(count);
7161    };
7162
7163    var getCurrentProdNo = function () {
7164        return current_prod_idx;
7165    };
7166
7167    // current_prod_code 는 initDetail 이 마지막으로 초기화한 값이라 페이지를 떠나도 남는다.
7168    // 리스트 페이지에서 퀵뷰를 열면 모달의 initDetail 이 값을 덮어쓰는데, 모달을 닫을 때의 복원은
7169    // 상품 상세 위에서 띄운 경우에만 동작하므로 리스트 페이지에는 잔류값이 그대로 남는다.
7170    // 모달이 닫힐 때 모달 본문 마크업이 지워지는 것을 이용해, 상품 상세 마크업이 지금 문서에
7171    // 붙어 있을 때만 "고객이 지금 보고 있는 상품"으로 인정한다.
7172    var isProdDetailMounted = function () {
7173        return $('#prod_goods_form').length > 0;
7174    };
7175
7176    // 이름과 달리 getCurrentProdNo 가 돌려주는 것은 idx 다. 상품 코드가 필요한 쪽은 이것을 쓴다.
7177    var getCurrentProdCode = function () {
7178        if (!isProdDetailMounted()) return '';
7179        return current_prod_code;
7180    };
7181
7182    var saveSelectedProd = function () {
7183        if (typeof window.sessionStorage === "undefined") {
7184            return false;
7185        }
7186
7187        // 초기화
7188        var session_storage_keys = Object.keys(sessionStorage);
7189        session_storage_keys.forEach(function (_key) {
7190            if (_key.indexOf('PROD_SELECTED_OPTION_') !== -1) {
7191                sessionStorage.removeItem(_key);
7192            }
7193        });
7194
7195        // 마지막으로 본 것만 남도록 추가
7196        sessionStorage.setItem("LAST_SELECTED_PROD", current_prod_idx);
7197        sessionStorage.setItem(("PROD_SELECTED_OPTION_" + current_prod_idx), JSON.stringify({
7198            'prodIdx': current_prod_idx,
7199            'optDataList': selected_options,
7200            'orderCount': order_count
7201        }));
7202    };
7203
7204    /**
7205     * 로그인 이전에 선택한 옵션이 세션에 저장되어 있을 경우 로그인 후 해당 옵션을 다시 설정해줌
7206     * @param is_set
7207     * @returns {boolean}
7208     */
7209    var setSelectedProd = function (is_set) {
7210        if (typeof window.sessionStorage === "undefined") {
7211            return false;
7212        }
7213
7214        var selected_data = JSON.parse(sessionStorage.getItem("PROD_SELECTED_OPTION_" + current_prod_idx));
7215        var last_prod_idx = sessionStorage.getItem("LAST_SELECTED_PROD");
7216
7217        // 초기화
7218        var session_storage_keys = Object.keys(sessionStorage);
7219        session_storage_keys.forEach(function (_key) {
7220            if (_key.indexOf('PROD_SELECTED_OPTION_') !== -1) {
7221                sessionStorage.removeItem(_key);
7222            }
7223        });
7224
7225        // 마지막으로 본 상품이랑 일치하지 않는경우 리셋하고 끝
7226        if (last_prod_idx != current_prod_idx) return false;
7227        if (typeof selected_data == "undefined") return false;
7228        if (selected_data == null) return false;
7229        if (Object.keys(selected_data).length <= 0) return false;
7230        if (!is_set) return false;
7231
7232        for (var _key in selected_data) {
7233            switch (_key) {
7234                case "optDataList":
7235                    selected_options = selected_data[_key];
7236                    break;
7237                case "orderCount":
7238                    order_count = selected_data[_key];
7239                    break;
7240            }
7241        }
7242        updateSelectedOptions('prod');
7243        if (IS_MOBILE) {
7244            showMobileOptions(options.only_regularly ? 'regularly' : 'buy');
7245        } else {
7246            showPCOptions('order');
7247        }
7248    };
7249
7250    var openCouponDownload = function () {
7251        $.ajax({
7252            type: 'POST',
7253            data: {},
7254            url: ('/shop/open_coupon_download.cm'),
7255            dataType: 'json',
7256            cache: false,
7257            async: false,
7258            success: function (result) {
7259                if (result.msg === 'SUCCESS') {
7260                    $.cocoaDialog.open({ type: 'site_alert', custom_popup: result.html, width: 800 });
7261                } else {
7262                    alert(result.msg);
7263                }
7264            }
7265        });
7266    };
7267
7268    var setReviewCarousel = function () {
7269        var checkWidth = $(window).width();
7270        var owl = $('._review_modal_body .owl-carousel');
7271        if (checkWidth > 768) {
7272            owl.owlCarousel({
7273                'loop': owl.children().length > 1,
7274                'margin': 0,
7275                'nav': false,
7276                'items': 1
7277            });
7278        }
7279    };
7280
7281    var setReviewClass = function (idx) {
7282        if (idx <= 0) {
7283            $body.toggleClass('no-images', true);
7284            $body.find('.modal_prod_detail.review .modal-content').toggleClass('clearfix', true);
7285        } else {
7286            $body.toggleClass('no-images', false);
7287            $body.find('.modal_prod_detail.review .modal-content').toggleClass('clearfix', false);
7288        }
7289    };
7290
7291    var setReviewBtnAnimation = function () {
7292        var $btn_nav = $body.find('._btn_nav');
7293        setTimeout(function () {
7294            $btn_nav.attr('aria-hidden', true);
7295        }, 3000);
7296        $body.find('.modal-left').off('touchstart').on('touchstart', function () {
7297            $btn_nav.attr('aria-hidden', false);
7298            setTimeout(function () {
7299                $btn_nav.attr('aria-hidden', true);
7300            }, 3000);
7301        });
7302    };
7303
7304    var setReviewSwipe = function (prev_idx, next_idx) {
7305        $body.find('._review_modal_body').swipe({
7306            swipe: function (event, direction, distance, duration, fingerCount, fingerData) {
7307                if (direction == 'left') {
7308                    if (next_idx > 0) {
7309                        $('body').find('._btn_nav_wrap').remove();
7310                        changeReviewDetail(next_idx);
7311                    }
7312                } else if (direction == 'right') {
7313                    if (prev_idx > 0) {
7314                        $('body').find('._btn_nav_wrap').remove();
7315                        changeReviewDetail(prev_idx);
7316                    }
7317                }
7318            },
7319            threshold: 0,
7320            allowPageScroll: 'vertical',
7321            excludedElements: 'button, a, textarea, input'
7322        });
7323    };
7324
7325    var dispatchSeeMoreEvent = function (expanded) {
7326        try {
7327            document.dispatchEvent(new CustomEvent('imweb:prod_detail:seemore', {
7328                detail: {
7329                    expanded: expanded
7330                }
7331            }));
7332        } catch (e) {
7333            // CustomEvent 미지원 환경 대비
7334        }
7335    };
7336
7337    var showSeeMoreButton = function () {
7338        $prod_detail_content_pc.toggleClass('active', true);
7339        $prod_detail_content_mobile.toggleClass('active', true);
7340        dispatchSeeMoreEvent(true);
7341    };
7342    var hideSeeMoreButton = function () {
7343        $prod_detail_content_pc.toggleClass('active', false);
7344        $prod_detail_content_mobile.toggleClass('active', false);
7345        dispatchSeeMoreEvent(false);
7346    };
7347
7348    var openRequireSelect = function (option_code) {
7349        var $form_select = $('._form_select_wrap_' + option_code);
7350        var $goods_wrap = $body.find('#goods_wrap');
7351        $form_select.addClass('open');
7352        if ($('body').hasClass('mobile_focus_on')) {
7353            $('body').removeClass('mobile_focus_on');
7354        }
7355        var top = Math.floor($form_select.offset().top); // 셀렉트 박스의 위치
7356        var scroll_top = Math.floor($goods_wrap.scrollTop()); // 컨테이너의 스크롤 top 위치
7357        var position_top = $('#option_' + option_code).position().top;
7358        var height = Math.floor($('.opt-group').height()); // 스크롤 영역의 높이
7359        var default_value = 180;
7360
7361        $form_select.parent('._form_parent').nextAll().toggleClass('disabled', true);
7362        if (top - height >= default_value) {
7363            $goods_wrap.animate({ scrollTop: position_top - 50 }, 0);
7364            if (scroll_top >= default_value) {
7365                $goods_wrap.animate({ scrollTop: position_top + $('.option_box_wrap').outerHeight() }, 0);
7366            }
7367        }
7368    };
7369
7370    var addRegularlyCart = function () {
7371        addCart(function () {
7372            showAddCartAlarm();
7373        });
7374    };
7375
7376	var toggleWishButtons = function($wishButtons, wishCount, showWishCount, isActive) {
7377		const handleRoot = ($root) => {
7378			if (isActive) {
7379				$root.addClass('active');
7380			} else {
7381				$root.removeClass('active');
7382			}
7383		}
7384
7385		const handleIcon = ($root) => {
7386			const $icon = $($root).find('._wish_button_icon');
7387
7388			if (isActive) {
7389				$icon.removeClass('im-ico-like');
7390				$root.addClass('active');
7391				$icon.addClass('im-ico-liked');
7392			} else {
7393				$icon.removeClass('im-ico-liked');
7394				$root.removeClass('active');
7395				$icon.addClass('im-ico-like');
7396			}
7397		}
7398
7399		const handleText = ($root) => {
7400			const $label = $($root).find('._wish_button_count_label');
7401
7402			if (showWishCount) {
7403				$label.text(wishCount);
7404			} else {
7405				$label.text('');
7406			}
7407		}
7408
7409		$wishButtons.each(($idx, root) => {
7410			const $root = $(root);
7411
7412			handleRoot($root);
7413			handleIcon($root);
7414			handleText($root);
7415		});
7416	}
7417
7418
7419	/**
7420	 *
7421	 * @returns {Promise}
7422	 * 사은품 선택 다이얼로그를 띄우고, 사은품 선택이 완료되면 resolve 를 호출한다. 이때 유저가 선택한 사은품을 획득한다.
7423	 */
7424	async function selectFreebieAsync () {
7425		cart_type = document.querySelector('#prod_period input[name=add_cart_type]:checked')?.value || 'normal';
7426		if (!isFreebieMfeMounted() || cart_type === 'regularly' || !checkRequireOption()) {
7427			// 사은품 미사용 사이트이거나, 정기결제 상품인 경우 사은품 선택을 하지 않는다.
7428			return Promise.resolve([]);
7429		}
7430
7431		// selected_options
7432		const items = [
7433			...(selected_options.filter(optionSelected => optionSelected.require).length === 0
7434			// 옵션이 없거나, 필수 옵션이 없으면 본품을 추가한다.
7435				? [{
7436						prodIdx: current_prod_idx,
7437						// prod code는 브라우저에서 채워넣는다.
7438						prodCode: null,
7439						optionDetailCode: "",
7440						prodOptions: [],
7441						qty: order_count,
7442					}]
7443				: []),
7444			...(selected_options.map((option) => {
7445				return {
7446					prodIdx: current_prod_idx,
7447					prodCode: null,
7448					qty: option.count,
7449					optionDetailCode: option.option_mix_type === "SINGLE" ? undefined : option.option_detail_code,
7450					prodOptions: option.options.filter(option => option.value_type === 'SELECT').map((option) => {
7451						return {
7452							optionCode: option.option_code,
7453							valueCode: option.value_code,
7454						}
7455					}),
7456				}
7457			}))
7458		];
7459
7460		// selected_prod_additionals
7461		// selected_prod_additional_options
7462		const item_additional = selected_prod_additionals.flatMap((additional) => {
7463			const additionalOptions = selected_prod_additional_options
7464				.filter(option => option.prod_idx === additional.idx);
7465			const requiresOption = prod_additional_require_option_count[additional.idx] > 0;
7466			const hasRequiredOption = additionalOptions.some(option => option.require);
7467
7468			// 주문에서 제외되는 추가상품은 사은품 조건 금액과 수량에도 포함하지 않는다.
7469			if (requiresOption && !hasRequiredOption) {
7470				return [];
7471			}
7472
7473			if (additionalOptions.length === 0) {
7474				return [{
7475					prodIdx: additional.idx,
7476					prodCode: null,
7477					optionDetailCode: '',
7478					prodOptions: [],
7479					qty: additional.count,
7480				}]
7481			}
7482			return additionalOptions.map((option) => {
7483				return {
7484					prodIdx: additional.idx,
7485					prodCode: null,
7486					optionDetailCode: option.option_detail_code,
7487					prodOptions: option.options.filter(option => option.value_type === 'SELECT').map((option) => {
7488						return {
7489							optionCode: option.option_code,
7490							valueCode: option.value_code,
7491						}
7492					}),
7493					qty: option.count,
7494				}
7495			});
7496		})
7497
7498		return new Promise((resolve) => {
7499			// TODO: 사려고 하는 상품의 정보를 전달해줘야 함.
7500			document.dispatchEvent(new CustomEvent('@im/fo-prod-detail-freebie:openDialogSelectFreebie', { detail : {
7501				callback: resolve,
7502				orderItems: [...items, ...item_additional],
7503			} } ))
7504		})
7505	}
7506	/**
7507	 * 장바구니에 상품이 있는지 체크해서 있으면 장바구니 상품 구매유도 알럿을 띄우고 아니면 바로 주문으로 이동하는 메소드입니다.
7508	 * type: "guest_login" | "npay" | "talkpay" | "normal"
7509	 */
7510	async function confirmOrderWithCartItems(type, backurl, params) {
7511		if (!ensureAddOrderPermission()) {
7512			return;
7513		}
7514
7515        const verified = await SHIPPING_SERVICE.verify({ type });
7516        if (!verified) {
7517            return;
7518        }
7519
7520		if (!options.is_price_view_permission &&
7521			window.IS_GUEST &&
7522			cm_data.use_login_popup === 'Y' &&
7523			options.is_using_nomember_order_login_after
7524		) {
7525			// 구매 권한이 전체가 아닌 상품을 비회원이 구매 하려는 경우 사이트 공통 디자인이 로그인 모달을 사용중이라면 비회원 바로구매 기능이 켜져있는 경우 로그인 모달을 띄워준다.
7526			// 비회원 바로 구매기능이 켜져있지 않다면, site_ui.cls 의 getAnchor 함수를 통해 로그인이 모달이 필요한 상황에 로그인 모달을 표시할 수 있지만,
7527			// 비회원 바로 구매기능이 켜져있는 경우에는 로그인 모달을 보여주는 처리를 해주고 있지 않기 때문.
7528			// site_ui.cls 의 getAnchor 함수에서 해당 로직을 처리하지 않는 이유는 getAnchor 함수는 $params 인자로 받는 도착지 url의 상품 또는 상품 카테고리에 대한 정보를 얻을 수 없기 때문
7529			window.SITE_MEMBER.openLogin(
7530				encodeURIComponent(base64Encode(window.location.href)),
7531				'payment',
7532				_confirmOrderWithCartItems,
7533				'N',
7534				'payment',
7535			);
7536		} else {
7537			_confirmOrderWithCartItems()
7538		}
7539
7540
7541		function proceedToOrder () {
7542			if(typeof params.onMoveOrder === 'function') {
7543				// params.onMoveOrder 는 talkpay 주문시 전달되는 콜백 함수
7544				params.onMoveOrder()
7545			} else if (window.USE_OMS === true) {
7546				if (type === 'guest_login' || type === 'npay' || type === 'talkpay') {
7547					OMS_addOrder(type, backurl, params);
7548				} else {
7549					// TODO: 사은품 가능 여부 체크 후 선택 UI등 표시
7550					selectFreebieAsync()
7551						.then((selected_freebies) => {
7552							params.selected_freebies = selected_freebies || [];
7553							OMS_addOrder(type, backurl, params)
7554						});
7555				}
7556			} else {
7557				addOrder(type, backurl, params);
7558			}
7559		}
7560
7561		async function _confirmOrderWithCartItems () {
7562			// "regularly" | "normal"
7563			cart_type = document.querySelector('#prod_period input[name=add_cart_type]:checked')?.value || 'normal';
7564			params = params || {};
7564 // 기본값 설정
7565
7566			if (!ensureAddOrderPermission()) {
7567				return;
7568			}
7569
7570			// 장바구니가 비어있는지 체크 api 호출
7571			const res = await fetch('/shop/check_cart_empty.cm', {
7572				method: 'POST',
7573				headers: {
7574					'Content-Type': 'application/x-www-form-urlencoded',
7575				},
7576				body: serializeToUrlencoded({
7577					'prodIdx': current_prod_idx,
7578					'options': selected_options,
7579					'orderCount': order_count,
7580					'deliv_type': $deliv_type,
7581					'deliv_pay_type': $deliv_pay_type,
7582					'cart_type': cart_type
7583				})
7584			});
7585			const result = await res.json();
7586
7587			// 통신이 실패했거나, 장바구니가 비어있거나, 정기결제 상품이거나, 게스트일 경우 바로 주문으로 이동
7588			if (!res.ok || result.isGuest || result.msg !== 'SUCCESS' || result.cart_count === 0 || cart_type === 'regularly' ) {
7589				proceedToOrder();
7590				return;
7591			}
7592			// 카트에 상품이 있으며 정기결제 상품이 아닐 경우 장바구니 추가 구매유도 알럿을 띄움
7593			// 장바구니 추가 구매유도 다이얼로그 띄우기
7594			$confirm_order_with_cart_alarm.show();
7595			$confirm_order_with_cart_alarm.off('click').click(function (e) {
7596				if(this === e.target) {
7597					$(this).hide()
7598				}
7599			})
7600			$confirm_order_with_cart_alarm.find('#move_to_cart').off('click').click(function () {
7601				if (typeof params.onMoveCart === 'function') {
7602					params.onMoveCart();
7603				} else {
7604					// confirmOrderWithCartItems에서 이미 shippingService 검증을 완료했으므로 스킵
7605					addCart(function () {
7606						moveCart();
7607					}, { skipShippingValidation: true });
7608				}
7609				$confirm_order_with_cart_alarm.hide();
7610			});
7611			$confirm_order_with_cart_alarm.find('#buy_now').off('click').click(function () {
7612				proceedToOrder();
7613				$confirm_order_with_cart_alarm.hide()
7614			});
7615		}
7616	}
7617
7618	const trackClickPurchaseShopView = function (paymentButtonType) {
7619		if (typeof BrandScope !== "undefined") {
7620			try {
7621				BrandScope.track('click_purchase_shop_view', {
7622					'action': 'click',
7623					'content': 'purchase',
7624					'where': 'shop_view',
7625					'payment_button_type': paymentButtonType,
7626					'prod_code': current_prod_code,
7627					'prod_type': prod_type,
7628					'is_regularly_prod': false
7629				});
7630			} catch {
7631				// ignore
7632			}
7633		}
7634	};
7635
7636	function getThumbnailBadgeStyleByPosition({ badgeColor, badgePosition, imageWidth = ''}) {
7637		const BADGE_POSITION_LEFT_TOP = 'left_top';
7638		const BADGE_POSITION_BOTTOM = 'bottom';
7639
7640		const containerWidth = imageWidth;
7641		if (!containerWidth) return '';
7642
7643		// 최소/최대 프레임 너비 설정
7644		const MIN_FRAME_WIDTH = 120;
7645		const MAX_FRAME_WIDTH = 800;
7646
7647		// 프레임 너비 기준
7648		const frameWidth = Math.max(MIN_FRAME_WIDTH, Math.min(MAX_FRAME_WIDTH, containerWidth));
7649
7650		// 프레임 너비에 따른 비율 계산
7651		// 기준 frameWidth: 308px
7652		const leftTopFontRatio = 0.035;   // frameWidth × 0.035 (308px일 때 약 10.78px)
7653		const leftTopPaddingRatio = 0.015; // frameWidth × 0.015 (308px일 때 약 4.62px)
7654		const bottomFontRatio = 0.04;     // frameWidth × 0.04 (308px일 때 약 12.32px)
7655		const bottomPaddingRatio = 0.02;  // frameWidth × 0.02 (308px일 때 약 6.16px)
7656
7657		// 실제 크기 계산
7658		let leftTopFontSize = Math.round(frameWidth * leftTopFontRatio * 1000) / 1000;
7659		let leftTopPadding = Math.round(frameWidth * leftTopPaddingRatio * 1000) / 1000;
7660		let bottomFontSize = Math.round(frameWidth * bottomFontRatio * 1000) / 1000;
7661		let bottomPadding = Math.round(frameWidth * bottomPaddingRatio * 1000) / 1000;
7662
7663		// 최소/최대 값 제한
7664		leftTopFontSize = Math.max(10, Math.min(13, leftTopFontSize));   // min: 9px / max: 13px
7665		leftTopPadding = Math.max(3, Math.min(6, leftTopPadding));     // min: 3px / max: 6px
7666		bottomFontSize = Math.max(12, Math.min(14, bottomFontSize));   // min: 12px / max: 14px
7667		bottomPadding = Math.max(4, Math.min(8, bottomPadding));       // min: 4px / max: 8px
7668
7669		// badgeLeftTopGapSize 추가 (기존 로직 보존)
7670		const badgeLeftTopGapSize = roundTo3(leftTopFontSize * (8 / 28));
7671
7672		if (badgePosition === BADGE_POSITION_LEFT_TOP) {
7673			const style = [
7674				'position: absolute',
7675				'border-radius: 3px',
7676				`top: ${badgeLeftTopGapSize}px`,
7677				`left: ${badgeLeftTopGapSize}px`,
7678				`padding: ${leftTopPadding}px`,
7679				'color: #FFF',
7680				'vertical-align: middle',
7681				'display: inline-flex',
7682				'align-items: center',
7683				'justify-content: center',
7684				`background-color: ${badgeColor}`,
7685				'z-index: 10',
7686				`font-size: ${leftTopFontSize}px;`
7687			].join('; ');
7688			return style;
7689		}
7690
7691		if (badgePosition === BADGE_POSITION_BOTTOM) {
7692			const style = [
7693				'position: absolute',
7694				'left: 0',
7695				'bottom: 0',
7696				'width: 100%',
7697				`padding: ${bottomPadding}px`,
7698				'color: #FFF',
7699				'vertical-align: middle',
7700				'display: inline-flex',
7701				'align-items: center',
7702				'justify-content: center',
7703				`background-color: ${badgeColor}`,
7704				'z-index: 10',
7705				`font-size: ${bottomFontSize}px;`
7706			].join('; ');
7707			return style;
7708		}
7709
7710		return '';
7711	}
7712
7713	function roundTo3(val) {
7714		return Math.round(val * 1000) / 1000;
7715	}
7716
7717    // BO sales tool 미리보기 트리거 수신: URL에 sales_tool_preview 파라미터가 있을 때(=미리보기 모드)에만
7718    // 부모 BO에서 보낸 메시지에 따라 알림 모달이나 옵션 시트를 즉시 띄운다.
7719    var salesToolPreviewParams = (function () {
7720        try {
7721            return new URLSearchParams(window.location.search);
7722        } catch (e) {
7723            return null;
7724        }
7725    })();
7726    var isInSalesToolPreview = salesToolPreviewParams ? salesToolPreviewParams.has('sales_tool_preview') : false;
7727    var salesToolPreviewDevice = salesToolPreviewParams ? salesToolPreviewParams.get('device') : null;
7728    _isFreeShipNotifySalesToolPreview = isInSalesToolPreview
7729        && salesToolPreviewParams.get('sales_tool_preview') === 'freeShipNotify';
7730    var _isFreeShipNotifyTemplatePreview = _isFreeShipNotifySalesToolPreview
7731        && (parseInt(salesToolPreviewParams.get('idx') || '0', 10) || 0) <= 0;
7732
7733    function isAllowedSalesToolPreviewOrigin(origin) {
7734        if (origin === window.location.origin) return true;
7735        if (/(^|\.)imtest\.me$/.test(window.location.hostname) && origin === 'https://localhost:5173') return true;
7736        return /^https:\/\/[a-z0-9-]+\.crm\.(imweb|imtest)\.me$/.test(origin);
7737    }
7738
7739    function getSalesToolPreviewParentOrigin() {
7740        try {
7741            if (document.referrer) {
7742                return new URL(document.referrer).origin;
7743            }
7744        } catch (e) {}
7745        return '*';
7746    }
7747
7748    function notifySalesToolPreviewReady() {
7749        if (!isInSalesToolPreview || !window.parent || window.parent === window) return;
7750        if (!_initDetailDone) return;
7751        if (_isFreeShipNotifySalesToolPreview && !isFreeShipNotifyPreviewReady()) {
7752            if (!_isWaitingFreeShipNotifyPreviewReadyToNotify) {
7753                _isWaitingFreeShipNotifyPreviewReadyToNotify = true;
7754                waitForFreeShipNotifyPreviewReady(function () {
7755                    _isWaitingFreeShipNotifyPreviewReadyToNotify = false;
7756                    notifySalesToolPreviewReady();
7757                });
7758            }
7759            return;
7760        }
7761        window.parent.postMessage({ type: 'sales-tool-preview:ready' }, getSalesToolPreviewParentOrigin());
7762    }
7763
7764    if (isInSalesToolPreview) {
7765        // body 마커 클래스 — BO sales tool 미리보기 컨텍스트 식별 (CSS 분기/디버깅 용도)
7766        // 실제 스크롤바 숨김 등 스타일은 site2.css 의 body.sales-tool-preview 룰이 담당.
7767        var addSalesToolPreviewBodyClass = function () {
7768            if (document.body) {
7769                document.body.classList.add('sales-tool-preview');
7770            }
7771        };
7772        if (document.body) {
7773            addSalesToolPreviewBodyClass();
7774        } else {
7775            document.addEventListener('DOMContentLoaded', addSalesToolPreviewBodyClass);
7776        }
7777        window.addEventListener('imweb:freeShipNotify:sdk-ready', notifySalesToolPreviewReady);
7778
7779        _handleSalesToolPreviewMessage = function (data) {
7780            if (data.type === 'sales-tool-preview:request-ready') {
7781                notifySalesToolPreviewReady();
7782                return;
7783            }
7784
7785            if (data.type === 'sales-tool-preview:show-add-cart-alarm') {
7786                showAddCartAlarm();
7787                return;
7788            }
7789
7790            // BO 미리보기 — 마스터 토글 off 나 스타일 전환 시 명시적으로 알림 모달을 닫는다.
7791            if (data.type === 'sales-tool-preview:hide-add-cart-alarm') {
7792                hideAddCartAlarm();
7793                return;
7794            }
7795
7796            if (data.type === 'sales-tool-preview:show-options') {
7797                // 기존 알림 모달이 떠 있다면 닫기 (cocoaDialog/imSheet 양쪽 idempotent 처리)
7798                hideAddCartAlarm();
7799
7800                if (salesToolPreviewDevice === 'mobile') {
7801                    showMobileOptions('buy');
7802                } else if (salesToolPreviewDevice === 'desktop' && shop_view_style === 'b') {
7803                    showPCOptions('buy', undefined, true);
7804                }
7805                return;
7806            }
7807
7808            // BO 미리보기 — 마스터 토글 off 나 스타일 전환 시 옵션 패널을 닫는다 (mobile/PC 양쪽 idempotent).
7809            if (data.type === 'sales-tool-preview:hide-options') {
7810                hideMobileOptions();
7811                hidePCOptions();
7812                return;
7813            }
7814
7815            // mfe magnet에 직접 전달되는 동적 설정 — 페이로드를 그대로 CustomEvent로 re-dispatch.
7816            // mfe(fo-shopping/free-ship-notify)의 freeShipNotifySettings 스토어가 이 이벤트를 구독해 즉시 반영한다.
7817            // 파일 상단의 imweb:freeShipNotify:settings:update 리스너가 --free-ship-notify-accent도 함께 따라간다.
7818            if (data.type === 'sales-tool-preview:free-ship-notify-settings'
7819                || data.type === 'sales-tool-preview:settings') {
7820                var settingsPayload = data.payload || {};
7821                window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:settings:update', {
7822                    detail: settingsPayload
7823                }));
7824
7825                // shopDetailStyle이 CART_POPUP이 아닌 값으로 전환되면 떠있는 장바구니 담기 알림 시트/모달을 닫는다.
7826                // (장바구니 담기 알림은 CART_POPUP 모드 전용이므로 다른 모드 전환 시 잔존하면 backdrop이 남는 증상이 생긴다.)
7827                // BO가 명시적 hide-add-cart-alarm을 보내지 않더라도 settings 전이만으로 안전하게 정리되도록 방어한다.
7828                if (typeof settingsPayload.shopDetailStyle === 'string'
7829                    && settingsPayload.shopDetailStyle !== 'cart_popup') {
7830                    hideAddCartAlarm();
7831                }
7832            }
7833        };
7834
7835        window.addEventListener('message', function (event) {
7836            if (!isAllowedSalesToolPreviewOrigin(event.origin)) return;
7837            var data = event.data;
7838            if (!data || typeof data !== 'object') return;
7839
7840            if (!_initDetailDone) {
7841                _pendingSalesToolPreviewMessages.push(data);
7842                return;
7843            }
7844            _handleSalesToolPreviewMessage(data);
7845        });
7846    }
7847
7848    return {
7849        addProdWish: function (prod_code, back_url) {
7850            addProdWish(prod_code, back_url);
7851        },
7852        /** type (prod/cart) */
7853        increaseOptionCount: function (optNo, type) {
7854            increaseOptionCount(optNo, function () {
7855                updateSelectedOptions(type);
7856            });
7857        },
7858        /** type (prod/cart) */
7859        decreaseOptionCount: function (optNo, type) {
7860            decreaseOptionCount(optNo, function () {
7861                updateSelectedOptions(type);
7862            });
7863        },
7864	      /** type (prod/cart) */
7865        increaseProdAdditionalOptionCount: function (optNo) {
7866            increaseProdAdditionalOptionCount(optNo, function () {
7867                updateSelectedOptions('prod');
7868            });
7869        },
7870        /** type (prod/cart) */
7871        decreaseProdAdditionalOptionCount: function (optNo) {
7872            decreaseProdAdditionalOptionCount(optNo, function () {
7873                updateSelectedOptions('prod');
7874            });
7875        },
7876        changeProdAdditionalOptionCount: function (optNo, toBeCount) {
7877            changeProdAdditionalOptionCount(optNo, toBeCount, function () {
7878                updateSelectedOptions('prod');
7879            });
7880        },
7881        // 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){
7882        // 	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);
7883        // },
7884        initDetail: function (option) {
7885            initDetail(option);
7886            this.setShippingTemplateCode(options.shipping_template_code || '');
7887        },
7888        initProdStock: function (stock_use, stock, stock_un_limit) {
7889            initProdStock(stock_use, stock, stock_un_limit);
7890        },
7891        initLocalize: function (code) {
7892            initLocalize(code);
7893        },
7894        selectOption: function (prod_idx, {options, require, count, idx = null, skip_quantity_validation = false}, success, failed) {
7895            selectOption(
7896                prod_idx,
7897                {
7898                    options: options,
7899                    require : require,
7900                    count : count,
7901                    idx : idx,
7902                    skip_quantity_validation : skip_quantity_validation
7903                },
7904                function () {
7905                    success();
7906                },
7907                function (msg) {
7908                    failed(msg);
7909                }
7910            );
7911        },
7912        removeSelectedOption: function (optNo, type) {
7913            removeSelectedOption(optNo, function () {
7914                selected_require_options = [];		// 선택된 필수 옵션 초기화
7915                loadOption(type, current_prod_idx);		// 옵션을 다시 로드함
7916                updateSelectedOptions(type);
7917            });
7918        },
7919        resetSelectedRequireOptions: function () {
7920            selected_require_options = [];
7921        },
7922	      removeSelectedProdAdditional: function (prodIdx, type) {
7923		      removeSelectedProdAdditional(prodIdx, function () {
7924                loadProdAdditional(current_prod_idx);		// 옵션을 다시 로드함
7925	              updateSelectedOptions(type);
7926            });
7927        },
7928	      removeSelectedProdAdditionalOption: function (optNo, type) {
7929		      removeSelectedProdAdditionalOption(optNo, function () {
7930                loadProdAdditional(current_prod_idx);		// 옵션을 다시 로드함
7931	              updateSelectedOptions(type);
7932            });
7933        },
7934        selectRequireOption: function (type, prod_idx, option_code, value_code, value_name, success) {
7935            selectRequireOption(type, prod_idx, option_code, value_code, value_name, function () {
7936                success();
7937            });
7938        },
7939	      selectProdAdditionalRequireOption: (prod_idx, option_code, value_code, value_name, success, is_shop_free_ship_notify = false) => {
7940			      var data = { 'value_type': 'SELECT', 'option_code': option_code, 'value_code': value_code, 'value_name': value_name };
7941			      var no = findSelectedProdAdditionalRequireOption(option_code);
7942						/* @TODO selected_require_options 세분화해야 함(아니면 option_code만으로 구분 가능하게 처리하거나 -> length 비교하므로 분리되어야 하는 게 맞음 */
7943			      if (no == -1) {
7944				      if (value_code == '') return;
7945				      /** 처음 선택된 옵션인 경우 새로 추가*/
7946				      selected_prod_additional_require_options.push(data);
7947			      } else {
7948				      if (value_code == '') {
7949					      /** 옵션 삭제 */
7950					      selected_prod_additional_require_options.splice(no, (selected_prod_additional_require_options.length - no));
7951				      } else {
7952					      selected_prod_additional_require_options[no] = data;
7953					      /** 이미 선택된 옵션인 경우 기존 값 교체 */
7954					      if (no < selected_prod_additional_require_options.length - 1) {
7955						      selected_prod_additional_require_options.splice(no + 1, (selected_prod_additional_require_options.length - (no + 1)));
7956					      }
7957				      }
7958			      }
7959			      if (selected_prod_additional_require_options.length == prod_additional_require_option_count[prod_idx]) {
7960							if(isFreeShipNotifyAdditionalActive()){
7961								// 필수 옵션이 모두 선택되었다고 하더라도 무료 배송 안내 유도 상품 모달에서는 추가 버튼을 눌러야 반영
7962								// 추가하기 버튼 누를 때까지 저장처리하지 않고 선택 처리만 한 상태로 대기
7963								loadProdAdditionalOptions(prod_idx);
7964							}else{
7965								/** 필수 옵션이 모두 선택되었을 경우*/
7966								selectProdAdditionalOption(prod_idx, selected_prod_additional_require_options, true, 1, function () {
7967									if(is_mobile_width){
7968										imSheet.close('prod_additional_sheet');
7969									}
7970									current_select_additional_prod_idx = 0;
7971									selected_prod_additional_require_options = [];
7972									$prod_additional_options.html('');
7973									$('#prod_additional_drop_menu_content').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // PC
7974									$('#btnProdAdditionalSelect').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // 모바일
7975									success();
7976								}, function (msg) {
7977									alert(msg);
7978								});
7979							}
7980			      } else {
7981				      /** 필수옵션 선택이 아직 끝나지 않았을 경우 옵션 재로드 */
7982				      loadProdAdditionalOptions(prod_idx);
7983			      }
7984	      },
7985	      completeAdditionalProductSelection: () => {
7986		      try {
7987			      // 선택된 추가 상품이 있는 경우 selectProdAdditionalOption 호출
7988			      if (current_select_additional_prod_idx && current_select_additional_prod_idx > 0) {
7989				      var requireCount = prod_additional_require_option_count[current_select_additional_prod_idx] || 0;
7990				      if (selected_prod_additional_require_options.length < requireCount) {
7991					      alert(LOCALIZE.설명_필수옵션이모두선택되어있ì
7991§€ì•ŠìŠµë‹ˆë‹¤());
7992					      return;
7993				      }
7994				      selectProdAdditionalOption(current_select_additional_prod_idx, selected_prod_additional_require_options, true, 1, function () {
7995					      if(is_mobile_width){
7996						      imSheet.close('prod_additional_sheet');
7997					      }
7998					      current_select_additional_prod_idx = 0;
7999					      selected_prod_additional_require_options = [];
8000					      $prod_additional_options.html('');
8001					      $('#prod_additional_drop_menu_content').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // PC
8002					      $('#btnProdAdditionalSelect').text(getLocalizeString('타이틀_추가상품', '', '추가 상품'));  // 모바일
8003
8004					      var $optionModal = $('.modal_site_shop_free_ship_notify_option.in');
8005					      if ($optionModal.length > 0) {
8006						      $optionModal.modal('hide');
8007					      } else if (typeof $ !== 'undefined' && typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
8008						      $.cocoaDialog.close();
8009					      }
8010
8011					      updateSelectedOptions('prod');
8012				      }, function (msg) {
8013					      alert(msg);
8014				      });
8015			      } else {
8016				      var $optionModal = $('.modal_site_shop_free_ship_notify_option.in');
8017				      if ($optionModal.length > 0) {
8018					      $optionModal.modal('hide');
8019				      } else if (typeof $ !== 'undefined' && typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
8020					      $.cocoaDialog.close();
8021				      }
8022
8023				      updateSelectedOptions('prod');
8024			      }
8025		      } catch (error) {
8026			      console.error('[SITE_SHOP_DETAIL] 추가 상품 선택 완료 처리 중 오류:', error);
8027		      }
8028	      },
8029	      // 추가 상품 옵션 모달 진입 시 호출. 해당 prod_idx의 현재 selected_prod_additionals 항목과
8030	      // selected_prod_additional_options를 snapshot으로 반환한다. cancel/backdrop close 시
8031	      // restoreProdAdditionalForModal에 전달하면 진입 직전 상태로 복원되어, 같은 상품을 재추가하다가
8032	      // 취소해도 기존에 담았던 옵션이 잘못 사라지지 않는다.
8033	      snapshotProdAdditionalForModal: function (idx) {
8034		      var additional = selected_prod_additionals.find(function (v) { return v.idx === idx; });
8035		      var optionsForIdx = selected_prod_additional_options.filter(function (o) { return o.prod_idx === idx; });
8036		      return {
8037			      exists: !!additional,
8038			      count: additional ? additional.count : 0,
8039			      options: optionsForIdx.map(function (o) { return Object.assign({}, o); })
8040		      };
8041	      },
8042	      // snapshot으로 selected_prod_additionals/options를 복원한다. 진입 직전 항목이 없었으면 제거,
8043	      // 있었으면 count를 snapshot 값으로 되돌리고 options 배열도 snapshot 항목들로 교체.
8044	      restoreProdAdditionalForModal: function (idx, snapshot) {
8045		      if (!snapshot) return;
8046		      if (snapshot.exists) {
8047			      var found = selected_prod_additionals.find(function (v) { return v.idx === idx; });
8048			      if (found) {
8049				      found.count = snapshot.count;
8050			      }
8051		      } else {
8052			      selected_prod_additionals = selected_prod_additionals.filter(function (v) { return v.idx !== idx; });
8053			      delete prod_additional_require_option_count[idx];
8054		      }
8055		      selected_prod_additional_options = selected_prod_additional_options.filter(function (o) { return o.prod_idx !== idx; });
8056		      if (snapshot.options && snapshot.options.length > 0) {
8057			      selected_prod_additional_options = selected_prod_additional_options.concat(snapshot.options);
8058		      }
8059	      },
8060	      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) => {
8061		      try {
8062			      let unselected = [];
8063			      Object.keys(prod_additional_require_option_count).map((key) => {
8064				      if (prod_additional_require_option_count[key] > 0) {
8065					      if(selected_prod_additional_options.filter((option) => option.prod_idx == key)[0]?.options?.length < prod_additional_require_option_count[key]){
8066						      unselected.push(key);
8067					      }
8068				      }
8069			      });
8070
8071			      unselected.forEach((idx) => {
8072				      selected_prod_additionals = selected_prod_additionals.filter((prod_additional) =>
8072 prod_additional.idx != idx);
8073				      selected_prod_additional_options = selected_prod_additional_options.filter((option) => option.prod_idx != idx);
8074				      delete prod_additional_require_option_count[idx];
8075			      });
8076
8077			      // @TODO 기존 추가 상품 옵션 초기화, 추가 상품 완료가 안되었다면 selected_prod_additionals에서도 빠져야 함
8078			      selected_prod_additional_require_options = [];
8079
8080			      // 6837-6842라인: 상품 정보를 selected_prod_additionals에 추가
8081			      const prod_additional_found = selected_prod_additionals.find(v=>v.idx===product_idx);
8082			      if(prod_additional_found) {
8083				      prod_additional_found.count++;
8084			      }else{
8085				      selected_prod_additionals.push({
8086					      idx: product_idx,
8087					      price,
8088					      name,
8089					      maximum_purchase_quantity,
8090					      member_maximum_purchase_quantity,
8091					      maximum_purchase_quantity_type,
8092					      optional_limit,
8093					      optional_limit_type,
8094					      stock_use,
8095					      stock,
8096					      stock_unlimit,
8097					      option_mix_type,
8098					      prod_type,
8099					      membership_discount,
8100					      creator_discount_data: additional_creator_discount_data || null,
8101					      count: 1
8102				      });
8103				      setProdAdditionalPeriodDiscountData(product_idx, period_discount_flag, period_discount_data, period_discout_group_list);
8104			      }
8105
8106			      // current_select_additional_prod_idx 설정
8107			      current_select_additional_prod_idx = product_idx;
8108
8109			      if (typeof TEST_SERVER !== 'undefined' && TEST_SERVER) {
8110				      console.log('[SITE_SHOP_DETAIL] selectAdditionalProductForModal 완료:', current_select_additional_prod_idx);
8111			      }
8112		      } catch (error) {
8113			      console.error('[SITE_SHOP_DETAIL] selectAdditionalProductForModal 실행 중 오류:', error);
8114		      }
8115	      },
8116			    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) => {
8117					// 수량을 조절할 수 없는 상품(디지털/이용권 상품인데 옵션이 없는 경우)이 이미 추가된 상태에서 다시 선택할 시 얼럿 노출
8118			    const prod_additional_found = selected_prod_additionals.find(v=>v.idx===product_idx);
8119			    if(prod_type !== SHOP_CONST.PROD_TYPE.NORMAL && prod_additional_found){
8120						if(selected_prod_additional_options.filter((option) => option.prod_idx == product_idx).length === 0){
8121							// 옵션이 있다면 기존에 추가 시에 옵션도 함께 등록되게 됨
8122							alert(getLocalizeString("설명_이미선택된상품입니다", "", "이미 선택된 상품입니다."));
8123							return false;
8124						}
8125			    }
8126
8127                let unselected = [];
8128                Object.keys(prod_additional_require_option_count).map((key) => {
8129                        if (prod_additional_require_option_count[key] > 0) {
8130                            if(selected_prod_additional_options.filter((option) => option.prod_idx == key)[0]?.options?.length < prod_additional_require_option_count[key]){
8131                                unselected.push(key);
8132                            }
8133                        }
8134                    }
8135                );
8136
8137                if(unselected.length > 0){
8138                    Object.keys(unselected).map((key) => {
8139                        if (prod_additional_require_option_count[key] > 0) {
8140                            selected_prod_additionals = selected_prod_additionals.filter((prod_additional) => prod_additional.idx != key);
8141                            selected_prod_additional_options = selected_prod_additional_options.filter((option) => option.prod_idx != key);
8142                            delete prod_additional_require_option_count[key];
8143                        }
8144                    });
8145                }
8146
8147					// @TODO 기존 추가 상품 옵션 초기화, 추가 상품 완료가 안되었다면 selected_prod_additionals에서도 빠져야 함
8148			    selected_prod_additional_require_options = [];
8149			    if(prod_additional_found) {
8150				    prod_additional_found.count++;
8151			    }else{
8152					    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});
8153						setProdAdditionalPeriodDiscountData(product_idx, period_discount_flag, period_discount_data, period_discout_group_list);
8154			    }
8155			    current_select_additional_prod_idx = product_idx;
8156			    if(is_mobile_width){
8157						// 상품을 선택했을 때 1depth에는 옵션 목록을 띄
8157우고 2depth에는 다음 옵션 시트를 띄움
8158						// @TODO 1depth 모든 옵션 추가(선택 옵션 포함) > 열리지는 않게 처리하면 될 듯
8159				    loadOption('prod', current_prod_idx);		// 옵션을 다시 로드함
8160				    loadProdAdditionalOptions(product_idx);
8161			    }else{
8162						if(is_shop_free_ship_notify && isFreeShipNotifyAdditionalActive()){
8163							$prod_additional_options = $('#prod_free_ship_notify_additional_options');
8164						}else {
8165							// PC: 메인 옵션은 리로드하지 않음 (리로드시 추가상품 영역이 다시 그려져 인라인 옵션이 사라짐)
8166							// PC: 클릭한 추가상품 바로 아래에 옵션을 그리기 위해 임시 컨테이너를 삽입
8167							try {
8168								var $clicked = $(
8169									event && event.currentTarget ? event.currentTarget : null);
8170								var $listItem = ($clicked && $clicked.length)
8171									? $clicked.closest('.list-item')
8172									: $();
8173								if ($listItem.length) {
8174									var $inline = $(
8175										'<div class="_prod_additional_inline_options"></div>');
8176									$listItem.after($inline);
8177									$prod_additional_options = $inline;
8178								} else {
8179									$prod_additional_options = $prod_additional_options_default;
8180								}
8181							} catch (e) {
8182								$prod_additional_options = $prod_additional_options_default;
8183							}
8184						}
8185
8186				    loadProdAdditionalOptions(product_idx);
8187			    }
8188		    },
8189        selectOptionalOption: function (prod_idx, option_code, value_code, value_name, success) {
8190            selectOption(
8191                prod_idx,
8192                {
8193                    options:[
8194                        {
8195                            'value_type': 'SELECT',
8196                            'option_code': option_code,
8197                            'value_code': value_code,
8198                            'value_name': value_name
8199                        }
8200                    ],
8201                    require: false,
8202                    count: 1
8203                },
8204                function () {
8205                    success();
8206                },
8207                function (msg) {
8208                    alert(msg);
8209                }
8210            );
8211        },
8212	      selectProdAdditionalOptionalOption: function (prod_idx, option_code, value_code, value_name, success) {
8213            selectProdAdditionalOption(prod_idx, [{
8214                'value_type': 'SELECT',
8215                'option_code': option_code,
8216                'value_code': value_code,
8217                'value_name': value_name
8218            }], false, 1, function () {
8219                success();
8220		            if(is_mobile_width){
8221			            imSheet.close('prod_additional_sheet');
8222		            }
8223            }, function (msg) {
8224                alert(msg);
8225            });
8226        },
8227        /** type (prod/cart) */
8228        changeOptionCount: function (optNo, optCount, type) {
8229            changeOptionCount(optNo, optCount, function () {
8230                updateSelectedOptions(type);
8231            });
8232        },
8233        "getCurrentProdNo": function () {
8234            return getCurrentProdNo();
8235        },
8236        "getCurrentProdCode": function () {
8237            return getCurrentProdCode();
8238        },
8239        "saveSelectedProd": function () {
8240            saveSelectedProd();
8241        },
8242        "setSelectedProd": function (is_set) {
8243            setSelectedProd(is_set);
8244        },
8245        addOrder: function (type, backurl, params) {
8246            addOrder(type, backurl, params);
8247        },
8248        addGiftOrder: function (type, backurl, params = {}) {
8249            // 선물하기 구매 플래그 활성화
8250			params.is_gift_buy = true;
8251            this.addOrder(type, backurl, params);
8252        },
8253        addCart: function () {
8254            addCart(function () {
8255                showAddCartAlarm();
8256            });
8257        },
8258        setShippingTemplateCode: function (code) {
8259            selected_shipping_template_code = code || '';
8260        },
8261        getShippingTemplateCode: function () {
8262            return selected_shipping_template_code;
8263        },
8264        "digitalFileDownload": function (order_no) {
8265            digitalFileDownload(order_no);
8266        },
8267        "handleOmsDigitalDownload": function (e, href) {
8268            handleOmsDigitalDownload(e, href);
8269        },
8270        updateSelectedOptions: function (type) {
8271            updateSelectedOptions(type);
8272        },
8273        checkOptionLength: function (target, limit) {
8274            checkOptionLength(target, limit);
8275        },
8276        getSelectedOption: function () {
8277            return selected_options;
8278        },
8279        getRequireOptionCount: function () {
8280            return require_option_count;
8281        },
8282        getSelectedRequireOptions: function () {
8283            return selected_require_options;
8284        },
8285        changeOrderCount: function (type, count, success, is_alert) {
8286            return changeOrderCount(type, count, success, is_alert);
8287        },
8288		    changeProdAdditionalOrderCount: function (no, type, count, success, is_alert) {
8289			    return changeProdAdditionalOrderCount(no, type, count, success, is_alert);
8290		    },
8291        selectCartType: function (type) {
8292            return selectCartType(type);
8293        },
8294        checkProdStock: function (cnt) {
8295            return checkProdStock(cnt);
8296        },
8297        increaseOrderCount: function (type, success) {
8298            return increaseOrderCount(type, success);
8299        },
8300        decreaseOrderCount: function (type, success) {
8301            return decreaseOrderCount(type, success);
8302        },
8303		    increaseProdAdditionalOrderCount: function (no, type, success) {
8304			    return increaseProdAdditionalOrderCount(no, type, success);
8305		    },
8306		    decreaseProdAdditionalOrderCount: function (no, type, success) {
8307			    return decreaseProdAdditionalOrderCount(no, type, success);
8308		    },
8309        showMobileOptions: function (type) {
8310            return showMobileOptions(type);
8311        },
8312        changeValueChecked: function (type) {
8313            return changeValueChecked(type);
8314        },
8315        showMobileFirstSelect: function () {
8316            return showMobileFirstSelect();
8317        },
8318        hideMobileOptions: function () {
8319            return hideMobileOptions();
8320        },
8321        showPCOptions: function (type, backurl) {
8322            return showPCOptions(type, backurl, false);
8323        },
8324        hidePCOptions: function () {
8325            return hidePCOptions();
8326        },
8327        socialBtnResize: function () {
8328            return socialBtnResize();
8329        },
8330		externalwidgetWrapResize : function(target, pc_position, mo_position) {
8331			return externalwidgetWrapResize(target, pc_position, mo_position);
8332		},
8333        changeContentTab: function (type, r_p, q_p, paging_on, only_photo, rating = 0) {
8334            return changeContentTab(type, r_p, q_p, paging_on, only_photo, rating);
8335        },
8336        deleteReviewInTabDisplay: function (prod_code, only_photo) {
8337            deleteReviewInTabDisplay(prod_code, only_photo);
8338        },
8339        changeContentPCTab: function (type, r_p, q_p, paging_on, only_photo,  rating = 0 , shop_view_body_width) {
8340            return changeContentPCTab(type, r_p, q_p, paging_on, only_photo, rating, shop_view_body_width);
8341        },
8342        removeReviewHash: function () {
8343            return removeReviewHash();
8344        },
8345        removeQnawHash: function () {
8346            return removeQnawHash();
8347        },
8348        getOnlyPhotoReview: function (only_photo_switch, is_mobile, is_one_page, rating) {
8349            return getOnlyPhotoReview(only_photo_switch, is_mobile, is_one_page, rating);
8350        },
8351        getStarRatingReview: function (only_photo_switch, is_mobile, is_one_page, rating) {
8352            return getStarRatingReview(only_photo_switch, is_mobile, is_one_page, rating);
8353        },
8354        getFirstPhotoReview: function (page) {
8355            return getFirstPhotoReview(page);
8356        },
8357        viewPhotoReviewMore: function (is_mobile, is_one_page) {
8358            return viewPhotoReviewMore(is_mobile, is_one_page);
8359        },
8360        getReviewSummary: function (target) {
8361            return getReviewSummary(target);
8362        },
8363        changeReviewDetail: function (idx) {
8364            return changeReviewDetail(idx);
8365        },
8366        viewReviewDetail: function (idx, r_p, only_photo, is_hash) {
8367            return viewReviewDetail(idx, r_p, only_photo, is_hash);
8368        },
8369        "setMembershipSaleData": function (data, is_main_product_membership_discount) {
8370            setMembershipSaleData(data, is_main_product_membership_discount);
8371        },
8372        "setPeriodDiscountData": function (flag, $data2, group_list) {
8373            setPeriodDiscountData(flag, $data2, group_list);
8374        },
8375        "setCreatorDiscountData": function (data) {
8376            // 크리에이터 전용 혜택가 데이터 주입 (PHP detail.sub 에서 호출).
8377            // 배너 DOM 은 PHP 측 detail/creator-benefit-banner.sub 가 출력한다 — JS 측에서 다시 그리지 않는다 (DOM 중복 회피).
8378            creator_discount_data = data || null;
8379            creator_discount_max_reached = false;
8380            hideCreatorDiscountMaxToast();
8381            refreshFreeShipNotifyInitialTotal();
8382        },
8383        "setFreeShipNotifyProdAdditionalRegularPrice": function (prod_idx, regular_price) {
8384            setFreeShipNotifyProdAdditionalRegularPrice(prod_idx, regular_price);
8385        },
8386		    "setProdAdditionalPeriodDiscountData": function (no, flag, $data2, group_list) {
8387			    setProdAdditionalPeriodDiscountData(no, flag, $data2, group_list);
8388		    },
8389		    "setProdAdditionalRequireOptionCount": function (require_option_count, require_input_option_count) {
8390			    prod_additional_require_option_count = {
8391				    ...prod_additional_require_option_count,
8392				    ...(require_option_count || {})
8393			    };
8394			    prod_additional_require_input_option_count = {
8395				    ...prod_additional_require_input_option_count,
8396				    ...(require_input_option_count || {})
8397			    };
8398		    },
8399        viewQnaDetail: function (idx, q_p, is_hash) {
8400            return viewQnaDetail(idx, q_p, is_hash);
8401        },
8402        changeProdImageRolling: function (no) {
8403            return changeProdImageRolling(no);
8404        },
8405        startProdImageRolling: function (autoWidth) {
8406            return startProdImageRolling(autoWidth);
8407        },
8408        setOrderCount: function (count) {
8409            order_count = count;
8410        },
8411        getOrderCount: function () {
8412            return order_count;
8413        },
8414        addNPayWish: function () {
8415            return addNPayWish();
8416        },
8417        hideAddCartAlarm: function () {
8418            hideAddCartAlarm();
8419        },
8420        moveCart: function () {
8421            moveCart();
8422        },
8423        trackAddToCartPopupAction: function (eventName, content, options) {
8424            trackAddToCartPopupAction(eventName, content, options);
8425        },
8426        loadProductDetail: function (code, prod_idx, menu_code, current_url) {
8427            loadProductDetail(code, prod_idx, menu_code, current_url);
8428        },
8429		    loadBookingDetail: function (code, idx, day, endDay, current_url) {
8430			    loadBookingDetail(code, idx, day, endDay, current_url);
8431		    },
8432		    loadCode: function (code) {
8433			    loadCode(code);
8434		    },
8435        loadOption: function (type, prod_idx) {
8436            return loadOption(type, prod_idx);
8437        },
8438        loadDelivSetting: function (prod_idx, change_country, deliv_type, deliv_pay_type) {
8439            return loadDelivSetting(prod_idx, change_country, deliv_type, deliv_pay_type);
8440        },
8441        updateRefundData: (refundData) => {
8442            return updateRefundData(refundData);
8443        },
8444        changeRequireInputOption: function (type, prod_idx, option_code, msg, success) {
8445            changeRequireInputOption(type, prod_idx, option_code, msg, function () {
8446                success();
8447            });
8448        },
8449	      changeProdAdditionalRequireInputOption: function (type, prod_idx, option_code, msg, success) {
8450		        changeProdAdditionalRequireInputOption(type, prod_idx, option_code, msg, function () {
8451                success();
8452            });
8453        },
8454        changeInput: function () {
8455            changeInput();
8456        },
8457        changeTab: function (type) {
8458            changeTab(type);
8459        },
8460        countryCodeChange: function (country) {
8461            countryCodeChange(country);
8462        },
8463        DetailItemMake: function (idx, change_country, deliv_type, deliv_pay_type) {
8464            return DetailItemMake(idx, change_country, deliv_type, deliv_pay_type);
8465        },
8466        loadItemInfo(params) {
8467            return loadItemInfo(params);
8468        },
8469        addDelivType: function (type) {
8470	        $deliv_type = type;
8471        },
8472        addDelivPayType: function (type) {
8473            $deliv_pay_type = type;
8474        },
8475        addDelivCountry: function (country) {
8476            $deliv_country = country;
8477        },
8478        getDelivType: function () {
8479            return $deliv_type || '';
8480        },
8481        getDelivPayType: function () {
8482            return $deliv_pay_type || '';
8483        },
8484        getDelivCountry: function () {
8485            return $deliv_country || '';
8486        },
8487        visitFormMake: function () {
8488            $deliv_visit_wrap.show();
8489        },
8490        openProdDetailFromShoppingList: function (idx, back_url, is_prod_detail_page, is_mobile, prod_idx_org) {
8491            return openProdDetailFromShoppingList(idx, back_url, is_prod_detail_page, is_mobile, prod_idx_org);
8492        },
8493        'getReviewQnaCount': function (prod_code) {
8494            return getReviewQnaCount(prod_code);
8495        },
8496        'setReviewQnaCountText': function ($target, count) {
8497            return setReviewQnaCountText($target, count);
8498        },
8499        getReviewCountFromShoppingList: function (prod_code_list) {
8500            return getReviewCountFromShoppingList(prod_code_list);
8501        },
8502        openBuyerReview: function (prod_code) {
8503            return openBuyerReview(prod_code);
8504        },
8505        deleteReviewImage: function (obj) {
8506            return deleteReviewImage(obj);
8507        },
8508        openCouponDownload: function () {
8509            return openCouponDownload();
8510        },
8511        setReviewCarousel: function () {
8512            return setReviewCarousel();
8513        },
8514        setReviewClass: function (idx) {
8515            return setReviewClass(idx);
8516        },
8517        setReviewBtnAnimation: function () {
8518            return setReviewBtnAnimation();
8519        },
8520        setReviewSwipe: function (prev_idx, next_idx) {
8521            return setReviewSwipe(prev_idx, next_idx);
8522        },
8523        showSeeMoreButton: function () {
8524            return showSeeMoreButton();
8525        },
8526        hideSeeMoreButton: function () {
8527            return hideSeeMoreButton();
8528        },
8529        openRequireSelect: function (option_code) {
8530            return openRequireSelect(option_code);
8531        },
8532        addRegularlyCart: function () {
8533            return addRegularlyCart();
8534        },
8535	    toggleWishButtons: function($wishButtons, wishCount, isActive) {
8536			return toggleWishButtons($wishButtons, wishCount, isActive);
8537	    },
8538        // C3 메서드 추가
8539		OMS_addOrder : function(type, backurl, params){
8540			// 외부채널은 사은품 미지원
8541			if (type === 'npay' || type === 'talkpay') {
8542				OMS_addOrder(type, backurl, params);
8543				return;
8544			}
8545			selectFreebieAsync()
8546				.then((selected_freebies) => {
8547					params.selected_freebies = selected_freebies || [];
8548					OMS_addOrder(type, backurl, params);
8549				});
8550		},
8551		OMS_showPCOptions : function(type, backurl){
8552			return showPCOptions(type, backurl, true);
8553		},
8554		OMS_addGiftOrder: async function (type, backurl, params = {}) {
8555			params.is_gift_buy = true; // 선물하기 구매 플래그 활성화
8556
8557			// 빠른배송이 활성화된 경우 선물하기 불가 안내 모달
8558			const isShippingServiceActive = typeof SHIPPING_SERVICE !== 'undefined' && SHIPPING_SERVICE.isActive();
8559			if (isShippingServiceActive) {
8560				const confirmedGift = await SHIPPING_SERVICE.confirmGift();
8561				if (!confirmedGift) {
8562					return;
8563				}
8564				// 일반배송으로 구매 선택 시 일반배송 템플릿 코드 설정
8565				SITE_SHOP_DETAIL.setShippingTemplateCode('');
8566			}
8567
8568			if (type === 'guest_login') {
8569				OMS_addOrder(type, backurl, params)
8570				return;
8571			}
8572
8573			// 외부채널은 사은품 미지원
8574			if (type === 'npay' || type === 'talkpay') {
8575				OMS_addOrder(type, backurl, params)
8576				return;
8577			}
8578
8579			selectFreebieAsync()
8580				.then((selected_freebies) => {
8581					params.selected_freebies = selected_freebies || [];
8582					OMS_addOrder(type, backurl, params)
8583				});
8584		},
8585		confirmOrderWithCartItems: function (type, backurl, params) {
8586			// type: "guest_login" | "npay" | "talkpay" | "normal"
8587				confirmOrderWithCartItems(type, backurl, params);
8588		},
8589		trackClickPurchaseShopView: function (paymentButtonType) {
8590			trackClickPurchaseShopView(paymentButtonType);
8591		},
8592		hideConfirmOrderWithCartItems: function () {
8593			if($confirm_order_with_cart_alarm) $confirm_order_with_cart_alarm.hide();
8594		},
8595		getThumbnailBadgeStyleByPosition: function ({ badgeColor, badgePosition, imageWidth = ''}){
8596			return getThumbnailBadgeStyleByPosition({ badgeColor, badgePosition, imageWidth});
8597		},
8598		selectFreebieAsync: selectFreebieAsync.bind(this),
8599		// 장바구니 수량 제한 관련 함수 추가
8600		setMaxPurchaseQuantity: function(_max_prod, _max_member, _type, _opt_limit, _opt_limit_type, _prod_name) {
8601			max_prod_quantity = _max_prod || 0;
8602			max_member_quantity = _max_member || 0;
8603			maximum_purchase_quantity_type = _type || 'order';
8604			optional_limit = _opt_limit || 0;
8605			optional_limit_type = _opt_limit_type || 'relative';
8606			if (_prod_name) prod_name = _prod_name;
8607		},
8608		validateMaxPurchaseQuantity: function() {
8609			if (max_prod_quantity <= 0) return true;
8610
8611			var is_valid = true;
8612			var error_msg = '';
8613
8614			$.each(selected_options, function(idx, opt) {
8615				if (!is_valid) return false;
8616
8617				var count = opt.count;
8618				var is_require_option = opt.require;  // DOM 대신 opt.require 사용
8619				var should_check = false;
8620
8621				switch (maximum_purchase_quantity_type) {
8622					case 'prod':
8623						// 옵션별 수량: 본품, 필수옵션, 선택옵션 모두 각각 검증
8624						should_check = true;
8625						break;
8626					case 'required_option':
8627						// 옵션별 수량(필수옵션): 본품, 필수옵션만 검증 (선택옵션 제외)
8628						should_check = is_require_option;
8629						break;
8630					case 'optional_option':
8631						// 옵션별 수량(선택옵션): 선택옵션만 검증 (본품, 필수옵션 제외)
8632						should_check = !is_require_option;
8633						break;
8634					case 'order':
8635					default:
8636						// order 타입은 아래에서 전체 수량으로 체크
8637						break;
8638				}
8639
8640				if (should_check && count > max_prod_quantity) {
8641					is_valid = false;
8642					error_msg = LOCALIZE.설명_최대구매수량(max_prod_quantity);
8643				}
8644			});
8645
8646			// order 타입인 경우 전체 수량 합계 체크
8647			if (is_valid && maximum_purchase_quantity_type === 'order') {
8648				var total_count = 0;
8649				$.each(selected_options, function(idx, opt) {
8650					total_count += opt.count;
8651				});
8652				if (total_count > max_prod_quantity) {
8653					is_valid = false;
8654					error_msg = LOCALIZE.설명_최대구매수량(max_prod_quantity);
8655				}
8656
8657				// 회원별 최대 구매수량 검증 추가
8658				if (is_valid && max_member_quantity > 0) {
8659					if (total_count > max_member_quantity) {
8660						is_valid = false;
8661						error_msg = LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity);
8662					}
8663				}
8664			}
8665
8666			return is_valid ? true : error_msg;
8667		},
8668		// 본품 수량 검증 함수 (optional_option 타입 제외)
8669		checkMaxPurchaseQuantityForOrderCount: function(count) {
8670			if (max_prod_quantity > 0 && maximum_purchase_quantity_type !== 'optional_option') {
8671				var _total_count = count; // 본품 수량
8672
8673				// order 타입이면 본상품의 필수/선택 옵션도 합산
8674				if (maximum_purchase_quantity_type === 'order') {
8675					// 필수/선택 옵션 수량 포함
8676					$.each(selected_options, function (idx, obj) {
8677						_total_count += obj.count;
8678					});
8679				}
8680
8681				if (_total_count > max_prod_quantity) {
8682					return LOCALIZE.설명_최대구매수량(max_prod_quantity);
8683				}
8684			}
8685
8686			// 회원당 최대 구매수량 검증 (1회 구매 제한이 옵션별 타입이면 1인 제한 미적용 - 스펙)
8687			if (max_member_quantity > 0 && maximum_purchase_quantity_type === 'order') {
8688				var _member_total = count;
8689				// 필수/선택 옵션 수량 포함
8690				$.each(selected_options, function (idx, obj) {
8691					_member_total += obj.count;
8692				});
8693				if (_member_total > max_member_quantity) {
8694					return LOCALIZE.설명_회원당최대구매수량상품명포함(prod_name, max_member_quantity);
8695				}
8696			}
8697
8698			return true;
8699		},
8700    };
8701}();
8702
8703window.addEventListener('imweb:productRecommendationModal:requestClose', function () {
8704    if (window.SITE_SHOP_DETAIL && typeof window.SITE_SHOP_DETAIL.hideAddCartAlarm === 'function') {
8705        window.SITE_SHOP_DETAIL.hideAddCartAlarm();
8706    }
8707});
8708
8709var SITE_SHOP_CART = function () {
8710    var selectedCartItem = [];
8711    var $cartItemCheckboxList;
8712    var $cartAllCheckBox;
8713    var $shop_cart_list;
8714    var $shop_cart_wish_list;
8715    var $shop_cart_wish_list_empty;
8716    var $changeCartItemLayer;
8717    var $total_price;
8718    /** 크리에이터 전용 혜택가 데이터 (cart 컨텍스트) — PHP 에서 setCreatorDiscountData 로 주입. null 이면 비활성 */
8719    var creator_cart_data = null;
8720
8721    var $cart_list_wrap;
8722    var $global_select;
8723    var $cart_result_container;
8724
8725    var get_selected_price_request_seq = 0;
8726    var is_cart_changed = false;
8727    var currentCartCode = '';
8728    var current_backurl = '';
8729    var currentChangeCartItemCode = '';
8730
8731    var currentCartItemMaxQuantity = 0;
8732    var currentCartItemMaxQuantityType = 'order';
8733		var $deliv_country = '';
8734    var add_order_progress_check = false;
8735    var cart_type = '';
8736    // 무료배송 안내 모달이 떠있는 동안 원래 addOrderWithCart 호출 인자를 보류해둔다.
8737    // 사용자가 모달의 "주문하기" 버튼을 누르면 proceedOrderAfterFreeShipAlarm에서 이 인자로 함수를 재호출.
8738    var _pendingOrderWithCart = null;
8739
8740    // 수량별 할인 계산 헬퍼 (장바구니)
8741    // data-qd-exclude="Y" 카운터(선택옵션 등)는 조건 수량/할인 base 모두에서 제외 (IMIO-7150)
8742    // 품목 금액 초과 방지 클램핑 적용 (결제페이지/OMS 동일, IMIO-7184)
8743    const _calcCartQuantityDiscount = ($row, priceRaw) => {
8744        if (!$row || !$row.length) return 0;
8745        const tiersRaw = $row.attr('data-qd-tiers');
8746        if (!tiersRaw) return 0;
8747        try {
8748            const tiers = JSON.parse(tiersRaw);
8749            if (!Array.isArray(tiers) || tiers.length === 0) return 0;
8750
8751            // 카운터 순회: 제외 대상이면 선택옵션 금액 누적, 아니면 조건 수량 누적
8752            let conditionQty = 0;
8753            let excludedOptionPrice = 0;
8754            $row.find('._counter_value').each(function () {
8755                const $counter = $(this).closest('._cart_counter');
8756                const qty = parseInt(this.tagName === 'INPUT' ? this.value : this.textContent, 10) || 0;
8757                if ($counter.attr('data-qd-exclude') === 'Y') {
8758                    const unitPrice = parseFloat(
8759                        $counter.closest('._cart_option_card').find('._cart_option_price').attr('data-unit-price')
8760                    ) || 0;
8761                    excludedOptionPrice += unitPrice * qty;
8762                    return;
8763                }
8764                conditionQty += qty;
8765            });
8766            if (conditionQty <= 0) return 0;
8767
8768            // 충족된 tier 중 min_quantity 최댓값 선택
8769            const matchedTier = tiers
8770                .filter(t => conditionQty >= t.min_quantity)
8771                .sort((a, b) => b.min_quantity - a.min_quantity)[0];
8772            if (!matchedTier) return 0;
8773
8774            // base = 품목 금액 − 선택옵션 금액 (0 이상으로 클램핑)
8775            const base = Math.max((parseInt(priceRaw, 10) || 0) - excludedOptionPrice, 0);
8776            if (matchedTier.method === 'percentage') {
8777                return Math.min(Math.ceil(base * matchedTier.value / 100), base);
8778            }
8779            return Math.min(parseInt(matchedTier.value, 10) || 0, base);
8780        } catch { return 0; }
8781    };
8782
8783    /**
8784     * 카트 row 의 가격 표시를 일관되게 갱신.
8785     * basePrice는 period 차감/QD 미반영 상태(= data-discounted-price와 동일).
8786     * QD는 data-qd-tiers 기준으로 계산한다.
8787     * PC/mobile 양쪽의 가격 텍스트와 data 속성, origin 표시까지 한 번에 처리.
8788     *
8789     * @param {jQuery} $row - 카트 tr.content 요소
8790     * @param {Object} opts
8791     * @param {number} opts.basePrice - period 차감 후 row 단가 (QD 미반영)
8792     * @param {number} [opts.originPrice] - 원가 (strikethrough). 미지정 시 origin 갱신 안 함
8793     * @param {number} [opts.fpDiscount=0] - 첫 구매 혜택 차감액
8794     */
8795    const renderCartRowPrice = ($row, opts) => {
8796        if (!$row || !$row.length) return;
8797        const basePrice = parseInt(opts.basePrice, 10) || 0;
8798        const fpDiscount = parseInt(opts.fpDiscount, 10) || 0;
8799        const qd = _calcCartQuantityDiscount($row, basePrice);
8800        const finalPrice = Math.max(basePrice - qd - fpDiscount, 0);
8801        const finalText = LOCALIZE.getCurrencyFormat(finalPrice);
8802
8803        // PC 가격
8804        const $pcDefault = $row.find('._cart_price_default');
8805        if ($pcDefault.length) {
8806            $pcDefault.data('discounted-price', basePrice);
8807            $pcDefault.find('._cart_price_default_value').text(finalText);
8808        }
8809
8810        // Mobile 가격
8811        const $mobilePrice = $row.find('._cart_mobile_order_price');
8812        if ($mobilePrice.length) {
8813            $mobilePrice.data('discounted-price', basePrice);
8814            $mobilePrice.text(finalText);
8815        }
8816
8817        // Origin 표시 (선택)
8818        if (opts.originPrice !== undefined) {
8819            const originPrice = parseInt(opts.originPrice, 10) || 0;
8820            const originText = LOCALIZE.getCurrencyFormat(originPrice);
8821            const hasDiscount = originPrice !== finalPrice;
8822
8823            const $pcOrigin = $row.find('._cart_price_origin');
8824            if ($pcOrigin.length) {
8825                $pcOrigin.data('origin-price', originPrice).text(originText);
8826                $pcOrigin.data('default-visible', hasDiscount ? 'Y' : 'N').toggle(hasDiscount);
8827            }
8828
8829            const $mobileOrigin = $row.find('._cart_mobile_order_price_origin');
8830            if ($mobileOrigin.length) {
8831                $mobileOrigin.data('origin-price', originPrice).text(originText);
8832                $mobileOrigin.data('default-visible', hasDiscount ? 'Y' : 'N').toggle(hasDiscount);
8833            }
8834        }
8835    };
8836
8837    var initCart = function (cart_code, backurl, is_regularly, is_c3 = false){
8838        currentCartCode = cart_code;
8839        current_backurl = backurl;
8840        $shop_cart_list = $('#shop_cart_list');
8841        $changeCartItemLayer = $('#shop_cart_change_layer');
8842        $shop_cart_wish_list = $('#shop_cart_wish_list');
8843        $shop_cart_wish_list_empty = $('#shop_cart_wish_list_empty');
8844
8845        $cart_list_wrap = $shop_cart_list.find('._cart_list_wrap');
8846        $global_select = $shop_cart_list.find('._global_select');
8847        // 모듈 스코프 cart_type 에 일반/정기 구분값을 반영 (외부 함수에서 재사용)
8848        cart_type = SHOP_CONST.CART_TYPE_NORMAL;
8849        if (is_regularly == 'Y') cart_type = SHOP_CONST.CART_TYPE_REGULARLY;
8850        cartListMake(backurl, cart_type, is_c3);
8851    };
8852
8853    var findCartItemDelivTd = function ($row, itemCode) {
8854        var $delivTd = $cart_list_wrap.find('._cart_item_deliv_td[data-item-code="' + itemCode + '"]');
8855        if ($delivTd.length) return $delivTd;
8856
8857        $delivTd = $row.find('._cart_item_deliv_td');
8858        if ($delivTd.length) return $delivTd;
8859
8860        // PC 테이블은 묶음배송 그룹의 첫 행에만 배송비 셀이 있고 rowspan으로 묶인다.
8861        return $row.prevAll('tr.content').filter(function () {
8862            return $(this).find('._cart_item_deliv_td').length > 0;
8863        }).first().find('._cart_item_deliv_td');
8864    };
8865
8866    var findCartBundleShippingRow = function ($row, itemCode) {
8867        var $shippingRow = $cart_list_wrap.find('tr.im-tr-shipping[data-item-code="' + itemCode + '"]');
8868        if ($shippingRow.length) return $shippingRow;
8869
8870        return $row.nextAll('tr.im-tr-shipping').first();
8871    };
8872
8873    var updateCartItemDelivText = function ($delivTd, delivPriceText) {
8874        if (!$delivTd.length || delivPriceText === undefined) return;
8875
8876        var $delivPrice = $delivTd.find('._cart_item_deliv_price');
8877        if (!$delivPrice.length) return;
8878
8879        var $textWrap = $delivPrice.find('span.text-bold').first();
8880        if ($textWrap.length) {
8881            $textWrap.html(delivPriceText);
8882            return;
8883        }
8884
8885        if (delivPriceText !== '') {
8886            $delivPrice.prepend('<span class="text-bold">' + delivPriceText + '</span>');
8887        }
8888    };
8889
8890    var updateCartDeliveryData = function (itemDeliveryData) {
8891        if (!itemDeliveryData) return;
8892
8893        $.each(itemDeliveryData, function (itemCode, info) {
8894            const $itemRow = $cart_list_wrap.find(`tr[data-item-code="${itemCode}"]`);
8895            if (!$itemRow.length) return;
8896
8897            if (info.deliv_price_text !== undefined && info.deliv_price_text !== '') {
8898                updateCartItemDelivText(findCartItemDelivTd($itemRow, itemCode), info.deliv_price_text);
8899
8900                const $mobileDelivText = $cart_list_wrap.find(`._cart_mobile_deliv_price[data-item-code="${itemCode}"] ._cart_mobile_deliv_price_text`);
8901                if ($mobileDelivText.length) {
8902                    $mobileDelivText.html(info.deliv_price_text);
8903                }
8904            }
8905
8906            if (info.bundle_deliv_text !== undefined && info.bundle_deliv_text !== '') {
8907                const $shippingRow = findCartBundleShippingRow($itemRow, itemCode);
8908                if ($shippingRow.length) {
8909                    $shippingRow.find('._cart_bundle_deliv_text').html(info.bundle_deliv_text);
8910                }
8911            }
8912        });
8913    };
8914
8915    var getEmptySelectionDelivPriceText = function ($target) {
8916        var delivPriceText = $target.attr('data-free-deliv-price-text');
8917        if (delivPriceText !== undefined && delivPriceText !== '') return delivPriceText;
8918        return '<span>' + getLocalizeString('설명_무료', '', '무료') + '</span>';
8919    };
8920
8921    var updateCartDeliveryForEmptySelection = function () {
8922        $cart_list_wrap.find('._cart_item_deliv_td').each(function () {
8923            var $delivTd = $(this);
8924            updateCartItemDelivText($delivTd, getEmptySelectionDelivPriceText($delivTd));
8925        });
8926
8927        $cart_list_wrap.find('._cart_mobile_deliv_price').each(function () {
8928            var $mobileDelivPrice = $(this);
8929            var $mobileDelivText = $mobileDelivPrice.find('._cart_mobile_deliv_price_text');
8930            if ($mobileDelivText.length) {
8931                $mobileDelivText.html(getEmptySelectionDelivPriceText($mobileDelivPrice));
8932            }
8933        });
8934
8935        $cart_list_wrap.find('tr.im-tr-shipping').each(function () {
8936            var $shippingRow = $(this);
8937            var bundleDelivText = $shippingRow.attr('data-free-bundle-deliv-text');
8938            if (bundleDelivText !== undefined && bundleDelivText !== '') {
8939                $shippingRow.find('._cart_bundle_deliv_text').html(bundleDelivText);
8940            }
8941        });
8942    };
8943
8944    /**
8945     * 선택한 항목의 가격 계산
8946     */
8947    var getSelectedPrice = function (is_regularly) {
8948        var requestSeq = ++get_selected_price_request_seq;
8949        $.ajax({
8950            type: 'POST',
8951            data: {
8952                'item_list': selectedCartItem,
8953                'is_regularly': is_regularly,
8954                // 매출 상승 도구 미리보기 모드 감지용 - 서버에서 current_full_url의 GET 파라미터를 파싱
8955                'current_full_url': current_backurl || ''
8956            },
8957            url: ('/shop/get_cart_price.cm'),
8958            dataType: 'json',
8959            success: function (res) {
8960                if (requestSeq !== get_selected_price_request_seq) return;
8961                if (res.msg == 'SUCCESS') {
8962                    // CB-3865: setCartPriceHtml 보다 먼저 사은품 dispatch — UI 가격 갱신과 독립.
8963                    // setCartPriceHtml 내부의 jQuery 체인이 swallow 되면 dispatch 누락 방지.
8964                    var selectedSet = new Set(selectedCartItem);
8965                    _cartRawItems.forEach(function (item) {
8966                        item.selected = selectedSet.has(item.item_code);
8967                    });
8968                    dispatchFreebieCartChange();
8969                    setCartPriceHtml(res['price_data']);
8970                } else {
8971                    if (res.res_code != 1) {
8972                        alert(res.msg);
8973                    }
8974                    is_cart_changed = false;
8975                }
8976            }
8977        });
8978    };
8979
8980    var setCartPriceHtml = function (data) {
8981        var freeShipProgressPrice = data ? parseFloat(data['free_shipping_progress_price']) : NaN;
8982        var adjustedFreeShipProgressPrice = freeShipProgressPrice;
8983
8984        $total_price = $cart_list_wrap.find('._cart_main_total_price');
8985        $cart_result_container = $cart_list_wrap.find('._cart_result_container');
8986        var $mobile_cart_result_container = $cart_list_wrap.find('tr._cart_result_container');
8987        var $pc_cart_result_container = $cart_list_wrap.find('._cart_result_table').find('._cart_result_container');
8988
8989        $cart_list_wrap.find('._cart_count').text(data['count']);
8990        $cart_result_container.find('._cart_price').html(data['price']);
8991        $cart_result_container.find('._cart_deliv_price').html(data['deliv_price']);
8992        $cart_result_container.find('._cart_add_point').html(data['add_point']);
8993        if ($cart_list_wrap.find('._cartItemCheckbox:checked').length === 0) {
8994            updateCartDeliveryForEmptySelection();
8995        } else {
8996            updateCartDeliveryData(data['item_delivery_data']);
8997        }
8998        // 수량별 할인 합계 계산
8999        let _cartQuantityDiscountTotal = 0;
9000        $cart_list_wrap.find('tr.content[data-qd-tiers]').each(function () {
9001            const $row = $(this);
9002            const itemCode = String($row.data('item-code'));
9003            if ($.inArray(itemCode, selectedCartItem) === -1) return;
9004            // 크리에이터 매칭 item 은 수량별 할인 대상에서 제외 (회원할인과 일관)
9005            if (creator_cart_data && creator_cart_data.matched_items && creator_cart_data.matched_items[itemCode]) return;
9006            const priceRaw = parseInt($row.find('._cart_price_default').data('discounted-price'), 10) || 0;
9007            _cartQuantityDiscountTotal += _calcCartQuantityDiscount($row, priceRaw);
9008        });
9009
9010        // 회원 할인금액: 등급할인 base에서 수량별 할인 차감 후 재계산
9011        const serverMembershipRaw = parseInt(data['membership_discount_raw'], 10) || 0;
9012        const membershipTargetRaw = parseInt(data['membership_discount_target_raw'], 10) || 0;
9013        const membershipConfig = data['membership_discount_config'] || null;
9014        let membershipDiscountRaw = serverMembershipRaw;
9015        if (_cartQuantityDiscountTotal > 0 && membershipConfig && membershipTargetRaw > 0) {
9016            const adjustedTarget = Math.max(membershipTargetRaw - _cartQuantityDiscountTotal, 0);
9017            // 등급할인 재계산 (checkMembershipBenefit 서버 로직과 동일)
9018            let _mcDiscount = 0;
9019            if (membershipConfig['amount'] && (membershipConfig['minimum'] <= adjustedTarget)) {
9020                if (membershipConfig['type'] !== 'price') {
9021                    const _mcAmount = Math.min(membershipConfig['amount'], 100);
9022                    _mcDiscount = (adjustedTarget / 100) * _mcAmount;
9023                    if (_mcDiscount > membershipConfig['maximum']) {
9024                        _mcDiscount = membershipConfig['maximum'];
9025                    }
9026                } else {
9027                    _mcDiscount = membershipConfig['amount'];
9028                }
9029            }
9030            if (_mcDiscount > adjustedTarget) _mcDiscount = adjustedTarget;
9031            const decimalCount = LOCALIZE.getCurrencyDecimalCount();
9032            membershipDiscountRaw = decimalCount === 0
9033                ? Math.ceil(_mcDiscount)
9034                : Math.ceil(_mcDiscount * Math.pow(10, decimalCount)) / Math.pow(10, decimalCount);
9035        }
9036        const membershipDiscountText = membershipDiscountRaw > 0 ? LOCALIZE.getCurrencyFormat(membershipDiscountRaw) : '-';
9037        const membershipPriceExist = membershipDiscountRaw > 0;
9038        $cart_result_container.find('._cart_membership_discount').html(membershipDiscountText);
9039        $cart_list_wrap.find('._cart_membership_field').toggle(membershipPriceExist);
9040
9041        var isFreeShipNotifyAfterSale = data && data['free_shipping_deliv_price_cost_type'] === 'after_sale';
9042        if (isFreeShipNotifyAfterSale && !isNaN(adjustedFreeShipProgressPrice)) {
9043            var freeShipDiscountAdjust = _cartQuantityDiscountTotal;
9044            if (membershipDiscountRaw > 0) {
9045                var serverMembershipAppliedToFreeShip = false;
9046                if (serverMembershipRaw > 0 && membershipTargetRaw > 0) {
9047                    serverMembershipAppliedToFreeShip = adjustedFreeShipProgressPrice <= Math.max(membershipTargetRaw - serverMembershipRaw, 0) + 0.0001;
9048                }
9049                freeShipDiscountAdjust += membershipDiscountRaw;
9050                if (serverMembershipAppliedToFreeShip) {
9051                    freeShipDiscountAdjust -= serverMembershipRaw;
9052                }
9053            }
9054            adjustedFreeShipProgressPrice = Math.max(adjustedFreeShipProgressPrice - freeShipDiscountAdjust, 0);
9055        }
9056
9057        // 무료배송 안내 마그넷 — 실제 배송비 계산값은 건드리지 않고 안내용 진행/잔여 금액만 화면 할인값과 맞춘다.
9058        if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
9059            && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice === 'function'
9060            && data && 'free_shipping_remaining_price' in data) {
9061            var freeShipRemaining = ($('#shop_cart_list ._global_select').length > 0 && $deliv_country !== 'KR')
9062                ? null
9063                : data.free_shipping_remaining_price;
9064            var freeShipRemainingPrice = parseFloat(freeShipRemaining);
9065            if (isFreeShipNotifyAfterSale && !isNaN(freeShipRemainingPrice) && !isNaN(freeShipProgressPrice) && !isNaN(adjustedFreeShipProgressPrice)) {
9066                freeShipRemaining = Math.max((freeShipRemainingPrice + Math.max(freeShipProgressPrice, 0)) - adjustedFreeShipProgressPrice, 0);
9067                freeShipRemainingPrice = parseFloat(freeShipRemaining);
9068            }
9069            if (!isNaN(adjustedFreeShipProgressPrice) && adjustedFreeShipProgressPrice <= 0
9070                && !isNaN(freeShipRemainingPrice) && freeShipRemainingPrice <= 0) {
9071                freeShipRemaining = null;
9072            }
9073            window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice(freeShipRemaining);
9074        }
9075
9076        // 크리에이터 전용 혜택가 — PC 합계는 별도 row, 모바일 합계는 상품할인금액 하위 row 로 노출
9077        var creatorDiscountRaw = parseInt(data['creator_discount_raw'], 10) || 0;
9078        var creatorDiscountExist = creatorDiscountRaw > 0;
9079        var creatorDiscountText = data['creator_discount_text'] || '-';
9080
9081        // 상품할인금액 = 서버 period_price + 첫구매혜택 + 수량별 할인
9082        const periodPriceRaw = parseInt(data['period_price_raw'], 10) || 0;
9083        const benefitDiscountRaw = parseInt(data['benefit_discount_raw'], 10) || 0;
9084        const pcDiscountPrice = periodPriceRaw + benefitDiscountRaw + _cartQuantityDiscountTotal;
9085        const mobileDiscountPrice = pcDiscountPrice + creatorDiscountRaw;
9086        const pcDiscountPriceExist = pcDiscountPrice > 0;
9087        const mobileDiscountPriceExist = mobileDiscountPrice > 0;
9088
9089        $mobile_cart_result_container.find('._cart_period_field').toggle(mobileDiscountPriceExist);
9090        $mobile_cart_result_container.find('._cart_period_discount').text(mobileDiscountPriceExist ? LOCALIZE.getCurrencyFormat(mobileDiscountPrice) : '-');
9091        $mobile_cart_result_container.find('._cart_mobile_creator_discount_total_field').toggle(creatorDiscountExist);
9092        $mobile_cart_result_container.find('._cart_mobile_creator_discount_total').text(creatorDiscountExist ? '- ' + creatorDiscountText : '');
9093        $mobile_cart_result_container.find('._cart_mobile_discount_detail_wrap').toggle(creatorDiscountExist);
9094        $pc_cart_result_container.find('._cart_period_field').toggle(pcDiscountPriceExist);
9095        $pc_cart_result_container.find('._cart_period_discount').text(pcDiscountPriceExist ? LOCALIZE.getCurrencyFormat(pcDiscountPrice) : '-');
9096
9097        $mobile_cart_result_container.find('._cart_creator_field').hide();
9098        $pc_cart_result_container.find('._cart_creator_field').toggle(creatorDiscountExist);
9099        $pc_cart_result_container.find('._cart_creator_discount').text(creatorDiscountExist ? creatorDiscountText : '-');
9100        const cartSelectedCount = parseInt(data['count'], 10) || 0;
9101        const cartNoDeliveryCount = parseInt(data['no_deliv_count'], 10) || 0;
9102        if (cartSelectedCount === 0) {
9103            // 선택된 상품이 없을 때는 0원 합계의 기본 항목을 모두 노출한다.
9104            $cart_list_wrap.find('._cart_price_field').show();
9105            $cart_list_wrap.find('._cart_deliv_price_field').show();
9106            $cart_list_wrap.find('.im-ico-equals-plain').show();
9107            $mobile_cart_result_container.find('._plus_deliv_price').show();
9108        } else {
9109            $cart_list_wrap.find('._cart_deliv_price_field').toggle(cartSelectedCount !== cartNoDeliveryCount);
9110            if (data['membership_price_exist'] === false && mobileDiscountPriceExist === false && (cartSelectedCount === cartNoDeliveryCount)) {
9111                $cart_list_wrap.find('.im-ico-equals-plain').hide();
9112                $pc_cart_result_container.find('._cart_price_field').hide();
9113            } else {
9114                if (data['only_deliv_price_after']) {
9115                    $cart_list_wrap.find('._cart_deliv_price_field').show();
9116                    $cart_list_wrap.find('.im-ico-equals-plain').show();
9117                    $pc_cart_result_container.find('._cart_price_field').show();
9118                    $mobile_cart_result_container.find('._plus_deliv_price').hide();
9119                } else {
9120                    $cart_list_wrap.find('.im-ico-equals-plain').show();
9121                    $pc_cart_result_container.find('._cart_price_field').show();
9122                    $mobile_cart_result_container.find('._plus_deliv_price').show();
9123                }
9124            }
9125        }
9126
9127        // 총 주문금액 (수량별 할인 + 등급할인 보정 반영)
9128        const membershipAdjust = serverMembershipRaw - membershipDiscountRaw;
9129        const totalPriceRaw = Math.max(parseInt(data['total_price_raw'], 10) - _cartQuantityDiscountTotal + membershipAdjust, 0);
9130        $total_price.text(LOCALIZE.getCurrencyFormat(totalPriceRaw));
9131        $cart_list_wrap.find('._cart_result_table').show();
9132        setCartBenefitInfo(data);
9133
9134        // MFE 무료배송 안내 위젯(fo-shopping/free-ship-notify)의 게이지/임계값 계산용으로 숫자 총액을 브로드캐스트.
9135        // 배송 그룹이 분리된 경우에는 서버가 내려준 무료배송 안내 대상 그룹의 진행 금액만 사용한다.
9136        var totalPriceValue = !isNaN(adjustedFreeShipProgressPrice)
9137            ? adjustedFreeShipProgressPrice
9138            : (typeof data['total_price_raw'] === 'number' ? data['total_price_raw'] : 0);
9139        window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartTotal:update', {
9140            detail: { totalPrice: totalPriceValue }
9141        }));
9142
9143        // 같은 시점에 prod_code 단위 카트 수량 스냅샷도 dispatch.
9144        // 추천 상품 카드(fo-shopping/free-ship-notify ProductCard)가 "+ 아이콘 ↔ 담긴 수량 숫자" 전환에 사용.
9145        // 같은 prod_code 의 옵션 라인이 여러 row 로 나뉠 수 있어 합산.
9146        var itemCounts = {};
9147        $cart_list_wrap.find('tr.content[data-prod-code]').each(function () {
9148            var prodCode = this.getAttribute('data-prod-code');
9149            if (!prodCode) return;
9150            var rowCount = 0;
9151            this.querySelectorAll('._counter_value').forEach(function (input) {
9152                var qty = parseInt(input.value, 10);
9153                if (Number.isFinite(qty)) rowCount += qty;
9154            });
9155            itemCounts[prodCode] = (itemCounts[prodCode] || 0) + rowCount;
9156        });
9157        window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartItems:update', {
9158            detail: { counts: itemCounts }
9159        }));
9160    };
9161
9162    var setCartBenefitInfo = function (data) {
9163        var benefitInfo = data ? data['benefit_info'] : null;
9164        var $benefitItems = $cart_list_wrap.find('._cart_benefit_info');
9165        var $mobileCartResultContainer = $cart_list_wrap.find('tr._cart_result_container');
9166        var $pcCartResultContainer = $cart_list_wrap.find('._cart_result_table').find('._cart_result_container');
9167        var $mobileBenefitField = $mobileCartResultContainer.find('._cart_benefit_field');
9168        var $pcBenefitField = $pcCartResultContainer.find('._cart_benefit_field');
9169        var $benefitName = $cart_list_wrap.find('._cart_benefit_name');
9170        var $benefitPrice = $cart_list_wrap.find('._cart_benefit_price');
9171        var $pcBenefitLabel = $cart_list_wrap.find('._cart_benefit_pc_label');
9172        var $pcBenefitPrice = $cart_list_wrap.find('._cart_benefit_pc_price');
9173        var $pcBenefitSub = $cart_list_wrap.find('._cart_benefit_pc_sub');
9174        var $pcPriceDefault = $cart_list_wrap.find('._cart_price_default');
9175        var $mobileBenefitDiscountField = $cart_list_wrap.find('._cart_mobile_benefit_discount_field');
9176        var $mobileBenefitDiscountPrice = $cart_list_wrap.find('._cart_mobile_benefit_discount_price');
9177        var $mobileBenefitDetailField = $cart_list_wrap.find('._cart_mobile_benefit_detail_field');
9178        var $mobileBenefitDetailName = $cart_list_wrap.find('._cart_mobile_benefit_name');
9179        var $mobileBenefitDetailPrice = $cart_list_wrap.find('._cart_mobile_benefit_detail_price');
9180        var $mobileOrderHintField = $cart_list_wrap.find('._cart_mobile_order_hint_field');
9181        var $mobileOrderHint = $cart_list_wrap.find('._cart_mobile_order_hint');
9182        var $mobileOrderPriceOrigin = $cart_list_wrap.find('._cart_mobile_order_price_origin');
9183        var $pcPriceOrigin = $cart_list_wrap.find('._cart_price_origin');
9184
9185        $benefitItems.hide().text('');
9186        $mobileBenefitField.hide();
9187        $pcBenefitField.hide();
9188        $pcBenefitLabel.hide().text('');
9189        $pcBenefitPrice.hide().text('');
9190        $pcBenefitSub.hide().text('');
9191        $pcPriceDefault.show();
9192        $mobileBenefitDiscountField.hide();
9193        $mobileBenefitDiscountPrice.text('');
9194        $mobileBenefitDetailField.hide();
9195        $mobileBenefitDetailName.text('');
9196        $mobileBenefitDetailPrice.text('');
9197        $mobileOrderHintField.hide();
9198        $mobileOrderHint.text('');
9199        if ($mobileOrderPriceOrigin.length > 0) {
9200            $mobileOrderPriceOrigin.each(function () {
9201                var $origin = $(this);
9202                $origin.toggle($origin.data('default-visible') === 'Y');
9203            });
9204        }
9205        if ($pcPriceOrigin.length > 0) {
9206            $pcPriceOrigin.each(function () {
9207                var $origin = $(this);
9208                $origin.toggle($origin.data('default-visible') === 'Y');
9209            });
9210        }
9211
9212        // 첫구매 혜택 모달 description 서버 응답 기준으로 갱신 (모달 열릴 때 shadow DOM 재생성으로 반영)
9213        var $promotionModalShell = $('#_cart_promotion_modal_shell');
9214        if ($promotionModalShell.length > 0) {
9215            var modalDescription;
9216            if (!benefitInfo || benefitInfo['promotion_exists'] !== true) {
9217                modalDescription = '조건 충족 시 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9218            } else {
9219                var shortfallAmount = parseInt(benefitInfo['benefit_shortfall_amount'] || 0, 10) || 0;
9220                var shortfallQuantity = parseInt(benefitInfo['benefit_shortfall_quantity'] || 0, 10) || 0;
9221                if (shortfallAmount > 0) {
9222                    modalDescription = '<strong>' + LOCALIZE.getCurrencyFormat(shortfallAmount) + '</strong> 더 담으면 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9223                } else if (shortfallQuantity > 0) {
9224                    modalDescription = '<strong>' + shortfallQuantity.toLocaleString('ko-KR') + '개</strong> 더 담으면 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9225                } else {
9226                    modalDescription = '조건 충족 시 아래 상품 중 1개를<br/>첫 구매 혜택가에 구매할 수 있어요!';
9227                }
9228            }
9229            $promotionModalShell.attr('data-description', modalDescription);
9230        }
9231
9232        if (!benefitInfo || benefitInfo['promotion_exists'] !== true) {
9233            return;
9234        }
9235
9236        var benefitProducts = benefitInfo['benefit_products'] || [];
9237        var promotionName = benefitInfo['promotion_name'] || '';
9238        var isQualified = benefitInfo['is_qualified'] === true;
9239        var appliedItemCode = benefitInfo['applied_item_code'] || '';
9240        var benefitDiscountPrice = parseInt(benefitInfo['benefit_discount_price'] || 0, 10) || 0;
9241        var benefitDiscountText = benefitInfo['benefit_discount_price_text'] || '';
9242        if (benefitProducts.length === 0 || promotionName === '') {
9243            return;
9244        }
9245
9246        var benefitMap = {};
9247        $.each(benefitProducts, function (idx, benefitProduct) {
9248            if (!benefitProduct || !benefitProduct['prod_code']) {
9249                return;
9250            }
9251            benefitMap[benefitProduct['prod_code']] = benefitProduct;
9252        });
9253
9254        // 모든 row 가격을 base 기준으로 reset (FP 합성 분기 진입 전 초기화). QD는 헬퍼가 자동 차감.
9255        $cart_list_wrap.find('tr.content').each(function () {
9256            var $row = $(this);
9257            var $base = $row.find('._cart_price_default');
9258            if (!$base.length) $base = $row.find('._cart_mobile_order_price');
9259            var basePrice = parseInt($base.data('discounted-price'), 10);
9260            if (!isNaN(basePrice) && basePrice >= 0) {
9261                renderCartRowPrice($row, { basePrice: basePrice });
9262            }
9263        });
9264
9265        $benefitItems.each(function () {
9266            var $el = $(this);
9267            var itemCode = String($el.data('item-code'));
9268            var prodCode = $el.data('prod-code');
9269
9270            if ($.inArray(itemCode, selectedCartItem) === -1) {
9271                return;
9272            }
9273
9274            if (!benefitMap[prodCode]) {
9275                return;
9276            }
9277
9278            // 크리에이터 매칭 item 은 첫구매 BOGO row sub 영역 비노출 (회원할인과 일관)
9279            if (creator_cart_data && creator_cart_data.matched_items && creator_cart_data.matched_items[itemCode]) {
9280                return;
9281            }
9282
9283            var priceText = benefitMap[prodCode]['benefit_price_text'] || '';
9284            var $row = $cart_list_wrap.find('tr[data-item-code="' + itemCode + '"]');
9285            if ($row.length > 0) {
9286                if (isQualified && benefitMap[prodCode]['is_applied'] === true) {
9287                    if (appliedItemCode && itemCode !== appliedItemCode) {
9288                        return;
9289                    }
9290                    $row.find('._cart_benefit_pc_label').text('첫 구매 혜택').show();
9291                    var $pcOrderPrice = $row.find('._cart_price_default');
9292                    var basePriceForFp = parseInt($pcOrderPrice.data('discounted-price'), 10);
9293                    if (isNaN(basePriceForFp) || basePriceForFp < 0) {
9294                        basePriceForFp = parseInt($row.find('._cart_mobile_order_price').data('discounted-price'), 10);
9295                    }
9296                    var hasFpPrice = false;
9297                    if (!isNaN(basePriceForFp) && benefitDiscountPrice > 0) {
9298                        // 가격 표시는 헬퍼가 PC/mobile 동시 처리 (QD + FP 합성)
9299                        renderCartRowPrice($row, { basePrice: basePriceForFp, fpDiscount: benefitDiscountPrice });
9300                        hasFpPrice = true;
9301                    } else if (priceText) {
9302                        // 서버가 직접 표시 텍스트를 내려주는 fallback (할인 금액 없이 텍스트만)
9303                        $pcOrderPrice.find('._cart_price_default_value').text(priceText);
9304                        hasFpPrice = true;
9305                    }
9306
9307                    // PC 메타 (라벨/보조 가격/sub 영역)
9308                    $row.find('._cart_benefit_pc_price').hide().text('');
9309                    $row.find('._cart_price_default').show();
9310                    $row.find('._cart_benefit_pc_sub').hide().text('');
9311                    if (hasFpPrice) {
9312                        $row.find('._cart_price_origin').show();
9313                    }
9314
9315                    // Mobile 메타 (할인 합계, 혜택 명칭, 필드 토글) — 가격 자체는 위 헬퍼에서 처리됨
9316                    if (benefitDiscountPrice > 0 && benefitDiscountText) {
9317                        var periodDiscountPrice = 0;
9318                        var $mobilePeriodDiscountPrice = $row.find('._cart_mobile_period_discount_price');
9319                        if ($mobilePeriodDiscountPrice.length > 0) {
9320                            periodDiscountPrice = parseInt($mobilePeriodDiscountPrice.data('period-discount-price'), 10) || 0;
9321                            $row.find('._cart_mobile_period_discount_field').hide();
9322                        }
9323                        var totalDiscountPrice = periodDiscountPrice + benefitDiscountPrice;
9324                        $row.find('._cart_mobile_benefit_discount_price').text('- ' + LOCALIZE.getCurrencyFormat(totalDiscountPrice));
9325                        $row.find('._cart_mobile_benefit_detail_price').text('- ' + benefitDiscountText);
9326                        $row.find('._cart_mobile_benefit_name').text(promotionName);
9327                        $row.find('._cart_mobile_benefit_discount_field').show();
9328                        $row.find('._cart_mobile_benefit_detail_field').show();
9329                        $row.find('._cart_mobile_order_price_origin').show();
9330                    }
9331                } else if (!isQualified) {
9332                    if (priceText) {
9333                        var benefitLinkStyle = 'color:#0090D4;font-size:12px;font-weight:600;line-height:16px;display:inline-flex;align-items:center;';
9334                        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>';
9335                        // view_component_promotion_cart (PTY02) 트래킹용 도메인 데이터
9336                        var cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
9337                        var fpPromotionCode = $row.attr('data-fp-promotion-code') || '';
9338                        var prodType = $row.attr('data-prod-type') || 'normal';
9339                        var trackPromotionAttr = '';
9340                        if (fpPromotionCode) {
9341                            var trackDomain = {
9342                                prod_code: prodCode,
9343                                prod_type: prodType,
9344                                promotion_code: fpPromotionCode,
9345                                promotion_type: 'first_purchase',
9346                                promotion_product_role: 'target',
9347                                cart_code: cartCode,
9348                                cart_item_code: itemCode,
9349                            };
9350                            // attribute escape: 큰따옴표 attribute 안에 JSON 의 " 가 그대로 들어가면 attribute 가 깨지므로 & " 만 escape
9351                            var trackJson = JSON.stringify(trackDomain).replace(/&/g, '&amp;').replace(/"/g, '&quot;');
9352                            trackPromotionAttr = ' data-bs-promotion="' + trackJson + '"';
9353                        }
9354                        var pcBenefitLinkHtml = '<a href="#" onclick="SITE_SHOP_CART.openPromotionModal(); return false;" class="_cart_promotion_modal_link" style="' + benefitLinkStyle + '"' + trackPromotionAttr + '>첫 구매 혜택가 ' + priceText + benefitInfoIcon + '</a>';
9355                        var mobileBenefitLinkHtml = '<a href="#" onclick="SITE_SHOP_CART.openPromotionModal(); return false;" class="_cart_promotion_modal_link" style="' + benefitLinkStyle + '"' + trackPromotionAttr + '>첫 구매 혜택가 ' + priceText + benefitInfoIcon + '</a>';
9356                        $row.find('._cart_benefit_pc_sub').html(pcBenefitLinkHtml).show();
9357                        $row.find('._cart_mobile_order_hint').html(mobileBenefitLinkHtml);
9358                        $row.find('._cart_mobile_order_hint_field').show();
9359                    }
9360                }
9361            }
9362        });
9363
9364        var benefitDiscountText = benefitInfo['benefit_discount_price_text'] || '';
9365        if (isQualified && benefitDiscountText) {
9366            $benefitName.text(promotionName);
9367            $benefitPrice.text('- ' + benefitDiscountText);
9368            $mobileBenefitField.show();
9369        }
9370
9371    };
9372
9373    var openPromotionModal = function () {
9374        // shadow DOM은 최초 렌더 시 data-description을 1회만 읽으므로, promotion-modal 을 제거-재추가해 shadow DOM 을 재생성한다
9375        var $promotionModalShell = $('#_cart_promotion_modal_shell');
9376        if ($promotionModalShell.length > 0) {
9377            var shell = $promotionModalShell.get(0);
9378            if (shell) {
9379                var modalElement = shell.querySelector('promotion-modal');
9380                if (modalElement && modalElement.parentNode) {
9381                    modalElement.parentNode.removeChild(modalElement);
9382                    shell.appendChild(modalElement);
9383                }
9384            }
9385        }
9386        history.replaceState(null, '', '#promotion-modal');
9387        window.dispatchEvent(new HashChangeEvent('hashchange'));
9388    };
9389
9390    /**
9391     * 상품 위시리스트 추가 처리
9392     * @param cart_item_list
9393     */
9394    var addProdWish = function (cart_item_list, is_regularly) {
9395        $.ajax({
9396            type: 'POST',
9397            data: { 'type': 'add', 'cart_item_list': cart_item_list, 'is_regularly': is_regularly },
9398            url: ('/shop/add_prod_wish_cart.cm'),
9399            dataType: 'json',
9400            success: function (res) {
9401                if (res.msg == 'SUCCESS') {
9402                    for (var i = 0; i < cart_item_list.length; i++) {
9403                        if (typeof FB_PIXEL != 'undefined') FB_PIXEL.AddToWishlist();
9404                        $shop_cart_list.find("._wish_status_" + cart_item_list[i]).addClass('check');
9405                    }
9406                    loadProdWishList();
9407                } else
9408                    alert(res.msg);
9409            }
9410        });
9411    };
9412
9413    /**
9414     * 위시리스트 가져오기
9415     * @param prod_code
9416     */
9417    var loadProdWishList = function () {
9418			var is_design_mode = (location.pathname.indexOf('/admin/design') != -1);
9419        $.ajax({
9420            type: 'POST',
9421            data: { 'type': 'cart', 'is_design_mode': is_design_mode ? 'Y' : 'N' },
9422            url: ('/shop/get_prod_wish_list.cm'),
9423            dataType: 'json',
9424            success: function (res) {
9425                if (res.msg == 'SUCCESS') {
9426                    if (res.count > 0) {
9427                        $shop_cart_wish_list_empty.hide();
9428                        $shop_cart_wish_list.show().html(res.html);
9429                        setTimesale();
9430                    } else {
9431                        $shop_cart_wish_list_empty.show();
9432                        $shop_cart_wish_list.hide();
9433                    }
9434		                // 크리마 리뷰 사용 중일 시 위젯 호출
9435		                if(document.querySelector(".crema-product-reviews-score")){
9436			                if(typeof crema !== 'undefined' && typeof crema.run === 'function'){
9437				                crema.run();
9438			                }
9439		                }
9440                } else
9441                    alert(res.msg);
9442            }
9443        });
9444    };
9445
9446    /**
9447     * 위시리스트 제거
9448     * @param cart_item_list
9449     */
9450    var deleteProdWish = function (cart_item_list, is_regularly) {
9451        $.ajax({
9452            type: 'POST',
9453            data: { 'type': 'delete', 'cart_item_list': cart_item_list, 'is_regularly': is_regularly },
9454            url: ('/shop/add_prod_wish_cart.cm'),
9455            dataType: 'json',
9456            success: function (res) {
9457                if (res.msg == 'SUCCESS') {
9458                    for (var i = 0; i < cart_item_list.length; i++) {
9459                        $shop_cart_list.find("._wish_status_" + cart_item_list[i]).removeClass('check');
9460                    }
9461                    loadProdWishList();
9462                } else
9463                    alert(res.msg);
9464            }
9465        });
9466    };
9467
9468    /**
9469     * 위시리스트 제거 (상품코드사용
9470     * @param prod_code
9471     */
9472    var deleteProdWishByProdCode = function (prod_code) {
9473        $.ajax({
9474            type: 'POST',
9475            data: { 'type': 'delete_prodcode', 'prod_code': prod_code },
9476            url: ('/shop/add_prod_wish_cart.cm'),
9477            dataType: 'json',
9478            success: function (res) {
9479                if (res.msg == 'SUCCESS') {
9480                    if ($('#wish-item-' + prod_code).length > 0) {
9481                        $('#wish-item-' + prod_code).remove();
9482                    } else {
9483                        window.location.reload();
9484                    }
9485                } else
9486                    alert(res.msg);
9487            }
9488        });
9489    };
9490
9491    /**
9492     * 묶음배송 그룹에서 상품 행 삭제 전 rowspan/모바일 배송정보 행 보정
9493     */
9494    const _adjustBundleOnRemove = ($row) => {
9495        const $rowspanTd = $row.find('td[rowspan]');
9496        let $bundleFirstRow = null;
9497
9498        if ($rowspanTd.length) {
9499            const rowspan = parseInt($rowspanTd.attr('rowspan'), 10);
9500            if (rowspan > 1) {
9501                // 첫 번째 행 삭제: 배송비 td를 다음 행으로 이동
9502                const $next = $row.nextAll('tr.content').first();
9503                if ($next.length) {
9504                    $rowspanTd.attr('rowspan', rowspan - 1);
9505                    $next.append($rowspanTd);
9506                    $bundleFirstRow = $next;
9507                }
9508            }
9509        } else {
9510            // 첫 번째가 아닌 행 삭제: 그룹 첫 행의 rowspan 감소
9511            $row.prevAll('tr.content').each(function () {
9512                const $td = $(this).find('td[rowspan]');
9513                if ($td.length) {
9514                    const rs = parseInt($td.attr('rowspan'), 10);
9515                    if (rs > 1) $td.attr('rowspan', rs - 1);
9516                    $bundleFirstRow = $(this);
9517                    return false;
9518                }
9519            });
9520        }
9521
9522        // 모바일 묶음배송 정보 행: 삭제 후 남은 상품이 1개 이하면 숨김
9523        if ($bundleFirstRow && $bundleFirstRow.length) {
9524            const newRowspan = parseInt($bundleFirstRow.find('td[rowspan]').attr('rowspan'), 10) || 1;
9525            if (newRowspan <= 1) {
9526                let $cur = $bundleFirstRow;
9527                while ($cur.length) {
9528                    $cur = $cur.next('tr');
9529                    if ($cur.hasClass('im-tr-shipping')) { $cur.addClass('hide'); break; }
9530                    if (!$cur.hasClass('content')) break;
9531                }
9532            }
9533        }
9534    };
9535
9536    /**
9537     * click_remove_item_cart 트래킹 (서버 응답 200 + cart 화면 반영 직후)
9538     * @param {('option'|'product')} unit
9539     * @param {{prodCode, prodType, quantityBefore}} ctx
9540     * @param {string|null} cartItemCode option 삭제 시 채움. product 삭제 시 null
9541     * @param {string|null} optionInfo option 삭제 시 채움. product 삭제 시 null
9542     */
9543    const _trackRemoveItemCart = (unit, ctx, cartItemCode, optionInfo) => {
9544        if (typeof window.BrandScope === 'undefined' || !ctx) return;
9545        const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
9546        window.BrandScope.track('click_remove_item_cart', {
9547            action:          'click',
9548            content:         'remove_item',
9549            where:           'cart',
9550            prod_code:       ctx.prodCode || '',
9551            prod_type:       ctx.prodType || 'normal',
9552            cart_code:       cartCode,
9553            cart_item_code:  cartItemCode,
9554            option_info:     optionInfo,
9555            quantity_unit:   unit,
9556            quantity_before: ctx.quantityBefore,
9557            quantity_after:  0,
9558        });
9559    };
9560
9561    /**
9562     * 특정 카트 아이템 삭제
9563     */
9564    const removeCartItem = (item_codeList, is_regularly) => {
9565        // 트래킹 데이터 사전 캡처 (success 시 row 제거되므로 prod_code/prod_type/quantity_before 가 사라짐)
9566        const trackingContexts = item_codeList.map((code) => {
9567            const $row = $cart_list_wrap.find(`tr[data-item-code="${code}"]`);
9568            if (!$row.length) return null;
9569            // 상품 단위 총 수량 = 행 내 모든 _counter_value 합산 (옵션 라인 + product_counter)
9570            const totalCount = $row.find('._counter_value').toArray()
9571                .reduce((sum, el) => sum + (parseInt(el.value, 10) || 0), 0);
9572            return {
9573                prodCode:       $row.attr('data-prod-code') || '',
9574                prodType:       $row.attr('data-prod-type') || 'normal',
9575                quantityBefore: totalCount,
9576            };
9577        });
9578
9579        $.ajax({
9580            type: 'POST',
9581            data: { item_codeList, is_regularly },
9582            url: '/shop/remove_cart_item.cm',
9583            dataType: 'json',
9584            cache: false,
9585            success: ({ msg, delete_cart_info }) => {
9586                if (msg !== 'SUCCESS') return alert(msg);
9587
9588                item_codeList.forEach((item_code) => {
9589                    const info = delete_cart_info[item_code];
9590                    if (typeof AW_DEL !== 'undefined') AW_DEL(info['prod_no'], info['count']);
9591                    if (typeof AM_DEL !== 'undefined') AM_DEL(info['prod_no'], info['count']);
9592                });
9593                if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
9594
9595                // 페이지 리로드 대신 DOM 요소 제거
9596                const $rows = item_codeList
9597                    .map((code) => $cart_list_wrap.find(`tr[data-item-code="${code}"]`))
9598                    .filter(($el) => $el.length);
9599
9600                if (!$rows.length) return window.location.reload();
9601
9602                $rows.forEach(($row) => {
9603                    _adjustBundleOnRemove($row);
9604                    $row.remove();
9605                });
9606
9607                // click_remove_item_cart 트래킹 (휴지통: product 단위 삭제. cart_item_code/option_info 는 null)
9608                trackingContexts.forEach((ctx) => _trackRemoveItemCart('product', ctx, null, null));
9609
9610                // 장바구니가 비었으면 리로드하여 빈 상태 UI 표시
9611                if (!$cart_list_wrap.find('._cartItemCheckbox').length) return window.location.reload();
9612
9613                CartItemChanged(is_regularly === 'Y' ? 'Y' : 'N');
9614            }
9615        });
9616    };
9617
9618    /**
9619     * 단일 상품을 카트에 담기.
9620     * 무료 배송 안내 SDK 등 카트 페이지 컨텍스트에서 사용한다.
9621     *
9622     * 오버로드:
9623     *   addCart(prodIdx, callback)                                            // 옵션 없음 + 수량 1
9624     *   addCart(prodIdx, options, orderCount, callback, requestOptions)       // 옵션/수량/요청 옵션 지정
9625     *
9626     * @param {number} prodIdx 추가할 상품 idx
9627     * @param {Array|function} optionsOrCallback 옵션 배열 또는 콜백 (오버로드 분기)
9628     * @param {number} [orderCount] 주문 수량 (기본 1)
9629     * @param {function(boolean,string,object=):void} [callback] 완료 콜백 (성공 여부, 메시지, add_cart.cm 응답)
9630     * @param {object} [requestOptions] add_cart.cm 추가 요청 옵션
9631     */
9632    const addCart = (prodIdx, optionsOrCallback, orderCount, callback, requestOptions) => {
9633        // 오버로드 분기: 두 번째 인자가 function이면 (prodIdx, callback) 호출로 간주
9634        var options = [];
9635        var count = 1;
9636        var cb = null;
9637        if (typeof optionsOrCallback === 'function') {
9638            cb = optionsOrCallback;
9639        } else {
9640            options = Array.isArray(optionsOrCallback) ? optionsOrCallback : [];
9641            count = (typeof orderCount === 'number' && orderCount > 0) ? orderCount : 1;
9642            cb = (typeof callback === 'function') ? callback : null;
9643        }
9644        var cartRequestOptions = (requestOptions && typeof requestOptions === 'object') ? requestOptions : {};
9645        var shippingTemplateCode = typeof cartRequestOptions.shipping_template_code === 'string'
9646            ? cartRequestOptions.shipping_template_code
9647            : '';
9648        var requestCartType = typeof cartRequestOptions.cart_type === 'string'
9649            ? cartRequestOptions.cart_type
9650            : '';
9651        if (requestCartType !== SHOP_CONST.CART_TYPE_NORMAL && requestCartType !== SHOP_CONST.CART_TYPE_REGULARLY) {
9652            requestCartType = '';
9653        }
9654        var effectiveCartType = requestCartType || SHOP_CONST.CART_TYPE_NORMAL;
9655
9656        $.ajax({
9657            type: 'POST',
9658            url: '/shop/add_cart.cm',
9659            dataType: 'json',
9660            data: {
9661                prodIdx: prodIdx,
9662                options: options,
9663                orderCount: count,
9664                deliv_type: '',
9665                deliv_pay_type: '',
9666                cart_type: effectiveCartType,
9667                prod_additional_list: [],
9668                prod_additional_option_list: [],
9669                shipping_template_code: shippingTemplateCode
9670            },
9671            success: (result) => {
9672                if (result.msg !== 'SUCCESS') {
9673                    if (cb) cb(false, result.msg);
9674                    return;
9675                }
9676
9677                // 전환추적 (SITE_SHOP_DETAIL.addCart 와 동일 배터리)
9678                if (typeof NP_LOG !== 'undefined') NP_LOG.AddToCart();
9679                if (typeof NP_NDA !== 'undefined') NP_NDA.AddToCart(result.prod_code);
9680                if (typeof CRITEO !== 'undefined') CRITEO.AddToCart(result.prod_id, result.total_price);
9681                if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
9682                    // 구 provider에는 송신을 위임하지 않는다. 대상 응답이 잘못되어도 미송신을 유지한다.
9683                    try {
9684                        if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.AddToCartDryRun === 'function') {
9685                            FB_PIXEL.AddToCartDryRun(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, result.meta_compatibility);
9686                        } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
9687                            console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'AddToCart', disposition: 'provider_unsupported'});
9688                        }
9689                    } catch (e) { /* 관측 오류와 무관하게 후속 UI 및 다른 광고 채널을 계속 실행한다. */ }
9690                } else {
9691                    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);
9692                }
9693                if (typeof ACE_COUNTER !== 'undefined') ACE_COUNTER.AddToCart(result.prod_id, result.prod_name, result.prod_count);
9694                if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.AddToCart(result.prod_id, result.prod_count, result.total_price, result.currency);
9695                if (typeof ACE_COUNTER_PARTNER !== 'undefined') ACE_COUNTER_PARTNER.AddToCart(result.prod_id, result.prod_count, result.check_quantity);
9696                if (typeof AW_PRODUCT !== 'undefined') AW_PRODUCT(result.prod_count);
9697                if (typeof AM_PRODUCT !== 'undefined') AM_PRODUCT(result.prod_count);
9698                if (typeof TIKTOK_PIXEL !== 'undefined') {
9699                    TIKTOK_PIXEL.track('AddToCart', {
9700                        contents: [{
9701                            content_id: result.prod_id,
9702                            content_name: result.prod_name,
9703                            quantity: result.prod_count,
9704                            price: result.cart_price
9705                        }],
9706                        event_id: result.tiktok_event_id,
9707                        content_type: 'product',
9708                        value: result.total_price,
9709                        currency: result.currency
9710                    });
9711                }
9712                if (typeof CRM_ONSITE !== 'undefined') {
9713                    CRM_ONSITE.track({ name: 'ADD_TO_CART' });
9714                }
9715
9716                // 카트 리스트 부분 갱신 (전체 페이지 reload 대신 카트 영역만 재렌더링).
9717                // 단, 카트 페이지가 아닌 컨텍스트(상세 페이지의 장바구니 담기 완료 모달 내 매그넷 등)에서
9718                // SITE_SHOP_CART.addCart가 호출될 수 있는데, 이 경우 카트 DOM($cart_list_wrap)이 존재하지 않으므로 스킵한다.
9719                // initCart가 호출된 카트 페이지에서만 $cart_list_wrap이 셋업되어 있다.
9720                if (typeof $cart_list_wrap !== 'undefined' && $cart_list_wrap && $cart_list_wrap.length > 0) {
9721                    // addCart 흐름은 항상 C3/OMS 컨텍스트이므로 is_c3=true 로 고정
9722                    cartListMake(current_backurl, effectiveCartType, true);
9723                } else {
9724                    // 상세 페이지 컨텍스트: 카트 DOM이 없으므로 cartListMake 대신
9725                    // 무료배송 안내 모달용 누적 총액을 갱신하여 MFE 위젯에 imweb:freeShipNotify:cartTotal:update 이벤트 dispatch.
9726                    // 베이스라인은 SITE_SHOP_DETAIL.addCart 성공 시 메인 상품 가격으로 세팅된 window.__imwebFreeShipCartCumulative.
9727                    if (typeof window.__imwebFreeShipCartCumulative !== 'number') {
9728                        window.__imwebFreeShipCartCumulative = 0;
9729                    }
9730                    if (typeof window.__imwebFreeShipCartAdditionalsSum !== 'number') {
9731                        window.__imwebFreeShipCartAdditionalsSum = 0;
9732                    }
9733                    if (typeof window.__imwebFreeShipCartItemCounts !== 'object' || window.__imwebFreeShipCartItemCounts === null) {
9734                        window.__imwebFreeShipCartItemCounts = {};
9735                    }
9736
9737                    var freeShipNotifyCartPrice = (typeof result.free_shipping_price === 'number' ? result.free_shipping_price : (typeof result.total_price === 'number' ? result.total_price : 0));
9738                    window.__imwebFreeShipCartCumulative += freeShipNotifyCartPrice;
9739                    window.__imwebFreeShipCartAdditionalsSum += freeShipNotifyCartPrice;
9740
9741                    if (typeof result.prod_code === 'string' && result.prod_code !== '') {
9742                        window.__imwebFreeShipCartItemCounts[result.prod_code] =
9743                            (window.__imwebFreeShipCartItemCounts[result.prod_code] || 0) + 1;
9744
9745                        if (!Array.isArray(window.__imwebAddToCartProdCodeList)) {
9746                            window.__imwebAddToCartProdCodeList = [];
9747                        }
9748                        window.__imwebAddToCartProdCodeList.push(result.prod_code);
9749                        window.__imwebAddToCartHasFreeShipUpsell = true;
9750                    }
9751
9752                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartTotal:update', {
9753                        detail: { totalPrice: window.__imwebFreeShipCartCumulative }
9754                    }));
9755
9756                    var markerEl = document.getElementById('shop_detail_add_cart_alarm_with_free_ship_notify');
9757                    if (markerEl
9758                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK !== 'undefined'
9759                        && typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice === 'function') {
9760                        var initRemaining = parseFloat(markerEl.getAttribute('data-init-deliv-price-flexable-key') || '0');
9761                        var newRemaining = Math.max(0, initRemaining - window.__imwebFreeShipCartCumulative);
9762                        window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.setCartFreeShippingRemainingPrice(newRemaining);
9763                    }
9764
9765                    window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:cartItems:update', {
9766                        detail: { counts: Object.assign({}, window.__imwebFreeShipCartItemCounts) }
9767                    }));
9768                }
9769
9770                if (cb) cb(true, 'SUCCESS', result);
9771            },
9772            error: () => {
9773                if (cb) cb(false, '상품 담기 중 오류가 발생했습니다.');
9774            }
9775        });
9776    };
9777
9778    /**
9779     *  품절된 상품만 삭제
9780     * @param item_codeList
9781     * @param is_regularly
9782     */
9783    const removeSelectedCartSoldOutItem = (item_codeList, is_regularly) => {
9784        $.ajax({
9785            type: 'POST',
9786            data: { item_codeList, is_regularly },
9787            url: '/shop/remove_cart_soldout_item.cm',
9788            dataType: 'json',
9789            cache: false,
9790            success: ({ msg, delete_cart_info }) => {
9791                if (msg !== 'SUCCESS') return alert(msg);
9792
9793                item_codeList.forEach((item_code) => {
9794                    const info = delete_cart_info[item_code];
9795                    if (info) {
9796                        if (typeof AW_DEL !== 'undefined') AW_DEL(info['prod_no'], info['count']);
9797                        if (typeof AM_DEL !== 'undefined') AM_DEL(info['prod_no'], info['count']);
9798                    }
9799                });
9800                if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
9801
9802                // 페이지 리로드 대신 DOM 요소 제거
9803                const deletedCodes = Object.keys(delete_cart_info || {});
9804                const $rows = deletedCodes
9805                    .map((code) => $cart_list_wrap.find(`tr[data-item-code="${code}"]`))
9806                    .filter(($el) => $el.length);
9807
9808                if (!$rows.length) return window.location.reload();
9809
9810                $rows.forEach(($row) => {
9811                    _adjustBundleOnRemove($row);
9812                    $row.remove();
9813                });
9814
9815                // 장바구니가 비었으면 리로드하여 빈 상태 UI 표시
9816                if (!$cart_list_wrap.find('._cartItemCheckbox').length) return window.location.reload();
9817
9818                CartItemChanged(is_regularly === 'Y' ? 'Y' : 'N');
9819            }
9820        });
9821    };
9822
9823    /**
9824     * 특정 카트 아이템 옵션 삭제
9825     */
9826    const removeCartItemOption = (item_code, optionNo, is_regularly) => {
9827        // 트래킹 데이터 사전 캡처 (success 시 $card 제거되므로)
9828        const $cardBefore = $cart_list_wrap.find(`._cart_option_card[data-item-code="${item_code}"][data-option-no="${optionNo}"]`);
9829        const $rowBefore = $cardBefore.closest(`tr[data-item-code="${item_code}"]`);
9830        const trackingCtx = $cardBefore.length ? {
9831            prodCode:       $rowBefore.attr('data-prod-code') || '',
9832            prodType:       $rowBefore.attr('data-prod-type') || 'normal',
9833            quantityBefore: parseInt($cardBefore.find('._counter_value').val(), 10) || 0,
9834        } : null;
9835        let trackingOptionInfo = null;
9836        if ($cardBefore.length) {
9837            const parts = [];
9838            $cardBefore.find('.opt-name').each(function () {
9839                const text = $(this).parent().text().replace(/\s+/g, ' ').trim();
9840                if (text) parts.push(text);
9841            });
9842            if (parts.length) trackingOptionInfo = parts.join(' / ');
9843        }
9844
9845        $.ajax({
9846            type: 'POST',
9847            data: { item_code, optionNo, is_regularly },
9848            url: '/shop/remove_cart_item_option.cm',
9849            dataType: 'json',
9850            cache: false,
9851            success: ({ msg }) => {
9852                if (msg !== 'SUCCESS') return alert(msg);
9853
9854                const $card = $cart_list_wrap.find(`._cart_option_card[data-item-code="${item_code}"][data-option-no="${optionNo}"]`);
9855                if (!$card.length) return window.location.reload();
9856
9857                const $row = $card.closest(`tr[data-item-code="${item_code}"]`);
9858
9859                // 삭제되는 옵션의 가격을 행 가격에서 차감
9860                const optCount = parseInt($card.find('._counter_value').val(), 10) || 0;
9861                const $optPrice = $card.find('._cart_option_price');
9862                const $optOrigin = $card.find('._cart_option_origin_price');
9863                const discountedUnit = parseInt($optPrice.data('unit-price'), 10) || 0;
9864                const originUnit = $optOrigin.length ? (parseInt($optOrigin.data('unit-price'), 10) || 0) : discountedUnit;
9865                const discountedDelta = discountedUnit * optCount;
9866                const originDelta = originUnit * optCount;
9867
9868                // 가격 갱신 (PC/mobile 헬퍼 통합)
9869                const $pcDefault = $row.find('._cart_price_default');
9870                const $pcOrigin = $row.find('._cart_price_origin');
9871                const newDiscounted = (parseInt($pcDefault.data('discounted-price'), 10) || 0) - discountedDelta;
9872                const newOrigin = (parseInt($pcOrigin.data('origin-price'), 10) || 0) - originDelta;
9873                renderCartRowPrice($row, { basePrice: newDiscounted, originPrice: newOrigin });
9874
9875                // 타이틀 행 아이템 가격(strikethrough 영역) 별도 갱신
9876                $row.find('._cart_item_price').text(LOCALIZE.getCurrencyFormat(newOrigin));
9877
9878                $card.remove();
9879                // 옵션 삭제 후 남은 카드의 option-no를 재할당 (서버 인덱스와 동기화)
9880                $row.find('._cart_option_card').each(function (i) {
9881                    $(this).attr('data-option-no', i);
9882                    $(this).find('._cart_counter').data('optionNo', i).attr('data-option-no', i);
9883                });
9884                // 옵션 카드가 없어도 본품 카운터가 있으면 행 유지
9885                const hasProductCounter = $row.find('._cart_counter[data-mode="product_count"]').length > 0;
9886                if (!$row.find('._cart_option_card').length && !hasProductCounter) $row.remove();
9887                CartItemChanged(is_regularly);
9888                // click_remove_item_cart 트래킹 (옵션 X 버튼: option 단위 삭제)
9889                _trackRemoveItemCart('option', trackingCtx, item_code, trackingOptionInfo);
9890            }
9891        });
9892    };
9893
9894    /**
9895     * 체크박스 전체 선택 제어
9896     * @param bool 
9896b
9897     */
9898    var toggleAllCheckCartItem = function (b, is_regularly) {
9899        $cart_list_wrap.find('._cartItemCheckbox').each(function () {
9900            $(this).prop('checked', (b === true));
9901        });
9902        CartItemChanged(is_regularly);
9903    };
9904
9905    /**
9906     * 체크박스 선택 제어
9907     */
9908    var toggleCheckCartItem = function (is_regularly) {
9909        var $cartItemCheckbox = $cart_list_wrap.find('._cartItemCheckbox');
9910        var $cartItemCheckboxSeleted = $cart_list_wrap.find('._cartItemCheckbox:checked');
9911        $cart_list_wrap.find('._all_check').prop('checked', ($cartItemCheckbox.length == $cartItemCheckboxSeleted.length));
9912        CartItemChanged(is_regularly);
9913    };
9914
9915    /**
9916     * 체크박스 변경시 재계산
9917     */
9918    var CartItemChanged = function (is_regularly) {
9919        // 초기화
9920        selectedCartItem = [];
9921        $cart_list_wrap.find('._cartItemCheckbox:checked').each(function () {
9922            selectedCartItem.push($(this).val());
9923        });
9924        getSelectedPrice(is_regularly);
9925    };
9926
9927    /**
9928     * 전체 선택인지 아닌지 체크
9929     */
9930    var toggleAllSelectCk = function () {
9931        var $cartItemSelectCount = 0;
9932        $cartItemCheckboxList = $shop_cart_list.find("input._cartItemCheckbox");
9933        $cartAllCheckBox = $shop_cart_list.find("input._all_check");
9934        $cartItemCheckboxList.each(function () {
9935            if ($(this).is(":checked") === true) {
9936                $cartItemSelectCount++;
9937            }
9938        });
9939        if ($cartItemCheckboxList.length == $cartItemSelectCount) {
9940            $cartAllCheckBox.prop('checked', true);
9941        } else {
9942            $cartAllCheckBox.prop('checked', false);
9943        }
9944    };
9945
9946    /**
9947     * 카트 전체 주문하기, C3 분기 추가
9948     * @param direct true/false (선택유무와 상관없이 바로 주문)
9949		 * TODO: 실행시 유저가 받을 사은품을 전달받아 페이로드에 담도록 한다.
9950     */
9951    /**
9952     * CB-3985 — 주문 생성 응답으로 머천트 전환/마케팅 픽셀 발화 (단일 소스).
9953     *
9954     * addOrderWithCart 콜백(npay / talkpay onPayOrder / default)의 픽셀 블록을 함수로 추출.
9955     * 레거시 장바구니 주문(바로구매 포함)과 MFE 장바구니(SITE_SHOP_CART.OMS_fireOrderTracking 노출)가
9956     * 동일 코드를 호출해 멱등성을 보장한다 — 픽셀 로직을 MFE 가 재구현하지 않는다.
9957     *
9958     * - 각 SDK 는 머천트 설정에 따라 주입되므로 typeof 가드 필수 (미설정 머천트는 자동 skip).
9959     * - npay 성인인증(auth_type==2)은 레거시와 동일하게 픽셀 미발화.
9960     * - npay 추가배송비 안내 / adult_auth redirect / advanced_kakao_trace 등 부수 흐름은 호출자 책임(범위 밖).
9961     */
9962    var fireOrderTracking = function (type, result) {
9963        // CB-3985 — 픽셀 SDK(머천트 설치)에서 예외가 나도 주문 redirect 등 후속 로직을 끊지 않도록 차단.
9964        try {
9965        if (type === 'npay') {
9966            if (result.auth_type == 2) return;
9967            var npay_order_info = result['npay_order_info'];
9968            if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
9969                try {
9970                    if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
9971                        FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
9972                    } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
9973                        console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
9974                    }
9975                } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
9976            } else {
9977                if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
9978            }
9979            if (typeof FB_PIXEL != 'undefined' && FB_PIXEL) {
9980                if (result.fb_npay_switch === 'Y') FB_PIXEL.addNpayOrder(npay_order_info);
9981            }
9982            if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder();
9983            if (result.google_analytics_type == 'G' && result.is_ga_api_secret === false) {
9984                if (typeof GOOGLE_ANAUYTICS != 'undefined') GOOGLE_ANAUYTICS.addNpayOrder(npay_order_info);
9985            }
9986            if (typeof GOOGLE_ADWORDS_TRACE != 'undefined' && result.google_ads_include_npay === 'Y') GOOGLE_ADWORDS_TRACE.addNpayOrder(npay_order_info);
9987            if (typeof CRITEO != 'undefined') CRITEO.npayTrackTransaction(npay_order_info);
9988            if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
9989            if (typeof TIKTOK_PIXEL != 'undefined') {
9990                TIKTOK_PIXEL.track('InitiateCheckout', {
9991                    event_id: result.order_code,
9992                    contents: result.prod_list.map(prod => ({
9993                        content_id: prod.id,
9994                        content_name: prod.name,
9995                        price: prod.price,
9996                    })),
9997                    content_type: 'product',
9998                    value: result.total_price,
9999                    currency: result.currency,
10000                });
10001            }
10002        } else {
10003            // normal / regularly / talkpay onPayOrder — 일반 주문 픽셀.
10004            if (Object.prototype.hasOwnProperty.call(result, 'meta_compatibility')) {
10005                try {
10006                    if (typeof FB_PIXEL !== 'undefined' && FB_PIXEL && typeof FB_PIXEL.InitiateCheckoutDryRun === 'function') {
10007                        FB_PIXEL.InitiateCheckoutDryRun(result.ic_event_id, result.total_price, result.currency, result.fb_external_id, result.meta_compatibility);
10008                    } else if (typeof console !== 'undefined' && typeof console.info === 'function') {
10009                        console.info('[FS-3585][META_EVENT_DRY_RUN]', {feature_key: 'FS-3585', mode: 'dry_run', provider: 'meta', event_name: 'InitiateCheckout', disposition: 'provider_unsupported'});
10010                    }
10011                } catch (e) { /* 관측 실패도 기존 결제와 타 provider를 계속 진행한다. */ }
10012            } else {
10013                if (typeof FB_PIXEL != 'undefined') FB_PIXEL.InitiateCheckout(result.ic_event_id, result.total_price, result.currency, result.fb_external_id);
10014            }
10015            if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.AddOrder();
10016            if (typeof NP_NDA != 'undefined') NP_NDA.BeginCheckout(result.prod_code_list);
10017            if (typeof TIKTOK_PIXEL != 'undefined') {
10018                TIKTOK_PIXEL.track('InitiateCheckout', {
10019                    event_id: result.order_code,
10020                    contents: result.prod_list.map(prod => ({
10021                        content_id: prod.id,
10022                        content_name: prod.name,
10023                        price: prod.price,
10024                    })),
10025                    content_type: 'product',
10026                    value: result.total_price,
10027                    currency: result.currency,
10028                });
10029            }
10030        }
10031        } catch (e) {
10032            // 픽셀 발화 실패는 무시 (주문 흐름 보호).
10033        }
10034    };
10035
10036    var addOrderWithCart = async function (type, item_code, backurl, direct, is_oms, params, is_gift_buy, skipFreeShipNotifyAlarm){
10037			if (is_cart_changed) return false;
10038			if (add_order_progress_check) return false;
10039			if (currentCartCode == '') {
10040				alert(LOCALIZE.설명_장바구니가비어있습니다());
10041				return false;
10042			}
10043
10044			//type 이 guest_login 으로 넘어오는경우 비회원주문+로그인페이지로이동후주문 방식임 type 을 normal 로처리해야 다른 로직에 영향이 없어 이렇게 처리함
10045			var is_guest_login = false;
10046			if (type == 'guest_login') {
10047				is_guest_login = true;
10048				type = 'normal';
10049			}
10050
10051			var item_code_list = [];
10052			if (item_code == '')
10053				item_code_list = selectedCartItem;
10054			else
10055				item_code_list = [item_code];
10056
10057			if (item_code_list.length <= 0) {
10058				alert(LOCALIZE.설명_주문하실상품을선택해주세요());
10059				return false;
10060			}
10061
10062			// 배송 국가 선택 유효성 검사
10063			if ($global_select && $global_select.children().length > 0 && (!$deliv_country || $deliv_country === 'none')) {
10064				$('._deliv_country_selector').addClass('warning_select');
10065				alert(typeof LOCALIZE.설명_배송받을국가를선택해주세요 === 'function' ? LOCALIZE.설명_배송받을국가를선택해주세요() : '배송받을 국가를 선택해주세요.');
10066				return false;
10067			}
10068
10069			// 무료배송 안내 모달 - 주문 진행 전 사용자에게 노출 (마커가 DOM에 있을 때만)
10070			// 페이지뷰 1회만 노출하기 위해 SDK의 가드를 사용한다. 다른 페이지를 다녀오면 SDK 클로저가 리셋되어 다시 노출됨.
10071			var freeShipNotifySdk = window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK;
10072			if (!skipFreeShipNotifyAlarm && freeShipNotifySdk && !freeShipNotifySdk.hasOrderAlarmShown()
10073				&& ($('#shop_cart_list ._global_select').length === 0 || $deliv_country === 'KR')) {
10074				var $orderAlarmMarker = $('#shop_cart_order_alarm_with_free_ship_notify');
10075				if ($orderAlarmMarker.length > 0) {
10076					var initDelivPriceFlexableKey = parseFloat($orderAlarmMarker.attr('data-init-deliv-price-flexable-key') || '0');
10077					if (initDelivPriceFlexableKey > 0) {
10078						freeShipNotifySdk.markOrderAlarmShown();
10079						_pendingOrderWithCart = {
10080							type: type,
10081							item_code: item_code,
10082							backurl: backurl,
10083							direct: direct,
10084							is_oms: is_oms,
10085							params: params,
10086							is_gift_buy: is_gift_buy
10087						};
10088						$.ajax({
10089							type: 'POST',
10090							url: '/shop/free_ship_notify/get_order_alarm_modal.cm',
10091							data: { init_deliv_price_flexable_key: initDelivPriceFlexableKey },
10092							dataType: 'html',
10093							cache: false,
10094							success: function (html) {
10095								// SITE_SHOP_DETAIL.is_mobile_width와 동일 기준 (window.innerWidth < 768)
10096								var is_mobile_width = (window.innerWidth < 768);
10097								if (is_mobile_width) {
10098									imSheet.close('shop_cart_order_alarm_sheet', function () {
10099										imSheet.open({
10100											id: 'shop_cart_order_alarm_sheet',
10101											html: html,
10102											backdrop: 'rgba(0, 0, 0, 0.15)',
10103											zIndex: 17001
10104										});
10105									});
10106								} else {
10107									if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
10108										$.cocoaDialog.close();
10109									}
10110									$.cocoaDialog.open({
10111										type: 'site_shop_free_ship_notify',
10112										custom_popup: html,
10113										pc_width: 440
10114									});
10115								}
10116							},
10117							error: function () {
10118								// fetch 실패 시 보류 인자 정리하고 모달 없이 주문 진행
10119								_pendingOrderWithCart = null;
10120								addOrderWithCart(type, item_code, backurl, direct, is_oms, params, is_gift_buy, true);
10121							}
10122						});
10123						return false;
10124					}
10125				}
10126			}
10127
10128			if (is_oms) {
10129				const $cartContainer = $shop_cart_list || $('#shop_cart_list');
10130				const $cartTable = $cartContainer.find('._shop_table').first();
10131				const hasDefaultShippingAddress = $cartTable.attr('data-has-default-shipping-address') === 'Y';
10132				let hasFastShipping = false;
10133				for (let i = 0; i < item_code_list.length; i++) {
10134					const code = item_code_list[i];
10135					const $checkbox = $cartContainer.find(`input._cartItemCheckbox[value="${code}"]`).first();
10136					if ($checkbox.length === 0) continue;
10137					if ($checkbox.attr('data-fast-shipping') === 'Y') {
10138						hasFastShipping = true;
10139						break;
10140					}
10141				}
10142				if (hasFastShipping && !hasDefaultShippingAddress) {
10143					alert('기본 배송지가 등록되지 않아 일반배송으로 자동 변경돼요.');
10144				}
10145				if (hasFastShipping && (is_gift_buy || type === 'npay' || type === 'talkpay')) {
10146					if (is_gift_buy) {
10147						alert('일반배송으로 선물하기가 진행돼요');
10148					} else {
10149						alert('선택한 배송은 톡체크아웃·네이버페이와 같은 간편구매에서는 이용할 수 없어요. 구매는 일반배송으로 자동 변경돼요.');
10150					}
10151				}
10152			}
10153
10154			add_order_progress_check = true;
10155
10156			if(is_oms){
10157				if (localStorage.getItem("selectedDeliveryCountry") === "undefined") {
10158					localStorage.removeItem("selectedDeliveryCountry");
10159				}
10160				if (typeof $deliv_country !== 'undefined') {
10161					localStorage.setItem("selectedDeliveryCountry", $deliv_country);
10162				}
10163			}
10164
10165			const [orderBtn, orderRegularlyBtn, orderGiftBtn] = [$('._btn_order'), $('._btn_order_regularly'), $('._btn_order_gift')];
10166			const [orderBtnText, orderRegularlyBtnText, orderGiftBtnText] = [orderBtn.first().text(), orderRegularlyBtn.first().text(), orderGiftBtn.first().text()];
10167
10168			const setSpinner = function ($buttons) {
10169				const spinner = '<div class="loading-spinner-container"><div class="loading-spinner"></div></div>';
10170
10171				if (Array.isArray($buttons)) {
10172					for (const $button of $buttons) {
10173						if ($button.length > 0) {
10174							$button.html(spinner);
10175						}
10176					}
10177				} else {
10178					if ($buttons.length > 0) {
10179						$buttons.html(spinner);
10180					}
10181				}
10182			}
10183
10184			const unsetSpinner = function ($btn, text) {
10185				if ($btn.length > 0) {
10186					$btn.text(text);
10187				}
10188			}
10189
10190			// 간편결제 버튼은 PG사 정책 우려로 인해 스피너 적용하지 않음
10191			if (type !== 'npay' && type !== 'talkpay') {
10192				// 주문하기, 정기구독 버튼에 로딩 스피너 추가
10193				setSpinner([orderBtn, orderRegularlyBtn, orderGiftBtn]);
10194			}
10195
10196			const { freebiesUserSelected, selectedAllFreebiesRequired } = await new Promise((resolve) => {
10197				// MFE 장바구니가 사은품 선택값을 직접 전달한 경우(모바일 바로구매 — 포함형 cart MFE라
10198				// 이벤트 pull 응답자 없음). 이벤트 발송/타임아웃/게이팅 없이 전달분을 그대로 사용한다.
10199				// MFE 측에서 이미 선택형 사은품 충족을 검증하므로 selectedAllFreebiesRequired=true.
10200				if (params && Array.isArray(params.freebie_list)) {
10201					resolve({ freebiesUserSelected: params.freebie_list, selectedAllFreebiesRequired: true });
10202					return;
10203				}
10204				// OMS 주문이 아니거나 정기구독인 경우 사은품 선택 없이 바로 진행
10205				if (!is_oms || type === 'regularly') {
10206					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10207					return;
10208				}
10209
10210				if (!isFreebieMfeMounted()) {
10211					// 사은품 미사용 사이트는 MFE 미적재 → 사은품 선택 없이 바로 진행
10212					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10213					return;
10214				}
10215
10216				// 모바일 장바구니 MFE 는 사은품 선택 UI(freebiesUserSelectedOnCartMobile 이벤트)를
10217				// 사용하지 않는다. 응답자가 없어 아래 dispatch 가 10초 타임아웃까지 대기 → 모바일 바로구매/
10218				// 주문 지연의 원인. cart MFE 활성(magnet-shell[data-mfe="fo-shopping/cart"], cartListMake
10219				// 와 동일 식별) + 모바일이면 사은품 선택 없이 바로 진행한다.
10220				// (카트 셸이 없는 화면에서는 기존 사은품 선택 흐름 그대로 유지)
10221				if (window.IS_MOBILE && $('#shop_cart_list magnet-shell[data-mfe="fo-shopping/cart"]').length > 0) {
10222					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10223					return;
10224				}
10225
10226				// 네이버페이, 톡체크아웃인 경우 안내 후 사은품 없이 진행
10227				if (type === 'npay' || type === 'talkpay') {
10228					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10229					return;
10230				}
10231
10232				// 일반 OMS 주문인 경우 사은품 선택 이벤트 발송
10233				let settled = false;
10234				const timeoutId = setTimeout(() => {
10235					if (settled) return;
10236					settled = true;
10237					// 10000ms 내 MFE 응답 없으면 사은품 없이 주문 진행 (사은품 미사용 사이트 포함)
10238					resolve({ freebiesUserSelected: [], selectedAllFreebiesRequired: true });
10239				}, 10000);
10240
10241				window.document.dispatchEvent(new CustomEvent('@im/fo-prod-detail-freebie:freebiesUserSelectedOnCart' + (window.IS_MOBILE ? 'Mobile' : ''), {
10242					detail: {
10243						// (args: { freebiesUserSelected: Freebie[]; selectedAllFreebiesRequired: boolean }) => void
10244						resolveCallback: (args) => {
10245							if (settled) return;
10246							settled = true;
10247							clearTimeout(timeoutId);
10248
10249							if (args.isReady === false) {
10250								alert('사은품 정보를 불러오는 중입니다. 잠시 후 다시 시도해 주세요.');
10251								unsetSpinner(orderBtn, orderBtnText);
10252								unsetSpinner(orderRegularlyBtn, orderRegularlyBtnText);
10253								unsetSpinner(orderGiftBtn, orderGiftBtnText);
10254								add_order_progress_check = false;
10255								return;
10256							}
10257
10258							resolve(args);
10259						}
10260					}
10261				}))
10262			})
10263
10264			if (!selectedAllFreebiesRequired) {
10265				// 일반 주문에서만 사은품 관련 처리 - 정기구독에서의 사은품은 스팩아웃
10266				unsetSpinner(orderBtn, orderBtnText);
10267				alert('아직 사은품을 선택하지 않았습니다. 사은품을 선택해주세요.');
10268				add_order_progress_check = false;
10269				return false;
10270			}
10271
10272			let shippingTemplateCodeMap = {};
10273			if (is_oms) {
10274				const $cartContainer = $shop_cart_list || $('#shop_cart_list');
10275				const templateMap = {};
10276				for (let i = 0; i < item_code_list.length; i++) {
10277					const code = item_code_list[i];
10278					const $checkbox = $cartContainer.find(`input._cartItemCheckbox[value="${code}"]`).first();
10279					if ($checkbox.length === 0) continue;
10280					const templateCode = $checkbox.attr('data-shipping-template-code');
10281					if (templateCode) {
10282						templateMap[code] = templateCode;
10283					}
10284				}
10285				if (Object.keys(templateMap).length > 0) {
10286					shippingTemplateCodeMap = templateMap;
10287				}
10288			}
10289
10290			const url = is_oms ? '/shop/oms/OMS_add_order_cart.cm' : "/shop/add_order_cart.cm";
10291			const headers = new Headers();
10292			headers.append('Content-Type', 'application/x-www-form-urlencoded; charset=UTF-8');
10293			const _landingUrlValue = getBsLandingUrlHeaderValue();
10294			if (_landingUrlValue) {
10295				headers.append('imweb-landing-url', _landingUrlValue);
10296			}
10297			const requestPayload = {
10298				'type': type,
10299				'cart_code': currentCartCode,
10300				'item_code_list': item_code_list,
10301				'backurl': backurl,
10302				'direct': (direct ? 'Y' : 'N'),
10303				'is_gift_buy': is_gift_buy,
10304				'deliv_country': $deliv_country || '',
10305				freebie_list: (type !== 'npay' && type !== 'talkpay') ? freebiesUserSelected : undefined,
10306				ace_pid: is_oms ?  (window._AcePID || window._AceMID) : undefined,
10307				'infoUrl': is_oms ? location.origin : undefined,
10308			};
10309			if (is_oms && Object.keys(shippingTemplateCodeMap).length > 0) {
10310				requestPayload.shipping_template_code = JSON.stringify(shippingTemplateCodeMap);
10311			}
10312			const body = serializeToUrlencoded(requestPayload);
10313
10314			try {
10315				const response = await fetch(url, { method: 'POST', headers, body });
10316				if (!response.ok) {
10317					throw new Error(`HTTP error! status: ${response.status}`);
10318				}
10319				const result = await response.json();
10320				add_order_progress_check = false;
10321
10322				if (result.msg == 'SUCCESS') {
10323					switch (type) {
10324						case 'npay': {
10325							if (result.npay_url == '') {
10326								if (result.errmsg) {
10327									alert(escape_javascript(result.errmsg));
10328								} else {
10329									alert(LOCALIZE.설명_네이버페이상품구매실패(escape_javascript(result.errmsg)));
10330								}
10331							} else {
10332								if (!!result.shopping_additional_price_msg) {
10333									alert(result.shopping_additional_price_msg);
10334								}
10335								if (result.advanced_kakao_trace_data != null) {
10336									$('body').append(result.advanced_kakao_trace_data);
10337								}
10338								if (result.auth_type == 2) {
10339										// 성인 인증이 필요할 경우
10340										window.location.href = '/?mode=adult_auth_npay&data=' + result.data;
10341								} else {
10342									// 성인 인증이 필요하지 않는 경우
10343									// 네이버 페이 구매시 전환추적 추가
10344									// CB-3985 — 머천트 PG/마케팅 픽셀 발화 (fireOrderTracking 단일 소스). 네이버페이.
10345									fireOrderTracking('npay', result);
10346									window.location.href = result.npay_url;
10347								}
10348							}
10349							break;
10350						}
10351						case 'talkpay': {
10352							switch (params.type) {
10353								case 'onOrder':
10354									if (result.msg == 'SUCCESS') {
10355										if (result.order_sheet_id) {
10356											params.onSuccess(result.order_sheet_id);
10357										} else {
10358											params.onFailure({ message: result.msg });
10359										}
10360									} else {
10361										params.onFailure({ message: result.msg });
10362									}
10363									break;
10364								case 'onPayOrder':
10365									if (result.msg == 'SUCCESS') {
10366											// CB-3985 — 머천트 PG/마케팅 픽셀 발화 (fireOrderTracking 단일 소스). 톡체크아웃 onPayOrder.
10367											fireOrderTracking('talkpay', result);
10368										var shop_payment_url = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code) + '&order_no=' + encodeURIComponent(result.order_no);
10369										if (result.member_code) {
10370											shop_payment_url += "&order_member=" + encodeURIComponent(result.member_code);
10371										}
10372										if ( result.kakaopay_only === 'Y' ) {
10373											shop_payment_url += "&kakaopay_only=Y";
10374										}
10375										window.location.href = shop_payment_url;
10376									}
10377									break;
10378							};
10379							break;
10380						}
10381						default: {
10382							// CB-3985 — 머천트 PG/마케팅 픽셀 발화 (fireOrderTracking 단일 소스). 일반/정기 주문.
10383							fireOrderTracking(type, result);
10384							if (is_guest_login) {	//비회원주문시 로그인 페이지로 이동처리
10385								window.location.href = '/login?shopping_order_code=' + result.order_code + '&back_url=' + encodeURIComponent(result.back_url_base64);
10386							}
10387							else {	//일반주문인경우 결제화면으로 이동
10388								if (is_oms) {
10389									if (typeof result.c3_result === "undefined" || typeof result.c3_result.orderCode === "undefined" || typeof result.c3_result.orderNo === "undefined") return
10390									var shop_payment_url = "/shop_payment/?order_code=" + encodeURIComponent(result.c3_result.orderCode) + "&order_no=" + encodeURIComponent(result.c3_result.orderNo);
10391
10392									if (result.c3_result.memberCode) {
10393										shop_payment_url += "&order_member=" + encodeURIComponent(result.c3_result.memberCode);
10394									}
10395									window.location.href = shop_payment_url;
10396								} else {
10397									window.location.href = "/shop_payment/?order_code=" + encodeURIComponent(result.order_code);
10398								}
10399							}
10400						}
10401					}
10402				} else {
10403					// 주문하기 버튼에 로딩 스피너 제거
10404					unsetSpinner(orderBtn, orderBtnText);
10405					// 정기구독 버튼에 로딩 스피너 제거
10406					unsetSpinner(orderRegularlyBtn, orderRegularlyBtnText);
10407					// 선물하기 버튼에 로딩 스피너 제거
10408					unsetSpinner(orderGiftBtn, orderGiftBtnText);
10409
10410					if (type == 'talkpay') {
10411						switch (params.type) {
10412							case 'onOrder': {
10413								params.onFailure({ message: result.msg });
10414								break;
10415							}
10416							case 'onPayOrder': {
10417								alert(result.msg);
10418								break;
10419							}
10420						}
10421					} else {
10422						if(result.code === 2){
10423							$('._deliv_country_selector').toggleClass('warning_select');
10424						}
10425						// 코드 7: 비회원 이용권 구매 시 로그인 페이지로
10426						if (result.code === 7) {
10427							redirectToLoginPage();
10428						} else {
10429							alert(result.msg.replace(/\\n/g, '\n'));
10430							// 코드 11: 기존 로직 유지
10431							if (is_guest_login && result.code === 11) {
10432								var back_url_base64 = window.location.pathname;
10433								var url_param = document.location.href.split("?");
10434								if (url_param.length > 1) {
10435									back_url_base64 += "?" + url_param[1];
10436								}
10437								window.location.href = '/login?back_url=' + encodeURIComponent(btoa(back_url_base64));
10438							}
10439						}
10440					}
10441				}
10442
10443			} catch (e) {
10444				add_order_progress_check = false;
10445				// 주문하기 버튼에 로딩 스피너 제거
10446				unsetSpinner(orderBtn, orderBtnText);
10447				// 정기구독 버튼에 로딩 스피너 제거
10448				unsetSpinner(orderRegularlyBtn, orderRegularlyBtnText);
10449				// 선물하기 버튼에 로딩 스피너 제거
10450				unsetSpinner(orderGiftBtn, orderGiftBtnText);
10451			}
10452    };
10453
10454    var addOrderRegularly = function (backurl, params, is_c3 = false){
10455        addOrderWithCart('regularly', '', backurl, false, is_c3, params);
10456    };
10457
10458		const trackClickPurchaseCart = function (paymentButtonType) {
10459			if (typeof BrandScope !== "undefined") {
10460				try {
10461					BrandScope.track('click_purchase_cart', {
10462						'action': 'click',
10463						'content': 'purchase',
10464						'where': 'cart',
10465						'is_single_purchase': false,
10466						'payment_button_type': paymentButtonType,
10467						'is_regularly_prod': false
10468					});
10469				} catch {
10470					// ignore
10471				}
10472			}
10473		}
10474
10475    /**
10476     * 카트 전체 네이버찜등록
10477     */
10478    var addNPayWishWithCart = function (item_code) {
10479        if (currentCartCode == '') {
10480            alert(LOCALIZE.설명_장바구니가비어있습니다());
10481            return false;
10482        }
10483        $.ajax({
10484            type: 'POST',
10485            data: { 'cart_code': currentCartCode, 'item_code': item_code },
10486            url: ('/shop/add_npay_wish_cart.cm'),
10487            dataType: 'json',
10488            cache: false,
10489            success: function (result) {
10490                if (result.msg == 'SUCCESS') {
10491                    if (result.npay_url == '')
10492                        alert(LOCALIZE.설명_네이버페이찜등록실패(escape_javascript(result.errmsg)));
10493                    else {
10494                        if (result.mobile == 'Y')
10495                            window.location.href = result.npay_url;
10496                        else
10497                            window.open(result.npay_url);
10498                    }
10499                } else {
10500                    alert(result.msg);
10501                }
10502            }
10503        });
10504    };
10505    /**
10506     * click_change_options_cart 트래킹 (모달 진입 버튼 클릭 즉시)
10507     */
10508    const _trackChangeOptionsCart = (item_code) => {
10509        if (typeof window.BrandScope === 'undefined') return;
10510        const $row = $cart_list_wrap.find(`tr.content[data-item-code="${item_code}"]`);
10511        if (!$row.length) return;
10512        const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
10513        window.BrandScope.track('click_change_options_cart', {
10514            action:         'click',
10515            content:        'change_options',
10516            where:          'cart',
10517            prod_code:      $row.attr('data-prod-code') || '',
10518            prod_type:      $row.attr('data-prod-type') || 'normal',
10519            cart_code:      cartCode,
10520            cart_item_code: item_code,
10521            has_options:    $row.find('._cart_option_card').length > 0,
10522        });
10523    };
10524
10525    /**
10526     * click_complete_change_options 트래킹 (서버 응답 200 + cart 재렌더링 직후)
10527     * cartListMake 가 비동기라 발사 시점엔 cart row 가 변경 전 옵션 카드 보존 →
10528     * dt.options(변경 후) 와 row(변경 전) 직접 비교로 added/removed/qty_changed 산출
10529     * 옵션 라인 키 = 라인 내 value_name 들 sorted-join (DOM·API 응답 양쪽 동일 형식)
10530     */
10531    const _trackCompleteChangeOptions = (dt) => {
10532        if (typeof window.BrandScope === 'undefined') return;
10533        const $row = $cart_list_wrap.find(`tr.content[data-item-code="${dt.item_code}"]`);
10534        if (!$row.length) return;
10535
10536        const oldOptions = $row.find('._cart_option_card').toArray().map(card => ({
10537            key:   $(card).find('.opt-value').toArray().map(el => el.textContent.trim()).sort().join('|'),
10538            count: parseInt($(card).find('._counter_value').val(), 10) || 0,
10539        }));
10540        const newOptions = (dt.options || []).map(sel => ({
10541            key:   (sel.options || []).map(o => o.value_name).sort().join('|'),
10542            count: parseInt(sel.count, 10) || 0,
10543        }));
10544
10545        const oldByKey = new Map(oldOptions.map(o => [o.key, o.count]));
10546        const newByKey = new Map(newOptions.map(o => [o.key, o.count]));
10547        let added = 0, removed = 0, qtyChanged = 0;
10548        new Set([...oldByKey.keys(), ...newByKey.keys()]).forEac
10548h(k => {
10549            if (!oldByKey.has(k)) added++;
10550            else if (!newByKey.has(k)) removed++;
10551            else if (oldByKey.get(k) !== newByKey.get(k)) qtyChanged++;
10552        });
10553
10554        const sum = (arr) => arr.reduce((s, o) => s + o.count, 0);
10555        const oldQty = oldOptions.length ? sum(oldOptions)
10556            : parseInt($row.find('._cart_counter[data-mode="product_count"] ._counter_value').val(), 10) || 0;
10557        const newQty = newOptions.length ? sum(newOptions)
10558            : parseInt(dt.order_count, 10) || 0;
10559
10560        const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
10561        window.BrandScope.track('click_complete_change_options', {
10562            action:                        'click',
10563            content:                       'complete',
10564            where:                         'change_options',
10565            prod_code:                     $row.attr('data-prod-code') || '',
10566            prod_type:                     $row.attr('data-prod-type') || 'normal',
10567            cart_code:                     cartCode,
10568            cart_item_code:                dt.item_code,
10569            option_added_count:            added,
10570            option_removed_count:          removed,
10571            option_quantity_changed_count: qtyChanged,
10572            quantity_unit:                 'product',
10573            quantity_before:               oldQty,
10574            quantity_after:                newQty,
10575        });
10576    };
10577
10578    /**
10579     * 장바구니 옵션변경 화면 표시
10580     */
10581    var showChangeCartItem = function (item_code, is_regularly) {
10582        _trackChangeOptionsCart(item_code);
10583        $.ajax({
10584            type: 'POST',
10585            data: { 'item_code': item_code, 'is_regularly': is_regularly },
10586            url: ('/shop/change_cart_item_request.cm'),
10587            dataType: 'json',
10588            cache: false,
10589            success: function (result) {
10590                if (result.msg == 'SUCCESS') {
10591                    $changeCartItemLayer.find('div.container-fluid').html(result.html);
10592                    $changeCartItemLayer.show();
10593
10594                    SITE_SHOP_DETAIL.initDetail({
10595                        prod_idx: result.prodIdx,
10596                        prod_price: result.price,
10597                        require_option_count: result.require_option_count,
10598						use_image_optimizer_on_product_detail_markup: false,
10599                        shop_use_full_load: false,
10600                    });
10601
10602                    SITE_SHOP_DETAIL.initProdStock(result.prod_stock.stock_use, result.prod_stock.stock_no_option, result.prod_stock.stock_unlimit);
10603
10604                    // 수량 제한 설정 - AJAX 성공 직후 바로 호출 (옵션 복원 전)
10605                    if (result.max_prod_quantity !== undefined) {
10606                        SITE_SHOP_DETAIL.setMaxPurchaseQuantity(
10607                            result.max_prod_quantity,
10608                            result.max_member_quantity,
10609                            result.maximum_purchase_quantity_type,
10610                            result.optional_limit,
10611                            result.optional_limit_type,
10612                            result.prod_name
10613                        );
10614                    }
10615
10616                    SITE_SHOP_CART.changeCartLoadOption(result.prodIdx);
10617                    var selected_option_count = result.selected_option_list.length;
10618                    var cnt = 0;
10619                    currentChangeCartItemCode = item_code;
10620                    currentCartItemMaxQuantity = result.maximum_purchase_quantity || 0;
10621                    currentCartItemMaxQuantityType = result.maximum_purchase_quantity_type || 'order';
10622                    $.each(result.selected_option_list, function (no, data) {
10623                        SITE_SHOP_DETAIL.selectOption(
10624                            result.prodIdx,
10625                            {
10626                                options: data.options,
10627                                require: data.require,
10628                                count: data.count,
10629                                idx: data.idx,
10630                                skip_quantity_validation: true
10631                            },
10632                            function () {
10633                                cnt++;
10634                                if (cnt >= selected_option_count){
10635                                    SITE_SHOP_DETAIL.updateSelectedOptions('cart');
10636                                }
10637                            },
10638                            function () {
10639                                cnt++;
10640                                if (cnt >= selected_option_count) {
10641                                    SITE_SHOP_DETAIL.updateSelectedOptions('cart');
10642                                }
10643                            }
10644                        );
10645                    });
10646                    if (result.require_option_count == 0) {
10647                        SITE_SHOP_CART.changeCartOrderCount("pc", result.count, false);
10648                        SITE_SHOP_CART.changeCartOrderCount("mobile", result.count, false);
10649                    }
10650                } else {
10651                    alert(result.msg);
10652                }
10653            }
10654        });
10655    };
10656    var hideChangeCartItem = function () {
10657        $changeCartItemLayer.find('div.container-fluid').empty();
10658        $changeCartItemLayer.hide();
10659        currentChangeCartItemCode = '';
10660    };
10661    /**
10662     * 장바구니 옵션 변경 완료 처리
10663     */
10664    var changeCartItemComplete = function (current_ul, is_c3 = false) {
10665        var is_regularly = 'N';
10666        if (cart_type == SHOP_CONST.CART_TYPE_REGULARLY) {
10667            is_regularly = 'Y';
10668        }
10669        if (currentChangeCartItemCode != '') {
10670            // 프론트엔드 수량 검증 (옵션별 최대 구매수량)
10671            var validation_result = SITE_SHOP_DETAIL.validateMaxPurchaseQuantity();
10672            if (validation_result !== true) {
10673                alert(validation_result);
10674                return;
10675            }
10676            changeCartItemData({
10677                "mode": "detail",
10678                "item_code": currentChangeCartItemCode,
10679                "options": SITE_SHOP_DETAIL.getSelectedOption(),
10680                "order_count": SITE_SHOP_DETAIL.getOrderCount(),
10681                "is_regularly": is_regularly,
10682                "current_ul": current_ul
10683            }, is_c3);
10684        }
10685    };
10686
10687    var changeCartItemDelivType = function (item_code, deliv_type, is_regularly, is_c3 = false) {
10688        changeCartItemData({
10689            "mode": "delivery",
10690            "item_code": item_code,
10691            "deliv_type": deliv_type,
10692            "is_regularly": is_regularly
10693        }, is_c3);
10694    };
10695
10696    var changeCartItemDelivPayType = function (item_code, deliv_pay_type, is_regularly, is_c3 = false) {
10697        changeCartItemData({
10698            "mode": "delivery",
10699            "item_code": item_code,
10700            "deliv_pay_type": deliv_pay_type,
10701            "is_regularly": is_regularly
10702        }, is_c3);
10703    };
10704
10705    var changeCartItemData = function (dt, is_c3 = false) {
10706        if (typeof dt['item_code'] == 'undefined') return false;
10707        $.ajax({
10708            type: 'POST',
10709            data: dt,
10710			url : is_c3 ? '/shop/oms/OMS_change_cart_item.cm' : '/shop/change_cart_item.cm',
10711            dataType: 'json',
10712            cache: false,
10713            success: function (result) {
10714                if (result.msg == 'SUCCESS') {
10715                    if (typeof AW_INOUT != 'undefined') {
10716                        AW_INOUT(result.prod_no, result.after_count);
10717                    }
10718                    if (typeof AM_INOUT !== 'undefined') {
10719                        AM_INOUT(result.prod_no, result.after_count);
10720                    }
10721                    if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
10722                    var cart_type = SHOP_CONST.CART_TYPE_NORMAL;
10723                    if (dt['is_regularly'] == 'Y') {
10724                        cart_type = SHOP_CONST.CART_TYPE_REGULARLY;
10725                    }
10726                    $changeCartItemLayer.hide();
10727                    cartListMake(dt['current_url'], cart_type, is_c3);
10728                    if (dt['mode'] === 'detail') _trackCompleteChangeOptions(dt);
10729                } else {
10730                    alert(result.msg);
10731                }
10732            }
10733        });
10734    };
10735
10736    var countryCodeChange = function (country) {
10737			$deliv_country = country;
10738        $.ajax({
10739            type: 'POST',
10740            data: { 'country': country },
10741            url: ('/shop/country_code_change.cm'),
10742            dataType: 'json',
10743            cache: false,
10744            success: function (result) {
10745                if (result.msg != "SUCCESS") {
10746                    alert(result.msg);
10747                } else {
10748                    window.location.reload();
10749                }
10750            }
10751        });
10752    };
10753
10754    // ───── CB-3865: 사은품 MFE 에 cartChange payload(cart raw) push ───────────────
10755    // freebie MFE 가 자체 OMS endpoint 호출 없이 사은품 결정하도록 cart raw 를 push.
10756    // OMS_get_cart_list_make 응답의 cart 를 closure 캐시 → mutation 시 count 부분 patch.
10757    // raw → orderItems 변환은 freebie 측 책임 (single source of truth — apply-promotion 입력).
10758    // BE 변경 0. dedupe: 동일 raw 면 dispatch skip (React Query 의 새 queryKey 회피).
10759    var _cartRawItems = [];
10760    var _lastDispatchedCartSig = '';
10761
10762    var dispatchFreebieCartChange = function () {
10763        var sig = JSON.stringify(_cartRawItems);
10764        if (sig === _lastDispatchedCartSig) return;
10765        _lastDispatchedCartSig = sig;
10766        window.document.dispatchEvent(new CustomEvent(
10767            '@im/fo-prod-detail-freebie:cartChange',
10768            { detail: { cart: _cartRawItems } }
10769        ));
10770    };
10771
10772    /**
10773     * 장바구니 목록 생성 -  C3 바로구매 분기 추가
10774     * @param current_full_url
10775     * @param type		normal 일반구매, regularly 정기구독
10776     */
10777    var cartListMake = function (current_full_url, type, is_c3 = false) {
10778        selectedCartItem = [];
10779        // MFE 모드인 경우 PHP AJAX 렌더를 스킵 (MFE 셸이 자체적으로 데이터 페치)
10780        // cart 전용 magnet-shell([data-mfe="fo-shopping/cart"]) 직속 자식 존재 여부로 식별 — 같은 페이지의 사은품/프로모션 shell 과 구분
10781        if ($cart_list_wrap && $cart_list_wrap.length && $cart_list_wrap.children('magnet-shell[data-mfe="fo-shopping/cart"]').length) {
10782            cart_type = type;
10783            is_cart_changed = false;
10784            // CB-3816: 호스트 측 cart 변경 (옵션 다이얼로그 확정 등) 후 MFE 에 재조회 요청.
10785            // MFE 가 자체 mutation 으로 트리거한 변경은 이 경로를 타지 않으므로 중복 발행 없음.
10786            // detail.cart_type 으로 변경된 탭을 명시 — MFE 의 현재 활성 탭과 다를 수 있어
10787            // (탭 전환 race) 정확한 query key 를 invalidate 하기 위해 발행자가 알려야 함.
10788            window.dispatchEvent(new CustomEvent('imweb:cart:invalidated', { detail: { cart_type: type } }));
10789            return;
10790        }
10791        $.ajax({
10792            type: 'POST',
10793            data: { 'current_full_url': current_full_url, 'type': type },
10794			url : is_c3 ? '/shop/oms/OMS_get_cart_list_make.cm' : '/shop/get_cart_list_make.cm',
10795            dataType: 'json',
10796            success: function (res) {
10797                if (res.msg == 'SUCCESS') {
10798                    // CB-3865: 페이지 로드 / 항목 추가·삭제·옵션변경 후 cart raw 캐시 갱신.
10799                    // 이후 getSelectedPrice → dispatchFreebieCartChange 로 사은품 MFE push.
10800                    _cartRawItems = res.cart || [];
10801                    $cart_list_wrap.html('');
10802										const range = document.createRange();
10803										// <script type="module"> 태그가 .html() 으로 삽입되면 모듈이 로드되지 않으므로, createContextualFragment를 사용하여 HTML을 삽입
10804                    const table_fragment = range.createContextualFragment(res.table_html);
10805                    $cart_list_wrap[0].appendChild(table_fragment);
10806                    if (res.global_select_html !== '') {
10807                        $global_select.html(res.global_select_html);
10808												$deliv_country = $global_select.find('[selected]').val();
10809                    } else {
10810                        $global_select.remove();
10811                    }
10812                    if (res.cart_item_count > 0) {
10813                        $('.pay-box').show();
10814                        $shop_cart_list.removeClass('cart_empty_wrap');
10815                        fixedShopTableHeader();
10816                        resizeShopTableHeader();
10817                    } else {
10818                        $('.pay-box').hide();
10819                        $shop_cart_list.addClass('cart_empty_wrap');
10820                    }
10821                    cart_type = type;
10822                    if (type === SHOP_CONST.CART_TYPE_REGULARLY) {
10823                        currentCartCode = res.cart_code;
10824                        $shop_cart_list.find('._btn_order').hide();
10825                        $shop_cart_list.find('._btn_order_gift').hide();
10826                        $shop_cart_list.find('._btn_order_regularly').css('display', 'flex');
10827                        $shop_cart_list.find('._social_pay').hide();
10828                        $('#cart_normal_tab').removeClass('active');
10829                        $('#cart_regularly_tab').addClass('active');
10830                    } else {
10831                        currentCartCode = res.cart_code;
10832                        $shop_cart_list.find('._btn_order_regularly').hide();
10833                        $shop_cart_list.find('._btn_order').css('display', 'flex');
10834                        $shop_cart_list.find('._btn_order_gift').css('display', 'flex');
10835                        $shop_cart_list.find('._social_pay').show();
10836                        $('#cart_regularly_tab').removeClass('active');
10837                        $('#cart_normal_tab').addClass('active');
10838                    }
10839                    // 미리보기 모드든 일반 모드든 동일하게 getSelectedPrice 호출 (
10839서버에서 current_full_url로 미리보기 분기).
10840                    getSelectedPrice(type === SHOP_CONST.CART_TYPE_REGULARLY);
10841
10842                    // 매출 상승 도구 미리보기 모드 - 주문/체크박스/수량/삭제 등 후속 동작 차단 + 안내 배너
10843                    if (res.is_preview_mode) {
10844                        applyCartPreviewLock();
10845                    }
10846                } else
10847                    alert(res.msg);
10848
10849                is_cart_changed = false;
10850            },
10851            error: function () {
10852                alert(getLocalizeString('설명_잠시후다시시도해주세요', '', '잠시 후 다시 시도해주세요.'));
10853            }
10854        });
10855    };
10856
10857    /**
10858     * 매출 상승 도구 미리보기 모드 - 카트의 모든 인터랙션(주문/체크/수량/삭제)을 잠그고 안내 배너 표시.
10859     * cartListMake가 응답에 is_preview_mode=true를 받았을 때 호출된다.
10860     * 주문/결제 버튼은 시각적으로는 일반 상태 그대로 노출하고 클릭 동작만 차단한다.
10861     * totals/할인/배송비 등은 서버 측 get_cart_price.cm이 동일한 미리보기 분기로 정상 응답하므로 별도 주입 불필요.
10862     */
10863    var applyCartPreviewLock = function () {
10864        if (!$cart_list_wrap || $cart_list_wrap.length === 0) return;
10865
10866        // 카트 영역 인터랙션 차단
10867        $cart_list_wrap.find('._cartItemCheckbox, ._all_check').prop('disabled', true);
10868        $cart_list_wrap.find('._cart_counter button, ._cart_counter input').prop('disabled', true);
10869        $cart_list_wrap.css('pointer-events', 'none');
10870
10871        // 결제/주문 버튼: 노출은 그대로 두고 클릭 동작만 차단 (capture 단계에서 가로채 inline onclick까지 무력화)
10872        $shop_cart_list.find('._btn_order, ._btn_order_regularly, ._btn_order_gift, ._social_pay').each(function () {
10873            var el = this;
10874            el.removeEventListener('click', _previewLockClickHandler, true);
10875            el.addEventListener('click', _previewLockClickHandler, true);
10876        });
10877    };
10878
10879    var _previewLockClickHandler = function (e) {
10880        e.preventDefault();
10881        e.stopImmediatePropagation();
10882        return false;
10883    };
10884
10885    var fixedShopTableHeader = function () {
10886        var window_width = window.innerWidth;
10887        if (window_width < 768) {
10888            var section_fixed_height = getMobileSectionFixedHeight();
10889            var $cart_list_thead = $cart_list_wrap.find('._shop_table thead');
10890            $cart_list_thead.css('top', section_fixed_height + 'px');
10891        }
10892    };
10893
10894    var getMobileSectionFixedHeight = function () {
10895        /* 기본 top을 0px로 설정 시 1px 가량의 여백이 생겨 뒤가 비쳐보이는 경우가 있어 -1px로 설정 */
10896        var section_fixed_height = -1;
10897        var $fixed_header_disable = $('#inline_header_mobile').find('._fixed_header_section');
10898        for (var i = 0; i < $fixed_header_disable.length; i++) {
10899            var target = $fixed_header_disable[i].getBoundingClientRect();
10900            section_fixed_height += target.height;
10901        }
10902        var prev_fixed_section = $shop_cart_list.parents('div[doz_type="section"]').prevAll('._fixed_section[doz_mobile_hide="N"]')[0];
10903        if (typeof prev_fixed_section !== 'undefined') {
10904            section_fixed_height += prev_fixed_section.getBoundingClientRect().height;
10905        }
10906        return section_fixed_height;
10907    };
10908
10909    var resizeShopTableHeader = function () {
10910        var is_resizing = false;
10911        $(window).resize(function () {
10912            if (!is_resizing) {
10913                is_resizing = true;
10914                setTimeout(function () {
10915                    fixedShopTableHeader();
10916                    is_resizing = false;
10917                }, 100);
10918            }
10919        });
10920    };
10921
10922    var setTimesale = function () {
10923        var $doz_timesale_wrap = $shop_cart_wish_list.find('._doz_timesale_wrap');
10924        if ($doz_timesale_wrap.length > 0) {
10925            $doz_timesale_wrap.each(function () {
10926                var $that = $(this);
10927                var start_time = ($that.find('._doz_timesale').attr('data-start-time') * 1000);
10928                var $doz_timesale = $that.find('._doz_timesale');
10929                var timesale_interval = setInterval(function () {
10930                    var remain_ms = ($doz_timesale.attr('data-end-time') * 1000) - start_time;
10931                    if (remain_ms > 0) {
10932                        var remain_d = Math.floor(remain_ms / 86400000);
10933                        var remain_h = Math.floor((remain_ms % 86400000) / 3600000);
10934                        var remain_m = Math.floor((remain_ms % 3600000) / 60000);
10935                        var remain_s = Math.floor((remain_ms % 60000) / 1000);
10936
10937                        var remain_hh = remain_h < 10 ? '0' + remain_h : '' + remain_h;
10938                        var remain_mm = remain_m < 10 ? '0' + remain_m : '' + remain_m;
10939                        var remain_ss = remain_s < 10 ? '0' + remain_s : '' + remain_s;
10940                        if (remain_d >= 1) {
10941                            $doz_timesale.text(getLocalizeString('설명_종료까지n1일n2시n3분n4초남음', [remain_d, remain_hh, remain_mm, remain_ss], '종료까지 %1일 %2:%3:%4'));
10942                        } else {
10943                            $doz_timesale.text(getLocalizeString('설명_종류까지n1시n2분n3초남음', [remain_hh, remain_mm, remain_ss], '종료까지 %1:%2:%3 남음'));
10944                        }
10945                        start_time = start_time + 1000;
10946                    } else {
10947                        /* 타임세일 종료 */
10948                        clearInterval(timesale_interval);
10949                        $that.remove();
10950                    }
10951                }, 1000);
10952            });
10953        }
10954    };
10955
10956    // BO sales tool 미리보기 트리거 수신: URL에 sales_tool_preview 파라미터가 있을 때만 동작
10957    var salesToolPreviewParams = (function () {
10958        try {
10959            return new URLSearchParams(window.location.search);
10960        } catch (e) {
10961            return null;
10962        }
10963    })();
10964    var isInSalesToolPreview = salesToolPreviewParams ? salesToolPreviewParams.has('sales_tool_preview') : false;
10965
10966    function isAllowedSalesToolPreviewOrigin(origin) {
10967        if (origin === window.location.origin) return true;
10968        if (/(^|\.)imtest\.me$/.test(window.location.hostname) && origin === 'https://localhost:5173') return true;
10969        return /^https:\/\/[a-z0-9-]+\.crm\.(imweb|imtest)\.me$/.test(origin);
10970    }
10971
10972    /**
10973     * 미리보기 전용: 주문 알림 모달(get_order_alarm_modal.cm)을 유효성 검사 없이 즉시 표시.
10974     * 운영 흐름의 addOrderWithCart는 다수 검증을 거친 뒤에야 모달을 띄우는데, 미리보기에서는 검증을 모두 건너뛰고 모달만 노출한다.
10975     */
10976    var showOrderAlarmModalForPreview = function () {
10977        var $orderAlarmMarker = $('#shop_cart_order_alarm_with_free_ship_notify');
10978        if ($orderAlarmMarker.length === 0) return;
10979        var initDelivPriceFlexableKey = parseFloat($orderAlarmMarker.attr('data-init-deliv-price-flexable-key') || '0');
10980        if (initDelivPriceFlexableKey <= 0) return;
10981
10982        $.ajax({
10983            type: 'POST',
10984            url: '/shop/free_ship_notify/get_order_alarm_modal.cm',
10985            data: { init_deliv_price_flexable_key: initDelivPriceFlexableKey },
10986            dataType: 'html',
10987            cache: false,
10988            success: function (html) {
10989                var is_mobile_width = (window.innerWidth < 768);
10990                if (is_mobile_width) {
10991                    imSheet.close('shop_cart_order_alarm_sheet', function () {
10992                        imSheet.open({
10993                            id: 'shop_cart_order_alarm_sheet',
10994                            html: html,
10995                            backdrop: 'rgba(0, 0, 0, 0.15)',
10996                            zIndex: 17001
10997                        });
10998                    });
10999                } else {
11000                    if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
11001                        $.cocoaDialog.close();
11002                    }
11003                    $.cocoaDialog.open({
11004                        type: 'site_shop_free_ship_notify',
11005                        custom_popup: html,
11006                        pc_width: 440
11007                    });
11008                }
11009            }
11010        });
11011    };
11012
11013    /**
11014     * 미리보기 전용: 표시 중인 주문 알림 모달을 닫는다 (cocoaDialog/imSheet 양쪽 idempotent).
11015     */
11016    var hideOrderAlarmModalForPreview = function () {
11017        if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
11018            $.cocoaDialog.close();
11019        }
11020        if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
11021            imSheet.close('shop_cart_order_alarm_sheet');
11022        }
11023    };
11024
11025    /**
11026     * 미리보기 전용: 장바구니 페이지의 magnet-shell(cart.sub의 #foFreeShipNotifyCartPageMagnet) 표시 토글.
11027     * 구매하기 버튼 클릭 후 팝업 스타일에서는 in-page magnet이 중복으로 보이지 않도록 감추고,
11028     * 페이지 최상단 스타일에선 다시 보이도록 한다. wrapper div 단위로 padding까지 함께 사라지도록 부모를 토글.
11029     */
11030    var setCartMagnetVisibilityForPreview = function (visible) {
11031        var $magnet = $('#foFreeShipNotifyCartPageMagnet');
11032        if ($magnet.length === 0) return;
11033        var $wrapper = $magnet.parent();
11034        if (visible) {
11035            $wrapper.show();
11036        } else {
11037            $wrapper.hide();
11038        }
11039    };
11040
11041    if (isInSalesToolPreview) {
11042        window.addEventListener('message', function (event) {
11043            if (!isAllowedSalesToolPreviewOrigin(event.origin)) return;
11044            var data = event.data;
11045            if (!data || typeof data !== 'object') return;
11046
11047            if (data.type === 'sales-tool-preview:show-order-alarm') {
11048                // 팝업 스타일이므로 in-page magnet은 감추고 모달만 노출
11049                setCartMagnetVisibilityForPreview(false);
11050                showOrderAlarmModalForPreview();
11051                return;
11052            }
11053            if (data.type === 'sales-tool-preview:hide-order-alarm') {
11054                // 페이지 최상단 스타일로 복귀 — magnet 다시 보이도록
11055                hideOrderAlarmModalForPreview();
11056                setCartMagnetVisibilityForPreview(true);
11057                return;
11058            }
11059            if (data.type === 'sales-tool-preview:free-ship-notify-settings'
11060                || data.type === 'sales-tool-preview:settings') {
11061                var settingsPayload = data.payload || {};
11062                window.dispatchEvent(new CustomEvent('imweb:freeShipNotify:settings:update', {
11063                    detail: settingsPayload
11064                }));
11065            }
11066        });
11067    }
11068
11069    // '장바구니에 담았습니다' 확인 모달을 document.body 에 1회 생성해 노출/재사용
11070    // (정적 마크업은 상세 템플릿 전용이라 다른 페이지엔 없음). 계속쇼핑=닫기 / 장바구니가기=/shop_cart.
11071    var showAddCartAnywhereAlarm = function () {
11072        var MODAL_ID = 'shop_cart_add_to_cart_anywhere_alarm';
11073
11074        var existing = document.getElementById(MODAL_ID);
11075        if (existing) {
11076            existing.style.display = 'block';
11077            return;
11078        }
11079
11080        // DOM API 로 구성 (textContent 만 사용, XSS 무관). 스타일은 전역 custom.sub 의 modal_site_alert 재사용.
11081        var el = function (tag, className) {
11082            var node = document.createElement(tag);
11083            if (className) node.className = className;
11084            return node;
11085        };
11086
11087        var message = el('p', 'text-center');
11088        message.style.marginBottom = '0';
11089        message.textContent = getLocalizeString('설명_선택하신상품을장바구니에담았습니다', '', '선택하신 상품을 장바구니에 담았습니다.');
11090        var container = el('div', 'container-fluid');
11091        container.appendChild(message);
11092
11093        var continueBtn = el('a', 'btn');
11094        continueBtn.setAttribute('href', 'javascript:');
11095        continueBtn.textContent = getLocalizeString('버튼_계속쇼핑', '', '계속 쇼핑');
11096        continueBtn.addEventListener('click', function () {
11097            try {
11098                SITE_SHOP_DETAIL.trackAddToCartPopupAction('click_continue_shopping_add_to_cart_popup', 'continue_shopping', { isFreeShippingUpsell: false });
11099            } catch (e) {}
11100            var m = document.getElementById(MODAL_ID);
11101            if (m) m.style.display = 'none';
11102        });
11103
11104        var goCartBtn = el('a', 'btn right');
11105        goCartBtn.setAttribute('href', 'javascript:');
11106        goCartBtn.textContent = getLocalizeString('버튼_장바구니가기', '', '장바구니 가기');
11107        goCartBtn.addEventListener('click', function () {
11108            try {
11109                SITE_SHOP_DETAIL.trackAddToCartPopupAction('click_go_cart_add_to_cart_popup', 'go_cart', { isFreeShippingUpsell: false });
11110            } catch (e) {}
11111            window.location.href = '/shop_cart';
11112        });
11113
11114        var buttons = el('div', 'btn-group-justified');
11115        buttons.appendChild(continueBtn);
11116        buttons.appendChild(goCartBtn);
11117
11118        var layer = el('div', 'layer_pop');
11119        layer.appendChild(container);
11120        layer.appendChild(buttons);
11121        var content = el('div', 'modal-content');
11122        content.appendChild(layer);
11123        var dialog = el('div', 'modal-dialog');
11124        dialog.appendChild(content);
11125
11126        var modal = el('div', 'modal in modal_site_alert');
11127        modal.id = MODAL_ID;
11128        modal.setAttribute('tabindex', '-1');
11129        modal.setAttribute('role', 'dialog');
11130        modal.appendChild(dialog);
11131
11132        document.body.appendChild(modal);
11133        modal.style.display = 'block';
11134    };
11135
11136    // 외부 위젯(이프두 SUGGEST 등)이 SITE_SHOP_CART.addCartAnywhere(idx) 로 호출하는 공개 진입점.
11137    // 코드베이스 내 호출부가 없어도 외부에서 사용하므로, 미사용으로 오인해 제거하지 말 것.
11138    var addCartAnywhere = function (idx) {
11139        var pid = parseInt(idx, 10);
11140        if (!pid || pid <= 0) return;
11141        if (typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK === 'undefined'
11142            || typeof window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.selectAdditionalProductByIdx !== 'function') {
11143            return;
11144        }
11145
11146        // 담기 전 상품 존재 확인 → 없으면 안내 후 중단 (SDK 는 무수정)
11147        $.ajax({
11148            type: 'POST',
11149            url: '/shop/free_ship_notify/get_additional_product_info.cm',
11150            data: { 'product_idx': pid },
11151            dataType: 'json',
11152            cache: false,
11153            timeout: 10000,
11154            success: function (result) {
11155                if (!result || result.msg !== 'SUCCESS' || !result.product_info) {
11156                    alert(getLocalizeString('설명_상품이존재하지않습니다', '', '존재하지 않는 상품입니다.'));
11157                    return;
11158                }
11159
11160                // 확인 모달은 콜백이 아닌 담기 완료 이벤트 기준 + idx 일치 스코프
11161                // (옵션 상품은 콜백이 '옵션 모달 표시' 시점에 오고, 이벤트는 타 담기 흐름에서도 발화하므로).
11162                var ADDED_EVENT = 'imweb:freeShipNotify:additionalProductAdded';
11163                var cleanup;
11164                var onAdded = function (e) {
11165                    var addedIdx = (e && e.detail) ? parseInt(e.detail.prodIdx, 10) : 0;
11166                    if (addedIdx !== pid) return;
11167                    cleanup();
11168                    showAddCartAnywhereAlarm();
11169                };
11170                var timer = setTimeout(function () { cleanup(); }, 5 * 60 * 1000);
11171                cleanup = function () {
11172                    window.removeEventListener(ADDED_EVENT, onAdded);
11173                    clearTimeout(timer);
11174                };
11175                window.addEventListener(ADDED_EVENT, onAdded);
11176
11177                try {
11178                    window.IMWEB_SHOP_FREE_SHIP_NOTIFY_SDK.selectAdditionalProductByIdx(pid, 'cart', function (success, message) {
11179                        if (!success) {
11180                            cleanup();
11181                            if (message) alert(message);
11182                        }
11183                    });
11184                } catch (e) {
11185                    cleanup();
11186                }
11187            },
11188            error: function () {
11189                alert(getLocalizeString('설명_오류가발생했습니다', '', '오류가 발생했습니다.'));
11190            }
11191        });
11192    };
11193
11194    return {
11195        initCart: function (cart_code, backurl, is_regularly) {
11196            initCart(cart_code, backurl, is_regularly);
11197        },
11198        loadProdWishList: function () {
11199            loadProdWishList();
11200        },
11201        deleteProdWish: function (cart_item, is_regularly) {
11202            deleteProdWish([cart_item], is_regularly);
11203        },
11204        deleteProdWishByProdCode: function (prod_code) {
11205            deleteProdWishByProdCode(prod_code);
11206        },
11207        addProdWish: function (cart_item, is_regularly) {
11208            addProdWish([cart_item], is_regularly);
11209        },
11210        addProdWishMulti: function (is_regularly) {
11211            if (selectedCartItem.length == 0) {
11212                alert(LOCALIZE.설명_선택한항목이없습니다());
11213                return false;
11214            }
11215            addProdWish(selectedCartItem, is_regularly);
11216        },
11217        removeCartItemOption: function (item_code, optionNo, is_regularly) {
11218            removeCartItemOption(item_code, optionNo, is_regularly);
11219        },
11220        removeCartItem: function (item_code, is_regularly) {
11221            removeCartItem([item_code], is_regularly);
11222        },
11223        addCart: function (prodIdx, optionsOrCallback, orderCount, callback, requestOptions) {
11224            addCart(prodIdx, optionsOrCallback, orderCount, callback, requestOptions);
11225        },
11226        addCartAnywhere: function (idx) {
11227            addCartAnywhere(idx);
11228        },
11229        removeSelectedCartItem: function (is_regularly) {
11230            if (selectedCartItem.length == 0) {
11231                alert(LOCALIZE.설명_선택한상품이없습니다());
11232                return false;
11233            }
11234            removeCartItem(selectedCartItem, is_regularly);
11235        },
11236        removeSelectedCartSoldOutItem: function (is_regularly) {
11237            if (selectedCartItem.length == 0) {
11238                alert(LOCALIZE.설명_선택한상품이없습니다());
11239                return false;
11240            }
11241            removeSelectedCartSoldOutItem(selectedCartItem, is_regularly);
11242        },
11243        "toggleCheckCartItem": function (is_regularly) {
11244            toggleCheckCartItem(is_regularly);
11245        },
11246        "toggleAllCheckCartItem": function (b, is_regularly) {
11247            toggleAllCheckCartItem(b, is_regularly);
11248        },
11249        addOrderWithCart: function (type, item_code, backurl, params) {
11250            addOrderWithCart(type, item_code, backurl, false, false, params);
11251        },
11252        addOrderWithCartDirect: function (type, item_code, backurl) {
11253            addOrderWithCart(type, item_code, backurl, true, false);
11254        },
11255        /**
11256         * 무료배송 안내 모달의 "주문하기" 버튼에서 호출.
11257         * 보류된 addOrderWithCart 인자를 사용해 skipFreeShipNotifyAlarm=true로 재호출하여 원래 주문 흐름 진행.
11258         * 모달이 떠있는 동안 매그넷에서 추가상품을 담아 카트가 갱신된 경우, cartListMake가 selectedCartItem을
11259         * 빈 배열로 리셋한 상태이므로 현재 체크된 카트 아이템을 재수집해야 신규 아이템도 주문에 포함된다.
11260         */
11261        proceedOrderAfterFreeShipAlarm: function () {
11262            if (!_pendingOrderWithCart) return;
11263            var a = _pendingOrderWithCart;
11264            _pendingOrderWithCart = null;
11265            // 모달/시트 양쪽 모두 닫기 (어느 쪽이 떴든 idempotent)
11266            if (typeof $.cocoaDialog === 'object' && typeof $.cocoaDialog.close === 'function') {
11267                $.cocoaDialog.close();
11268            }
11269            if (typeof imSheet !== 'undefined' && typeof imSheet.close === 'function') {
11270                imSheet.close('shop_cart_order_alarm_sheet');
11271            }
11272
11273            // 원래 item_code가 비어있을 때(전체 선택 주문)는 모달에서 추가된 아이템도 포함되도록 selectedCartItem 재구성.
11274            // 특정 item_code 직접 주문(addOrderWithCartDirect 등)은 원래 의도 보존을 위해 건드리지 않는다.
11275            if (!a.item_code && $cart_list_wrap && $cart_list_wrap.length > 0) {
11276                selectedCartItem = [];
11277                $cart_list_wrap.find('._cartItemCheckbox:checked').each(function () {
11278                    selectedCartItem.push($(this).val());
11279                });
11280            }
11281
11282            addOrderWithCart(a.type, a.item_code, a.backurl, a.direct, a.is_oms, a.params, a.is_gift_buy, true);
11283        },
11284        addOrderRegularly: function (backurl, params) {
11285            addOrderRegularly(backurl, params, false);
11286        },
11287        trackClickPurchaseCart: function (paymentButtonType) {
11288            trackClickPurchaseCart(paymentButtonType);
11289        },
11290        showChangeCartItem: function (item_code, is_regularly) {
11291            showChangeCartItem(item_code, is_regularly);
11292        },
11293        hideChangeCartItem: function () {
11294            hideChangeCartItem();
11295        },
11296        changeCartSelectRequireOption: function (prod_idx, option_code, value_code, value_name) {
11297            SITE_SHOP_DETAIL.selectRequireOption('cart', prod_idx, option_code, value_code, value_name, function () {
11298                SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11299            });
11300        },
11301        changeCartChangeRequireInputOption: function (prod_idx, option_code, msg) {
11302            SITE_SHOP_DETAIL.changeRequireInputOption('cart', prod_idx, option_code, msg, function () {
11303                SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11304            });
11305        },
11306        changeCartSelectOptionalOption: function (prod_idx, option_code, value_code, value_name) {
11307            SITE_SHOP_DETAIL.selectOptionalOption(prod_idx, option_code, value_code, value_name, function () {
11308                SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11309            });
11310        },
11311        changeCartItemIncrease: function (optNo, reload) {
11312            SITE_SHOP_DETAIL.increaseOptionCount(optNo, 'cart');
11313        },
11314        changeCartItemDecrease: function (optNo) {
11315            SITE_SHOP_DETAIL.decreaseOptionCount(optNo, 'cart');
11316        },
11317        changeCartIncrease: function (type) {
11318            var o = $changeCartItemLayer.find('input._order_count_' + type);
11319            var curCount = o.val();
11320            if (isNaN(curCount))
11321                curCount = 1;
11322            else
11323                curCount = parseInt(curCount) + 1;
11324
11325            var res = SITE_SHOP_DETAIL.checkProdStock(curCount);
11326            if (res === true) {
11327                // 최대 구매수량 검증 추가 (optional_option 타입 제외)
11328                var max_quantity_check = SITE_SHOP_DETAIL.checkMaxPurchaseQuantityForOrderCount(curCount);
11329                if (max_quantity_check !== true) {
11330                    alert(max_quantity_check);
11331                    return;
11332                }
11333                o.val(curCount);
11334                SITE_SHOP_DETAIL.setOrderCount(curCount);
11335            } else {
11336                alert(res);
11337            }
11338
11339            SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11340        },
11341        changeCartDecrease: function (type) {
11342            var o = $changeCartItemLayer.find('input._order_count_' + type);
11343            var curCount = o.val();
11344            if (isNaN(curCount))
11345                curCount = 1;
11346            else
11347                curCount = parseInt(curCount) - 1;
11348            if (curCount < 1) curCount = 1;
11349
11350            var res = SITE_SHOP_DETAIL.checkProdStock(curCount);
11351            if (res === true) {
11352                o.val(curCount);
11353                SITE_SHOP_DETAIL.setOrderCount(curCount);
11354            } else {
11355                alert(res);
11356            }
11357
11358            SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11359        },
11360        changeCartOrderCount: function (type, count, is_alert) {
11361            if (is_alert == void 0) is_alert = true;
11362
11363            if (isNaN(count))
11364                count = 1;
11365            else
11366                count = parseInt(count);
11367            if (count < 1) count = 1;
11368
11369            var res = SITE_SHOP_DETAIL.checkProdStock(count);
11370            if (res === true) {
11371                if (is_alert) {
11372                    // 최대 구매수량 검증 추가 (optional_option 타입 제외)
11373                    var max_quantity_check = SITE_SHOP_DETAIL.checkMaxPurchaseQuantityForOrderCount(count);
11374                    if (max_quantity_check !== true) {
11375                        alert(max_quantity_check);
11376                        return;
11377                    }
11378                }
11379                $changeCartItemLayer.find("input._order_count_" + type).val(count);
11380                SITE_SHOP_DETAIL.setOrderCount(count);
11381            } else {
11382                if (is_alert) alert(res);
11383            }
11384
11385            SITE_SHOP_DETAIL.updateSelectedOptions('cart');
11386        },
11387        changeCartItemRemove: function (optNo) {
11388            SITE_SHOP_DETAIL.removeSelectedOption(optNo, 'cart');
11389        },
11390        changeCartItemCount: function (optNo, optCount) {
11391            SITE_SHOP_DETAIL.changeOptionCount(optNo, optCount, 'cart');
11392        },
11393        // 스피너 애니메이션 keyframes 삽입 (1회)
11394        _spinnerStyleInjected: false,
11395        _ensureSpinnerStyle() {
11396            if (this._spinnerStyleInjected) return;
11397            const style = document.createElement('style');
11398            style.textContent = '@keyframes cartBtnSpin{100%{transform:rotate(360deg)}}';
11399            document.head.appendChild(style);
11400            this._spinnerStyleInjected = true;
11401        },
11402        // 수량 변경 AJAX 진행 카운터 (0이 되어야 로딩 해제)
11403        _pendingCount: 0,
11404        _btnOriginalTexts: null,
11405        _setLoading() {
11406            this._pendingCount++;
11407            if (this._pendingCount > 1) return; // 이미 로딩 중
11408            this._ensureSpinnerStyle();
11409            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>';
11410            const $btns = $('.bottom-btn.btn-wrap').find('a.btn');
11411            this._btnOriginalTexts = [];
11412            $btns.each((i, el) => { this._btnOriginalTexts.push($(el).html()); });
11413            $btns.each(function () { $(this).html(spinnerSvg); }
11413).addClass('disabled').css('pointer-events', 'none');
11414            $('._social_pay').css({ 'pointer-events': 'none', 'opacity': '0.4' });
11415        },
11416        _clearLoading() {
11417            this._pendingCount--;
11418            if (this._pendingCount > 0) return; // 아직 진행 중인 요청 있음
11419            this._pendingCount = 0;
11420            const $btns = $('.bottom-btn.btn-wrap').find('a.btn');
11421            if (this._btnOriginalTexts) {
11422                $btns.each((i, el) => {
11423                    if (this._btnOriginalTexts[i]) $(el).html(this._btnOriginalTexts[i]);
11424                });
11425                this._btnOriginalTexts = null;
11426            }
11427            $btns.removeClass('disabled').css('pointer-events', '');
11428            $('._social_pay').css({ 'pointer-events': '', 'opacity': '' });
11429        },
11430        // 키 기반 debounce 유틸리티
11431        _debounceTimers: {},
11432        _debounce(key, fn, delay) {
11433            clearTimeout(this._debounceTimers[key]);
11434            this._debounceTimers[key] = setTimeout(() => {
11435                delete this._debounceTimers[key];
11436                fn();
11437            }, delay);
11438        },
11439        // debounce 전 원래 수량 저장 (복원용)
11440        _originalCounts: {},
11441        // 수량 cell UI 즉시 업데이트 (단일상품만 즉시 반영, 옵션 상품의 품목 수량은 서버 응답으로 갱신)
11442        _updateCartCountCell($row, mode, option_no, count) {
11443            if (mode === 'product_count') {
11444                $row.find('._cart_item_count').text(count);
11445                const $countText = $row.find('._cart_item_count_text');
11446                if ($countText.length) {
11447                    $countText.text($countText.text().replace(/\d+/, count));
11448                }
11449            }
11450            // 옵션/상품 가격 업데이트 (정가 + 할인가)
11451            const updatePriceEl = ($container, priceClass, originPriceClass) => {
11452                const $price = $container.find('.' + priceClass);
11453                const $origin = $container.find('.' + originPriceClass);
11454                if ($price.length) {
11455                    const unitPrice = parseInt($price.data('unit-price'), 10);
11456                    if (unitPrice) $price.text(LOCALIZE.getCurrencyFormat(unitPrice * count));
11457                }
11458                if ($origin.length) {
11459                    const unitOrigin = parseInt($origin.data('unit-price'), 10);
11460                    if (unitOrigin) $origin.text(LOCALIZE.getCurrencyFormat(unitOrigin * count));
11461                }
11462            };
11463            if (mode === 'option_count') {
11464                const $card = $row.find('._cart_option_card[data-option-no="' + option_no + '"]');
11465                updatePriceEl($card, '_cart_option_price', '_cart_option_origin_price');
11466            } else if (mode === 'product_count') {
11467                updatePriceEl($row, '_cart_product_price', '_cart_product_origin_price');
11468            }
11469        },
11470        // click_change_quantity_cart 트래킹 (서버 응답 200 + cart 화면 반영 직후 호출)
11471        _trackChangeQuantityCart($counter, $row, mode, before, after, method) {
11472            if (typeof window.BrandScope === 'undefined' || !method) return;
11473            const isOption = mode === 'option_count';
11474            let optionInfo = null;
11475            if (isOption) {
11476                // 옵션 라벨 + 값 wrapper 텍스트 추출 (예: "포장방법: 포장백 / 색상: 빨강")
11477                // .opt-name 의 parent span 이 라벨+값 한 옵션을 묶고, 같은 카드에 옵션 N개면 N개 parent span
11478                const $optCard = $counter.closest('._cart_option_card');
11479                if ($optCard.length) {
11480                    const parts = [];
11481                    $optCard.find('.opt-name').each(function () {
11482                        const text = $(this).parent().text().replace(/\s+/g, ' ').trim();
11483                        if (text) parts.push(text);
11484                    });
11485                    if (parts.length) optionInfo = parts.join(' / ');
11486                }
11487            }
11488            const cartCode = (typeof currentCartCode === 'string' && currentCartCode) ? currentCartCode : '';
11489            window.BrandScope.track('click_change_quantity_cart', {
11490                action:                 'click',
11491                content:                'change_quantity',
11492                where:                  'cart',
11493                prod_code:              $row.attr('data-prod-code') || '',
11494                prod_type:              $row.attr('data-prod-type') || 'normal',
11495                cart_code:              cartCode,
11496                cart_item_code:         $counter.data('itemCode') || '',
11497                option_info:            optionInfo,
11498                quantity_change_method: method,
11499                quantity_unit:          isOption ? 'option' : 'product',
11500                quantity_before:        before,
11501                quantity_after:         after,
11502            });
11503        },
11504        // 수량 변경 후 서버 동기화 (debounce + AJAX, +/- 와 직접입력 공통)
11505        // trackingMethod: 'button_increase' | 'button_decrease' | 'input'
11506        // (click_change_quantity_cart 트래킹 시 사용 — 응답 200 + 화면 반영 직후 발사)
11507        _syncCartCount($counter, $val, $row, mode, option_no, timerKey, trackingMethod) {
11508            const { itemCode: item_code, isRegularly: is_regularly, isC3: is_c3 } = $counter.data();
11509            const rollback = (count) => {
11510                $val.val(count);
11511                this._updateCartCountCell($row, mode, option_no, count);
11512            };
11513            this._debounce(timerKey, () => {
11514                const originalCount = this._originalCounts[timerKey];
11515                delete this._originalCounts[timerKey];
11516                // debounce 대기 중 옵션이 삭제된 경우 요청 취소
11517                if (!$.contains(document, $counter[0])) return;
11518                const finalCount = parseInt($val.val()) || 1;
11519
11520                // 선택 상품이 없으면 item_list가 직렬화되지 않으므로 빈 선택 목록도 명시한다.
11521                const postData = { mode, item_code, count: finalCount, is_regularly, item_list: selectedCartItem, item_list_sent: 'Y' };
11522                if (mode === 'option_count') postData.option_no = option_no;
11523
11524                this._setLoading();
11525
11526                $.ajax({
11527                    type: 'POST',
11528                    data: postData,
11529                    url: is_c3 ? '/shop/oms/OMS_change_cart_item.cm' : '/shop/change_cart_item.cm',
11530                    dataType: 'json',
11531                    cache: false,
11532                    success: (result) => {
11533                        if (result.msg === 'SUCCESS') {
11534                            if (typeof AW_INOUT !== 'undefined') AW_INOUT(result.prod_no, result.after_count);
11535                            if (typeof AM_INOUT !== 'undefined') AM_INOUT(result.prod_no, result.after_count);
11536                            if (typeof CHANNEL_PLUGIN !== 'undefined') CHANNEL_PLUGIN.updateChannelProfileAttr('cart');
11537                            if (result.item_prices) {
11538                                $.each(result.item_prices, (itemCode, info) => {
11539                                    const $itemRow = $cart_list_wrap.find(`tr[data-item-code="${itemCode}"]`);
11540                                    if (!$itemRow.length) return;
11541                                    // 서버에서 계산된 정확한 품목 수량 반영
11542                                    if (info.count !== undefined) {
11543                                        $itemRow.find('._cart_item_count').text(info.count);
11544                                        const $countText = $itemRow.find('._cart_item_count_text');
11545                                        if ($countText.length) {
11546                                            $countText.text($countText.text().replace(/\d+/, info.count));
11547                                        }
11548                                    }
11549                                    // 가격 갱신 (PC/mobile + origin 헬퍼 통합, QD는 헬퍼에서 차감)
11550                                    renderCartRowPrice($itemRow, { basePrice: info.price_raw, originPrice: info.origin_price_raw });
11551                                    // 타이틀 행 아이템 가격(strikethrough 영역) 별도 갱신
11552                                    $itemRow.find('._cart_item_price').text(info.origin_price);
11553                                    // 수량별 할인 (JS 계산)
11554                                    const _quantityDiscountItemPrice = _calcCartQuantityDiscount($itemRow, parseInt(info.price_raw, 10) || 0);
11555
11556                                    // 배송비 업데이트
11557                                    if (info.deliv_price_text !== undefined && info.deliv_price_text !== '') {
11558                                        updateCartItemDelivText(findCartItemDelivTd($itemRow, itemCode), info.deliv_price_text);
11559
11560                                        // 모바일 개별 배송비 업데이트
11561                                        const $mobileDelivText = $cart_list_wrap.find(`._cart_mobile_deliv_price[data-item-code="${itemCode}"] ._cart_mobile_deliv_price_text`);
11562                                        if ($mobileDelivText.length) {
11563                                            $mobileDelivText.html(info.deliv_price_text);
11564                                        }
11565                                    }
11566                                    // 모바일 묶음배송 배송비 업데이트
11567                                    if (info.bundle_deliv_text !== undefined && info.bundle_deliv_text !== '') {
11568                                        const $shippingRow = findCartBundleShippingRow($itemRow, itemCode);
11569                                        if ($shippingRow.length) {
11570                                            $shippingRow.find('._cart_bundle_deliv_text').html(info.bundle_deliv_text);
11571                                        }
11572                                    }
11573                                    // 모바일 아이템별 할인 영역 업데이트
11574                                    if (info.period_discount !== undefined) {
11575                                        const $discountWrap = $itemRow.find('._cart_mobile_discount_wrap');
11576                                        const $periodField = $itemRow.find('._cart_mobile_period_discount_field');
11577                                        const $periodPrice = $itemRow.find('._cart_mobile_period_discount_price');
11578
11579                                        // _quantityDiscountItemPrice는 위에서 이미 계산됨
11580                                        const _periodDiscount = info.peri
11580od_discount;
11581                                        const _creatorDiscountItemPrice = parseInt(info.creator_discount, 10) || 0;
11582                                        const _totalItemDiscount = _periodDiscount + _quantityDiscountItemPrice + _creatorDiscountItemPrice;
11583
11584                                        if (_totalItemDiscount > 0) {
11585                                            // 상품 할인 금액 (합계)
11586                                            $periodPrice.data('period-discount-price', _totalItemDiscount);
11587                                            $periodPrice.text('- ' + LOCALIZE.getCurrencyFormat(_totalItemDiscount));
11588                                            $periodField.show();
11589
11590                                            // 즉시/기간 할인 하위 항목
11591                                            const $periodSub = $itemRow.find('._cart_mobile_period_sub_field');
11592                                            if (_periodDiscount > 0 && $periodSub.length) {
11593                                                $periodSub.find('._cart_mobile_period_sub_price').text('- ' + LOCALIZE.getCurrencyFormat(_periodDiscount));
11594                                                $periodSub.show();
11595                                            } else if ($periodSub.length) {
11596                                                $periodSub.hide();
11597                                            }
11598
11599                                            // 수량별 할인 하위 항목
11600                                            const $quantityDiscountField = $itemRow.find('._cart_mobile_qd_discount_field');
11601                                            const $quantityDiscountPrice = $itemRow.find('._cart_mobile_quantity_discount_price');
11602                                            if (_quantityDiscountItemPrice > 0) {
11603                                                $quantityDiscountPrice.text('- ' + LOCALIZE.getCurrencyFormat(_quantityDiscountItemPrice));
11604                                                $quantityDiscountField.show();
11605                                            } else {
11606                                                $quantityDiscountField.hide();
11607                                            }
11608
11609                                            // 크리에이터 할인 하위 항목
11610                                            const $creatorDiscountField = $itemRow.find('._cart_mobile_creator_discount_field');
11611                                            const $creatorDiscountPrice = $itemRow.find('._cart_mobile_creator_discount_price');
11612                                            if (_creatorDiscountItemPrice > 0) {
11613                                                $creatorDiscountPrice
11614                                                    .data('creator-discount-price', _creatorDiscountItemPrice)
11615                                                    .text('- ' + LOCALIZE.getCurrencyFormat(_creatorDiscountItemPrice));
11616                                                $creatorDiscountField.show();
11617                                            } else {
11618                                                $creatorDiscountPrice.data('creator-discount-price', 0).text('');
11619                                                $creatorDiscountField.hide();
11620                                            }
11621
11622                                            $discountWrap.show();
11623                                        } else {
11624                                            $periodPrice.data('period-discount-price', 0);
11625                                            $periodPrice.text('');
11626                                            $periodField.hide();
11627                                            $itemRow.find('._cart_mobile_creator_discount_price').data('creator-discount-price', 0).text('');
11628                                            $itemRow.find('._cart_mobile_creator_discount_field').hide();
11629                                            if ($discountWrap.length) $discountWrap.hide();
11630                                        }
11631                                    }
11632                                });
11633                            }
11634                            if (result.totals) {
11635                                setCartPriceHtml(result.totals);
11636                            }
11637                            // 사은품 MFE에 실제 콘텐츠가 렌더링되어 있으면 장바구니 변경 이벤트 발송
11638                            const freebieEl = document.querySelector('fo-prod-detail-freebie-cart') || document.querySelector('fo-prod-detail-freebie-cart-mobile');
11639                            const freebieRoot = freebieEl && freebieEl.shadowRoot ? freebieEl.shadowRoot.querySelector('[data-root] > div') : null;
11640                            if (freebieRoot) {
11641                                // CB-3865: 수량 변경 후 closure 의 count 만 patch.
11642                                // option_no 는 DOM data-option-no (0-based array index). 본품은 -1.
11643                                // totalItemPrice 는 patch 안 함 — apply-promotion schema 상 optional,
11644                                // BE 가 prodCode/qty 로 자체 계산. tiered pricing 부정확 회피.
11645                                var _patchedItem = _cartRawItems.find(function (i) { return i.item_code === $counter.data('itemCode'); });
11646                                if (_patchedItem) {
11647                                    var _target = (option_no === -1)
11648                                        ? _patchedItem
11649                                        : ((_patchedItem.options && _patchedItem.options.list) || [])[option_no];
11650                                    if (_target) _target.count = finalCount;
11651                                }
11652                                dispatchFreebieCartChange();
11653                            }
11654                            // click_change_quantity_cart 트래킹 (서버 응답 200 + 화면 반영 직후)
11655                            this._trackChangeQuantityCart($counter, $row, mode, originalCount, finalCount, trackingMethod);
11656                        } else {
11657                            rollback(originalCount);
11658                            alert(result.msg);
11659                        }
11660                    },
11661                    error: () => rollback(originalCount),
11662                    complete: () => {
11663                        this._clearLoading();
11664                    }
11665                });
11666            }, 300);
11667        },
11668        // 장바구니 1품목당 최대 수량 (프론트 입력 한도
11668)
11669        _MAX_CART_ITEM_COUNT: 9999,
11670        // 장바구니 목록에서 수량 직접 변경 (옵션/단일상품 공통, +/- 버튼 핸들러)
11671        directChangeCount(el, delta) {
11672            const $counter = $(el).closest('._cart_counter');
11673            const $val = $counter.find('._counter_value');
11674            const $row = $counter.closest('tr');
11675            const currentCount = parseInt($val.val()) || 1;
11676            let newCount = currentCount + delta;
11677            if (newCount < 1) newCount = 1;
11678            if (newCount > this._MAX_CART_ITEM_COUNT) newCount = this._MAX_CART_ITEM_COUNT;
11679            if (newCount === currentCount) return;
11680
11681            // 수량 증가 시 품절/재고 검증 (클라이언트 즉각 피드백)
11682            // 최대구매수량 검증은 서버에서 일괄 처리 (모달과 동일한 검증 로직 적용)
11683            if (delta > 0 && !this._checkStockLimit($counter, newCount)) return;
11684
11685            const { mode, optionNo } = $counter.data();
11686            const option_no = (mode === 'option_count') ? optionNo : -1;
11687            const timerKey = `${$counter.data('itemCode')}_${option_no}`;
11688
11689            // 최초 클릭 시 원래 수량 보관
11690            if (!(timerKey in this._originalCounts)) {
11691                this._originalCounts[timerKey] = currentCount;
11692            }
11693
11694            // 즉시 UI 반영
11695            $val.val(newCount);
11696            this._updateCartCountCell($row, mode, option_no, newCount);
11697
11698            // 트래킹용 변경 방식 (click_change_quantity_cart)
11699            const trackingMethod = delta > 0 ? 'button_increase' : 'button_decrease';
11700            this._syncCartCount($counter, $val, $row, mode, option_no, timerKey, trackingMethod);
11701        },
11702        // 장바구니 목록에서 수량 직접 입력 (input onchange 핸들러)
11703        directInputCount(el) {
11704            const $counter = $(el).closest('._cart_counter');
11705            const $val = $counter.find('._counter_value');
11706            const $row = $counter.closest('tr');
11707
11708            // focus 시점에 저장된 직전 값 (없으면 input 의 현재값을 fallback)
11709            const previousCount = parseInt(el.dataset.prevValue, 10) || (parseInt($val.val(), 10) || 1);
11710
11711            // 입력값 정규화:
11712            //   1) Number() 로 우선 파싱 → finite 면 Math.floor 적용 (소수점 절삭, 음수 보존)
11713            //   2) finite 가 아니면 (NaN/Infinity) 숫자 외 문자 제거 후 parseInt 폴백
11714            //   3) 최종값을 [1, _MAX_CART_ITEM_COUNT] 범위로 클램프 후 input 에 반영
11715            // 예: "1.5" → 1, "-5" → 1, "abc1234" → 1234, "12,000" → 9999, 빈문자열 → 1
11716            const rawInput = String($val.val());
11717            const parsed = Number(rawInput);
11718            let newCount;
11719            if (Number.isFinite(parsed)) {
11720                newCount = Math.floor(parsed);
11721            } else {
11722                const stripped = rawInput.replace(/[^0-9]/g, '');
11723                newCount = parseInt(stripped, 10);
11724            }
11725            if (isNaN(newCount) || newCount < 1) newCount = 1;
11726            if (newCount > this._MAX_CART_ITEM_COUNT) newCount = this._MAX_CART_ITEM_COUNT;
11727            $val.val(newCount);
11728
11729            if (newCount === previousCount) return;
11730
11731            // 수량 증가 시 품절/재고 검증
11732            if (newCount > previousCount && !this._checkStockLimit($counter, newCount)) {
11733                $val.val(previousCount);
11734                return;
11735            }
11736
11737            const { mode, optionNo } = $counter.data();
11738            const option_no = (mode === 'option_count') ? optionNo : -1;
11739            const timerKey = `${$counter.data('itemCode')}_${option_no}`;
11740
11741            // 최초 변경 시 원래 수량 보관 (서버 응답 실패 시 복원용)
11742            if (!(timerKey in this._originalCounts)) {
11743                this._originalCounts[timerKey] = previousCount;
11744            }
11745
11746            // 즉시 가격 셀 갱신
11747            this._updateCartCountCell($row, mode, option_no, newCount);
11748
11749            this._syncCartCount($counter, $val, $row, mode, option_no, timerKey, 'input');
11750        },
11751        // 품절/재고 한도 검증 (true: 통과, false: 차단)
11752        _checkStockLimit($counter, newCount) {
11753            if ($counter.data('soldout') === 'Y') {
11754                alert(LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(1));
11755                return false;
11756            }
11757            const maxStock = $counter.data('stock');
11758            if (maxStock !== undefined && newCount > parseInt(maxStock)) {
11759                alert(LOCALIZE.설명_현재재고부족으로N개이상구매할수없습니다(parseInt(maxStock) + 1));
11760                return false;
11761            }
11762            return true;
11763        },
11764        "changeCartItemDelivType": function (item_code, deliv_type, is_regularly) {
11765            changeCartItemDelivType(item_code, deliv_type, is_regularly);
11766        },
11767        "changeCartItemDelivPayType": function (item_code, deliv_pay_type, is_regularly) {
11768            changeCartItemDelivPayType(item_code, deliv_pay_type, is_regularly);
11769        },
11770        changeCartItemComplete: function (current_ul) {
11771            changeCartItemComplete(current_ul);
11772        },
11773        addNPayWishWithCart: function (item_code) {
11774            addNPayWishWithCart(item_code);
11775        },
11776        addSelectedCartItem: function (item_code) {
11777            selectedCartItem.push(item_code);
11778        },
11779        // MFE 주문 재시도 시 이전 선택을 누적하지 않고 현재 목록으로 교체한다.
11780        setSelectedCartItems: function (item_codes) {
11781            selectedCartItem = item_codes.slice();
11782        },
11783        changeCartLoadOption: function (prod_idx) {
11784            SITE_SHOP_DETAIL.loadOption('cart', prod_idx);
11785        },
11786        cartListMake: function (current_full_url, type) {
11787            cartListMake(current_full_url, type);
11788        },
11789        "setCreatorDiscountData": function (data) {
11790            // 크리에이터 전용 혜택가 데이터 주입 (PHP cart_main.sub 에서 호출).
11791            // BE 차감은 OMS 자동 처리이므로 여기선 표시용 데이터만 보관.
11792            creator_cart_data = data || null;
11793        },
11794        countryCodeChange: function (country) {
11795            countryCodeChange(country);
11796        },
11797        openPromotionModal: function () {
11798            openPromotionModal();
11799        },
11800        toggleAllSelectCk: function () {
11801            toggleAllSelectCk();
11802        },
11803        // C3 에 주문을 생성하는 endpoint를 바라보는 함수
11804		C3_initCart : function(cart_code, backurl, is_regularly){
11805			initCart(cart_code, backurl, is_regularly, true);
11806		},
11807		OMS_addOrderWithCart : function(type, item_code, backurl, params, is_gift_buy = false){
11808			addOrderWithCart(type, item_code, backurl, false, true, params, is_gift_buy);
11809		},
11810		OMS_addOrderWithCartDirect : function(type, item_code, backurl, cart_code, freebie_list){
11811			// IMIO-7501 — MFE 장바구니는 탭 전환 시 리로드가 없어 C3_initCart 가 재실행되지 않는다.
11812			// currentCartCode 가 첫 로드 type 에 고정돼 다른 탭 바로구매가 빈 cart 로 판정되므로,
11813			// MFE 가 넘긴 활성 탭 cart_code 로 호출 시점에 동기화한다.
11814			if (typeof cart_code === 'string' && cart_code !== '') {
11815				currentCartCode = cart_code;
11816			}
11817			// 모바일 MFE 장바구니는 사은품이 포함형(별도 freebie MFE 미적재)이라 host 의 사은품 pull
11818			// 이벤트에 응답할 주체가 없다. 바로구매는 MFE 가 선택한 사은품을 직접 인자로 넘기므로,
11819			// params.freebie_list 로 addOrderWithCart 에 전달해 주문서에 담는다 (레거시 바로구매 사은품 회귀 복구).
11820			addOrderWithCart(type, item_code, backurl, true, true, freebie_list ? { freebie_list: freebie_list } : undefined);
11821		},
11822		// CB-3985 — MFE 장바구니가 주문 응답으로 머천트 픽셀을 호스트에서 발화하기 위해 노출.
11823		OMS_fireOrderTracking : function (type, result) {
11824		    fireOrderTracking(type, result);
11825		},
11826		C3_cartListMake : function(current_full_url, type){
11827			cartListMake(current_full_url, type, true);
11828		},
11829		C3_changeCartItemComplete : function(current_ul){
11830			changeCartItemComplete(current_ul, true);
11831		},
11832		C3_changeCartItemDelivType: function(item_code, deliv_type,is_regularly){
11833			changeCartItemDelivType(item_code, deliv_type,is_regularly, true);
11834		},
11835		C3_changeCartItemDelivPayType: function(item_code, deliv_pay_type,is_regularly) {
11836			changeCartItemDelivPayType(item_code, deliv_pay_type,is_regularly, true);
11837		},
11838    };
11839}();
11840
11841var SITE_SHOP_REVIEW = function () {
11842    var $review_wrap;
11843    var $mobile_review_wrap;
11844    var $mobile_form;
11845    var $rating;
11846    var $star;
11847    var $m_rating;
11848    var $m_star;
11849    var $comment_body;
11850    var $comment_area;
11851    var review_body;
11852    var $review_container;
11853    var $review_form;
11854    var body_input;
11855    var placeholderText;
11856    var isIOS, isSafari, $fr_m_custom, $write_header, m_sticky_container_trigger_top, $toolbarContainer;
11857    var images = {};
11858    var add_review_wrap;
11859    var is_submit = false;
11860
11861		// 이미지 엑박 여부를 확인하는 함수
11862		const isImageBroken = (img) => {
11863			return img.complete && (img.naturalWidth === 0 || img.naturalHeight === 0);
11864		}
11865
11866		// 이미지를 다시 로드하는 함수
11867		// 업로드 직후 imgo 최적화본이 없으면 원본이 폴백으로 온다. jpg·png 는 그대로 표시되지만
11868		// heic·heif 는 브라우저가 디코드하지 못해 엑박이 된다. 변환이 끝나면 webp 로 바뀌므로 다시 받아 본다.
11869		// 재시도 간격(ms). 뒤로 갈수록 벌려 총 12초 안에 4번만 시도한다.
11870		const RELOAD_IMAGE_RETRY_DELAYS = [1000, 2000, 3000, 6000];
11871		const reloadImage = (img, src) => {
11872			// src 에 이미 ?w= 가 있으므로 "?t=" 를 이어 붙이면 w 값이 "300?t=..." 로 파싱되어
11873			// 폭이 NaN 이 되고 최소 프리셋(100px)이 내려온다. URL API 로 파라미터를 교체한다.
11874			const tried = Number(img.dataset.reloadRetry || 0);
11875			if (tried >= RELOAD_IMAGE_RETRY_DELAYS.length) {
11876				return;
11877			}
11878			img.dataset.reloadRetry = tried + 1;
11879			setTimeout(() => {
11880				const retryUrl = new URL(src);
11881				retryUrl.searchParams.set('t', Date.now()); // 캐시를 피하기 위해 쿼리 파라미터 추가
11882				img.src = retryUrl.toString();
11883			}, RELOAD_IMAGE_RETRY_DELAYS[tried]);
11884		}
11885
11886    var init = function (code) {
11887        $review_wrap = $('._review_wrap');
11888        $review_form = $('#review_form');
11889        $rating = $('#rating');
11890        $star = $('._star');
11891        $review_form.find('._btn_add_image').toggleClass('no-margin', true);
11892        review_body = $('#review_modal_body');
11893        $review_container = $('#review_container');
11894        body_input = $('#body_input');
11895        placeholderText = $('#placeholderText').val();
11896        $review_form.find('#review_image_upload_btn').fileupload({
11897            url: '/ajax/review_image_upload.cm',
11898            formData: { temp: 'Y', target: 'site_review', type: 'image' },
11899            dataType: 'json',
11900            singleFileUploads: true,
11901            limitMultiFileUploads: 1,
11902            start: function (e, data) {
11903            },
11904            progress: function (e, data) {
11905            },
11906            done: function (e, data) {
11907                $.each(data.result.files, function (e, tmp) {
11908                    if (tmp.error == null) {
11909                        $review_form.find("#review_image_box").show();
11910                        $review_form.find('._btn_add_image').toggleClass('no-margin', false);
11911												// 경로에 쿼리가 이미 있으면 '?w=300' 을 이어 붙일 때 w 가 파싱되지 않으므로 URL API 로 붙인다
11912												const _optimizedImageUrl = new URL(CDN_OPTIMIZED_URL + tmp.url);
11913												_optimizedImageUrl.searchParams.set('w', 300);
11914												const url = _optimizedImageUrl.toString();
11915                        images[tmp.code] = tmp.url;
11916                        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>';
11917                        $review_form.find("#review_image_box ._btn_add_image").before(html);
11918
11919		                    const reviewImage = document.getElementById(`review_${tmp.url}`);
11920												reviewImage.style.display = 'none';
11921
11922		                    // 이미지 로드 완료 후 확인
11923		                    reviewImage.onload = reviewImage.onerror = function() {
11924			                    if (isImageBroken(reviewImage)) {
11925				                    reloadImage(reviewImage, reviewImage.src);
11926			                    } else {
11927				                    reviewImage.style.display = 'block';
11928			                    }
11929		                    };
11930
11931		                    // 만약 이미 이미지가 로드되었으면 즉시 확인
11932		                    if (reviewImage.complete) {
11933			                    if (isImageBroken(reviewImage)) {
11934				                    reloadImage(reviewImage, reviewImage.src);
11935			                    } else {
11936				                    reviewImage.style.display = 'block';
11937			                    }
11938		                    }
11939                    } else {
11940                        alert(tmp.error);
11941                    }
11942                });
11943            },
11944            fail: function (e, data) {
11945                alert(getLocalizeString("설명_업로드에실패하였습니다", "", "업로드에 실패 하였습니다."));
11946            }
11947        });
11948    };
11949
11950    var deleteReviewImage = function (code) {
11951        if (typeof images[code] != 'undefined') {
11952            delete images[code];
11953        }
11954        $("span._img_" + code).remove();
11955    };
11956
11957    var openAddReview = function (idx, prod_code, prod_order_code, is_c3 = false) {
11958        $.cocoaDialog.close();
11959        $('#myModalReview').modal("hide");//review_remind 모달 id분리로 인한 처리
11960        $.ajax({
11961            type: 'POST',
11962            data: { 'idx': idx, 'prod_code': prod_code, 'prod_order_code': prod_order_code },
11963            url: ('/shop/open_add_review.cm'),
11964            dataType: 'json',
11965            cache: false,
11966            async: false,
11967            success: function (result) {
11968                if (result.msg === 'SUCCESS') {
11969                    $.cocoaDialog.open({
11970                        type: 'add_review', custom_popup: result.html, 'close_block': true, width: 800, hide_event: function () {
11971                            images = {};
11972                        }
11973                    });
11974                } else {
11975                    alert(result.msg);
11976                }
11977            }
11978        });
11979    };
11980
11981    var convertReviewImage = function (image_list) {
11982        $review_form.find('._btn_add_image').toggleClass('no-margin', false);
11983        for (var i = 0; i < image_list.length; i++) {
11984            let html;
11985			images[i] = image_list[i];
11986	        if (images[i].indexOf('.mp4') > -1) {
11987		        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>';
11988	        } else {
11989                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>';
11990	        }
11991            $review_form.find("#review_image_box ._btn_add_image").before(html);
11992        }
11993    };
11994
11995    function resizeStickyContainer() {
11996        var s_top = $(this).scrollTop();
11997        if (isIOS && isSafari) {
11998            $write_header.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
11999            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
12000            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
12001            if (s_top > m_sticky_container_trigger_top) {
12002                $toolbarContainer.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
12003                review_body.find('.fr-view').css('padding-top', '90px');
12004            } else {
12005                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
12006                review_body.find('.fr-view').css('padding-top', '');
12007            }
12008        } else {
12009            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
12010        }
12011        $toolbarContainer.find('.fr-toolbar').css('width', review_body.find('.fr-view').width());
12012        if ($(window).width() >= 768) {
12013            if ($review_container.hasClass('bg_on'))
12014                $review_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 487);
12015            else
12016                $review_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 180);
12017        }
12018    }
12019
12020    var initMobileReview = function () {
12021        $mobile_review_wrap = $('#prod_detail_content_mobile');
12022        $mobile_form = $mobile_review_wrap.find('#mobile_review_form');
12023        $m_rating = $mobile_form.find('#mobile_rating');
12024        $m_star = $mobile_form.find('._star');
12025        $mobile_form.find("#mobile_review_image_box").hide();
12026
12027        $mobile_form.find('#mobile_review_image_upload_btn').setUploadImage({
12028            url: '/shop/upload_image.cm',
12029            dropZone: 'icon_img_upload_wrap',
12030            singleFileUploads: true,
12031            formData: { temp: 'Y' }
12032        }, function (res, data) {
12033            $("#mobile_review_image_box").show();
12034            $.each(data, function (e, tmp) {
12035                if (tmp.error == "" || tmp.error == null) {
12036                    var url = CDN_UPLOAD_URL + tmp.url;
12037                    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>';
12038                    $("#mobile_review_image_box").append(html);
12039                } else {
12040                    alert(tmp.error);
12041                }
12042            });
12043        });
12044        autosize($('.textarea_block textarea'));
12045    };
12046
12047    var changeRating = function (t, n) {
12048
12049        if (t == 'desktop') {
12050            $rating.val(n + 1);
12051            $star.each(function (e) {
12052                if (n <= 0 && e == 0) {
12053                    if (n == -1) {
12054                        $(this).removeClass('active');
12055                    } else {
12056                        $(this).addClass('active');
12057                    }
12058                } else {
12059                    $(this).removeClass('active');
12060                    if (e <= n) {
12061                        $(this).addClass('active');
12062                    }
12063                }
12064            });
12065        } else {
12066            $m_rating.val(n + 1);
12067            $m_star.each(function (e) {
12068                if (n <= 0 && e == 0) {
12069                    if (n == -1) {
12070                        $(this).removeClass('active');
12071                    } else {
12072                        $(this).addClass('active');
12073                    }
12074                } else {
12075                    $(this).removeClass('active');
12076                    if (e <= n) {
12077                        $(this).addClass('active');
12078                    }
12079                }
12080            });
12081        }
12082    };
12083
12084    var reviewFormShow = function (t) {
12085        var sub_form = $("._sub_form_" + t);
12086
12087        sub_form.data('show', 'Y');
12088        sub_form.show();
12089        var comment_add_body = sub_form.find('._comment_add_body_' + t);
12090
12091        $('body').off('mouseup.sub_comment')
12092            .on('mouseup.sub_comment', function (e) {
12093                var $c_target = $(e.target);
12094                var $s_form = $c_target.closest('._sub_form_' + t + ', ._show_sub_form_btn_' + t);
12095                if ($s_form.length == 0) {
12096
12097                    var text = comment_add_body.val();
12098                    sub_form.data('show', 'N');
12099                    if (text == '') {
12100                        $('body').off('mouseup.sub_comment');
12101                        reviewFormHide();
12102                    }
12103                }
12104            });
12105    };
12106
12107    var reviewEditShow = function (t) {
12108        var editor_form = $("._sub_form_editor_" + t);
12109        editor_form.siblings().hide();
12110
12111        editor_form.data('show', 'Y');
12112        editor_form.show();
12113        autosize.update(editor_form.find('textarea'));
12114
12115    };
12116
12117    var reviewEditHide = function (t) {
12118        var editor_form = $("._sub_form_editor_" + t);
12119        editor_form.hide();
12120    };
12121
12122    var reviewFormHide = function () {
12123        $("._sub_review_form").hide();
12124    };
12125
12126    var reviewDelete = function (t, c, r_p, only_photo, buyer_permission, secret_pass) {
12127        only_photo = only_photo == 'Y' ? true : false;
12128        $.ajax({
12129            type: 'POST',
12130            data: { code: t, prod_code: c },
12131            url: ('/shop/check_review_point.cm'),
12132            dataType: 'json',
12133            success: function (res) {
12134                if (res.msg == 'SUCCESS') {
12135                    var msg = buyer_permission == 'Y' ? LOCALIZE.설명_삭제하시겠습니까삭제후재등록불가() : LOCALIZE.설명_삭제하시겠습니까();
12136                    if (res.alert_msg != '' && res.alert_msg != null) msg = res.alert_msg;
12137                    if (confirm(msg)) {
12138                        $.ajax({
12139                            type: 'POST',
12140                            data: { code: t, prod_code: c, change_member_point: res.change_member_point, review_pass: secret_pass || '' },
12141                            url: ('/shop/delete_review.cm'),
12142                            dataType: 'json',
12143                            success: function (result) {
12144                                if (result.msg == 'SUCCESS') {
12145                                    if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p);
12146                                    else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p, 0, false, only_photo);
12147
12148
12149                                    SITE_SHOP_DETAIL.deleteReviewInTabDisplay(c, only_photo);
12150                                    SITE_SHOP_DETAIL.getReviewQnaCount(c);
12151
12152
12153                                    $.cocoaDialog.close();
12154                                } else
12155                                    alert(result.msg);
12156                            }
12157                        });
12158                    }
12159                } else
12160                    alert(res.msg);
12161            }
12162        });
12163    };
12164
12165    var reviewHide = function (t, c, r_p) {
12166        if (confirm(LOCALIZE.설명_숨기시겠습니까())) {
12167            $.ajax({
12168                type: 'POST',
12169                data: { code: t, prod_code: c, is_visible: false },
12170                url: ('/shop/delete_review.cm'),
12171                dataType: 'json',
12172                success: function (result) {
12173                    if (result.msg == 'SUCCESS') {
12174                        if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p);
12175                        else SITE_SHOP_DETAIL.changeContentPCTab(SHOP_CONST.TAB_TYPE.REVIEW, r_p);
12176                        $.cocoaDialog.close();
12177                    } else
12178                        alert(result.msg);
12179                }
12180            });
12181        }
12182    };
12183
12184    var CheckSecret = function (code, secret_pass, callback) {
12185        $.ajax({
12186            type: 'post',
12187            data: { code: code, secret_pass: secret_pass, type: 'review' },
12188            url: '/ajax/check_review_pass.cm',
12189            dataType: 'json',
12190            success: function (result) {
12191                if (result.msg == 'SUCCESS') {
12192                    if (typeof callback == 'function')
12193                        callback();
12194                } else {
12195                    alert(result.msg);
12196                }
12197            }
12198        });
12199    };
12200    var EditReviewShow = function (t, c, idx) {
12201        var $show_secret_password = $('#show_secret_password');
12202        var $show_link = $(event.target);
12203        if ($show_secret_password.length == 0) {
12204            $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();
12205            $show_link.after($show_secret_password);
12206        }
12207        $show_secret_password.find('input').val('');
12208        $show_secret_password.show();
12209        $show_secret_password.off('click', '._confirm')
12210            .on('click', '._confirm', function () {
12211                var secret_pass = $show_secret_password.find('input').val();
12212                CheckSecret(t, secret_pass, function () {
12213                    SITE_SHOP_REVIEW.openAddReview(idx, c);
12214                });
12215                $show_secret_password.remove();
12216            });
12217        $('body').off('mousedown.show_secret')
12218            .on('mousedown.show_secret', function (e) {
12219                var $tmp = $(e.target).closest('#show_secret_password');
12220                if ($tmp.length == 0) {
12221					$show_secret_password.remove();
12222                    $('body').off('click.show_secret');
12223                }
12224            });
12225    };
12226
12227    var reviewDeleteShow = function (t, c, only_photo) {
12228        var $show_secret_password = $('#show_secret_password');
12229        var $show_link = $(event.target);
12230        if ($show_secret_password.length == 0) {
12231            $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();
12232            $show_link.after($show_secret_password);
12233        }
12234        $show_secret_password.find('input').val('');
12235        $show_secret_password.show();
12236        $show_secret_password.off('click', '._confirm')
12237            .on('click', '._confirm', function () {
12238                var secret_pass = $show_secret_password.find('input').val();
12239                CheckSecret(t, secret_pass, function () {
12240                    // 확인한 비밀번호를 삭제 요청에도 그대로 싣는다. 서버가 일괄등록 행에서
12241                    // 소유권 세션을 근거로 삼지 않으므로, 이 값이 없으면 정상 삭제도 거절된다.
12242                    reviewDelete(t, c, '', only_photo, undefined, secret_pass);
12243                });
12244				$show_secret_password.remove();
12245            });
12246        $('body').off('mousedown.show_secret')
12247            .on('mousedown.show_secret', function (e) {
12248                var $tmp = $(e.target).closest('#show_secret_password');
12249                if ($tmp.length == 0) {
12250					$show_secret_password.remove();
12251                    $('body').off('click.show_secret');
12252                }
12253            });
12254    };
12255
12256    var imageUploadInit = function (n) {
12257
12258        const $wrapper = $('.categorize, .categorize-mobile');
12259        $("#sub_review_image_box_" + n).hide();
12260        //$("#editor_review_image_box_"+n).hide();
12261
12262        $('#sub_review_image_upload_btn_' + n).setUploadImage({
12263            url: '/shop/upload_image.cm',
12264            dropZone: 'icon_img_upload_wrap',
12265            singleFileUploads: true,
12266            formData: { temp: 'Y' }
12267        }, function (res, data) {
12268            $("#sub_review_image_box_" + n).show();
12269            $.each(data, function (e, tmp) {
12270                if (tmp.error == "" || tmp.error == null) {
12271                    var url = CDN_UPLOAD_URL + tmp.url;
12272                    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>';
12273                    $("#sub_review_image_box_" + n).append(html);
12274                } else {
12275                    alert(tmp.error);
12276                }
12277            });
12278        });
12279
12280        $('#editor_review_image_upload_btn_' + n).setUploadImage({
12281            url: '/shop/upload_image.cm',
12282            dropZone: 'icon_img_upload_wrap',
12283            singleFileUploads: true,
12284            formData: { temp: 'Y' }
12285        }, function (res, data) {
12286            $("#editor_review_image_box_" + n).show();
12287            $.each(data, function (e, tmp) {
12288                if (tmp.error == "" || tmp.error == null) {
12289                    var url = CDN_UPLOAD_URL + tmp.url;
12290                    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>';
12291                    $("#editor_review_image_box_" + n).append(html);
12292                } else {
12293                    alert(tmp.error);
12294                }
12295            });
12296        });
12297    };
12298
12299    var submit = function () {
12300        if (is_submit) return false;
12301        is_submit = true;
12302        var data = $review_form.serializeObject();
12303        data.body = data.body.replace('/(?!br\\s*\\/?)[^>]+>/gi', '&gt;').replace('/<(?!img\\s*\\/?)[^>]/gi', '&lt;');
12304        data.images = images;
12305        $.ajax({
12306            type: 'POST',
12307            data: data,
12308            url: ('/shop/add_review.cm'),
12309            dataType: 'json',
12310            success: function (res) {
12311                if (res.review_token && res.review_token_key) {
12312                    $review_form.find('[name="review_token"]').val(res.review_token);
12313                    $review_form.find('[name="review_token_key"]').val(res.review_token_key);
12314                }
12315                if (res.review_result === 'RECOVERED') {
12316                    alert(res.msg);
12317                    $.cocoaDialog.close();
12318                    location.reload();
12319                } else if (res.msg == 'SUCCESS') {
12320                    IMWEB_SESSIONSTORAGE.clear("PROD_REVIEW.*");
12321                    reviewCompleted(res.received_point, res.check_received_point);
12322                    if (typeof CHANNEL_PLUGIN != "undefined") CHANNEL_PLUGIN.CompleteReview();
12323                } else {
12324                    is_submit = false;
12325                    alert(res.msg);
12326                }
12327            },
12328            error: function () {
12329                // 응답 유실 시 잠금 해제만 클라이언트 기능키로 제한한다. 자동 재시도하지 않는다.
12330                if (typeof DeployStrategy !== 'undefined' && DeployStrategy.isFeatureEnabled('IMOPS-15383')) {
12331                    is_submit = false;
12332                }
12333            }
12334        });
12335    };
12336
12337    var reviewCompleted = function (received_point, check_received_point) {
12338        $.cocoaDialog.close();
12339        $.ajax({
12340            type: 'POST',
12341            data: { 'received_point': received_point, 'check_received_point': check_received_point },
12342            url: ('/shop/add_review_completed.cm'),
12343            dataType: 'html',
12344            async: false,
12345            cache: false,
12346            success: function (html) {
12347                $.cocoaDialog.open({
12348                    type: 'prod_review_completed',
12349                    custom_popup: html,
12350                    'close_block': false,
12351                    hide_event: function () {
12352                        location.reload();
12353                    }
12354                });
12355            }
12356        });
12357    };
12358
12359    var reviewCancel = function () {
12360        if (isIOS && isSafari) {
12361            var s_top = $(this).scrollTop();
12362            $write_header.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': 0 });
12363            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
12364            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
12365            if (s_top > m_sticky_container_trigger_top) {
12366                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': $write_header.height() + 'px' });
12367            } else {
12368                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
12369            }
12370        }
12371        history.go(-1);
12372    };
12373
12374
12375    var createHtml = function (prod_idx, review_page, qna_page, paging_on, only_photo, rating) {
12376        const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
12377
12378        $review_wrap = $('._review_wrap');
12379
12380        const qna_url = isMobile_width ? '/shop/prod_review_mobile_html.cm' : '/shop/prod_review_pc_html.cm';
12381
12382        if ($review_wrap.length && !($review_wrap.hasClass('one_page_mode'))) {
12383            review_page = review_page || 0;
12384            qna_page = qna_page || 0;
12385
12386            var is_method_get = (review_page == 0 && qna_page == 0);
12387
12388            $.ajax({
12389                type: (is_method_get ? 'GET' : 'POST'),
12390                data: { prod_idx: prod_idx, review_page: review_page, qna_page: qna_page, only_photo: only_photo, rating: rating },
12391                url: qna_url,
12392                dataType: 'html',
12393                success: function (result) {
12394                    $review_wrap.html(result);
12395
12396                    if(isMobile_width) {
12397                        SITE_SHOP_DETAIL.getReviewSummary(document.querySelector(".detail_review_wrap_mobile ._review_summary_wrap_mobile"));
12398                    }
12399                }
12400            });
12401        }
12402
12403    };
12404
12405    var checkReviewData = function () {
12406        var check = Object.keys(images).length > 0 || review_body.val() != '';
12407        if (check) {
12408            if (confirm(LOCALIZE.설명_작성한내용이모두사라집니다())) {
12409                $.cocoaDialog.close();
12410            } else {
12411                return false;
12412            }
12413        } else {
12414            $.cocoaDialog.close();
12415        }
12416    };
12417
12418    var addBlockPost = function (login_member_code, review_code) {
12419        if (confirm(getLocalizeString('설명_게시글차단안내', '', '게시글을 차단하시겠습니까? 작성자의 다른 글과 댓글도 확인할 수 없습니다.'))) {
12420            addBlock(login_member_code, review_code, 'post');
12421        }
12422    };
12423
12424    var addBlockComment = function (login_member_code, review_code) {
12425        if (confirm(getLocalizeString('설명_님을차단안내', '', '댓글을 차단하시겠습니까? 작성자의 다른 댓글도 확인할 수 없습니다.'))) {
12426            addBlock(login_member_code, review_code, 'comment');
12427        }
12428    };
12429
12430    var switchToBlockCancelButton = function (login_member_code, review_code, review_member_code, type) {
12431        $('._review_button._block_' + review_member_code)
12432            .text(getLocalizeString("설명_차단해제", "", "차단해제"))
12433            .removeAttr('onclick')
12434            .off('click')
12435            .on('click', function (e) {
12436                e.stopPropagation();
12437                if (type === 'comment') {
12438                    SITE_SHOP_REVIEW.deleteBlockComment(login_member_code, review_code, review_member_code);
12439                } else {
12440                    SITE_SHOP_REVIEW.deleteBlockPost(login_member_code, review_code, review_member_code);
12441                }
12442            });
12443
12444    };
12445
12446    var switchToBlockCancelBody = function (review_member_code) {
12447        $('._review_body._block_' + review_member_code)
12448            .html("<p class='text-gray'><i aria-hidden='true' class='icon icon-exclamation' style='margin-right: 5px;'></i>" + getLocalizeString("설명_차단한작성자의구매평입니다", "", "차단한 작성자의 구매평입니다.") + "</p>")
12449            .attr('block', 'true');
12450
12451        //이미지 안보이게 수정
12452        $('._review_img._block_' + review_member_code).css('display', 'none');
12453
12454        //모달 리뷰 차단하였을 경우 이미지 변경
12455        $('._block_no_img._block_' + review_member_code)
12456            .css('display', 'block');
12457    };
12458
12459    var addBlock = function (login_member_code, review_code, type) {
12460        $.ajax({
12461            type: 'POST',
12462            data: {
12463                'login_member_code': login_member_code,
12464                'review_code': review_code,
12465                'type': type,
12466            },
12467            url: ('/shop/add_review_block.cm'),
12468            dataType: 'json',
12469            success: function (res) {
12470                if (res.msg === 'SUCCESS') {
12471                    switchToBlockCancelButton(login_member_code, review_code, res.review_member_code, type);
12472                    switchToBlockCancelBody(res.review_member_code);
12473
12474                    $('._block_review_command_' + res.review_member_code).css("display", "none");
12475                    // 댓글은 위에서 화면을 갱신한다. 목록 조회에는 후기 원문 코드만 보낸다.
12476                    if (type === 'post') {
12477                        SHOP_REVIEW_COMMENT.getReviewCommentHtml(review_code, 'accordion', $(this));
12478                    }
12479                } else {
12480                    alert(res.msg);
12481                }
12482            }
12483        });
12484    };
12485
12486    var deleteBlockPost = function (login_member_code, review_code, block_member_code) {
12487        if (confirm(getLocalizeString('설명_게시글차단해제안내', '', '게시글을 차단 해제하시겠습니까? 작성자의 다른 글과 댓글을 다시 확인할 수 있습니다.'))) {
12488            deleteBlock(login_member_code, review_code, block_member_code, 'post');
12489        }
12490    };
12491
12492    var deleteBlockComment = function (login_member_code, review_code, block_member_code) {
12493        var block_review_list_code = [];
12494
12495        if (confirm(getLocalizeString('설명_님을차단해제안내', '', '댓글을 차단 해제하시겠습니까? 작성자의 다른 댓글을 다시 확인할 수 있습니다.'))) {
12496            deleteBlock(login_member_code, review_code, block_member_code, 'comment');
12497        }
12498    };
12499
12500    var deleteBlock = function (login_member_code, review_code, block_member_code, t
12500ype) {
12501        var block_review_list_code = [];
12502
12503        block_review_list_code = get_block_review_list_code(block_member_code);
12504
12505        $.ajax({
12506            type: 'POST',
12507            data: {
12508                'login_member_code': login_member_code,
12509                'review_code': review_code,
12510                'block_review_list_code': block_review_list_code,
12511                'type': type
12512            },
12513            url: ('/shop/delete_review_block.cm'),
12514            dataType: 'json',
12515            success: function (res) {
12516                if (res.msg === 'SUCCESS') {
12517                    switchToBlockButton(login_member_code, review_code, res.review_member_code, type);
12518                    switchToBlockBody(res.review_member_code, block_review_list_code, res.block_review_data);
12519
12520                    $('._block_review_command_' + res.review_member_code).css("display", "block");
12521
12522                    if (type === 'post') {
12523                        SHOP_REVIEW_COMMENT.getReviewCommentHtml(review_code, 'accordion', $(this));
12524                    }
12525                } else {
12526                    alert(res.msg);
12527                }
12528            }
12529        });
12530    };
12531
12532    var switchToBlockButton = function (login_member_code, review_code, review_member_code, type) {
12533        $('._review_button._block_' + review_member_code)
12534            .text(getLocalizeString("설명_차단", "", "차단"))
12535            .removeAttr('onclick')
12536            .off('click')
12537            .on('click', function (e) {
12538                e.stopPropagation();
12539                if (type === 'comment') {
12540                    SITE_SHOP_REVIEW.addBlockComment(login_member_code, review_code);
12541                } else {
12542                    SITE_SHOP_REVIEW.addBlockPost(login_member_code, review_code);
12543                }
12544            });
12545    };
12546
12547    var switchToBlockBody = function (review_member_code, block_review_list_code, block_review_data) {
12548        //일반 이미지
12549        $('._review_img._block_' + review_member_code).removeAttr('style');
12550
12551        block_review_list_code.forEach(block_review_code => {
12552            var review_body_text = '';
12553            if (block_review_data && block_review_data[block_review_code] && block_review_data[block_review_code]['body']) {
12554                review_body_text = block_review_data[block_review_code]['body'];
12555            }
12556            var safe_review_body = $('<div>').text(review_body_text).html().replace(/(\n|\r\n)/g, '<br>');
12557            $('._review_body._review_code_' + block_review_code)
12558                .html(safe_review_body)
12559                .attr('block', 'false');
12560        });
12561        //모달 리뷰 이미지
12562        $('._block_no_img._block_' + review_member_code)
12563            .css('display', 'none');
12564    };
12565
12566    var get_block_review_list_code = function (block_member_code) {
12567        //차단 리스트 review code 가져오기
12568        var block_review_list_code = [];
12569
12570        $("._review_body._block_" + block_member_code + "[block='true']").each(function () {
12571            block_review_list_code.push($(this).attr('block-review-code'));
12572        });
12573
12574        return block_review_list_code;
12575    };
12576
12577    return {
12578        init: function (code) {
12579            init(code);
12580        },
12581        deleteReviewImage: function (code) {
12582            deleteReviewImage(code);
12583        },
12584        openAddReview: function (idx, prod_code, prod_order_code) {
12585            return openAddReview(idx, prod_code, prod_order_code);
12586        },
12587        convertReviewImage: function (image_list) {
12588            return convertReviewImage(image_list);
12589        },
12590        initMobileReview: function () {
12591            initMobileReview();
12592        },
12593        submit: function () {
12594            submit();
12595        },
12596        reviewCompleted: function (received_point, check_received_point) {
12597            reviewCompleted(received_point, check_received_point);
12598        },
12599        reviewCancel: function () {
12600            reviewCancel();
12601        },
12602        changeRating: function (t, n) {
12603            changeRating(t, n);
12604        },
12605        FormShow: function (t) {
12606            reviewFormShow(t);
12607        },
12608        Delete: function (t, c, r_p, only_photo, buyer_permission, secret_pass) {
12609            reviewDelete(t, c, r_p, only_photo, buyer_permission, secret_pass);
12610        },
12611        EditShow: function (t) {
12612            reviewEditShow(t);
12613        },
12614        EditReviewShow: function (t, c, idx) {
12615            EditReviewShow(t, c, idx);
12616        },
12617        DeleteShow: function (t, c, only_photo) {
12618            reviewDeleteShow(t, c, only_photo);
12619        },
12620        Hide: function (t, c, r_p) {
12621            reviewHide(t, c, r_p);
12622        },
12623        EditHide: function (t) {
12624            reviewEditHide(t);
12625        },
12626        imageUploadInit: function (n) {
12627            imageUploadInit(n);
12628        },
12629        createHtml: function (prod_idx, review_page, qna_page, paging_on, only_photo) {
12630            createHtml(prod_idx, review_page, qna_page, paging_on, only_photo);
12631        },
12632        checkReviewData: function () {
12633            checkReviewData();
12634        },
12635        addBlockPost: function (login_member_code, review_code) {
12636            addBlockPost(login_member_code, review_code);
12637        },
12638        addBlockComment: function (login_member_code, review_code) {
12639            addBlockComment(login_member_code, review_code);
12640        },
12641        deleteBlockPost: function (login_member_code, review_code, block_member_code) {
12642            deleteBlockPost(login_member_code, review_code, block_member_code);
12643        },
12644        deleteBlockComment: function (login_member_code, review_code, block_member_code) {
12645            deleteBlockComment(login_member_code, review_code, block_member_code);
12646        },
12647		C3_openAddReview : function(idx,prod_code,prod_order_code){
12648			return openAddReview(idx,prod_code,prod_order_code, true);
12649		},
12650    };
12651}();
12652
12653var SHOP_REVIEW_COMMENT = function () {
12654    var $review_comment_wrap;
12655    var $form;
12656    var $secret;
12657    var review_code;
12658    var device;
12659    var load_type; // 아코디언인지, 모달인지 체크
12660
12661    var init = function (code, type, device_type) {
12662        device = device_type;
12663        load_type = type;
12664        review_code = code;
12665        $form = $('.categorize-mobile #' + device + "_" + load_type + '_review_form_' + code + ', .categorize.review-box #' + device + "_" + load_type + '_review_form_' + code);
12666        $secret = $('._secret_btn');
12667        $secret.on('click', function () {
12668            if ($secret.hasClass('active')) {
12669                $secret.removeClass('active');
12670                $('#use_secret_review').val('N');
12671            } else {
12672                $secret.addClass('active');
12673                $('#use_secret_review').val('Y');
12674            }
12675        });
12676
12677        $form.each(function(){
12678            const $this = $(this);
12679            $this.find('#review_image_upload_btn_' + code).setUploadImage({
12680                url : '/shop/upload_image.cm',
12681                dropZone : 'icon_img_upload_wrap',
12682                singleFileUploads : true,
12683                formData : {temp : 'Y'}
12684            }, function(res, data){
12685                $this.find("#review_comment_image_box_" + code).show();
12686                $.each(data, function(e, tmp){
12687                    if(tmp.error == "" || tmp.error == null){
12688                        var url = CDN_UPLOAD_URL + tmp.url;
12689                        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>';
12690                        $this.find("#review_comment_image_box_" + code).append(html);
12691                    }else{
12692                        alert(tmp.error);
12693                    }
12694                });
12695            })
12696        });
12697    };
12698
12699    var imageUploadInit = function (idx) {
12700
12701        const $wrapper = $('.categorize-mobile, .categorize.review-box');
12702
12703        $wrapper.each(function(){
12704           const $this = $(this);
12705
12706            $this.find("#sub_review_image_box_" + idx).hide();
12707
12708            $this.find('#sub_review_image_upload_btn_' + idx).setUploadImage({
12709                url: '/shop/upload_image.cm',
12710                dropZone: 'icon_img_upload_wrap',
12711                singleFileUploads: true,
12712                formData: { temp: 'Y' }
12713            }, function (res, data) {
12714                $(".categorize #sub_review_image_box_" + idx).show();
12715                $(".categorize-mobile #sub_review_image_box_" + idx).show();
12716                $.each(data, function (e, tmp) {
12717                    if (tmp.error == "" || tmp.error == null) {
12718                        var url = CDN_UPLOAD_URL + tmp.url;
12719                        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>';
12720                        $this.find("#sub_review_image_box_" + idx).append(html);
12721                    } else {
12722                        alert(tmp.error);
12723                    }
12724                });
12725            });
12726
12727            $this.find('#editor_review_image_upload_btn_' + idx).setUploadImage({
12728                url: '/shop/upload_image.cm',
12729                dropZone: 'icon_img_upload_wrap',
12730                singleFileUploads: true,
12731                formData: { temp: 'Y' }
12732            }, function (res, data) {
12733                $(".categorize #editor_review_image_box_" + idx).show();
12734                $(".categorize-mobile #editor_review_image_box_" + idx).show();
12735                $.each(data, function (e, tmp) {
12736                    if (tmp.error == "" || tmp.error == null) {
12737                        var url = CDN_UPLOAD_URL + tmp.url;
12738                        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>';
12739                        $this.find("#editor_review_image_box_" + idx).append(html);
12740                    } else {
12741                        alert(tmp.error);
12742                    }
12743                });
12744            });
12745
12746        });
12747    };
12748
12749    var getReviewCommentHtml = function (code, type, object) {
12750        if (type == 'accordion') {
12751            $review_comment_wrap = $('.review_comment_wrap_' + code);
12752            if (!object.hasClass('active')) {
12753                $.ajax({
12754                    type: 'POST',
12755                    data: { 'code': code, 'type': type },
12756                    url: ('/shop/get_review_comment_list.cm'),
12757                    dataType: 'json',
12758                    async: false,
12759                    cache: false,
12760                    success: function (result) {
12761                        if (result.msg === 'SUCCESS') {
12762                            $review_comment_wrap.html(result.html);
12763                            $review_comment_wrap.show();
12764                        } else {
12765                            alert(result.msg);
12766                        }
12767                    }
12768                });
12769            } else {
12770                $review_comment_wrap.hide();
12771            }
12772        } else {
12773            $review_comment_wrap = $('#review_comment_section_' + code);
12774            $.ajax({
12775                type: 'POST',
12776                data: { 'code': code, 'type': type },
12777                url: ('/shop/get_review_comment_list.cm'),
12778                dataType: 'json',
12779                async: false,
12780                cache: false,
12781                success: function (result) {
12782                    if (result.msg === 'SUCCESS') {
12783                        $review_comment_wrap.html(result.html);
12784                    } else {
12785                        alert(result.msg);
12786                    }
12787                }
12788            });
12789        }
12790    };
12791    var submit = function (t, type, i) {
12792		// mobile 및 pc 에서 같은 id 값 공유 해당 부분 수정 필요...
12793		const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
12794		const isModal = document.querySelector('#cocoaModal').classList.contains('review');
12795        let data = {};
12796		switch (type) {
12797            case 'main': // pc 리뷰 댓글
12798	            if (isModal) {
12799					data = $('#_modal_review_form_' + review_code).serializeObject();
12800					break;
12801	            }
12802	            if (isMobile_width) {
12803		            data = $('.categorize-mobile #' + $form.attr('id')).serializeObject();
12804	            } else {
12805		            data = $('.categorize.review-box #' + $form.attr('id')).serializeObject();
12806	            }
12807                break;
12808            case 'sub_form': //pc 리뷰 대댓글
12809	            if (isModal) {
12810		            data = $('#sub_review_form_' + i).serializeObject();
12811		            break;
12812	            }
12813	            if (isMobile_width) {
12814					data = $('.categorize-mobile #sub_review_form_' + i).serializeObject();
12815	            } else {
12816					data = $('.categorize.review-box #sub_review_form_' + i).serializeObject();
12817	            }
12818                break;
12819            case 'editor': // pc 리뷰 댓글 수정
12820	            if (isModal) {
12821					data = $('#sub_review_editor_form_' + i).serializeObject();
12822					break;
12823	            }
12824                if (isMobile_width) {
12825		            data = $('.categorize-mobile #sub_review_editor_form_' + i).serializeObject();
12826	            } else {
12827		            data = $('.categorize.review-box #sub_review_editor_form_' + i).serializeObject();
12828	            }
12829                break;
12830            case 'mobile': // Mobile 리뷰 댓글
12831                data = $('#mobile_review_form').serializeObject();
12832                break;
12833            case 'mobile_sub_form': // Mobile 리뷰 대댓글
12834                data = $('#mobile_sub_review_form_' + i).serializeObject();
12835                break;
12836            case 'mobile_editor': // Mobile 리뷰 댓글 수정
12837                data = $('#mobile_sub_review_editor_form_' + i).serializeObject();
12838                break;
12839        }
12840        if (!t.hasClass("btn-writing")) {
12841            t.addClass("btn-writing");
12842        }
12843        $.ajax({
12844            type: 'POST',
12845            data: { data: data, review_code: review_code },
12846            url: ('/shop/add_review_comment.cm'),
12847            dataType: 'json',
12848            async: false,
12849            cache: false,
12850            success: function (result) {
12851                if (t.hasClass("btn-writing")) {
12852                    t.removeClass("btn-writing");
12853                }
12854                if (result.msg == 'SUCCESS') {
12855                    if (load_type == 'modal') {
12856                        $('#comment_count').text(" " + result.comment_count);
12857                        getReviewCommentHtml(review_code, 'modal', t);
12858                    } else {
12859                        $('.categorize #comment_count_' + review_code).text(" " + result.comment_count);
12860                        $('.categorize #comment_count_inner_' + review_code).text(" " + result.comment_count);
12861                        $('.categorize-mobile #comment_count_' + review_code).text(result.comment_count);
12862                        $('.categorize-mobile #comment_count_inner_' + review_code).text(" " + result.comment_count);
12863                        getReviewCommentHtml(review_code, 'accordion', t);
12864                    }
12865                    $("div[id^='sub_review_image_box_']").hide();
12866                } else
12867                    alert(result.msg);
12868            }
12869        });
12870    };
12871    var EditHide = function (idx) {
12872        var editor_form = $("._sub_form_editor_" + idx);
12873        editor_form.hide();
12874        $('.tools').show();
12875        editor_form.siblings('._comment_body').show();
12876        editor_form.closest('.comment_area').siblings().show();
12877        editor_form.closest('.comment_area').css('padding-top', '');
12878        editor_form.css('margin-top', '');
12879    };
12880
12881    var EditShow = function (idx) {
12882        var editor_form = $("._sub_form_editor_" + idx);
12883        editor_form.siblings().hide();
12884        editor_form.closest('.comment_area').siblings().hide();
12885        editor_form.closest('.comment_area').css('padding-top', 0);
12886        editor_form.css('margin-top', 0);
12887        editor_form.data('show', 'Y');
12888        editor_form.show();
12889        autosize.update(editor_form.find('textarea'));
12890    };
12891
12892    var reviewFormHide = function () {
12893        $("._sub_review_form").hide();
12894    };
12895
12896    var FormShow = function (idx) {
12897        var sub_form = $("._sub_form_" + idx);
12898
12899        sub_form.data('show', 'Y');
12900        sub_form.show();
12901        var comment_add_body = sub_form.find('._comment_add_body_' + idx);
12902
12903        $('body').off('mouseup.sub_comment')
12904            .on('mouseup.sub_comment', function (e) {
12905                var $c_target = $(e.target);
12906                var $s_form = $c_target.closest('._sub_form_' + idx + ', ._show_sub_form_btn_' + idx);
12907                if ($s_form.length == 0) {
12908
12909                    var text = comment_add_body.val();
12910                    sub_form.data('show', 'N');
12911                    if (text == '') {
12912                        $('body').off('mouseup.sub_comment');
12913                        reviewFormHide();
12914                    }
12915                }
12916            });
12917    };
12918
12919    var reviewCommentDelete = function (code) {
12920        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
12921            $.ajax({
12922                type: 'POST',
12923                data: { code: code, review_code: review_code },
12924                url: ('/shop/delete_review_comment.cm'),
12925                async: false,
12926                dataType: 'json',
12927                success: function (result) {
12928                    if (result.msg == 'SUCCESS') {
12929                        if (load_type == 'modal') {
12930                            $('#comment_count').text(result.comment_count);
12931                            getReviewCommentHtml(review_code, 'modal', $(this));
12932                        } else {
12933                            $('.categorize #comment_count_' + review_code).text(result.comment_count);
12934                            $('.categorize #comment_count_inner_' + review_code).text(result.comment_count);
12935                            $('.categorize-mobile #comment_count_' + review_code).text(result.comment_count);
12936                            $('.categorize-mobile #comment_count_inner_' + review_code).text(result.comment_count);
12937                            getReviewCommentHtml(review_code, 'accordion', $(this));
12938                        }
12939
12940
12941                    } else
12942                        alert(result.msg);
12943                }
12944            });
12945        }
12946    };
12947    return {
12948        init: function (code, type, device_type) {
12949            init(code, type, device_type);
12950        },
12951        imageUploadInit: function (idx) {
12952            imageUploadInit(idx);
12953        },
12954        getReviewCommentHtml: function (code, type, object) {
12955            getReviewCommentHtml(code, type, object);
12956        },
12957        submit: function (t, type, i) {
12958            submit(t, type, i);
12959        },
12960        Delete: function (code) {
12961            reviewCommentDelete(code);
12962        },
12963        EditShow: function (idx) {
12964            EditShow(idx);
12965        },
12966        EditHide: function (idx) {
12967            EditHide(idx);
12968        },
12969        FormShow: function (idx) {
12970            FormShow(idx);
12971        }
12972    };
12973}();
12974
12975var SITE_QNA_COMMENT = function () {
12976    var $form;
12977    var $secret;
12978    var $qna_comment_section;
12979    var qna_code;
12980
12981    var init = function (code) {
12982        qna_code = code;
12983        var $comment_area = $('.comment_textarea');
12984        $secret = $('._secret_btn');
12985        $secret.on('click', function () {
12986            if ($secret.hasClass('active')) {
12987                $secret.removeClass('active');
12988                $('#use_secret_qna').val('N');
12989            } else {
12990                $secret.addClass('active');
12991                $('#use_secret_qna').val('Y');
12992            }
12993        });
12994
12995        $form = $('#qna_form');
12996        $form.find('#qna_image_upload_btn').setUploadImage({
12997            url: '/shop/upload_image.cm',
12998            dropZone: 'icon_img_upload_wrap',
12999            singleFileUploads: true,
13000            formData: { temp: 'Y' }
13001        }, function (res, data) {
13002            $("#qna_image_box").show();
13003            $.each(data, function (e, tmp) {
13004                if (tmp.error == "" || tmp.error == null) {
13005                    var url = CDN_UPLOAD_URL + tmp.url;
13006                    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>';
13007                    $("#qna_image_box").append(html);
13008                } else {
13009                    alert(tmp.error);
13010                }
13011            });
13012        });
13013
13014    };
13015
13016    var QnaCommentDelete = function (code, is_visible) {
13017        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
13018            $.ajax({
13019                type: 'POST',
13020                data: { code: code, is_visible: is_visible },
13021                url: ('/shop/delete_qna_comment.cm'),
13022                async: false,
13023                dataType: 'json',
13024                success: function (result) {
13025                    if (result.msg == 'SUCCESS') {
13026                        getQnaCommentHtml(qna_code);
13027                    } else
13028                        alert(result.msg);
13029                }
13030            });
13031        }
13032    };
13033
13034    var imageUploadInit = function (idx) {
13035        $form = $('#qna_form');
13036        $form.find('#qna_image_upload_btn').setUploadImage({
13037            url: '/shop/upload_image.cm',
13038            dropZone: 'icon_img_upload_wrap',
13039            singleFileUploads: true,
13040            formData: { temp: 'Y' }
13041        }, function (res, data) {
13042            $("#qna_image_box").show();
13043            $.each(data, function (e, tmp) {
13044                if (tmp.error == "" || tmp.error == null) {
13045                    var url = CDN_UPLOAD_URL + tmp.url;
13046                    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>';
13047                    $("#qna_image_box").append(html);
13048                } else {
13049                    alert(tmp.error);
13050                }
13051            });
13052        });
13053
13054        $("#sub_qna_image_box_" + idx).hide();
13055
13056        $('#sub_qna_image_upload_btn_' + idx).setUploadImage({
13057            url: '/shop/upload_image.cm',
13058            dropZone: 'icon_img_upload_wrap',
13059            singleFileUploads: true,
13060            formData: { temp: 'Y' }
13061        }, function (res, data) {
13062            $("#sub_qna_image_box_" + idx).show();
13063            $.each(data, function (e, tmp) {
13064                if (tmp.error == "" || tmp.error == null) {
13065                    var url = CDN_UPLOAD_URL + tmp.url;
13066                    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>';
13067                    $("#sub_qna_image_box_" + idx).append(html);
13068                } else {
13069                    alert(tmp.error);
13070                }
13071            });
13072        });
13073
13074        $('#editor_qna_image_upload_btn_' + idx).setUploadImage({
13075            url: '/shop/upload_image.cm',
13076            dropZone: 'icon_img_upload_wrap',
13077            singleFileUploads: true,
13078            formData: { temp: 'Y' }
13079        }, function (res, data) {
13080            $("#editor_qna_image_box_" + idx).show();
13081            $.each(data, function (e, tmp) {
13082                if (tmp.error == "" || tmp.error == null) {
13083                    var url = CDN_UPLOAD_URL + tmp.url;
13084                    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>';
13085                    $("#editor_qna_image_box_" + idx).append(html);
13086                } else {
13087                    alert(tmp.error);
13088                }
13089            });
13090        });
13091    };
13092
13093
13094    var getQnaCommentHtml = function (qna_code) {
13095        $qna_comment_section = $('#qna_comment_section');
13096        $.ajax({
13097            type: 'POST',
13098            data: { qna_code: qna_code },
13099            url: ('/ajax/get_qna_comment_list.cm'),
13100            dataType: 'html',
13101            cache: false,
13102            success: function (result) {
13103                $qna_comment_section.html(result);
13104            }
13105        });
13106    };
13107    var submit = function (t, type, i) {
13108        switch (type) {
13109            case 'main': // 1:1 문의 페이지에서 바로 작성하는 폼
13110                var data = $form.serializeObject();
13111                break;
13112            case 'sub_form': // 등록되어 있는 qna에 답변을 다는 폼
13113                var data = $('#sub_qna_form_' + i).serializeObject();
13114                break;
13115            case 'editor': // 등록되어 있는 qna를 수정하는 폼
13116                var data = $('#sub_qna_editor_form_' + i).serializeObject();
13117                break;
13118        }
13119        if (!t.hasClass("btn-writing")) {
13120            t.addClass("btn-writing");
13121        }
13122        $.ajax({
13123            type: 'POST',
13124            data: { data: data, type: type },
13125            url: ('/shop/add_qna_comment.cm'),
13126            dataType: 'json',
13127            cache: false,
13128            success: function (result) {
13129                if (t.hasClass("btn-writing")) {
13130                    t.removeClass("btn-writing");
13131                }
13132                if (result.msg == 'SUCCESS') {
13133                    getQnaCommentHtml(qna_code);
13134                    $("div[id^='sub_qna_image_box_']").hide();
13135                } else
13136                    alert(result.msg);
13137            }
13138        });
13139    };
13140    var EditShow = function (idx) {
13141        var editor_form = $("._sub_form_editor_" + idx);
13142        editor_form.siblings().hide();
13143        editor_form.closest('.comment_area').siblings().hide();
13144        editor_form.closest('.comment_area').css('padding-top', 0);
13145        editor_form.css('margin-top', 0);
13146        editor_form.data('show', 'Y');
13147        editor_form.show();
13148        autosize.update(editor_form.find('textarea'));
13149
13150    };
13151
13152    var EditHide = function (idx) {
13153        var editor_form = $("._sub_form_editor_" + idx);
13154        editor_form.hide();
13155        $('.tools').show();
13156        editor_form.siblings('._comment_body').show();
13157        editor_form.closest('.comment_area').siblings().show();
13158        editor_form.closest('.comment_area').css('padding-top', '');
13159        editor_form.css('margin-top', '');
13160    };
13161
13162    var qnaFormHide = function () {
13163        $("._sub_qna_form").hide();
13164    };
13165
13166    var FormShow = function (idx) {
13167        var sub_form = $("._sub_form_" + idx);
13168
13169        sub_form.data('show', 'Y');
13170        sub_form.show();
13171        var comment_add_body = sub_form.find('._comment_add_body_' + idx);
13172
13173        $('body').off('mouseup.sub_comment')
13174            .on('mouseup.sub_comment', function (e) {
13175                var $c_target = $(e.target);
13176                var $s_form = $c_target.closest('._sub_form_' + idx + ', ._show_sub_form_btn_' + idx);
13177                if ($s_form.length == 0) {
13178
13179                    var text = comment_add_body.val();
13180                    sub_form.data('show', 'N');
13181                    if (text == '') {
13182                        $('body').off('mouseup.sub_comment');
13183                        qnaFormHide();
13184                    }
13185                }
13186            });
13187    };
13188    return {
13189        init: function (code) {
13190            init(code);
13191        },
13192        imageUploadInit: function (idx) {
13193            imageUploadInit(idx);
13194        },
13195        getQnaCommentHtml: function (qna_code) {
13196            getQnaCommentHtml(qna_code);
13197        },
13198        submit: function (t, type, i) {
13199            submit(t, type, i);
13200        },
13201        Delete: function (code, is_visible) {
13202            QnaCommentDelete(code, is_visible);
13203        },
13204        EditShow: function (idx) {
13205            EditShow(idx);
13206        },
13207        EditHide: function (idx) {
13208            EditHide(idx);
13209        },
13210        FormShow: function (idx) {
13211            FormShow(idx);
13212        }
13213    };
13214}();
13215
13216var SITE_SHOP_QNA = function () {
13217    var $qna_wrap;
13218    var $mobile_qna_wrap;
13219    var $form;
13220    var $mobile_form;
13221    var $comment_body;
13222    var $qna_image_box;
13223    var $comment_area;
13224    var $secret;
13225    var $m_secret;
13226    var qna_body;
13227    var $qna_container;
13228    var $qna_form;
13229    var body_input;
13230    var $show_secret_password;
13231    var isIOS, isSafari, $fr_m_custom, $write_header, m_sticky_container_trigger_top, $toolbarContainer;
13232
13233    var init = function (code, qna_secret_type) {
13234        $qna_wrap = $('._qna_wrap');
13235        $qna_form = $('#qna_form');
13236        qna_body = $('#qna_body');
13237        $qna_container = $('#qna_container');
13238        $secret = $('._secret_btn');
13239        $qna_form.find("#qna_image_box").hide();
13240        body_input = $('#body_input');
13241        if (qna_secret_type == 'secret') {
13242            $secret.addClass('active');
13243            $('._secret').val('Y');
13244        } else {
13245            $secret.on('click', function () {
13246                if ($secret.hasClass('active')) {
13247                    $secret.removeClass('active');
13248                    $('._secret').val('N');
13249                } else {
13250                    $secret.addClass('active');
13251                    $('._secret').val('Y');
13252                }
13253            });
13254        }
13255        if ($('._secret').val() != '') {//수정일 경우 비밀글 체크
13256            if ($('._secret').val() == 'Y') {
13257                $secret.addClass('active');
13258                $('._secret').val('Y');
13259            } else {
13260                $secret.removeClass('active');
13261                $('._secret').val('N');
13262            }
13263        }
13264
13265
13266        if (IE_VERSION < 10) {
13267            CKEDITOR.replace('qna_body', {
13268                filebrowserImageUploadUrl: '/ajax/post_image_upload.cm?board_code=' + code
13269            });
13270        } else {
13271            if (android_version() == 4) {
13272                qna_body.addClass('legacy_webview');
13273            }
13274            var image_insert_key2 = 'image_insert_key2';
13275            setFroala('#qna_body', {
13276                code: '',
13277                placeholderText: getLocalizeString('설명_문의작성시개인정보입력주의', '', '개인정보(휴대폰번호, 계좌번호 등)는 입력하지 않도록 주의해주세요.'),
13278                // 파일 첨부, 비디오, 테이블 제외
13279                toolbarButtons: {
13280                    'moreText': {
13281                        'buttons': ['bold', 'italic', 'underline', 'fontSize', 'textColor', 'strikeThrough', 'backgroundColor', 'clearFormatting', '|', 'align', 'formatOL', 'formatUL', '|', image_insert_key2, 'insertLink', '|', 'undo', 'redo', 'html', 'emoticons'],
13282                        'buttonsVisible': 50
13283                    }
13284                },
13285                toolbarButtonsMobile: {
13286                    'moreText': {
13287                        'buttons': [image_insert_key2, 'insertLink', '|', "fontSize", "bold", "italic", "underline", "strikeThrough", "align", "textColor", "clearFormatting", '|', "formatOL", "formatUL"],
13288                        'buttonsVisible': 50
13289                    }
13290                },
13291                image_upload_url: "/ajax/post_image_upload.cm",
13292                image_insert_key: image_insert_key2,
13293                image_align: 'center',
13294                image_display: 'block',
13295                toolbarStickyOffset: 38,
13296                heightMin: 200,
13297                heightMax: 600,
13298                mobile_custom: true,
13299                linkAlwaysBlank: true
13300            });
13301        }
13302
13303        isIOS = /(iPad|iPhone|iPod)/g.test(navigator.userAgent);
13304        isSafari = /^((?!chrome|android).)*safari/i.test(navigator.userAgent);
13305        $fr_m_custom = $qna_container.find('._fr-m-custom');
13306        $write_header = $qna_container.find('._write_header');
13307        m_sticky_container_trigger_top = $fr_m_custom.offset().top - $fr_m_custom.height();
13308        $toolbarContainer = $fr_m_custom.find('#toolbarContainer');
13309        if (isIOS && isSafari) {
13310            $write_header.css('position', 'absolute');
13311        }
13312        var timeoutTime = isIOS && isSafari ? 100 : 10;
13313        var resize_time;
13314        resizeStickyContainer();
13315        $(window).off('scroll.mobile_write resize.mobile_write').on('scroll.mobile_write resize.mobile_write', function () {
13316            var s_top = $(this).scrollTop();
13317            if (isIOS && isSafari) {
13318                $write_header.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 0 });
13319                if (s_top > m_sticky_container_trigger_top) {
13320                    $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 0 });
13321                }
13322            }
13323            if (resize_time) {
13324                clearTimeout(resize_time);
13325            }
13326            resize_time = setTimeout(function () {
13327                resizeStickyContainer();
13328            }, timeoutTime);
13329        });
13330        autosize($('.textarea_block textarea'));
13331    };
13332
13333    function resizeStickyContainer() {
13334        var s_top = $(this).scrollTop();
13335        if (isIOS && isSafari) {
13336            $write_header.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
13337            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
13338            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
13339            if (s_top > m_sticky_container_trigger_top) {
13340                $toolbarContainer.css({ '-webkit-transition': 'top 100ms', 'transition': 'top 100ms', 'top': s_top + 'px' });
13341                qna_body.find('.fr-view').css('padding-top', '90px');
13342            } else {
13343                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
13344                qna_body.find('.fr-view').css('padding-top', '');
13345            }
13346        } else {
13347            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
13348        }
13349        $toolbarContainer.find('.fr-toolbar').css('width', qna_body.find('.fr-view').width());
13350        if ($(window).width() >= 768) {
13351            if ($qna_container.hasClass('bg_on'))
13352                $qna_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 487);
13353            else
13354                $qna_container.find('#toolbarContainer').toggleClass('pc_sticky_toolbar', s_top > 180);
13355        }
13356    }
13357
13358    var initMobileQna = function () {
13359        $mobile_qna_wrap = $('#prod_detail_content_mobile');
13360        $mobile_form = $mobile_qna_wrap.find('#mobile_qna_form');
13361        $m_secret = $mobile_form.find('._secret_btn');
13362        $mobile_form.find("#mobile_qna_image_box").hide();
13363
13364        $m_secret.on('click', function () {
13365            if ($m_secret.hasClass('active')) {
13366                $m_secret.removeClass('active');
13367                $mobile_form.find('._secret').val('N');
13368            } else {
13369                $m_secret.addClass('active');
13370                $mobile_form.find('._secret').val('Y');
13371            }
13372        });
13373
13374        $mobile_form.find('#mobile_qna_image_upload_btn').setUploadImage({
13375            url: '/shop/upload_image.cm',
13376            dropZone: 'icon_img_upload_wrap',
13377            singleFileUploads: true,
13378            formData: { temp: 'Y' }
13379        }, function (res, data) {
13380            $("#mobile_qna_image_box").show();
13381            $.each(data, function (e, tmp) {
13382                if (tmp.error == "" || tmp.error == null) {
13383                    var url = CDN_UPLOAD_URL + tmp.url;
13384                    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>';
13385                    $("#mobile_qna_image_box").append(html);
13386                } else {
13387                    alert(tmp.error);
13388                }
13389            });
13390        });
13391        autosize($('.textarea_block textarea'));
13392    };
13393
13394    var qnaFormShow = function (t) {
13395        var sub_form = $("._sub_form_" + t);
13396
13397        sub_form.data('show', 'Y');
13398        sub_form.show();
13399        var comment_add_body = sub_form.find('._comment_add_body_' + t);
13400
13401        $('body').off('mouseup.sub_comment')
13402            .on('mouseup.sub_comment', function (e) {
13403                var $c_target = $(e.target);
13404                var $s_form = $c_target.closest('._sub_form_' + t + ', ._show_sub_form_btn_' + t);
13405                if ($s_form.length == 0) {
13406
13407                    var text = comment_add_body.val();
13408                    sub_form.data('show', 'N');
13409                    if (text == '') {
13410                        $('body').off('mouseup.sub_comment');
13411                        qnaFormHide();
13412                    }
13413                }
13414            });
13415    };
13416
13417    var EditQnaShow = function (t, c, idx) {
13418        $show_secret_password = $('#show_secret_password');
13419        var $show_link = $(event.target);
13420        if ($show_secret_password.length == 0) {
13421            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13422            $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();
13423            $show_link.after($show_secret_password);
13424        }
13425        $show_secret_password.find('input').val('');
13426        $show_secret_password.show();
13427        $show_secret_password.off('click', '._confirm')
13428            .on('click', '._confirm', function () {
13429                var secret_pass = $show_secret_password.find('input').val();
13430                CheckSecret(t, secret_pass, function () {
13431                    window.location.href = "?prod_code=" + c + "&qmode=write&back_url=&idx=" + idx;
13432                });
13433                $show_secret_password.hide();
13434            });
13435        $('body').off('mousedown.show_secret')
13436            .on('mousedown.show_secret', function (e) {
13437                var $tmp = $(e.target).closest('#show_secret_password');
13438                if ($tmp.length == 0) {
13439                    $show_secret_password.hide();
13440                    $('body').off('click.show_secret');
13441                }
13442            });
13443    };
13444
13445    var ViewQnaShow = function (t, c, idx, qna_page) {
13446        $show_secret_password = $('#show_secret_password');
13447        var $show_link = $(event.target);
13448        if ($show_secret_password.length == 0) {
13449            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13450            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" tabindex="0" style="position:absolute;
13450left:' + 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();
13451            $show_link.after($show_secret_password);
13452        }
13453        $show_secret_password.find('input').val('');
13454        $show_secret_password.show();
13455        $show_secret_password.off('click', '._confirm')
13456            .on('click', '._confirm', function () {
13457                var secret_pass = $show_secret_password.find('input').val();
13458                CheckSecret(t, secret_pass, function () {
13459                    SITE_SHOP_DETAIL.viewQnaDetail(idx, qna_page);
13460                    $show_secret_password.hide();
13461                });
13462                $show_secret_password.hide();
13463            });
13464        $('body').off('mousedown.show_secret')
13465            .on('mousedown.show_secret', function (e) {
13466                var $tmp = $(e.target).closest('#show_secret_password');
13467                if ($tmp.length == 0) {
13468                    $show_secret_password.hide();
13469                    $('body').off('click.show_secret');
13470                }
13471            });
13472    };
13473
13474    var CheckSecret = function (code, secret_pass, callback) {
13475        $.ajax({
13476            type: 'post',
13477            data: { code: code, secret_pass: secret_pass, type: 'qna' },
13478            url: '/ajax/check_review_pass.cm',
13479            dataType: 'json',
13480            success: function (result) {
13481                if (result.msg == 'SUCCESS') {
13482                    if (typeof callback == 'function')
13483                        callback();
13484                } else {
13485                    alert(result.msg);
13486                }
13487            }
13488        });
13489    };
13490
13491    var qnaEditShow = function (t) {
13492        var editor_form = $("._sub_form_editor_" + t);
13493        editor_form.siblings().hide();
13494
13495        editor_form.data('show', 'Y');
13496        editor_form.show();
13497        autosize.update(editor_form.find('textarea'));
13498
13499    };
13500
13501    var qnaEditHide = function (t) {
13502        var editor_form = $("._sub_form_editor_" + t);
13503        editor_form.hide();
13504    };
13505
13506    var qnaFormHide = function () {
13507        $("._sub_qna_form").hide();
13508    };
13509
13510    var qnaDelete = function (code, prod_code, secret_pass, q_p) {
13511        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
13512            $.ajax({
13513                type: 'POST',
13514                data: { code: code, prod_code: prod_code, secret_pass: secret_pass, qna_page: q_p },
13515                url: ('/shop/delete_qna.cm'),
13516                dataType: 'json',
13517                success: function (result) {
13518                    if (result.msg == 'SUCCESS') {
13519                        if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab('qna', 0, q_p);
13520                        else SITE_SHOP_DETAIL.changeContentPCTab('qna', 0, q_p);
13521                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.addCountUserProfileAttr('qnaCount', -1);
13522
13523                        SITE_SHOP_DETAIL.getReviewQnaCount(prod_code);
13524
13525                        $.cocoaDialog.close();
13526                    } else
13527                        alert(result.msg);
13528                }
13529            });
13530        }
13531    };
13532
13533    var qnaModify = function (idx, prod_code, secret_pass, is_book, code) {
13534        $.ajax({
13535            type: 'POST',
13536            data: { idx: idx, prod_code: prod_code, secret_pass: secret_pass, is_book: is_book, code: code },
13537            url: ('/shop/show_secret_qna.cm'),
13538            dataType: 'json',
13539            success: function (result) {
13540                if (result.msg == 'SUCCESS') {
13541                    qnaEditShow(idx);
13542                } else
13543                    alert(result.msg);
13544            }
13545        });
13546    };
13547
13548    var qnaShow = function (idx, prod_code, secret_pass, is_book, code) {
13549        $.ajax({
13550            type: 'POST',
13551            data: { idx: idx, prod_code: prod_code, secret_pass: secret_pass, is_book: is_book, code: code },
13552            url: ('/shop/show_secret_qna.cm'),
13553            dataType: 'json',
13554            success: function (result) {
13555                if (result.msg == 'SUCCESS') {
13556                    $("._comment_body_" + idx).html(result.html);
13557                    $('._comment_body_' + idx).closest('.comment_area').find('#_show_content_btn').hide();		// 보기 버튼 제거
13558                    if (result.isSubComment) {
13559                        for (var i in result.sub_comment) {
13560                            var sub_data = result.sub_comment[i];
13561                            $("._comment_child_" + idx + "_" + sub_data.idx).html(sub_data.html);
13562                        }
13563                    }
13564                } else
13565                    alert(result.msg);
13566            }
13567        });
13568    };
13569
13570    var qnaConfirmShow = function (e, idx, prod_code, type, code) {
13571        $show_secret_password = $('#show_secret_password');
13572        var $show_link = $(event.target);
13573        if ($show_secret_password.length == 0) {
13574            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13575            $show_secret_password = $('<div class="remove-pop" id="show_secret_password" tabindex="0" style="position:absolute;
13575left:' + 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();
13576            $show_link.after($show_secret_password);
13577        }
13578
13579        $show_secret_password.find('input').val('');
13580        $show_secret_password.show();
13581        $show_secret_password.off('click', '._confirm')
13582            .on('click', '._confirm', function () {
13583                var secret_pass = $show_secret_password.find('input').val();
13584                $show_secret_password.hide();
13585                switch (type) {
13586                    case 'show':
13587                        qnaShow(idx, prod_code, secret_pass, 'N', code);
13588                        break;
13589                    case 'modify':
13590                        qnaModify(idx, prod_code, secret_pass, 'N', code);
13591                        break;
13592                    case 'delete':
13593                        qnaDelete(code, prod_code, secret_pass);
13594                        break;
13595
13596                }
13597            });
13598        $('body').off('mousedown.show_secret')
13599            .on('mousedown.show_secret', function (e) {
13600                var $tmp = $(e.target).closest('#show_secret_password');
13601                if ($tmp.length == 0) {
13602                    $show_secret_password.hide();
13603                    $('body').off('click.show_secret');
13604                }
13605            });
13606    };
13607
13608    var imageUploadInit = function (n) {
13609        $("#sub_qna_image_box_" + n).hide();
13610
13611        $('#sub_qna_image_upload_btn_' + n).setUploadImage({
13612            url: '/shop/upload_image.cm',
13613            dropZone: 'icon_img_upload_wrap',
13614            singleFileUploads: true,
13615            formData: { temp: 'Y' }
13616        }, function (res, data) {
13617            $("#sub_qna_image_box_" + n).show();
13618            $.each(data, function (e, tmp) {
13619                if (tmp.error == "" || tmp.error == null) {
13620                    var url = CDN_UPLOAD_URL + tmp.url;
13621                    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>';
13622                    $("#sub_qna_image_box_" + n).append(html);
13623                } else {
13624                    alert(tmp.error);
13625                }
13626            });
13627        });
13628
13629        $('#editor_qna_image_upload_btn_' + n).setUploadImage({
13630            url: '/shop/upload_image.cm',
13631            dropZone: 'icon_img_upload_wrap',
13632            singleFileUploads: true,
13633            formData: { temp: 'Y' }
13634        }, function (res, data) {
13635            $("#editor_qna_image_box_" + n).show();
13636            $.each(data, function (e, tmp) {
13637                if (tmp.error == "" || tmp.error == null) {
13638                    var url = CDN_UPLOAD_URL + tmp.url;
13639                    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>';
13640                    $("#editor_qna_image_box_" + n).append(html);
13641                } else {
13642                    alert(tmp.error);
13643                }
13644            });
13645        });
13646    };
13647
13648    var submit = function () {
13649        if (IE_VERSION < 10) {
13650            var body = CKEDITOR.instances.qna_body.getData();
13651            body_input.val(body);
13652            $qna_form.submit();
13653        } else {
13654            if (qna_body.hasClass('fr-code-view'))
13655                FroalaEditor('#qna_body').codeView.toggle();
13656            var body = FroalaEditor('#qna_body').html.get(true);
13657            body_input.val(body);
13658            $qna_form.submit();
13659        }
13660    };
13661
13662    var qnaCancel = function () {
13663        if (isIOS && isSafari) {
13664            var s_top = $(this).scrollTop();
13665            $write_header.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': 0 });
13666            $fr_m_custom.toggleClass('m_sticky_container', s_top > m_sticky_container_trigger_top);
13667            $fr_m_custom.toggleClass('m_sticky_container_ios', s_top > m_sticky_container_trigger_top);
13668            if (s_top > m_sticky_container_trigger_top) {
13669                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'position': 'fixed', 'top': $write_header.height() + 'px' });
13670            } else {
13671                $toolbarContainer.css({ '-webkit-transition': 'none', 'transition': 'none', 'top': 'auto' });
13672            }
13673        }
13674        history.go(-1);
13675    };
13676
13677    var createHtml = function (prod_idx, review_page, qna_page, paging_on) {
13678        const isMobile_width = (window.innerWidth < SHOP_CONST.WIDTH_MOBILE);
13679        const review_url = isMobile_width ? "/shop/prod_qna_mobile_html.cm" : "/shop/prod_qna_pc_html.cm";
13680
13681        $qna_wrap = $('._qna_wrap');
13682        if ($qna_wrap.length) {
13683            var is_cache = false;
13684            var callback = function (result) {
13685                $qna_wrap.html(result);
13686                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);
13687            };
13688
13689            var html = IMWEB_SESSIONSTORAGE.get(`${isMobile_width ? "PROD_QNA_MOBILE_" : "PROD_QNA_PC_"}` + prod_idx + "_" + review_page + '_' + qna_page);
13690            if (html) {
13691                is_cache = true;
13692                callback(html);
13693            } else {
13694                $.ajax({
13695                    type: 'POST',
13696                    data: { prod_idx: prod_idx, review_page: review_page, qna_page: qna_page },
13697                    url: review_url,
13698                    dataType: 'html',
13699                    cache: false,
13700                    success: callback
13701                });
13702            }
13703        }
13704    };
13705
13706    var qnaHide = function (t, c, q_p) {
13707        if (confirm(LOCALIZE.설명_숨기시겠습니까())) {
13708            $.ajax({
13709                type: 'POST',
13710                data: { code: t, prod_code: c, is_visible: false },
13711                url: ('/shop/delete_qna.cm'),
13712                dataType: 'json',
13713                cache: false,
13714                success: function (result) {
13715                    if (result.msg == 'SUCCESS') {
13716                        if (IS_MOBILE) SITE_SHOP_DETAIL.changeContentTab('qna', q_p);
13717                        else SITE_SHOP_DETAIL.changeContentPCTab('qna', q_p);
13718                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.addCountUserProfileAttr('qnaCount', -1);
13719                        $.cocoaDialog.close();
13720                    } else
13721                        alert(result.msg);
13722                }
13723            });
13724        }
13725    };
13726
13727    var qnaDeleteShow = function (qna_code, prod_code) {
13728        var $show_secret_password = $('#show_secret_password');
13729        var $show_link = $(event.target);
13730        if ($show_secret_password.length == 0) {
13731            var left = $(window).width() >= 768 ? 200 : ($(window).width() - 325) / 2;
13732            $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();
13733            $show_link.after($show_secret_password);
13734        }
13735        $show_secret_password.find('input').val('');
13736        $show_secret_password.show();
13737        $show_secret_password.off('click', '._confirm')
13738            .on('click', '._confirm', function () {
13739                var secret_pass = $show_secret_password.find('input').val();
13740                CheckSecret(qna_code, secret_pass, function () {
13741                    qnaDelete(qna_code, prod_code, secret_pass);
13742                });
13743                $show_secret_password.hide();
13744            });
13745        $('body').off('mousedown.show_secret')
13746            .on('mousedown.show_secret', function (e) {
13747                var $tmp = $(e.target).closest('#show_secret_password');
13748                if ($tmp.length == 0) {
13749                    $show_secret_password.hide();
13750                    $('body').off('click.show_secret');
13751                }
13752            });
13753    };
13754
13755
13756    return {
13757        init: function (code, qna_secret_type) {
13758            init(code, qna_secret_type);
13759        },
13760        initMobileQna: function () {
13761            initMobileQna();
13762        },
13763        submit: function () {
13764            submit();
13765        },
13766        qnaCancel: function () {
13767            qnaCancel();
13768        },
13769        FormShow: function (t) {
13770            qnaFormShow(t);
13771        },
13772        EditQnaShow: function (t, c, idx) {
13773            EditQnaShow(t, c, idx);
13774        },
13775        ViewQnaShow: function (t, c, idx, qna_page) {
13776            ViewQnaShow(t, c, idx, qna_page);
13777        },
13778        Delete: function (code, prod_code, q_p) {
13779            qnaDelete(code, prod_code, q_p);
13780        },
13781        EditShow: function (t) {
13782            qnaEditShow(t);
13783        },
13784        EditHide: function (t) {
13785            qnaEditHide(t);
13786        },
13787        imageUploadInit: function (n) {
13788            imageUploadInit(n);
13789        },
13790        createHtml: function (prod_idx, review_page, qna_page, paging_on) {
13791            createHtml(prod_idx, review_page, qna_page, paging_on);
13792        },
13793        confirmShow: function (e, idx, prod_code, type, code) {
13794            qnaConfirmShow(e, idx, prod_code, type, code);
13795        },
13796        Hide: function (t, c, q_p) {
13797            qnaHide(t, c, q_p);
13798        },
13799        DeleteShow: function (qna_code, prod_code) {
13800            qnaDeleteShow(qna_code, prod_code);
13801        },
13802    };
13803}();
13804
13805var SITE_PERSONAL_QNA = function () {
13806    var $personal_qna_wrap;
13807    var $mobile_qna_wrap;
13808    var $form;
13809    var $mobile_form;
13810    var $comment_body;
13811    var $qna_image_box;
13812
13813
13814    var $comment_area;
13815    var $secret;
13816
13817    var init = function () {
13818        $form = $('#qna_form');
13819        $secret = $form.find('._secret');
13820
13821        $secret.on('click', function () {
13822            if ($secret.hasClass('active')) {
13823                $secret.removeClass('active');
13824                $form.find('#secret').val('N');
13825            } else {
13826                $secret.addClass('active');
13827                $form.find('#secret').val('Y');
13828            }
13829        });
13830
13831        $('#qna_image_upload_btn').setUploadImage({
13832            url: '/shop/upload_image.cm',
13833            dropZone: 'icon_img_upload_wrap',
13834            singleFileUploads: true,
13835            formData: { temp: 'Y' }
13836        }, function (res, data) {
13837            $("#qna_image_box").show();
13838            $.each(data, function (e, tmp) {
13839                if (tmp.error == "" || tmp.error == null) {
13840                    var url = CDN_UPLOAD_URL + tmp.url;
13841                    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>';
13842                    $("#qna_image_box").append(html);
13843                } else {
13844                    alert(tmp.error);
13845                }
13846            });
13847        });
13848        autosize($('.textarea_block textarea'));
13849    };
13850
13851    var submit = function (t, type, i) {
13852        switch (type) {
13853            case 'main': // 1:1 문의 페이지에서 바로 작성하는 폼
13854                var data = $form.serializeObject();
13855                break;
13856            case 'sub_form': // 등록되어 있는 qna에 답변을 다는 폼
13857                var data = $('#sub_qna_form_' + i).serializeObject();
13858                break;
13859            case 'editor': // 등록되어 있는 qna를 수정하는 폼
13860                var data = $('#sub_qna_editor_form_' + i).serializeObject();
13861                break;
13862        }
13863        if (!t.hasClass("btn-writing")) {
13864            t.addClass("btn-writing");
13865        }
13866        $.ajax({
13867            type: 'POST',
13868            data: { data: data, type: type, personal_qna: 'Y' },
13869            url: ('/shop/add_qna.cm'),
13870            dataType: 'json',
13871            cache: false,
13872            success: function (result) {
13873                if (t.hasClass("btn-writing")) {
13874                    t.removeClass("btn-writing");
13875                }
13876                if (result.msg == 'SUCCESS') {
13877                    createHtml(result.page);
13878                } else
13879                    alert(result.msg);
13880            }
13881        });
13882    };
13883
13884    var Delete = function (code, prod_code) {
13885        if (confirm(LOCALIZE.설명_삭제하시겠습니까())) {
13886            $.ajax({
13887                type: 'POST',
13888                data: { code: code, prod_code: prod_code },
13889                url: ('/shop/delete_qna.cm'),
13890                dataType: 'json',
13891                success: function (result) {
13892                    if (result.msg == 'SUCCESS') {
13893                        if (typeof CHANNEL_PLUGIN != 'undefined') CHANNEL_PLUGIN.addCountUserProfileAttr('qnaCount', -1);
13894                        createHtml();
13895                    } else
13896                        alert(result.msg);
13897                }
13898            });
13899        }
13900    };
13901
13902    var FormShow = function (t) {
13903        var sub_form = $("._sub_form_" + t);
13904
13905        sub_form.data('show', 'Y');
13906        sub_form.show();
13907        var comment_add_body = sub_form.find('._comment_add_body_' + t);
13908
13909        $('body').off('mouseup.sub_comment')
13910            .on('mouseup.sub_comment', function (e) {
13911                var $c_target = $(e.target);
13912                var $s_form = $c_target.closest('._sub_form_' + t + ', ._show_sub_form_btn_' + t);
13913                if ($s_form.length == 0) {
13914
13915                    var text = comment_add_body.val();
13916                    sub_form.data('show', 'N');
13917                    if (text == '') {
13918                        $('body').off('mouseup.sub_comment');
13919                        FormHide();
13920                    }
13921                }
13922            });
13923    };
13924
13925    var FormHide = function () {
13926        $("._sub_qna_form").hide();
13927    };
13928
13929    var EditShow = function (t) {
13930        var editor_form = $("._sub_form_editor_" + t);
13931        editor_form.siblings('.block-postmeta').find('.write').hide();
13932        editor_form.siblings('.block-postmeta').find('.comment_area').hide();
13933        editor_form.data('show', 'Y');
13934        editor_form.show();
13935    };
13936
13937    var EditHide = function (t) {
13938        var editor_form = $("._sub_form_editor_" + t);
13939        editor_form.hide();
13940        editor_form.siblings('.block-postmeta').find('.write').show();
13941        editor_form.siblings('.block-postmeta').find('.comment_area').show();
13942    };
13943
13944    var imageUploadInit = function (n) {
13945        $("#sub_image_box_" + n).hide();
13946
13947        $('#sub_image_upload_btn_' + n).setUploadImage({
13948            url: '/shop/upload_image.cm',
13949            dropZone: 'icon_img_upload_wrap',
13950            singleFileUploads: true,
13951            formData: { temp: 'Y' }
13952        }, function (res, data) {
13953            $("#sub_image_box_" + n).show();
13954            $.each(data, function (e, tmp) {
13955                if (tmp.error == "" || tmp.error == null) {
13956                    var url = CDN_UPLOAD_URL + tmp.url;
13957                    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>';
13958                    $("#sub_image_box_" + n).append(html);
13959                } else {
13960                    alert(tmp.error);
13961                }
13962            });
13963        });
13964
13965        $('#editor_image_upload_btn_' + n).setUploadImage({
13966            url: '/shop/upload_image.cm',
13967            dropZone: 'icon_img_upload_wrap',
13968            singleFileUploads: true,
13969            formData: { temp: 'Y' }
13970        }, function (res, data) {
13971            $("#editor_image_box_" + n).show();
13972            $.each(data, function (e, tmp) {
13973                if (tmp.error == "" || tmp.error == null) {
13974                    var url = CDN_UPLOAD_URL + tmp.url;
13975                    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>';
13976                    $("#editor_image_box_" + n).append(html);
13977                } else {
13978                    alert(tmp.error);
13979                }
13980            });
13981        });
13982    };
13983
13984    var createHtml = function (page) {
13985        $personal_qna_wrap = $('._personal_qna_wrap');
13986        $.ajax({
13987            type: 'POST',
13988            data: { page: page },
13989            url: ('/shop/personal_qna_list.cm'),
13990            dataType: 'html',
13991            cache: false,
13992            success: function (result) {
13993                $personal_qna_wrap.html(result);
13994            }
13995        });
13996    };
13997
13998    return {
13999        init: function () {
14000            init();
14001        },
14002        submit: function (t, type, i) {
14003            submit(t, type, i);
14004        },
14005        Delete: function (code, prod_code) {
14006            Delete(code, prod_code);
14007        },
14008        FormShow: function (t) {
14009            FormShow(t);
14010        },
14011        EditShow: function (t) {
14012            EditShow(t);
14013        },
14014        EditHide: function (t) {
14015            EditHide(t);
14016        },
14017        imageUploadInit: function (n) {
14018            imageUploadInit(n);
14019        },
14020        createHtml: function (page) {
14021            createHtml(page);
14022        }
14023    };
14024}();
14025
14026/**
14027 * 배송 서비스 관련 기능
14028 */
14029const SHIPPING_SERVICE = function () {
14030    const isActive = function () {
14031        const activeShippingTab = document.querySelector('.shipping-service-tab.active');
14032        return activeShippingTab !== null && activeShippingTab.getAttribute('data-service-code') !== '';
14033    };
14034
14035    const isZipcodeAvailable = function () {
14036        const activeShippingTab = document.querySelector('.shipping-service-tab.active');
14037        return activeShippingTab && activeShippingTab.getAttribute('data-zipcode-available') === 'Y';
14038    };
14039
14040    /**
14041     * 현재 배송지가 설정되어 있는지 확인
14042     * @returns {boolean} - 배송지가 설정되어 있으면 true, 아니면 false
14043     */
14044    const hasAddress = function () {
14045        const addressLink = document.querySelector('.shipping-service-address-link');
14046        if (!addressLink) {
14047            return false;
14048        }
14049        const addressCode = addressLink.getAttribute('data-address-code');
14050        return addressCode !== null && addressCode !== '';
14051    };
14052
14053    /**
14054     * 비회원 여부 확인
14055     * @returns {boolean} - 비회원이면 true, 회원이면 false
14056     */
14057    const isGuest = function () {
14058        return IS_GUEST === true;
14059    };
14060
14061    /**
14062     * 비회원 일반배송 확인 모달
14063     * @returns {Promise<boolean>} - 일반배송 구매 시 true, 배송 다시 선택 시 false
14064     */
14065    const confirmGuest = async function () {
14066        // 회원인 경우 바로 true 반환
14067        if (!isGuest()) {
14068            return true;
14069        }
14070
14071        const confirmed = await confirmModal({
14072            title: '일반배송으로 구매가 진행돼요',
14073            description: [
14074                '비회원으로 구매 시 일반배송으로 자동 변경돼요.',
14075                '로그인하면 다른 배송 방법으로 주문할 수 있어요.'
14076            ],
14077            confirmText: '일반배송 구매',
14078            cancelText: '배송 다시 선택하기'
14079        });
14080
14081        return confirmed;
14082    };
14083
14084    /**
14085     * 외부채널 구매 시 일반배송 확인 모달
14086     * @returns {Promise<boolean>} - 일반배송 구매 시 true, 배송 다시 선택 시 false
14087     */
14088    const confirmExternalChannel = async function () {
14089        const confirmed = await confirmModal({
14090            title: '일반배송으로 구매가 진행돼요',
14091            description: '선택한 배송은 톡체크아웃·네이버페이와 같은 간편구매에서는 이용할 수 없어요. 구매는 일반배송으로 자동 변경돼요.',
14092            confirmText: '일반배송 구매',
14093            cancelText: '배송 다시 선택하기'
14094        });
14095        return confirmed;
14096    };
14097
14098    const validateAddress = async function () {
14099        if (hasAddress()) {
14100            return true;
14101        }
14102
14103        const confirmed = await confirmModal({
14104            title: '배송지를 등록해야 해당 배송방법을 이용할 수 있어요',
14105            description: '등록된 배송지가 없어서 다른 배송 방법을 선택할 수 없어요. 배송지를 추가하면 이용할 수 있어요.',
14106            confirmText: '배송지 추가',
14107            cancelText: '취소'
14108        });
14109        // 취소 버튼 클릭 시 - 아무것도 하지 않음 (사용자가 배송 다시 선택)
14110        if (confirmed) {
14111            // 모바일 체크 (화면 너비 768px 이하 )
14112            const isMobile = window.innerWidth <= 768;
14113            const redirectUrl = '/shop_mypage/address';
14114
14115            if (isMobile) {
14116                window.location.href = redirectUrl;
14117                return;
14118            }
14119
14120            // PC 환경: 팝업 열기
14121            const popupWidth = 500;
14122            const popupHeight = 800;
14123            const left = (screen.width - popupWidth) / 2;
14124            const top = (screen.height - popupHeight) / 2;
14125            const features = `width=${popupWidth},height=${popupHeight},left=${left},top=${top},scrollbars=yes,resizable=yes`;
14126            window.open(redirectUrl, 'addressPopup', features);
14127            return;
14128        }
14129    }
14130
14131    const confirmZipcode = async function () {
14132        if (isZipcodeAvailable()) {
14133            return true;
14134        }
14135        const confirmed = await confirmModal({
14136            title: '일반배송으로 구매가 진행돼요',
14137            description: [
14138                '선택한 배송은 배송 불가 지역이라 이용할 수 없어요.',
14139                '구매는 일반배송으로 자동 변경돼요.'
14140            ],
14141            confirmText: '일반배송 구매',
14142            cancelText: '배송 다시 선택하기'
14143        });
14144        // 취소 버튼 클릭 시 - 아무것도 하지 않음 (사용자가 배송 다시 선택)
14145        if (!confirmed) {
14146            return false;
14147        }
14148
14149        return true;
14150    };
14151    /**
14152     * 모달 닫기
14153     */
14154    const closeModal = function () {
14155        const $modal = $('#shipping_general_modal');
14156        if ($modal.length) {
14157            $modal.fadeOut(200, function() {
14158                $(this).remove();
14159            });
14160        }
14161    };
14162
14163    /**
14164     * 확인 모달을 표시하고 사용자의 선택을 Promise로 반환
14165     * @param {Object} options - 모달 옵션
14166     * @param {string} options.title - 모달 제목
14167     * @param {string|string[]} options.description - 모달 설명 (문자열 또는 문자열 배열)
14168     * @param {string} options.confirmText - 확인 버튼 텍스트
14169     * @param {string} options.cancelText - 취소 버튼 텍스트
14170     * @returns {Promise<boolean>} - 확인 시 true, 취소 시 false
14171     */
14172    const confirmModal = function (options) {
14173        if (options === undefined) {
14174            return Promise.resolve(true);
14175        }
14176        // description이 문자열이면 배열로 변환
14177        const descriptionArray = Array.isArray(options.description) ? options.description : [options.description];
14178
14179        // description HTML 생성
14180        const descriptionHtml = descriptionArray.map(function(text, index) {
14181            const marginBottom = index < descriptionArray.length - 1 ? '4px' : '0';
14182            return `<p style="margin: 0 0 ${marginBottom} 0;">${text}</p>`;
14183        }).join('');
14184
14185        return new Promise(function (resolve, reject) {
14186            // 기존 모달이 있으면 제거
14187            closeModal();
14188
14189            // 모달 HTML 생성
14190            const modalHtml = `\
14191                <div id="shipping_general_modal" style="\
14192                    position: fixed;\
14193                    top: 0;\
14194                    left: 0;\
14195                    width: 100%;\
14196                    height: 100%;\
14197                    z-index: 17000;\
14198                    display: none;\
14199                ">\
14200                    <!-- Overlay (ë°°ê²½) -->\
14201                    <div class="modal-overlay" style="\
14202                        position: absolute;\
14203                        top: 0;\
14204                        left: 0;\
14205                        width: 100%;\
14206                        height: 100%;\
14207                        background-color: rgba(0, 0, 0, 0.5);\
14208                        z-index: 1;\
14209                    "></div>\
14210                    <!-- Modal Container -->\
14211                    <div style="\
14212                        position: relative;\
14213                        width: 100%;\
14214                        height: 100%;\
14215                        display: flex;\
14216                        align-items: center;\
14217                        justify-content: center;\
14218                        z-index: 2;\
14219                    ">\
14220                        <!-- Modal Content -->\
14221                        <div style="\
14222                            background: #ffffff;\
14223                            border-radius: 4px;\
14224                            box-shadow: 0px 0px 1px 0px rgba(75,81,91,0.2), 0px 20px 40px 0px rgba(75,81,91,0.2);\
14225                            overflow: hidden;\
14226                            max-width: 448px;\
14227                            width: calc(100% - 40px);\
14228                            margin: 0 20px;\
14229                        ">\
14230                            <!-- Modal Header -->\
14231                            <div style="\
14232                                background: white;\
14233                                padding: 20px;\
14234                                display: flex;\
14235                                flex-direction: column;\
14236                                gap: 4px;\
14237                            ">\
14238                                <div style="width: 100%;">\
14239                                    <p style="\
14240                                        font-weight: 700;\
14241                                        font-size: 20px;\
14242                                        line-height: 1.6;\
14243                                        color: #15181e;\
14244                                        margin: 0;\
14245                                    ">${options.title}</p>\
14246                                </div>\
14247                                <div style="width: 100%; margin-top: 4px;">\
14248                                    <div style="\
14249                                        font-weight: 400;\
14250                                        font-size: 14px;\
14251                                        line-height: 1.6;\
14252                                        color: #717680;\
14253                                    ">${descriptionHtml}</div>\
14254                                </div>\
14255                            </div>\
14256                            <!-- Modal Footer -->\
14257                            <div style="\
14258                                padding: 0 20px 20px 20px;\
14259                                display: flex;\
14260                                flex-direction: column;\
14261                                gap: 8px;\
14262                            ">\
14263                                <!-- 확인 버튼 -->\
14264                                <button class="btn-confirm" style="\
14265                                    width: 100%;\
14266                                    height: 48px;\
14267                                    background-color: var(--unit-style-brand_color, #006fff);\
14268                                    border: none;\
14269                                    border-radius: 8px;\
14270                                    font-weight: 700;\
14271                                    font-size: 16px;\
14272                                    line-height: 1.6;\
14273                                    color: #ffffff;\
14274                                    cursor: pointer;\
14275                                    transition: background-color 0.2s;\
14276                                ">${options.confirmText}</button>\
14277                                <!-- 취소 버튼 -->\
14278                                <button class="btn-cancel" style="\
14279                                    width: 100%;\
14280                                    height: 48px;\
14281                                    background-color: #ffffff;\
14282                                    border: 1px solid #d2d7e0;\
14283                                    border-radius: 8px;\
14284                                    font-weight: 700;\
14285                                    font-size: 16px;\
14286                                    line-height: 1.6;\
14287                                    color: #15181e;\
14288                                    cursor: pointer;\
14289                                    transition: background-color 0.2s;\
14290                                ">${options.cancelText}</button>\
14291                            </div>\
14292                        </div>\
14293                    </div>\
14294                </div>`;
14295
14296            // DOM에 모달 추가
14297            $('body').append(modalHtml);
14298            const $modal = $('#shipping_general_modal');
14299
14300            // 확인 버튼 이벤트
14301            $modal.find('.btn-confirm').on('click', function() {
14302                closeModal();
14303                resolve(true);
14304            });
14305
14306            // 취소 버튼 이벤트
14307            $modal.find('.btn-cancel').on('click', function() {
14308                closeModal();
14309                resolve(false);
14310            });
14311
14312            // 배경 클릭 시 모달 닫기
14313            $modal.find('.modal-overlay').on('click', function() {
14314                closeModal();
14315                resolve(false);
14316            });
14317
14318            // ESC 키로 모달 닫기
14319            $(document).on('keydown.shipping_modal', function(e) {
14320                if (e.keyCode === 27) { // ESC
14321                    closeModal();
14322                    resolve(false);
14323                    $(document).off('keydown.shipping_modal');
14324                }
14325            });
14326
14327            // 모달 표시 (페이드인 효과)
14328            $modal.fadeIn(200);
14329        });
14330    };
14331
14332    /**
14333     * 선물하기 빠른배송 불가 확인 모달
14334     * 빠른배송이 활성화된 상태에서 선물하기 시도 시 일반배송으로 변경 안내
14335     * @returns {Promise<boolean>} - 일반배송 구매 시 true, 배송 다시 선택 시 false
14336     */
14337    const confirmGift = async function () {
14338        // 빠른배송이 활성화되어 있지 않으면 바로 true 반환
14339        if (!isActive()) {
14340            return true;
14341        }
14342
14343        const confirmed = await confirmModal({
14344            title: '일반배송으로 선물하기가 진행돼요',
14345            description: [
14346                '선택한 배송은 선물하기에서 사용할 수 없어요.',
14347                '선물하기는 일반배송으로 자동 변경돼요.'
14348            ],
14349            confirmText: '일반배송으로 선물하기',
14350            cancelText: '배송 다시 선택하기'
14351        });
14352
14353        return confirmed;
14354    };
14355
14356    const verify = async function ({ type } = {}) {
14357        const isShippingServiceActive = typeof SHIPPING_SERVICE !== 'undefined' && SHIPPING_SERVICE.isActive();
14358        if (!isShippingServiceActive) {
14359            return true;
14360        }
14361        if (isShippingServiceActive) {
14362            // 외부채널 구매 시 일반배송 확인 모달
14363            if (type === 'npay' || type === 'talkpay') {
14364                const confirmed = await SHIPPING_SERVICE.confirmExternalChannel();
14365                if (!confirmed) {
14366                    return false;
14367                }
14368                SITE_SHOP_DETAIL.setShippingTemplateCode('');
14369                return true;
14370            }
14371
14372
14373            // 비회원인 경우 일반배송 확인 모달
14374            const confirmedGuest = await SHIPPING_SERVICE.confirmGuest();
14375            if (confirmedGuest && SHIPPING_SERVICE.isGuest()) {
14376                // 비회원이 일반배송 구매 선택 시 일반배송으로 변경
14377                SITE_SHOP_DETAIL.setShippingTemplateCode('');
14378                return true;
14379            } else if (!confirmedGuest) {
14380                return false;
14381            }
14382
14383            // 회원인 경우 배송지 확인
14384            if (!SHIPPING_SERVICE.isGuest()) {
14385                const isValidAddress = await SHIPPING_SERVICE.validateAddress();
14386                if (!isValidAddress) {
14387                    return false;
14388                }
14389
14390                const isZipcodeAvailable = SHIPPING_SERVICE.isZipcodeAvailable();
14391                if (!isZipcodeAvailable) {
14392                    const confirmedZipcode = await SHIPPING_SERVICE.confirmZipcode();
14393                    // 일반배송으로 구매 시 일반배송 템플릿 코드 설정
14394                    if (confirmedZipcode) {
14395                        SITE_SHOP_DETAIL.setShippingTemplateCode('');
14396                        return true;
14397                    } else {
14398                        return false;
14399                    }
14400                }
14401            }
14402        }
14403        return true;
14404    };
14405
14406    return {
14407        isActive,
14408        isZipcodeAvailable,
14409        isGuest,
14410        validateAddress,
14411        confirmGuest,
14412        confirmZipcode,
14413        confirmGift,
14414        confirmExternalChannel,
14415        verify,
14416    };
14417}();
14418
14419/**
14420 * 크리에이터 혜택 종료일 카운트다운 배너
14421 *
14422 * 모바일은 기존 sticky 정책을 유지하고, 사이트 헤더 영역 하단에서 16px
14423 * 아래에 위치하도록 --creator-banner-top CSS 변수를 주입한다.
14424 * PC는 상품 이미지 영역(#prod_image_list) 내부로 이동해 absolute 배치한다.
14425 *
14426 * 측정 방식:
14427 *   #doz_header_wrap 의 getBoundingClientRect().bottom (viewport 기준 좌표) 사용.
14428 *   - 스크롤 전: wrap 의 실제 끝점 (자연 흐름)
14429 *   - 스크롤 후: wrap 이 viewport 위로 사라지면 wrap 안의 ._new_fixed_header
14430 *               (viewport 상단에 박힌 fixed 헤더) 의 bottom 으로 fallback
14431 *   - 운영자 추가 top_banner / fixed_header_section 도 동일 방식으로 max 누적
14432 *
14433 *   getBoundingClientRect 는 element 가 자연 흐름이든 fixed 든 동일하게
14434 *   viewport 기준 실시간 좌표를 반환하므로 body class 분기가 불필요.
14435 */
14436var CREATOR_BENEFIT_BANNER = (function () {
14437    var $banner = null;
14438    var $countdown = null;
14439    var $slides = null;
14440    var $placeholder = null;
14441    var slideIndex = 0;
14442    var endTimestamp = 0;
14443    var rafScheduled = false;
14444    var countdownTimer = null;
14445    var slideTimer = null;
14446    var BANNER_GAP_PX_MOBILE = 16; // 모바일:   헤더 하단으로부터 16px
14447    var MOBILE_BREAKPOINT_PX = (typeof SHOP_CONST !== 'undefined' && SHOP_CONST.WIDTH_MOBILE) ? SHOP_CONST.WIDTH_MOBILE : 768;
14448    var SLIDE_INTERVAL_MS = 3000;  // 텍스트 슬라이드 롤링 간격
14449
14450    function isMobileViewport() {
14451        return window.innerWidth < MOBILE_BREAKPOINT_PX;
14452    }
14453
14454    function getHeaderOffsetTop() {
14455        var maxBottom = 0;
14456
14457        // #doz_header_wrap — 사이트 빌더 헤더 본체
14458        var $wrap = $('#doz_header_wrap');
14459        if ($wrap.length) {
14460            var wrapRect = $wrap[0].getBoundingClientRect();
14461            if (wrapRect.bottom > maxBottom) {
14462                maxBottom = wrapRect.bottom;
14463            }
14464            // wrap 자체가 viewport 위로 사라진 경우, 안에 박힌 fixed 헤더의 bottom 으로 fallback
14465            var $fixed = $wrap.find('._new_fixed_header').filter(':visible');
14466            if ($fixed.length) {
14467                var fixedRect = $fixed[0].getBoundingClientRect();
14468                if (fixedRect.bottom > maxBottom) {
14469                    maxBottom = fixedRect.bottom;
14470                }
14471            }
14472        }
14473
14474        // 운영자 추가 상단 배너 (reseller_banner / 일반 banner-container)
14475        $('.top_banner_wrap:visible, .banner-container:visible').each(function () {
14476            var r = this.getBoundingClientRect();
14477            if (r.bottom > maxBottom) maxBottom = r.bottom;
14478        });
14479
14480        // 페이지 내 추가 fixed 섹션
14481        $('._fixed_header_section:visible').each(function () {
14482            var r = this.getBoundingClientRect();
14483            if (r.bottom > maxBottom) maxBottom = r.bottom;
14484        });
14485
14486        return Math.max(0, Math.ceil(maxBottom));
14487    }
14488
14489    function applyTop() {
14490        if (!$banner || !$banner.length) return;
14491        if (!isMobileViewport()) {
14492            document.documentElement.style.removeProperty('--creator-banner-top');
14493            return;
14494        }
14495        var topPx = getHeaderOffsetTop() + BANNER_GAP_PX_MOBILE;
14496        document.documentElement.style.setProperty('--creator-banner-top', topPx + 'px');
14497    }
14498
14499    function applyPlacement() {
14500        if (!$banner || !$banner.length) return;
14501
14502        if (!$placeholder) {
14503            $placeholder = $('<span class="_creator_benefit_banner_placeholder" style="display:none;"></span>');
14504            $banner.before($placeholder);
14505        }
14506
14507        if (isMobileViewport()) {
14508            if ($placeholder && $placeholder.length && !$banner.prev().is($placeholder)) {
14509                $placeholder.after($banner);
14510            }
14511            return;
14512        }
14513
14514        var $imageWrap = $('#prod_detail #prod_image_list:visible').first();
14515        if (!$imageWrap.length) {
14516            $imageWrap = $('#prod_image_list:visible').first();
14517        }
14518        if ($imageWrap.length && !$banner.parent().is($imageWrap)) {
14519            $imageWrap.append($banner);
14520        }
14521    }
14522
14523    function syncLayout() {
14524        applyPlacement();
14525        applyTop();
14526    }
14527
14528    function scheduleLayout() {
14529        if (rafScheduled) return;
14530        rafScheduled = true;
14531        window.requestAnimationFrame(function () {
14532            rafScheduled = false;
14533            syncLayout();
14534        });
14535    }
14536
14537    function pad2(n) {
14538        return n < 10 ? '0' + n : '' + n;
14539    }
14540
14541    function formatRemaining(ms) {
14542        if (ms <= 0) return '00:00:00';
14543        var total = Math.floor(ms / 1000);
14544        var days  = Math.floor(total / 86400);
14545        var hours = Math.floor((total % 86400) / 3600);
14546        var mins  = Math.floor((total % 3600) / 60);
14547        var secs  = total % 60;
14548        var time  = pad2(hours) + ':' + pad2(mins) + ':' + pad2(secs);
14549        return days > 0 ? (days + '일 ' + time) : time;
14550    }
14551
14552    function tickCountdown() {
14553        if (!$countdown || !$countdown.length) return;
14554        var remain = endTimestamp - Date.now();
14555        if (remain <= 0) {
14556            $countdown.text('00:00:00');
14557            destroy();
14558            return;
14559        }
14560        $countdown.text(formatRemaining(remain));
14561    }
14562
14563    // 두 슬라이드 vertical roll: 현재 → is-above (위로 빠짐), 다음 → is-active (아래에서 올라옴).
14564    // transition(0.4s) 종료 후 prev 를 transition off 한 채 is-below 로 reset → 다음 cycle 에 다시 아래에서 올라올 준비.
14565    function tickSlide() {
14566        if (!$slides || $slides.length < 2) return;
14567        var prevIndex = slideIndex;
14568        slideIndex = (slideIndex + 1) % $slides.length;
14569        var $prev = $slides.eq(prevIndex);
14570        var $next = $slides.eq(slideIndex);
14571
14572        $prev.removeClass('is-active is-below').addClass('is-above');
14573        $next.removeClass('is-below is-above').addClass('is-active');
14574
14575        setTimeout(function () {
14576            if (!$prev || !$prev.length) return;
14577            $prev.addClass('no-transition').removeClass('is-above').addClass('is-below');
14578            if ($prev[0]) {
14579                /* eslint-disable-next-line no-unused-expressions */
14580                $prev[0].offsetHeight;
14581            }
14582            $prev.removeClass('no-transition');
14583        }, 450);
14584    }
14585
14586    function clearTimers() {
14587        if (countdownTimer !== null) {
14588            clearInterval(countdownTimer);
14589            countdownTimer = null;
14590        }
14591        if (slideTimer !== null) {
14592            clearInterval(slideTimer);
14593            slideTimer = null;
14594        }
14595    }
14596
14597    function destroy() {
14598        clearTimers();
14599        $(window).off('scroll.creatorBanner resize.creatorBanner load.creatorBanner');
14600        $('#doz_header_wrap').off('transitionend.creatorBanner');
14601        if ($banner && $banner.length) {
14602            $banner.remove();
14603        }
14604        if ($placeholder && $placeholder.length) {
14605            $placeholder.remove();
14606        }
14607        $banner = null;
14608        $countdown = null;
14609        $slides = null;
14610        $placeholder = null;
14611        document.documentElement.style.removeProperty('--creator-banner-top');
14612    }
14613
14614    function init() {
14615        $banner = $('._creator_benefit_banner');
14616        if (!$banner.length) return;
14617
14618        // 카운트다운 영역은 end_date 가 유효한 미래일 때만 가동.
14619        // 시간 제한 없는 혜택(end_date 부재/과거)은 카운트다운만 비활성, 배너 자체는 그대로 표시.
14620        var endStr = $banner.attr('data-creator-end') || '';
14621        var parsed = endStr ? Date.parse(endStr.replace(' ', 'T')) : NaN;
14622        var hasCountdown = !!(parsed && !isNaN(parsed) && parsed > Date.now());
14623
14624        if (hasCountdown) {
14625            endTimestamp = parsed;
14626            $countdown = $banner.find('._creator_benefit_banner__countdown');
14627            tickCountdown();
14628            countdownTimer = setInterval(tickCountdown, 1000);
14629        }
14630
14631        $slides = $banner.find('._creator_benefit_banner__slide');
14632        slideIndex = Math.max(0, $slides.index($slides.filter('.is-active').eq(0)));
14633
14634        syncLayout();
14635        $banner.addClass('is-ready');
14636
14637        // 슬라이드가 2개 이상일 때만 롤링
14638        if ($slides.length >= 2) {
14639            slideTimer = setInterval(tickSlide, SLIDE_INTERVAL_MS);
14640        }
14641
14642        // 헤더 height 변동 및 PC/모바일 전환 트리거
14643        $(window).on('scroll.creatorBanner resize.creatorBanner', scheduleLayout);
14644        $(window).on('load.creatorBanner', scheduleLayout);
14645        $('#doz_header_wrap').on('transitionend.creatorBanner', scheduleLayout);
14646    }
14647
14648    return {
14649        init: init,
14650        destroy: destroy
14651    };
14652})();
14653
14654$(function () {
14655    CREATOR_BENEFIT_BANNER.init();
14656});

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.