PageSourceSearch

https://gdwt.co.kr/admin/js/admin.js?1790925174

js gdwt.co.kr collected 2026-10-08 04:45:57 UTC 47,493 bytes, 1,923 lines download raw bytes

1var ADMIN = (function(){
2	
3	var init = function(){
4		
5	};
6	
7	var openProfile = function(){
8		$.ajax({
9			type: 'POST',
10			data: {},
11			url: ('/admin/ajax/dialog/profile.cm'),
12			dataType: 'html',
13			async: false,
14			cache: false,
15			success: function (res) {
16				var $html = $(res);
17				$.cocoaDialog.open({type:'mypage',custom_popup:$html});
18			}
19		});
20	};
21
22	var openOpinion = function(){
23		$.ajax({
24			type: 'POST',
25			data: {},
26			url: ('/admin/ajax/dialog/opinion.cm'),
27			dataType: 'html',
28			async: false,
29			cache: false,
30			success: function (res) {
31				var $html = $(res);
32				$.cocoaDialog.open({type:'opinion',custom_popup:$html});
33			}
34		});
35	};
36
37	var showLoader = function(){
38		$('#dashboard_loader').show();
39	};
40
41	var hideLoader = function(){
42		$('#dashboard_loader').hide();
43	};
44
45    /***
46	 * 관리자페이지 언어 변경처리
47     * @param lang
48     */
49	var changeAdminLang = function(lang){
50        $.ajax({
51            type: 'POST',
52            data: {'lang':lang},
53            url: ('/admin/ajax/change_admin_lang.cm'),
54            dataType: 'json',
55            async: false,
56            cache: false,
57            success: function (res) {
58            	if(res.msg == 'SUCCESS'){
59					location.href = res.back_url;
60				}else{
61            		console.log(res.msg);
62				}
63            }
64        });
65	};
66
67	return {
68		init : function(){
69			init();
70		},
71		openProfile : function(){
72			openProfile();
73		},
74		openOpinion : function(){
75			openOpinion();
76		},
77		showLoader : function(){
78			showLoader();
79		},
80		hideLoader : function(){
81			hideLoader();
82		},
83        changeAdminLang : function(lang){
84			changeAdminLang(lang);
85		}
86	};
87})();
88var HEADER_CONTROL = function(){
89	var $header, $option_list,$footer_option_list;
90	var btn_list = [];
91	var btn_type = {
92		"save": {
93			key : 'save',
94			name : getLocalizeString("버튼_저장", "", "저장"),
95			changing : true
96		},
97		"add": {
98			key : 'add',
99			name : getLocalizeString("버튼_추가", "", "추가"),
100			changing : true
101		},
102		"cancel": {
103			key : 'cancel',
104			name : getLocalizeString("버튼_취소", "", "취소"),
105			changing : false
106		},
107		"delete": {
108			key : 'delete',
109			name : getLocalizeString("버튼_삭제", "", "삭제"),
110			changing : false
111		},
112		"navi": {
113			key : 'navi',
114			name : 'navi',
115			changing : false
116		}
117	};
118	var init = function(){
119		$header = $('#header');
120		$option_list = $header.find('._option_list');
121		$footer_option_list = $('<div />');
122		$('#content').find("section").append($footer_option_list);
123
124		$(window).bind('beforeunload', function(){
125			if(!$(".headerbar").find(".btn-primary").hasClass('disabled')){
126				return getLocalizeString("설명_변경사항이저장되지않을", "", "변경사항이 저장되지 않을 수 있습니다.");
127			}
128		});
129	};
130
131	var init2 = function($save_btn_bottom_wrap){
132		$header = $('#header');
133		$option_list = $header.find('._option_list');
134		$footer_option_list = $('<div />');
135		if( $save_btn_bottom_wrap !== undefined ){
136			$save_btn_bottom_wrap.append($footer_option_list);
137		}
138
139
140		$(window).bind('beforeunload', function(){
141			if(!$(".headerbar").find(".btn-primary").hasClass('disabled')){
142				return getLocalizeString("설명_변경사항이저장되지않을", "", "변경사항이 저장되지 않을 수 있습니다.");
143			}
144		});
145	};
146
147	var addBtn = function(type, callback){
148		switch(type){
149			case 'save' :
150			case 'add' :
151				var $footer_btn_wrap;
152				var $footer_btn;
153				var $btn_wrap = $('<li />');
154				var $btn = $('<a class="btn btn-primary" />')
155					.text(btn_type[type].name)
156					.toggleClass('disabled', btn_type[type].changing)
157					.data({
158						btn_data : btn_type[type],
159						changing : false
160					});
161				if(typeof callback == 'function'){
162					$btn.off('click').on('click', function(){
163						if($(this).data('changing') || !btn_type[type].changing){
164							callback();
165						}
166					});
167				}
168				$footer_btn_wrap = $btn_wrap.clone();
169				$footer_btn = $btn.clone(true);
170
171				$btn_wrap.append($btn);
172				$option_list.append($btn_wrap);
173				btn_list.push($btn);
174
175				$footer_option_list.append($footer_btn);
176				btn_list.push($footer_btn);
177				break;
178			case 'delete' :
179			case 'cancel' :
180				var $btn_wrap = $('<li />');
181				var $btn = $('<a class="btn btn-default-bright" href="javascript:;" />')
182					.text(btn_type[type].name)
183					.data({
184					btn_data : btn_type[type]
185				});
186				if(typeof callback == 'function'){
187					$btn.off('click').on('click', function(){
188						callback();
189					});
190				}
191
192				$btn_wrap.append($btn);
193				$option_list.append($btn_wrap);
194				btn_list.push($btn);
195				break;
196			case 'navi' :
197				var $btn_wrap = $('<li />');
198				var $btn_group = $('<div class="btn-group" role="group" aria-label="button group" />');
199				var $btn_prev = $('<a class="btn btn-default-bright" href="javascript:;" role="button"/><i class="btl bt-angle-left" /></a>').data({
200					btn_data : btn_type[type]
201				});
202				var $btn_next = $('<a class="btn btn-default-bright" href="javascript:;" role="button"/><i class="btl bt-angle-right" /></a>').data({
203					btn_data : btn_type[type]
204				});
205				if(typeof callback == 'function'){
206					$btn_prev.off('click').on('click', function(){
207						callback('prev');
208					});
209					$btn_next.off('click').on('click', function(){
210						callback('next');
211					});
212				}
213				$btn_group.append($btn_prev);
214				$btn_group.append($btn_next);
215				$btn_wrap.append($btn_group);
216				$option_list.append($btn_wrap);
217				btn_list.push($btn);
218				break;
219		}
220
221	};
222	var change = function(){
223		$.each(btn_list, function(e, $v){
224			if($v.data('btn_data').changing){
225				$v.toggleClass('disabled', false).data({
226					changing : true
227				});
228			}
229		});
230	};
231
232	var save = function(){
233		$.each(btn_list, function(e, $v){
234			if($v.data('btn_data').changing){
235				$v.toggleClass('disabled', true).data({
236					changing : false
237				});
238			}
239		});
240	};
241
242	var isChange = function(){
243		var is = false;
244		$.each(btn_list, function(e, $v){
245			if($v.data('btn_data').changing){
246				if($v.data('changing')){
247					is = true;
248					return false;
249				}
250			}
251		});
252		return is;
253	};
254
255
256	return {
257		init : function(){
258			init();
259		},
260		init2: function($save_btn_bottom_wrap){
261			init2($save_btn_bottom_wrap);
262		},
263		change : function(){
264			change();
265		},
266		save : function(){
267			save();
268		},
269		addBtn : function(type, callback){
270			addBtn(type, callback);
271		},
272		isChange : function(){
273			return isChange();
274		}
275	};
276};
277
278var IDENTITY_VERIFICATION_POPUP = function(){
279  const openVerificationPopup = function(){
280    $.ajax({
281      type : 'GET',
282      url : ('/admin/ajax/dialog/popup_identity_verification.cm'),
283      dataType: 'html',
284      async: false,
285      cache: false,
286      success: function (res) {
287        var $html = $(res);
288        $.cocoaDialog.open({type:'verification_popup', custom_popup:$html});
289      }
290    });
291  };
292  return {
293    openVerificationPopup : function(){
294      openVerificationPopup();
295    },
296  };
297}()
298var SADMIN_POPUP = (function(){
299	var openPopup = function(issue){
300		if(typeof issue == 'undefined')
301			issue = '';
302		$.ajax({
303			type : 'POST',
304			data : {'issue' : issue},
305			url : ('/admin/ajax/dialog/popup.cm'),
306			dataType: 'html',
307			async: false,
308			cache: false,
309			success: function (res) {
310				var $html = $(res);
311				$.cocoaDialog.open({type:'admin',custom_popup:$html});
312			}
313		});
314	};
315
316  var openPopupExpire = function(popup_type, remain_period){
317    $.ajax({
318      type : 'POST',
319      data : {
320        'popup_type' : popup_type,
321        'remain_period' : remain_period
322      },
323      url : ('/admin/ajax/dialog/popup_expire.cm'),
324      dataType: 'html',
325      async: false,
326      cache: false,
327      success: function (res) {
328        var $html = $(res);
329        if($html) {
330          $.cocoaDialog.open({type:'alert_expire',custom_popup:$html});
331        }
332      }
333    });
334  };
335
336  const openPopupCrmService = () => {
337		$.ajax({
338			type: 'GET',
339			url: '/admin/ajax/dialog/popup_crm_service.cm',
340			dataType: 'html',
341			async: false,
342			cache: false,
343			success: function(res) {
344				const $html = $(res);
345				if($html){
346					$.cocoaDialog.open({
347						type: 'ad_service',
348						custom_popup: $html,
349						pc_width: 380,
350					});
351
352					const setCookieTodayExpire = function(name, value) {
353						const today = new Date();
354				
355						today.setDate(today.getDate() + 14);
356				
357						document.cookie = name + "=" + encodeURIComponent(value) + "; path=/; expires=" + today.toUTCString() + ";";
358					}
359
360					$('#_close_btn_crm_service_popup').off('click').on('click', function() {
361						setCookieTodayExpire('close_popup_crm_promotion', 'Y');
362						$.cocoaDialog.close();
363					});
364				}
365			}
366		});
367  };
368  const openPopupCrmCouponService = () => {
369	if(window.location.search) {
370		return;
371	}
372
373
374		$.ajax({
375			type: 'GET',
376			url: '/admin/ajax/dialog/popup_crm_service-coupon.cm',
377			dataType: 'html',
378			async: false,
379			cache: false,
380			success: function(res) {
381				const $html = $(res);
382				if($html){
383					$.cocoaDialog.open({
384						type: 'ad_service',
385						custom_popup: $html,
386						pc_width: 380,
387					});
388
389					const setCookieTodayExpire = function(name, value) {
390						const today = new Date();
391				
392						today.setDate(today.getDate() + 7);
393				
394						document.cookie = name + "=" + encodeURIComponent(value) + "; path=/; expires=" + today.toUTCString() + ";";
395					}
396
397					$('#_close_btn_crm_service_popup').off('click').on('click', function() {
398						$.cocoaDialog.close();
399					});
400					$('#_close_seven_day_btn_crm_service_popup').off('click').on('click', function() {
401						setCookieTodayExpire('close_popup_crm_coupon', 'Y');
402						$.cocoaDialog.close();
403					});
404				}
405			}
406		});
407  };
408  const openPopupAdService = () => {
409		$.ajax({
410			type: 'GET',
411			url: ('/admin/ajax/dialog/popup_ad_service.cm'),
412			dataType: 'html',
413			async: false,
414			cache: false,
415			success: function(res) {
416				const $html = $(res);
417				if($html){
418					$.cocoaDialog.open({
419						type: 'ad_service',
420						custom_popup: $html,
421						pc_width: 380,
422					});
423
424					const setCookieTodayExpire = function(name, value) {
425						const today = new Date();
426				
427						today.setDate(today.getDate() + 12);
428				
429						document.cookie = name + "=" + encodeURIComponent(value) + "; path=/; expires=" + today.toUTCString() + ";";
430					}
431
432					$('#_close_btn_ad_service_popup').off('click').on('click', function() {
433						setCookieTodayExpire('close_popup_ad_promotion', 'Y');
434						$.cocoaDialog.close();
435					});
436				}
437			}
438		});
439  };
440
441	const openPopupFcmHttpV1 = () => {
442		$.ajax({
443			type: 'GET',
444			url: ('/admin/ajax/dialog/popup_fcm_httpv1.cm'),
445			dataType: 'html',
446			async: false,
447			cache: false,
448			success: function(res) {
449				const $html = $(res);
450				if($html){
451					$.cocoaDialog.open({
452						type: 'fcm_httpv1',
453						custom_popup: $html,
454						pc_width: 380,
455					});
456
457					const setCookieTodayExpire = function(name, value) {
458						const today = new Date();
459
460						today.setHours(23);
461						today.setMinutes(59);
462						today.setSeconds(59);
463
464						document.cookie = name + "=" + encodeURIComponent(value) + "; path=/; expires=" + today.toUTCString() + ";";
465					}
466
467					$('#_close_btn_fcm_httpv1_popup').off('click').on('click', function() {
468						setCookieTodayExpire('CLOSE_POPUP_FCM_HTTPV1', 'Y');
469						$.cocoaDialog.close();
470					});
471				}
472			}
473		});
474	};
475
476	const openPopupTiktokWebinar = () => {
477		$.ajax({
478			type: 'GET',
479			url: ('/admin/ajax/dialog/popup_tiktok_webinar.cm'),
480			dataType: 'html',
481			async: false,
482			cache: false,
483			success: function(res) {
484				const $html = $(res);
485				if($html){
486					$.cocoaDialog.open({
487						type: 'ad_service',
488						custom_popup: $html,
489						pc_width: 380,
490					});
491	
492					$('#_close_btn_tiktok_webinar').off('click').on('click', function() {
493						setCookie('close_popup_tiktok_webinar', 'Y', 365);
494						$.cocoaDialog.close();
495					});
496				}
497			}
498		});
499	};
500
501  const openPopupImwebPrivacyAgree = function(){
502	  $.ajax({
503		  type : 'POST',
504		  data : {},
505		  url : ('/admin/ajax/dialog/popup_imweb_privacy_agree.cm'),
506		  dataType: 'html',
507		  async: false,
508		  cache: false,
509		  success: function (html) {
510			  const $html = $(html);
511
512			  if (!$html) {
513				  return window.alert('개인정보 처리 방침 동의 모달을 불러오는데 실패하였습니다');
514			  }
515
516			  const modalOptions = {
517				  type: 'alert_privacy_agree',
518				  close_block: true,
519				  custom_popup: $html,
520			  };
521
522			  $.cocoaDialog.open(modalOptions, function(){
523				  $html.find('#imweb_privacy_agree_btn').on('click', function(){
524					  var payload = {
525						  'privacy_agree': 'Y', // 해당 버튼 클릭은 동의를 의미하므로 Y로 고정
526						  'marketing_agree': 'N', // 마케팅 동의는 현재 사용하지 않아 N으로 고정
527					  };
528
529					  $.ajax({
530						  type : 'POST',
531						  data : payload,
532						  url : ('/admin/ajax/imweb_privacy_agree.cm'),
533						  dataType: 'JSON',
534						  async: false,
535						  cache: false,
536						  success: function (res) {
537							  if(res.msg != 'SUCCESS'){
538								  alert(res.msg);
539							  }else{
540								  location.reload();
541							  }
542						  }
543					  });
544				  });
545			  });
546		  }
547	  });
548  }
549
550  return {
551    openPopup : function(issue){
552      openPopup(issue);
553    },
554    openPopupExpire : function(popup_type, remain_period) {
555      openPopupExpire(popup_type, remain_period);
556    },
557		openPopupCrmService: function() {
558			openPopupCrmService();
559		},
560		openPopupCrmCouponService: function() {
561			openPopupCrmCouponService();	
562		},
563		openPopupAdService: function() {
564			openPopupAdService();
565		},
566		openPopupTiktokWebinar: function() {
567			openPopupTiktokWebinar();
568		},
569	  openPopupImwebPrivacyAgree : function(){
570		  openPopupImwebPrivacyAgree();
571	  },
572	  openPopupFcmHttpV1: function() {
573		  openPopupFcmHttpV1();
574	  },
575  };
576})();
577
578var ADMIN_POPUP = (function(){
579	const setCookieTodayExpires = function(name, value) {
580		const today = new Date();
581
582		today.setHours(23);
583		today.setMinutes(59);
584		today.setSeconds(59);
585
586		document.cookie = name + "=" + encodeURIComponent(value) + "; path=/; expires=" + today.toUTCString() + ";";
587	}
588
589	const changeBackdropColor = (bgColor = 'rgba(0, 0, 0, 0.15)') => {
590		const $backdrop = $('.modal-backdrop.in');
591
592		$backdrop.css('background-color', bgColor);
593		$backdrop.css('opacity', 1);
594	}
595
596	// 컨텐츠 영역이 backdrop 부분을 가려 모달이 닫히지 않을 경우 해당 함수 사용
597	const attachCloseEventOnModalDialog = (type, selector) => {
598		const $dialog = $(`.modal_${type}`).find(selector);
599
600		$dialog.on('click', function(e) {
601			const event = e.originalEvent;
602
603			if (event.target === event.currentTarget) {
604				$.cocoaDialog.hide();
605			}
606		});
607	}
608
609	var openPopupGiftNudge = function() {
610		const COOKIE_NAME = 'close_popup_gift_nudge';
611
612		$.ajax({
613			type: 'GET',
614			url: '/admin/ajax/dialog/popup_gift_nudge.cm',
615			dataType: 'html',
616			async: false,
617			cache: false,
618			success: function(res) {
619				const $html = $(res);
620
621				const callback = () => {
622					const $animationContainer = document.querySelector('._animation-container');
623					const $submitButton = $('._btn_submit');
624					const $closeButton = $('._btn-close');
625
626					const animation = bodymovin.loadAnimation({
627						container: $animationContainer,
628						path: '/admin/json/gift-nudge-visual.json',
629						renderer: 'svg',
630						rendererSettings: {
631							preserveAspectRatio: 'xMaxYMax meet',
632							filterSize: {
633								width: '200%',
634								height: '200%',
635								x: '-50%',
636								y: '-50%',
637							}
638						}
639					});
640
641					$submitButton.on('click', () => {
642						$.ajax({
643							type: 'POST',
644							url: '/admin/enable_gift_by_popup.cm',
645							dataType: 'json',
646							async: false,
647							cache: false,
648							success: function(res) {
649								if (res.msg === 'SUCCESS') {
650									location.href = '/admin/shopping/gift';
651								}else{
652									alert(res.msg);
653								}
654							}
655						});
656					});
657
658					$closeButton.on('click', function() {
659						setCookieTodayExpires(COOKIE_NAME, 'Y');
660
661						$.cocoaDialog.close();
662					});
663
664					$.cocoaDialog.obj.on('hidden.bs.modal', () => {
665						animation.destroy();
666					});
667				};
668
669				$.cocoaDialog.open({ type: 'gift_nudge', custom_popup: $html, pc_width: 380 }, callback);
670			}
671		});
672	}
673
674	var openOMSTransitionPendingModal = function() {
675		const COOKIE_NAME = 'close_oms_transition_pending_modal';
676
677		$.ajax({
678			type: 'GET',
679			url: '/admin/ajax/dialog/commerce_bo/20250508_oms_transition_pending_modal.cm',
680			dataType: 'html',
681			async: false,
682			cache: false,
683			success: function(res) {
684				const $html = $(res);
685
686				const callback = () => {				
687					const $closeButton = $('._btn-close');
688					$closeButton.on('click', function() {
689						setCookieTodayExpires(COOKIE_NAME, 'Y');
690						$.cocoaDialog.close();
691					});
692
693					const $close2Button = $('._btn-close2');
694					$close2Button.on('click', function() {
695						$.cocoaDialog.close();
696					});
697					
698					const $animationContainer = document.querySelector('._animation-container');
699					const animation = bodymovin.loadAnimation({
700						container: $animationContainer,
701						path: '/admin/json/oms-activate-usage.json',
702						renderer: 'svg',
703						rendererSettings: {
704							preserveAspectRatio: 'xMidYMin slice',
705						}
706					});
707
708					$.cocoaDialog.obj.on('hidden.bs.modal', () => {
709						animation.destroy();
710					});
711
712				};
713
714				$.cocoaDialog.open({ type: 'oms_transition_pending', custom_popup: $html, pc_width: 540 }, callback);
715			}
716		});
717	}
718
719	var openOMSTransitionReadyModal = function() {
720		const COOKIE_NAME = 'close_oms_transition_ready_modal';
721
722		$.ajax({
723			type: 'GET',
724			url: '/admin/ajax/dialog/commerce_bo/20250508_oms_transition_ready_modal.cm',
725			dataType: 'html',
726			async: false,
727			cache: false,
728			success: function(res) {
729				const $html = $(res);
730
731				const callback = () => {				
732					const $closeButton = $('._btn-close');
733					$closeButton.on('click', function() {
734						setCookieTodayExpires(COOKIE_NAME, 'Y');
735						$.cocoaDialog.close();
736					});
737
738					const $close2Button = $('._btn-close2');
739					$close2Button.on('click', function() {
740						$.cocoaDialog.close();
741					});
742					
743					const $animationContainer = document.querySelector('._animation-container');
744					const animation = bodymovin.loadAnimation({
745						container: $animationContainer,
746						path: '/admin/json/oms-activate-usage.json',
747						renderer: 'svg',
748						rendererSettings: {
749							preserveAspectRatio: 'xMidYMin slice',
750						}
751					});
752
753					$.cocoaDialog.obj.on('hidden.bs.modal', () => {
754						animation.destroy();
755					});
756
757				};
758
759				$.cocoaDialog.open({ type: 'oms_transition_ready', custom_popup: $html, pc_width: 540 }, callback);
760			}
761		});
762	}
763
764	var openOMSPaypalModal = function() {
765		const COOKIE_NAME = 'close_oms_paypal_modal';
766
767		$.ajax({
768			type: 'GET',
769			url: '/admin/ajax/dialog/commerce_bo/20250509_oms_paypal_modal.cm',
770			dataType: 'html',
771			async: false,
772			cache: false,
773			success: function(res) {
774				const $html = $(res);
775
776				const callback = () => {				
777					const $closeButton = $('._btn-close');
778					$closeButton.on('click', function() {
779						setCookieTodayExpires(COOKIE_NAME, 'Y');
780						$.cocoaDialog.close();
781					});
782
783					const $close2Button = $('._btn-close2');
784					$close2Button.on('click', function() {
785						$.cocoaDialog.close();
786					});
787				};
788
789				$.cocoaDialog.open({ type: 'oms_paypal', custom_popup: $html, pc_width: 540 }, callback);
790			}
791		});
792	}
793
794	var creatorMarketingModalRequested = false;
795	var openCreatorMarketingVisualModal = function(siteCode) {
796		// 크리에이터 마케팅(어필리에이트) 유도 비주얼 모달
797		var CAMPAIGN = '202610';
798		var STORAGE_KEY = 'creator_marketing_visual_modal:' + CAMPAIGN + ':' + siteCode;
799		var AFFILIATE_PRODUCT_URL = '/admin/affiliate/product';
800		var ACTION_LOG_URL = '/admin/ajax/affiliate/creator_marketing_visual_modal_action_log.cm';
801		var ACTION_TYPE_POSITIVE = 'positive';
802		var ACTION_TYPE_NEGATIVE = 'negative';
803		var isActionHandled = false;
804		if (typeof siteCode !== 'string' || !siteCode || creatorMarketingModalRequested || $('.modal:visible').length) {
805			return;
806		}
807		try {
808			if (Number(window.localStorage.getItem(STORAGE_KEY)) > Date.now()) {
809				return;
810			}
811		} catch (e) {
812			// 숨김 선택을 유지할 수 없는 브라우저에서는 반복 노출하지 않는다.
813			return;
814		}
815		creatorMarketingModalRequested = true;
816		var logAction = function(actionType) {
817			if (window.navigator && typeof window.navigator.sendBeacon === 'function' && typeof window.FormData === 'function') {
818				var formData = new FormData();
819				formData.append('action_type', actionType);
820				window.navigator.sendBeacon(ACTION_LOG_URL, formData);
821				return;
822			}
823
824			$.ajax({
825				type: 'POST',
826				url: ACTION_LOG_URL,
827				data: {
828					action_type: actionType
829				},
830				async: true
831			});
832		};
833		var handleAction = function(actionType) {
834			if (isActionHandled) {
835				return;
836			}
837
838			isActionHandled = true;
839			try {
840				window.localStorage.setItem(STORAGE_KEY, String(Date.now() + 365 * 24 * 60 * 60 * 1000));
841			} catch (e) {
842				// 저장 실패가 닫기와 메뉴 이동을 막지 않도록 한다.
843			}
844			logAction(actionType);
845		};
846
847		$.ajax({
848			type: 'GET',
849			url: '/admin/ajax/dialog/affiliate/creator_marketing_visual_modal.cm',
850			data: { campaign: CAMPAIGN },
851			dataType: 'html',
852			async: true,
853			timeout: 5000,
854			cache: false,
855			success: function(res) {
856				var $html = $($.parseHTML(res || '', document, false));
857				var $content = $html.filter('.creator-marketing-modal');
858				// 구버전 템플릿·빈 응답·로그인 화면에서는 모달을 열지 않는다.
859				if ($content.length !== 1 || $content.attr('data-campaign') !== CAMPAIGN
860					|| $content.attr('data-site-code') !== siteCode) {
861					return;
862				}
863
864				var callback = function() {
865					var $modal = $('#cocoaModal.modal_creator_marketing_202610');
866					// 시작 버튼: 기존 크리에이터 마케팅 메뉴로 이동
867					$content.find('._btn-explore').on('click.creatorMarketing', function() {
868						handleAction(ACTION_TYPE_POSITIVE);
869						location.href = AFFILIATE_PRODUCT_URL;
870					});
871
872					// '다시 보지 않기': 모달 닫기
873					$content.find('._btn-close').on('click.creatorMarketing', function() {
874						handleAction(ACTION_TYPE_NEGATIVE);
875						$.cocoaDialog.close();
876					});
877
878					$modal.one('hide.bs.modal.creatorMarketingVisualModal', function() {
879						handleAction(ACTION_TYPE_NEGATIVE);
880						$(this).off('hide.bs.modal.creatorMarketingVisualModal');
881					});
882
883					// 백드롭 오버레이 색상 보정 (rgba(0, 0, 0, 0.15))
884					changeBackdropColor();
885					// cocoaDialog가 열기 직후 포커스를 해제하므로 그 처리가 끝난 뒤 이동한다.
886					window.setTimeout(function() {
887						if ($modal.is(':visible')) {
888							$content.find('._btn-explore')[0].focus({ preventScroll: true });
889						}
890					}, 10);
891				};
892
893				// 이미지 누락·부분 배포에서는 깨진 비주얼을 노출하지 않는다.
894				var images = $content.find('img').toArray();
895				Promise.all(images.map(function(img) {
896					return new Promise(function(resolve, reject) {
897						img.onload = resolve;
898						img.onerror = reject;
899						if (img.complete) {
900							if (img.naturalWidth > 0) resolve();
901							else reject();
902						}
903					});
904				})).then(function() {
905					// 조회 중 먼저 열린 안내/동의 팝업을 덮지 않는다.
906					if (!$('.modal:visible').length) {
907						$.cocoaDialog.open({ type: 'creator_marketing_202610', custom_popup: $html, pc_width: 380 }, callback);
908					}
909				}).catch(function() {
910					// 이미지 실패는 이번 진입의 모달만 생략한다.
911				});
912			}
913		});
914	}
915
916	const openIntegrationNudge = function() {
917		const type = 'integration_nudge';
918		const cookieName = 'close_integration_nudge';
919
920		$.ajax({
921			type: 'GET',
922			url: '/admin/ajax/dialog/integration_nudge.cm',
923			dataType: 'html',
924			async: false,
925			cache: false,
926			success: function(res) {
927				const $html = $(res);
928
929				const callback = () => {
930					mixpanel.track('view_bo_dashboard_visual_modal_banner',{'site_code':SITE_CODE}
930);
931					const $closeButton = $('._btn-close');
932
933					$closeButton.on('click', function() {
934						setCookieTodayExpires(cookieName, 'Y');
935
936						$.cocoaDialog.hide();
937					});
938
939					changeBackdropColor();
940					attachCloseEventOnModalDialog(type, '.modal-content');
941				};
942
943				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
944			}
945		});
946	}
947
948	const openPopupInterlocking = function() {
949		const type = 'popup_interlocking';
950		const cookieName = 'close_popup_interlocking';
951
952		$.ajax({
953			type: 'GET',
954			url: '/admin/ajax/dialog/popup_interlocking.cm',
955			dataType: 'html',
956			async: false,
957			cache: false,
958			success: function(res) {
959				const $html = $(res);
960
961				const callback = () => {
962					const $checkButton = $('._btn-check');
963					const $closeButton = $('._btn-close');
964
965					$checkButton.on('click', function() {
966						window.open('https://smartstore-link.imweb.me/', '_blank');
967						$.cocoaDialog.close();
968					});
969
970					$closeButton.on('click', function() {
971						setCookieTodayExpires(cookieName, 'Y');
972						$.cocoaDialog.close();
973					});
974
975					changeBackdropColor();
976					attachCloseEventOnModalDialog(type, '.modal-content');
977				};
978
979				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
980
981				// $.cocoaDialog.obj.on('hidden.bs.modal', () => {
982				// 	if ( getCookie('NO_EXPIRE_POPUP') !== "Y" && EXPIRE_TYPE !== '') {
983				// 		SADMIN_POPUP.openPopupExpire(EXPIRE_TYPE, EXPIRE_PERIOD);
984				// 	}
985				// });
986			}
987		});
988	}
989
990	const openFontExpirationNudge = function() {
991		const type = 'font_expiration_nudge';
992		const cookieName = 'close_font_expiration_nudge';
993
994		$.ajax({
995			type: 'GET',
996			url: '/admin/ajax/dialog/font_expiration_nudge.cm',
997			dataType: 'html',
998			async: false,
999			cache: false,
1000			success: function(res) {
1001				const $html = $(res);
1002
1003				const callback = () => {
1004					const $checkButton = $('._btn-check');
1005					const $closeButton = $('._btn-close');
1006
1007					$checkButton.on('click', function() {
1008						$.cocoaDialog.close();
1009					});
1010
1011					$closeButton.on('click', function() {
1012						setCookieTodayExpires(cookieName, 'Y');
1013
1014						$.cocoaDialog.close();
1015					});
1016
1017					changeBackdropColor();
1018					attachCloseEventOnModalDialog(type, '.modal-content');
1019				};
1020
1021				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
1022
1023				$.cocoaDialog.obj.on('hidden.bs.modal', () => {
1024					if ( getCookie('NO_EXPIRE_POPUP') !== "Y" && EXPIRE_TYPE !== '') {
1025						SADMIN_POPUP.openPopupExpire(EXPIRE_TYPE, EXPIRE_PERIOD);
1026					}
1027				});
1028			}
1029		});
1030	}
1031
1032	const openFontAdditionNudge = function() {
1033		console.log('openFontAdditionNudge');
1034		const type = 'font_addition_nudge';
1035		const cookieName = 'close_font_addition_nudge';
1036
1037		$.ajax({
1038			type: 'GET',
1039			url: '/admin/ajax/dialog/font_addition_nudge.cm',
1040			dataType: 'html',
1041			async: false,
1042			cache: false,
1043			success: function(res) {
1044				const $html = $(res);
1045
1046				const callback = () => {
1047					const $checkButton = $('._btn-check');
1048					const $closeButton = $('._btn-close');
1049
1050					$checkButton.on('click', function() {
1051						$.cocoaDialog.close();
1052					});
1053
1054					$closeButton.on('click', function() {
1055						setCookieTodayExpires(cookieName, 'Y');
1056
1057						$.cocoaDialog.close();
1058					});
1059
1060					changeBackdropColor();
1061					attachCloseEventOnModalDialog(type, '.modal-content');
1062				};
1063
1064				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
1065
1066				$.cocoaDialog.obj.on('hidden.bs.modal', () => {
1067					if ( getCookie('NO_EXPIRE_POPUP') !== "Y" && EXPIRE_TYPE !== '') {
1068						SADMIN_POPUP.openPopupExpire(EXPIRE_TYPE, EXPIRE_PERIOD);
1069					}
1070				});
1071			}
1072		});
1073	}
1074
1075	const openPopupNotificationLaunching = function() {
1076		const COOKIE_NAME = 'close_popup_notification_launching';
1077
1078		$.ajax({
1079			type: 'POST',
1080			dataType: 'html',
1081			url: '/admin/ajax/dialog/popup_notification_launching.cm',
1082			async: false,
1083			cache: false,
1084			success: function(res) {
1085				const $html = $(res);
1086
1087				$($html).find('._button-close').click(function() {
1088					setCookieTodayExpires(COOKIE_NAME, 'Y');
1089
1090					$.cocoaDialog.close();
1091				});
1092
1093				$($html).find('._button-notification').click(function() {
1094					window.setTimeout(() => {
1095						mixpanel.track('click_bo_notification', {
1096							'site_code': SITE_CODE,
1097							'source': 'modal',
1098						});
1099
1100						getNotifyHandler().openModal();
1101					}, 0);
1102
1103					$.cocoaDialog.close();
1104				});
1105
1106				$.cocoaDialog.open({
1107					type: 'notification_launching',
1108					custom_popup: $html,
1109				}, function() {
1110					changeBackdropColor();
1111				});
1112			}
1113		});
1114	}
1115
1116	const openPopupCollectCategory = function() {
1117		const type = 'collect-category';
1118
1119		$.ajax({
1120			cache: false,
1121			type: 'POST',
1122			dataType: 'html',
1123			url: '/admin/ajax/dialog/popup_collect_category.cm',
1124		}).then((html) => {
1125			const $html = $(html);
1126
1127			$.cocoaDialog.open({
1128				type,
1129				custom_popup: $html,
1130			}, function() {
1131				changeBackdropColor();
1132				attachCloseEventOnModalDialog(type, `._${type}`);
1133			});
1134		});
1135	}
1136
1137	const openPopupSiteVersionUpgrade = function() {
1138		const type = 'site-version-upgrade';
1139
1140		$.ajax({
1141			cache: false,
1142			type: 'POST',
1143			dataType: 'html',
1144			url: '/admin/ajax/dialog/popup_site_version_upgrade.cm',
1145		}).then((html) => {
1146			const $html = $(html);
1147
1148			const callback = () => {
1149				$html.find('._btn-close').on('click', function() {
1150					$.cocoaDialog.close();
1151				});
1152
1153				changeBackdropColor();
1154				attachCloseEventOnModalDialog(type, '.modal-content');
1155			}
1156
1157			$.cocoaDialog.open({ type, custom_popup: $html }, callback);
1158		});
1159	}
1160
1161	return {
1162		openPopupGiftNudge,
1163		openCreatorMarketingVisualModal,
1164		openCreatorMarketingVisualModal202610: openCreatorMarketingVisualModal,
1165		openIntegrationNudge,
1166		openFontExpirationNudge,
1167		openFontAdditionNudge,
1168		openPopupNotificationLaunching,
1169		openPopupCollectCategory,
1170		openOMSTransitionPendingModal,
1171		openOMSTransitionReadyModal,
1172		openOMSPaypalModal,
1173		openPopupSiteVersionUpgrade,
1174		openPopupInterlocking,
1175	}
1176})();
1177
1178var SADMIN_MENU_ALERT = function(){
1179	var openAlert = function(){
1180		this.ajaxUrl = '/admin/ajax/dialog/menu_alert.cm';
1181		var str_url = location.pathname;
1182
1183		if ( IMWEB_SESSIONSTORAGE.get("MENU_ALERT_EMPTY_" + str_url) == 'Y' ) return;
1184
1185		$.ajax({
1186			url : this.ajaxUrl,
1187			type : 'POST',
1188			dataType : 'HTML',
1189			data : {'mode' : 'open', 'str_url': str_url},
1190			success : function(res){
1191				if ( res.length == 0 ) {
1192					IMWEB_SESSIONSTORAGE.set("MENU_ALERT_EMPTY_" + str_url, 'Y', 60);
1193				} else {
1194					if($("#menu_alert").length == 0 ){
1195						$('#content #alertBox').append(res);
1196						$("#menu_alert").show();
1197					}
1198				}
1199			}
1200		});
1201	};
1202
1203	var closeAlert = function(idx){
1204		$.ajax({
1205			url : this.ajaxUrl,
1206			type : 'POST',
1207			dataType : 'HTML',
1208			data : {mode : 'close', 'idx' : idx},
1209			success : function(){
1210				$("#menu_alert").hide();
1211			}
1212		});
1213	};
1214	var closeBannerAlert = function(type){
1215		$.ajax({
1216			url : '/admin/ajax/dialog/banner_alert.cm',
1217			type : 'POST',
1218			dataType : 'HTML',
1219			data : {'type' : type},
1220			success : function(){
1221				$("#banner_alert_"+type).css({'display':'none'});
1222			}
1223		});
1224	};
1225	return {
1226		ajaxUrl : null,
1227		'openAlert' : function(){
1228			openAlert();
1229		},
1230		'closeAlert' : function(idx){
1231			closeAlert(idx);
1232		},
1233		'closeBannerAlert' : function(type){
1234			closeBannerAlert(type);
1235		}
1236	};
1237}();
1238
1239const dateFormatUtils = (function() {
1240	const pad = (v) => {
1241		return `0${v}`.slice(-2);
1242	}
1243
1244	const toHyphenString = (dateObj) => {
1245		const year = dateObj.getFullYear();
1246		const month = pad(dateObj.getMonth() + 1);
1247		const date = pad(dateObj.getDate());
1248
1249		return [year, month, date].join('-');
1250	}
1251
1252	const toButtonString = (dateString) => {
1253		const [year, month, date] = dateString.split('-').map(v => parseInt(v, 10));
1254
1255		const dateObj = new Date(year, month - 1, date);
1256
1257		const day = dateObj.toLocaleString(window.ADMIN_LANG_CODE || 'en', { weekday: 'short' });
1258
1259		const yearText = year.toString().substring(2);
1260		const monthText = pad(month);
1261		const dateText = pad(date);
1262
1263		return `${yearText}.${monthText}.${dateText} (${day})`;
1264	}
1265
1266	return { pad, toHyphenString, toButtonString };
1267})();
1268
1269const datepickerButtonHandler = (function($) {
1270	const defaultRefreshUrl = (start, end) => {
1271		const parser = querystringParser();
1272		const query = parser(window.location.search);
1273
1274		window.location.href = getQuerystring({
1275			...query,
1276			start,
1277			end,
1278		});
1279	}
1280
1281	function DatepickerButtonHandler(options) {
1282		this.$container = options.$container;
1283		this.$start = options.$start;
1284		this.$end = options.$end;
1285		this.$middleIcon = options.$middleIcon;
1286		this.startDate = options.startDate;
1287		this.endDate = options.endDate;
1288	}
1289
1290	DatepickerButtonHandler.prototype.init = function() {
1291		const { $container, $start, $end, startDate, endDate } = this;
1292
1293		const update = ($e, date) => {
1294			if (!isValidDate(date)) {
1295				return false;
1296			}
1297
1298			const hyphenText = dateFormatUtils.toHyphenString(date);
1299			const buttonText = dateFormatUtils.toButtonString(hyphenText);
1300
1301			$e.data('text.hyphen', hyphenText);
1302			$e.text(buttonText);
1303
1304			return { buttonText, hyphenText };
1305		}
1306
1307		const { hyphenText: startText } = update($start, startDate);
1308		const { hyphenText: endText } = update($end, endDate);
1309
1310		if (startText === endText)  {
1311			$container.addClass('same');
1312		} else {
1313			$container.removeClass('same');
1314		}
1315	}
1316
1317	DatepickerButtonHandler.prototype.update = function(startDate, endDate) {
1318		this.startDate = new Date(startDate);
1319		this.endDate = new Date(endDate);
1320
1321		this.init();
1322	}
1323
1324	DatepickerButtonHandler.prototype.onClick = function(callback) {
1325		const self = this;
1326		const { $container } = self
1327
1328		if ($container.data('has.click')) {
1329			return false;
1330		}
1331
1332		$container.data('has.click', true);
1333
1334		const handler = function() {
1335			if (callback) {
1336				const { startDate, endDate } = self;
1337
1338				callback(self, startDate, endDate);
1339			}
1340		}
1341
1342		$container.on('click', handler);
1343
1344		return handler;
1345	}
1346
1347	DatepickerButtonHandler.prototype.refreshUrl = function() {
1348		const { $start, $end } = this;
1349
1350		const startHyphenText = $start.data('text.hyphen');
1351		const endHyphenText = $end.data('text.hyphen');
1352
1353		DatepickerButtonHandler.dependencies.refreshUrl(startHyphenText, endHyphenText);
1354	}
1355
1356	DatepickerButtonHandler.prototype.hyphenText = function() {
1357		const { $start, $end } = this;
1358
1359		return {
1360			start: $start.data('text.hyphen'),
1361			end: $end.data('text.hyphen'),
1362		};
1363	}
1364
1365	DatepickerButtonHandler.dependencies = {
1366		refreshUrl: defaultRefreshUrl,
1367	};
1368
1369	/**
1370	 * @param {Object} options - 옵션
1371	 * @param {string} [options.container='._datepicker'] - container 셀렉터
1372	 * @param {Object} [options.selectors={}] - start, end 텍스트 selectors
1373	 * @param {string} [options.selectors.start='._datepicker-start'] - start 텍스트 셀렉터
1374	 * @param {string} [options.selectors.middleIcon='._datepicker-middle-icon'] - middle icon 셀렉터
1375	 * @param {string} [options.selectors.end='._datepicker-end'] - end 텍스트 셀렉터
1376	 * @param {Date} [options.startDate] - start date
1377	 * @param {Date} [options.endDate] - end date
1378	 */
1379	return (options = {}) => {
1380		const container = options.container || '._datepicker';
1381		const selectors = options.selectors || {};
1382		const startDate = options.startDate;
1383		const endDate = options.endDate;
1384
1385		const startDateSelector = selectors.start || '._datepicker-start';
1386		const middleIconSelector = selectors.middleIcon || '._datepicker-middle-icon';
1387		const endDateSelector = selectors.end || '._datepicker-end';
1388
1389		const _options = {
1390			$container: $(container),
1391			$start: $(startDateSelector),
1392			$middleIcon: $(middleIconSelector),
1393			$end: $(endDateSelector),
1394			startDate: new Date(startDate),
1395			endDate: new Date(endDate),
1396		};
1397
1398		return new DatepickerButtonHandler(_options);
1399	};
1400})(window.jQuery);
1401
1402const getNotifyHandler = (function() {
1403	const utils = (function () {
1404		const throttle = (callback, delay = 0) => {
1405			let timer = null;
1406
1407			return function(...args) {
1408				if (timer !== null) {
1409					return false;
1410				}
1411
1412				timer = window.setTimeout(() => {
1413					callback.call(this, ...args);
1414
1415					timer = null;
1416				}, delay)
1417			}
1418		}
1419
1420		const listenEvents = (events, command) => {
1421			Object.entries(events).forEach((event) => {
1422				const [eventName, listener] = event;
1423
1424				window[`${command}EventListener`](eventName, listener);
1425			});
1426		}
1427
1428		const dismissMobileMenubar = () => {
1429			Object.getPrototypeOf(window.doznutadmin.AppNavigation)._handleMenubarLeave();
1430		}
1431
1432		const blurCurrentActiveElement = () => {
1433			if (document.activeElement instanceof HTMLElement) {
1434				document.activeElement.blur();
1435			}
1436		}
1437
1438		const getWindowWidth = () => {
1439			return window.innerWidth;
1440		}
1441
1442		const isMobileBreakpoint = (breakpointKey) => {
1443			return breakpointKey !== 'lg';
1444		}
1445
1446		const getBreakpointKey = (windowWidth, breakpoints) => {
1447			const breakpoint = Object.values(breakpoints).find((breakpoint) => {
1448				return windowWidth <= breakpoint.threshold;
1449			}) ?? breakpoints.lg;
1450
1451			return breakpoint.key;
1452		}
1453
1454		const getCurrentBreakpointKey = (breakpoints) => {
1455			const windowWidth = getWindowWidth();
1456
1457			return getBreakpointKey(windowWidth, breakpoints);
1458		}
1459
1460		const getIframeURL = (url, breakpoints) => {
1461			const params = {
1462				from: location.href,
1463				layout: 'panel',
1464				breakpoint: getCurrentBreakpointKey(breakpoints),
1465			};
1466
1467			const query = new URLSearchParams(params);
1468
1469			return `${url}?${query.toString()}`;
1470		}
1471
1472		const isSidebarMini = () => {
1473			return $('body').hasClass('sidebar-mini');
1474		}
1475
1476		return {
1477			throttle,
1478			listenEvents,
1479			dismissMobileMenubar,
1480			blurCurrentActiveElement,
1481			getWindowWidth,
1482			isMobileBreakpoint,
1483			getBreakpointKey,
1484			getCurrentBreakpointKey,
1485			getIframeURL,
1486			isSidebarMini,
1487		};
1488	})();
1489
1490	const scrollLock = (function() {
1491		const state = {
1492			isLock: false,
1493			restore: {
1494				scrollY: 0,
1495				bodyStyles: null,
1496			},
1497		};
1498
1499		const isScrollable = () => {
1500			return Boolean(window.innerWidth - document.documentElement.clientWidth);
1501		}
1502
1503		const getCurrentScrollY = () => {
1504			return window.scrollY;
1505		}
1506
1507		const getCurrentBodyStyles = () => {
1508			const bodyStyles = document.body.style;
1509
1510			return {
1511				position: bodyStyles.position,
1512				top: bodyStyles.top,
1513				overflowY: bodyStyles.overflowY,
1514				width: bodyStyles.width,
1515			};
1516		}
1517
1518		const lock = () => {
1519			const scrollable = isScrollable();
1520			const currentScrollY = getCurrentScrollY();
1521			const currentBodyStyles = getCurrentBodyStyles();
1522
1523			state.isLock = true;
1524			state.restore.scrollY = currentScrollY;
1525			state.restore.bodyStyles = currentBodyStyles;
1526
1527			document.body.style.setProperty('position', 'fixed');
1528			document.body.style.setProperty('top', `-${currentScrollY}px`);
1529			document.body.style.setProperty('width', '100%');
1530
1531			if (scrollable) {
1532				document.body.style.setProperty('overflow-y', 'scroll');
1533			}
1534		}
1535
1536		const unlock = () => {
1537			const { scrollY, bodyStyles } = state.restore;
1538
1539			document.body.style.setProperty('position', bodyStyles.position);
1540			document.body.style.setProperty('top', bodyStyles.top);
1541			document.body.style.setProperty('width', bodyStyles.width);
1542			document.body.style.setProperty('overflow-y', bodyStyles.overflowY);
1543
1544			window.scrollTo(0, scrollY);
1545
1546			state.isLock = false;
1547			state.restore.scrollY = 0;
1548			state.restore.bodyStyles = null;
1549		}
1550
1551		const wrap = (isLockCondition, fn) => () => {
1552			if (state.isLock === isLockCondition) {
1553				fn();
1554			}
1555		}
1556
1557		return {
1558			lock: wrap(false, lock),
1559			unlock: wrap(true, unlock)
1560		};
1561	})();
1562
1563	const createChannel = ($iframe, messageHandler) => {
1564		const channel = new MessageChannel();
1565
1566		$iframe.addEventListener('load', function() {
1567			window.setTimeout(() => {
1568				this.contentWindow.postMessage('init:port', location.origin, [channel.port2]);
1569			}, 10);
1570		});
1571
1572		channel.port1.onmessage = messageHandler;
1573
1574		const postMessage = (message) => {
1575			channel.port1.postMessage(message);
1576		}
1577
1578		const close = () => {
1579			channel.port1.close();
1580			channel.port2.close();
1581		}
1582
1583		return { postMessage, close };
1584	}
1585
1586	function NotifyHandler(options) {
1587		const { $provider, $button } = options;
1588
1589		this.$provider = $provider;
1590		this.$button = $button;
1591		this.isOpen = false;
1592		this.breakpointKey = '';
1593		this.channel = null;
1594		this.windowEvent = {
1595			isAttached: false,
1596			events: {
1597				resize: utils.throttle(this.handleWindowResize, 500).bind(this),
1598				click: this.handleWindowClick.bind(this),
1599				keydown: this.handleWindowKeydown.bind(this),
1600			},
1601		};
1602
1603		this.handleMessage = this.handleMessage.bind(this);
1604	}
1605
1606	NotifyHandler.prototype.handleWindowResize = function() {
1607		const breakpoints = NotifyHandler.CONST.breakpoints;
1608
1609		const nextBreakpointKey = utils.getCurrentBreakpointKey(breakpoints);
1610
1611		if (utils.isMobileBreakpoint(nextBreakpointKey)) {
1612			scrollLock.lock();
1613		} else {
1614			scrollLock.unlock();
1615		}
1616
1617		this.breakpointKey = nextBreakpointKey;
1618
1619		this.postMessage(`resize:${nextBreakpointKey}`);
1620	}
1621
1622	NotifyHandler.prototype.handleWindowClick = function(e) {
1623		const { $provider } = this;
1624
1625		const isProviderContext = $provider.contains(e.target);
1626
1627		if (isProviderContext) {
1628			return false;
1629		}
1630
1631		this.closeModal();
1632	}
1633
1634	NotifyHandler.prototype.handleWindowKeydown = function(e) {
1635		const { breakpointKey } = this;
1636
1637		if (e.key === 'Escape' && utils.isMobileBreakpoint(breakpointKey)) {
1638			this.closeModal();
1639		}
1640	}
1641
1642	NotifyHandler.prototype.attachWindowEvents = function() {
1643		const { windowEvent } = this;
1644
1645		windowEvent.isAttached = true;
1646
1647		utils.listenEvents(windowEvent.events, 'add');
1648	}
1649
1650	NotifyHandler.prototype.detachWindowEvents = function() {
1651		const { windowEvent } = this;
1652
1653		windowEvent.isAttached = false;
1654
1655		utils.listenEvents(windowEvent.events, 'remove');
1656	}
1657
1658	NotifyHandler.prototype.postMessage = function(message) {
1659		this.channel.postMessage(message);
1660	}
1661
1662	NotifyHandler.prototype.handleMessage = function(e) {
1663		const { data } = e;
1664
1665		// 알림 모달 열였을 시
1666		if (data === 'open') {
1667			// Nothing
1668		}
1669
1670		// 알림 모달 닫았을 시
1671		if (data === 'close') {
1672			this.closeModal();
1673		}
1674
1675		// 단일 읽기 처리 시
1676		if (data === 'confirm') {
1677			// Nothing
1678		}
1679
1680		// 전체 읽기 처리 시
1681		if (data === 'confirm:all') {
1682			this.interval.clearBadge();
1683		}
1684
1685		// 설정 화면 진입 버튼 클릭 시
1686		if (data === 'open:setting') {
1687			mixpanel.track('click_bo_notification_settings', { 'site_code': SITE_CODE });
1688		}
1689
1690		// 알림 항목 클릭 시
1691		if (data.startsWith('itemclick')) {
1692			const type = data.split(':')[1];
1693
1694			mixpanel.track('click_bo_notification_list_item', { type, 'site_code': SITE_CODE });
1695		}
1696
1697		// 배너 클릭 시
1698		if (data.startsWith('bannerclick')) {
1699			const name = data.split(':')[1];
1700
1701			mixpanel.track('click_bo_notification_banner', { name, 'site_code': SITE_CODE });
1702		}
1703	}
1704
1705	NotifyHandler.prototype.openModal = function() {
1706		const { $provider, $button } = this;
1707
1708		const createElement = () => {
1709			const $modal = document.createElement('div');
1710			const $backdrop = document.createElement('div');
1711			const $panel = document.createElement('div');
1712			const $contents = document.createElement('div');
1713			const $iframe = document.createElement('iframe');
1714
1715			$modal.classList.add('notify-modal');
1716			$backdrop.classList.add('notify-modal__backdrop');
1717			$panel.classList.add('notify-modal__panel');
1718			$contents.classList.add('notify-modal__contents');
1719			$iframe.classList.add('notify-modal__iframe');
1720
1721			if (utils.isSidebarMini()) {
1722				$modal.classList.add('notify-modal--narrow');
1723			}
1724
1725			$iframe.referrerPolicy = 'strict-origin';
1726			$iframe.src = utils.getIframeURL(NotifyHandler.CONST.iframeSource, NotifyHandler.CONST.breakpoints);
1727
1728			$contents.appendChild($iframe);
1729
1730			$panel.appendChild($contents);
1731
1732			$modal.appendChild($backdrop);
1733			$modal.appendChild($panel);
1734
1735			return $modal;
1736		}
1737
1738		const attachEvent = ($modal) => {
1739			const $panel = $modal.querySelector('.notify-modal__panel');
1740
1741			$panel.addEventListener('click', () => {
1742				this.closeModal();
1743			});
1744		}
1745
1746		const breakpointKey = utils.getCurrentBreakpointKey(NotifyHandler.CONST.breakpoints);
1747
1748		const $modal = createElement();
1749
1750		const channel = createChannel(
1751				$modal.querySelector('.notify-modal__iframe'),
1752				this.handleMessage,
1753		);
1754
1755		attachEvent($modal);
1756
1757		$button.classList.add('focus');
1758
1759		$provider.appendChild($modal);
1760
1761		this.attachWindowEvents();
1762
1763		if (utils.isMobileBreakpoint(breakpointKey)) {
1764			utils.dismissMobileMenubar();
1765			scrollLock.lock();
1766		}
1767
1768		utils.blurCurrentActiveElement();
1769
1770		this.breakpointKey = breakpointKey;
1771		this.isOpen = true;
1772		this.channel = channel;
1773
1774		this.postMessage('open');
1775
1776		this.interval.stop();
1777	}
1778
1779	NotifyHandler.prototype.closeModal = function() {
1780		const { $provider, $button, channel } = this;
1781
1782		this.postMessage('close');
1783
1784		channel.close();
1785
1786		this.detachWindowEvents();
1787
1788		scrollLock.unlock();
1789
1790		$button.classList.remove('focus');
1791
1792		// channel.postMessage('close') 하기 위함
1793		window.setTimeout(() => {
1794			$provider.innerHTML = '';
1795		}, 0);
1796
1797		this.breakpointKey = '';
1798		this.isOpen = false;
1799		this.channel = null;
1800
1801		this.interval.start();
1802	}
1803
1804	NotifyHandler.prototype.toggleModal = function() {
1805		const { isOpen } = this;
1806
1807		if (isOpen) {
1808			this.closeModal();
1809		} else {
1810			this.openModal();
1811		}
1812	}
1813
1814	NotifyHandler.prototype.setInterval = function(interval) {
1815		this.interval = interval;
1816	}
1817
1818	NotifyHandler.CONST = {
1819		iframeSource: `${location.origin}/admin/notify`,
1820		providerSelector: '._notify-modal-provider',
1821		buttonSelector: '._gnb_notification_button',
1822		breakpoints: {
1823			sm: { key: 'sm', threshold: 587 },
1824			md: { key: 'md', threshold: 992 },
1825			lg: { key: 'lg', threshold: Number.MAX_VALUE },
1826		},
1827	};
1828
1829	let instance = null;
1830
1831	return () => {
1832		if (instance === null) {
1833			const options = {
1834				$provider: document.querySelector(NotifyHandler.CONST.providerSelector),
1835				$button: document.querySelector(NotifyHandler.CONST.buttonSelector),
1836			};
1837
1838			instance = new NotifyHandler(options);
1839		}
1840
1841		return instance;
1842	}
1843})();
1844
1845const getNotifyBadgeInterval = (function() {
1846	const SELECTOR = '_badge';
1847
1848	function fetchBadgeCount() {
1849		return $.ajax({
1850			cache: false,
1851			type: 'GET',
1852			dataType: 'json',
1853			url: '/ajax/notification/notification.cm?apiPath=list&req=count',
1854		}).promise().then(res => res.unReadTotalCount ?? 0);
1855	}
1856
1857	function createBadgeElement(type, text) {
1858		const $badge = document.createElement('span');
1859
1860		$badge.classList.add(SELECTOR);
1861
1862		$badge.textContent = text;
1863
1864		switch(type) {
1865			case 'count':
1866				$badge.classList.add('badge_icon', 'badge_count');
1867				break;
1868			case 'new':
1869				$badge.textContent = '';
1870				break;
1871		}
1872
1873		return $badge;
1874	}
1875
1876	function setBadge(count, $button) {
1877		const $prevBadge = $button.find(`.${SELECTOR}`);
1878
1879		if ($prevBadge.length) {
1880			$prevBadge.remove();
1881		}
1882
1883		const countText = count > 99 ? '99+' : count;
1884
1885		const $badge = count > 0 ? createBadgeElement('count', countText) : createBadgeElement('new', 'New');
1886
1887		$button.append($badge);
1888	}
1889
1890	function createUpdateBadgeCount($button) {
1891		return () => {
1892			return fetchBadgeCount().then((count) => setBadge(count, $button));
1893		}
1894	}
1895
1896	function Interval(fn, interval, $button) {
1897		let timer = null;
1898
1899		const start = () => {
1900			timer = window.setInterval(fn, interval);
1901		}
1902
1903		const stop = () => {
1904			window.clearInterval(timer);
1905		}
1906
1907		const force = () => {
1908			fn();
1909		}
1910
1911		const clearBadge = () => {
1912			setBadge(0, $button);
1913		}
1914
1915		return { start, stop, force, clearBadge };
1916	}
1917
1918	return ($button) => {
1919		const updateBadgeCount = createUpdateBadgeCount($button);
1920
1921		return new Interval(updateBadgeCount, 1000 * 60 * 1, $button);
1922	}
1923})();

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.