PageSourceSearch

https://numberplate.kr/admin/js/admin.js?1782885225

js numberplate.kr collected 2026-10-02 01:59:41 UTC 45,461 bytes, 1,875 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 openCreatorMarketingVisualModal = function() {
795		// 크리에이터 마케팅(어필리에이트) 유도 비주얼 모달
796		var COOKIE_NAME = 'close_creator_marketing_visual_modal';
797		var AFFILIATE_PRODUCT_URL = '/admin/affiliate/product';
798		var ACTION_LOG_URL = '/admin/ajax/affiliate/creator_marketing_visual_modal_action_log.cm';
799		var ACTION_TYPE_POSITIVE = 'positive';
800		var ACTION_TYPE_NEGATIVE = 'negative';
801		var isActionHandled = false;
802		var logAction = function(actionType) {
803			if (window.navigator && typeof window.navigator.sendBeacon === 'function' && typeof window.FormData === 'function') {
804				var formData = new FormData();
805				formData.append('action_type', actionType);
806				window.navigator.sendBeacon(ACTION_LOG_URL, formData);
807				return;
808			}
809
810			$.ajax({
811				type: 'POST',
812				url: ACTION_LOG_URL,
813				data: {
814					action_type: actionType
815				},
816				async: true
817			});
818		};
819		var handleAction = function(actionType) {
820			if (isActionHandled) {
821				return;
822			}
823
824			isActionHandled = true;
825			setCookie(COOKIE_NAME, 'Y', 365);
826			logAction(actionType);
827		};
828
829		$.ajax({
830			type: 'GET',
831			url: '/admin/ajax/dialog/affiliate/creator_marketing_visual_modal.cm',
832			dataType: 'html',
833			async: false,
834			cache: false,
835			success: function(res) {
836				var $html = $(res);
837
838				if (!$html) {
839					return;
840				}
841
842				var callback = function() {
843					// '크리에이터 마케팅 구경하기': 딜 관리 페이지로 이동
844					$('._btn-explore').off('click').on('click', function() {
845						handleAction(ACTION_TYPE_POSITIVE);
846						location.href = AFFILIATE_PRODUCT_URL;
847					});
848
849					// '다시 보지 않기': 모달 닫기
850					$('._btn-close').off('click').on('click', function() {
851						handleAction(ACTION_TYPE_NEGATIVE);
852						$.cocoaDialog.close();
853					});
854
855					$('#cocoaModal.modal_creator_marketing').off('hide.bs.modal.creatorMarketingVisualModal').on('hide.bs.modal.creatorMarketingVisualModal', function() {
856						handleAction(ACTION_TYPE_NEGATIVE);
857						$(this).off('hide.bs.modal.creatorMarketingVisualModal');
858					});
859
860					// 백드롭 오버레이 색상 보정 (rgba(0, 0, 0, 0.15))
861					changeBackdropColor();
862				};
863
864				$.cocoaDialog.open({ type: 'creator_marketing', custom_popup: $html, pc_width: 380 }, callback);
865			}
866		});
867	}
868
869	const openIntegrationNudge = function() {
870		const type = 'integration_nudge';
871		const cookieName = 'close_integration_nudge';
872
873		$.ajax({
874			type: 'GET',
875			url: '/admin/ajax/dialog/integration_nudge.cm',
876			dataType: 'html',
877			async: false,
878			cache: false,
879			success: function(res) {
880				const $html = $(res);
881
882				const callback = () => {
883					mixpanel.track('view_bo_dashboard_visual_modal_banner',{'site_code':SITE_CODE});
884					const $closeButton = $('._btn-close');
885
886					$closeButton.on('click', function() {
887						setCookieTodayExpires(cookieName, 'Y');
888
889						$.cocoaDialog.hide();
890					});
891
892					changeBackdropColor();
893					attachCloseEventOnModalDialog(type, '.modal-content');
894				};
895
896				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
897			}
898		});
899	}
900
901	const openPopupInterlocking = function() {
902		const type = 'popup_interlocking';
903		const cookieName = 'close_popup_interlocking';
904
905		$.ajax({
906			type: 'GET',
907			url: '/admin/ajax/dialog/popup_interlocking.cm',
908			dataType: 'html',
909			async: false,
910			cache: false,
911			success: function(res) {
912				const $html = $(res);
913
914				const callback = () => {
915					const $checkButton = $('._btn-check');
916					const $closeButton = $('._btn-close');
917
918					$checkButton.on('click', function() {
919						window.open('https://smartstore-link.imweb.me/', '_blank');
920						$.cocoaDialog.close();
921					});
922
923					$closeButton.on('click', function() {
924						setCookieTodayExpires(cookieName, 'Y');
925						$.cocoaDialog.close();
926					});
927
928					changeBackdropColor();
929					attachCloseEventOnModalDialog(type, '.modal-content');
930				};
931
932				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
933
934				// $.cocoaDialog.obj.on('hidden.bs.modal', () => {
935				// 	if ( getCookie('NO_EXPIRE_POPUP') !== "Y" && EXPIRE_TYPE !== '') {
936				// 		SADMIN_POPUP.openPopupExpire(EXPIRE_TYPE, EXPIRE_PERIOD);
937				// 	}
938				// });
939			}
940		});
941	}
942
943	const openFontExpirationNudge = function() {
944		const type = 'font_expiration_nudge';
945		const cookieName = 'close_font_expiration_nudge';
946
947		$.ajax({
948			type: 'GET',
949			url: '/admin/ajax/dialog/font_expiration_nudge.cm',
950			dataType: 'html',
951			async: false,
952			cache: false,
953			success: function(res) {
954				const $html = $(res);
955
956				const callback = () => {
957					const $checkButton = $('._btn-check');
958					const $closeButton = $('._btn-close');
959
960					$checkButton.on('click', function() {
961						$.cocoaDialog.close();
962					});
963
964					$closeButton.on('click', function() {
965						setCookieTodayExpires(cookieName, 'Y');
966
967						$.cocoaDialog.close();
968					});
969
970					changeBackdropColor();
971					attachCloseEventOnModalDialog(type, '.modal-content');
972				};
973
974				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
975
976				$.cocoaDialog.obj.on('hidden.bs.modal', () => {
977					if ( getCookie('NO_EXPIRE_POPUP') !== "Y" && EXPIRE_TYPE !== '') {
978						SADMIN_POPUP.openPopupExpire(EXPIRE_TYPE, EXPIRE_PERIOD);
979					}
980				});
981			}
982		});
983	}
984
985	const openFontAdditionNudge = function() {
986		console.log('openFontAdditionNudge');
987		const type = 'font_addition_nudge';
988		const cookieName = 'close_font_addition_nudge';
989
990		$.ajax({
991			type: 'GET',
992			url: '/admin/ajax/dialog/font_addition_nudge.cm',
993			dataType: 'html',
994			async: false,
995			cache: false,
996			success: function(res) {
997				const $html = $(res);
998
999				const callback = () => {
1000					const $checkButton = $('._btn-check');
1001					const $closeButton = $('._btn-close');
1002
1003					$checkButton.on('click', function() {
1004						$.cocoaDialog.close();
1005					});
1006
1007					$closeButton.on('click', function() {
1008						setCookieTodayExpires(cookieName, 'Y');
1009
1010						$.cocoaDialog.close();
1011					});
1012
1013					changeBackdropColor();
1014					attachCloseEventOnModalDialog(type, '.modal-content');
1015				};
1016
1017				$.cocoaDialog.open({ type, custom_popup: $html }, callback);
1018
1019				$.cocoaDialog.obj.on('hidden.bs.modal', () => {
1020					if ( getCookie('NO_EXPIRE_POPUP') !== "Y" && EXPIRE_TYPE !== '') {
1021						SADMIN_POPUP.openPopupExpire(EXPIRE_TYPE, EXPIRE_PERIOD);
1022					}
1023				});
1024			}
1025		});
1026	}
1027
1028	const openPopupNotificationLaunching = function() {
1029		const COOKIE_NAME = 'close_popup_notification_launching';
1030
1031		$.ajax({
1032			type: 'POST',
1033			dataType: 'html',
1034			url: '/admin/ajax/dialog/popup_notification_launching.cm',
1035			async: false,
1036			cache: false,
1037			success: function(res) {
1038				const $html = $(res);
1039
1040				$($html).find('._button-close').click(function() {
1041					setCookieTodayExpires(COOKIE_NAME, 'Y');
1042
1043					$.cocoaDialog.close();
1044				});
1045
1046				$($html).find('._button-notification').click(function() {
1047					window.setTimeout(() => {
1048						mixpanel.track('click_bo_notification', {
1049							'site_code': SITE_CODE,
1050							'source': 'modal',
1051						});
1052
1053						getNotifyHandler().openModal();
1054					}, 0);
1055
1056					$.cocoaDialog.close();
1057				});
1058
1059				$.cocoaDialog.open({
1060					type: 'notification_launching',
1061					custom_popup: $html,
1062				}, function() {
1063					changeBackdropColor();
1064				});
1065			}
1066		});
1067	}
1068
1069	const openPopupCollectCategory = function() {
1070		const type = 'collect-category';
1071
1072		$.ajax({
1073			cache: false,
1074			type: 'POST',
1075			dataType: 'html',
1076			url: '/admin/ajax/dialog/popup_collect_category.cm',
1077		}).then((html) => {
1078			const $html = $(html);
1079
1080			$.cocoaDialog.open({
1081				type,
1082				custom_popup: $html,
1083			}, function() {
1084				changeBackdropColor();
1085				attachCloseEventOnModalDialog(type, `._${type}`);
1086			});
1087		});
1088	}
1089
1090	const openPopupSiteVersionUpgrade = function() {
1091		const type = 'site-version-upgrade';
1092
1093		$.ajax({
1094			cache: false,
1095			type: 'POST',
1096			dataType: 'html',
1097			url: '/admin/ajax/dialog/popup_site_version_upgrade.cm',
1098		}).then((html) => {
1099			const $html = $(html);
1100
1101			const callback = () => {
1102				$html.find('._btn-close').on('click', function() {
1103					$.cocoaDialog.close();
1104				});
1105
1106				changeBackdropColor();
1107				attachCloseEventOnModalDialog(type, '.modal-content');
1108			}
1109
1110			$.cocoaDialog.open({ type, custom_popup: $html }, callback);
1111		});
1112	}
1113
1114	return {
1115		openPopupGiftNudge,
1116		openCreatorMarketingVisualModal,
1117		openIntegrationNudge,
1118		openFontExpirationNudge,
1119		openFontAdditionNudge,
1120		openPopupNotificationLaunching,
1121		openPopupCollectCategory,
1122		openOMSTransitionPendingModal,
1123		openOMSTransitionReadyModal,
1124		openOMSPaypalModal,
1125		openPopupSiteVersionUpgrade,
1126		openPopupInterlocking,
1127	}
1128})();
1129
1130var SADMIN_MENU_ALERT = function(){
1131	var openAlert = function(){
1132		this.ajaxUrl = '/admin/ajax/dialog/menu_alert.cm';
1133		var str_url = location.pathname;
1134
1135		if ( IMWEB_SESSIONSTORAGE.get("MENU_ALERT_EMPTY_" + str_url) == 'Y' ) return;
1136
1137		$.ajax({
1138			url : this.ajaxUrl,
1139			type : 'POST',
1140			dataType : 'HTML',
1141			data : {'mode' : 'open', 'str_url': str_url},
1142			success : function(res){
1143				if ( res.length == 0 ) {
1144					IMWEB_SESSIONSTORAGE.set("MENU_ALERT_EMPTY_" + str_url, 'Y', 60);
1145				} else {
1146					if($("#menu_alert").length == 0 ){
1147						$('#content #alertBox').append(res);
1148						$("#menu_alert").show();
1149					}
1150				}
1151			}
1152		});
1153	};
1154
1155	var closeAlert = function(idx){
1156		$.ajax({
1157			url : this.ajaxUrl,
1158			type : 'POST',
1159			dataType : 'HTML',
1160			data : {mode : 'close', 'idx' : idx},
1161			success : function(){
1162				$("#menu_alert").hide();
1163			}
1164		});
1165	};
1166	var closeBannerAlert = function(type){
1167		$.ajax({
1168			url : '/admin/ajax/dialog/banner_alert.cm',
1169			type : 'POST',
1170			dataType : 'HTML',
1171			data : {'type' : type},
1172			success : function(){
1173				$("#banner_alert_"+type).css({'display':'none'});
1174			}
1175		});
1176	};
1177	return {
1178		ajaxUrl : null,
1179		'openAlert' : function(){
1180			openAlert();
1181		},
1182		'closeAlert' : function(idx){
1183			closeAlert(idx);
1184		},
1185		'closeBannerAlert' : function(type){
1186			closeBannerAlert(type);
1187		}
1188	};
1189}();
1190
1191const dateFormatUtils = (function() {
1192	const pad = (v) => {
1193		return `0${v}`.slice(-2);
1194	}
1195
1196	const toHyphenString = (dateObj) => {
1197		const year = dateObj.getFullYear();
1198		const month = pad(dateObj.getMonth() + 1);
1199		const date = pad(dateObj.getDate());
1200
1201		return [year, month, date].join('-');
1202	}
1203
1204	const toButtonString = (dateString) => {
1205		const [year, month, date] = dateString.split('-').map(v => parseInt(v, 10));
1206
1207		const dateObj = new Date(year, month - 1, date);
1208
1209		const day = dateObj.toLocaleString(window.ADMIN_LANG_CODE || 'en', { weekday: 'short' });
1210
1211		const yearText = year.toString().substring(2);
1212		const monthText = pad(month);
1213		const dateText = pad(date);
1214
1215		return `${yearText}.${monthText}.${dateText} (${day})`;
1216	}
1217
1218	return { pad, toHyphenString, toButtonString };
1219})();
1220
1221const datepickerButtonHandler = (function($) {
1222	const defaultRefreshUrl = (start, end) => {
1223		const parser = querystringParser();
1224		const query = parser(window.location.search);
1225
1226		window.location.href = getQuerystring({
1227			...query,
1228			start,
1229			end,
1230		});
1231	}
1232
1233	function DatepickerButtonHandler(options) {
1234		this.$container = options.$container;
1235		this.$start = options.$start;
1236		this.$end = options.$end;
1237		this.$middleIcon = options.$middleIcon;
1238		this.startDate = options.startDate;
1239		this.endDate = options.endDate;
1240	}
1241
1242	DatepickerButtonHandler.prototype.init = function() {
1243		const { $container, $start, $end, startDate, endDate } = this;
1244
1245		const update = ($e, date) => {
1246			if (!isValidDate(date)) {
1247				return false;
1248			}
1249
1250			const hyphenText = dateFormatUtils.toHyphenString(date);
1251			const buttonText = dateFormatUtils.toButtonString(hyphenText);
1252
1253			$e.data('text.hyphen', hyphenText);
1254			$e.text(buttonText);
1255
1256			return { buttonText, hyphenText };
1257		}
1258
1259		const { hyphenText: startText } = update($start, startDate);
1260		const { hyphenText: endText } = update($end, endDate);
1261
1262		if (startText === endText)  {
1263			$container.addClass('same');
1264		} else {
1265			$container.removeClass('same');
1266		}
1267	}
1268
1269	DatepickerButtonHandler.prototype.update = function(startDate, endDate) {
1270		this.startDate = new Date(startDate);
1271		this.endDate = new Date(endDate);
1272
1273		this.init();
1274	}
1275
1276	DatepickerButtonHandler.prototype.onClick = function(callback) {
1277		const self = this;
1278		const { $container } = self
1279
1280		if ($container.data('has.click')) {
1281			return false;
1282		}
1283
1284		$container.data('has.click', true);
1285
1286		const handler = function() {
1287			if (callback) {
1288				const { startDate, endDate } = self;
1289
1290				callback(self, startDate, endDate);
1291			}
1292		}
1293
1294		$container.on('click', handler);
1295
1296		return handler;
1297	}
1298
1299	DatepickerButtonHandler.prototype.refreshUrl = function() {
1300		const { $start, $end } = this;
1301
1302		const startHyphenText = $start.data('text.hyphen');
1303		const endHyphenText = $end.data('text.hyphen');
1304
1305		DatepickerButtonHandler.dependencies.refreshUrl(startHyphenText, endHyphenText);
1306	}
1307
1308	DatepickerButtonHandler.prototype.hyphenText = function() {
1309		const { $start, $end } = this;
1310
1311		return {
1312			start: $start.data('text.hyphen'),
1313			end: $end.data('text.hyphen'),
1314		};
1315	}
1316
1317	DatepickerButtonHandler.dependencies = {
1318		refreshUrl: defaultRefreshUrl,
1319	};
1320
1321	/**
1322	 * @param {Object} options - 옵션
1323	 * @param {string} [options.container='._datepicker'] - container 셀렉터
1324	 * @param {Object} [options.selectors={}] - start, end 텍스트 selectors
1325	 * @param {string} [options.selectors.start='._datepicker-start'] - start 텍스트 셀렉터
1326	 * @param {string} [options.selectors.middleIcon='._datepicker-middle-icon'] - middle icon 셀렉터
1327	 * @param {string} [options.selectors.end='._datepicker-end'] - end 텍스트 셀렉터
1328	 * @param {Date} [options.startDate] - start date
1329	 * @param {Date} [options.endDate] - end date
1330	 */
1331	return (options = {}) => {
1332		const container = options.container || '._datepicker';
1333		const selectors = options.selectors || {};
1334		const startDate = options.startDate;
1335		const endDate = options.endDate;
1336
1337		const startDateSelector = selectors.start || '._datepicker-start';
1338		const middleIconSelector = selectors.middleIcon || '._datepicker-middle-icon';
1339		const endDateSelector = selectors.end || '._datepicker-end';
1340
1341		const _options = {
1342			$container: $(container),
1343			$start: $(startDateSelector),
1344			$middleIcon: $(middleIconSelector),
1345			$end: $(endDateSelector),
1346			startDate: new Date(startDate),
1347			endDate: new Date(endDate),
1348		};
1349
1350		return new DatepickerButtonHandler(_options);
1351	};
1352})(window.jQuery);
1353
1354const getNotifyHandler = (function() {
1355	const utils = (function () {
1356		const throttle = (callback, delay = 0) => {
1357			let timer = null;
1358
1359			return function(...args) {
1360				if (timer !== null) {
1361					return false;
1362				}
1363
1364				timer = window.setTimeout(() => {
1365					callback.call(this, ...args);
1366
1367					timer = null;
1368				}, delay)
1369			}
1370		}
1371
1372		const listenEvents = (events, command) => {
1373			Object.entries(events).forEach((event) => {
1374				const [eventName, listener] = event;
1375
1376				window[`${command}EventListener`](eventName, listener);
1377			});
1378		}
1379
1380		const dismissMobileMenubar = () => {
1381			Object.getPrototypeOf(window.doznutadmin.AppNavigation)._handleMenubarLeave();
1382		}
1383
1384		const blurCurrentActiveElement = () => {
1385			if (document.activeElement instanceof HTMLElement) {
1386				document.activeElement.blur();
1387			}
1388		}
1389
1390		const getWindowWidth = () => {
1391			return window.innerWidth;
1392		}
1393
1394		const isMobileBreakpoint = (breakpointKey) => {
1395			return breakpointKey !== 'lg';
1396		}
1397
1398		const getBreakpointKey = (windowWidth, breakpoints) => {
1399			const breakpoint = Object.values(breakpoints).find((breakpoint) => {
1400				return windowWidth <= breakpoint.threshold;
1401			}) ?? breakpoints.lg;
1402
1403			return breakpoint.key;
1404		}
1405
1406		const getCurrentBreakpointKey = (breakpoints) => {
1407			const windowWidth = getWindowWidth();
1408
1409			return getBreakpointKey(windowWidth, breakpoints);
1410		}
1411
1412		const getIframeURL = (url, breakpoints) => {
1413			const params = {
1414				from: location.href,
1415				layout: 'panel',
1416				breakpoint: getCurrentBreakpointKey(breakpoints),
1417			};
1418
1419			const query = new URLSearchParams(params);
1420
1421			return `${url}?${query.toString()}`;
1422		}
1423
1424		const isSidebarMini = () => {
1425			return $('body').hasClass('sidebar-mini');
1426		}
1427
1428		return {
1429			throttle,
1430			listenEvents,
1431			dismissMobileMenubar,
1432			blurCurrentActiveElement,
1433			getWindowWidth,
1434			isMobileBreakpoint,
1435			getBreakpointKey,
1436			getCurrentBreakpointKey,
1437			getIframeURL,
1438			isSidebarMini,
1439		};
1440	})();
1441
1442	const scrollLock = (function() {
1443		const state = {
1444			isLock: false,
1445			restore: {
1446				scrollY: 0,
1447				bodyStyles: null,
1448			},
1449		};
1450
1451		const isScrollable = () => {
1452			return Boolean(window.innerWidth - document.documentElement.clientWidth);
1453		}
1454
1455		const getCurrentScrollY = () => {
1456			return window.scrollY;
1457		}
1458
1459		const getCurrentBodyStyles = () => {
1460			const bodyStyles = document.body.style;
1461
1462			return {
1463				position: bodyStyles.position,
1464				top: bodyStyles.top,
1465				overflowY: bodyStyles.overflowY,
1466				width: bodyStyles.width,
1467			};
1468		}
1469
1470		const lock = () => {
1471			const scrollable = isScrollable();
1472			const currentScrollY = getCurrentScrollY();
1473			const currentBodyStyles = getCurrentBodyStyles();
1474
1475			state.isLock = true;
1476			state.restore.scrollY = currentScrollY;
1477			state.restore.bodyStyles = currentBodyStyles;
1478
1479			document.body.style.setProperty('position', 'fixed');
1480			document.body.style.setProperty('top', `-${currentScrollY}px`);
1481			document.body.style.setProperty('width', '100%');
1482
1483			if (scrollable) {
1484				document.body.style.setProperty('overflow-y', 'scroll');
1485			}
1486		}
1487
1488		const unlock = () => {
1489			const { scrollY, bodyStyles } = state.restore;
1490
1491			document.body.style.setProperty('position', bodyStyles.position);
1492			document.body.style.setProperty('top', bodyStyles.top);
1493			document.body.style.setProperty('width', bodyStyles.width);
1494			document.body.style.setProperty('overflow-y', bodyStyles.overflowY);
1495
1496			window.scrollTo(0, scrollY);
1497
1498			state.isLock = false;
1499			state.restore.scrollY = 0;
1500			state.restore.bodyStyles = null;
1501		}
1502
1503		const wrap = (isLockCondition, fn) => () => {
1504			if (state.isLock === isLockCondition) {
1505				fn();
1506			}
1507		}
1508
1509		return {
1510			lock: wrap(false, lock),
1511			unlock: wrap(true, unlock)
1512		};
1513	})();
1514
1515	const createChannel = ($iframe, messageHandler) => {
1516		const channel = new MessageChannel();
1517
1518		$iframe.addEventListener('load', function() {
1519			window.setTimeout(() => {
1520				this.contentWindow.postMessage('init:port', location.origin, [channel.port2]);
1521			}, 10);
1522		});
1523
1524		channel.port1.onmessage = messageHandler;
1525
1526		const postMessage = (message) => {
1527			channel.port1.postMessage(message);
1528		}
1529
1530		const close = () => {
1531			channel.port1.close();
1532			channel.port2.close();
1533		}
1534
1535		return { postMessage, close };
1536	}
1537
1538	function NotifyHandler(options) {
1539		const { $provider, $button } = options;
1540
1541		this.$provider = $provider;
1542		this.$button = $button;
1543		this.isOpen = false;
1544		this.breakpointKey = '';
1545		this.channel = null;
1546		this.windowEvent = {
1547			isAttached: false,
1548			events: {
1549				resize: utils.throttle(this.handleWindowResize, 500).bind(this),
1550				click: this.handleWindowClick.bind(this),
1551				keydown: this.handleWindowKeydown.bind(this),
1552			},
1553		};
1554
1555		this.handleMessage = this.handleMessage.bind(this);
1556	}
1557
1558	NotifyHandler.prototype.handleWindowResize = function() {
1559		const breakpoints = NotifyHandler.CONST.breakpoints;
1560
1561		const nextBreakpointKey = utils.getCurrentBreakpointKey(breakpoints);
1562
1563		if (utils.isMobileBreakpoint(nextBreakpointKey)) {
1564			scrollLock.lock();
1565		} else {
1566			scrollLock.unlock();
1567		}
1568
1569		this.breakpointKey = nextBreakpointKey;
1570
1571		this.postMessage(`resize:${nextBreakpointKey}`);
1572	}
1573
1574	NotifyHandler.prototype.handleWindowClick = function(e) {
1575		const { $provider } = this;
1576
1577		const isProviderContext = $provider.contains(e.target);
1578
1579		if (isProviderContext) {
1580			return false;
1581		}
1582
1583		this.closeModal();
1584	}
1585
1586	NotifyHandler.prototype.handleWindowKeydown = function(e) {
1587		const { breakpointKey } = this;
1588
1589		if (e.key === 'Escape' && utils.isMobileBreakpoint(breakpointKey)) {
1590			this.closeModal();
1591		}
1592	}
1593
1594	NotifyHandler.prototype.attachWindowEvents = function() {
1595		const { windowEvent } = this;
1596
1597		windowEvent.isAttached = true;
1598
1599		utils.listenEvents(windowEvent.events, 'add');
1600	}
1601
1602	NotifyHandler.prototype.detachWindowEvents = function() {
1603		const { windowEvent } = this;
1604
1605		windowEvent.isAttached = false;
1606
1607		utils.listenEvents(windowEvent.events, 'remove');
1608	}
1609
1610	NotifyHandler.prototype.postMessage = function(message) {
1611		this.channel.postMessage(message);
1612	}
1613
1614	NotifyHandler.prototype.handleMessage = function(e) {
1615		const { data } = e;
1616
1617		// 알림 모달 열였을 시
1618		if (data === 'open') {
1619			// Nothing
1620		}
1621
1622		// 알림 모달 닫았을 시
1623		if (data === 'close') {
1624			this.closeModal();
1625		}
1626
1627		// 단일 읽기 처리 시
1628		if (data === 'confirm') {
1629			// Nothing
1630		}
1631
1632		// 전체 읽기 처리 시
1633		if (data === 'confirm:all') {
1634			this.interval.clearBadge();
1635		}
1636
1637		// 설정 화면 진입 버튼 클릭 시
1638		if (data === 'open:setting') {
1639			mixpanel.track('click_bo_notification_settings', { 'site_code': SITE_CODE });
1640		}
1641
1642		// 알림 항목 클릭 시
1643		if (data.startsWith('itemclick')) {
1644			const type = data.split(':')[1];
1645
1646			mixpanel.track('click_bo_notification_list_item', { type, 'site_code': SITE_CODE });
1647		}
1648
1649		// 배너 클릭 시
1650		if (data.startsWith('bannerclick')) {
1651			const name = data.split(':')[1];
1652
1653			mixpanel.track('click_bo_notification_banner', { name, 'site_code': SITE_CODE });
1654		}
1655	}
1656
1657	NotifyHandler.prototype.openModal = function() {
1658		const { $provider, $button } = this;
1659
1660		const createElement = () => {
1661			const $modal = document.createElement('div');
1662			const $backdrop = document.createElement('div');
1663			const $panel = document.createElement('div');
1664			const $contents = document.createElement('div');
1665			const $iframe = document.createElement('iframe');
1666
1667			$modal.classList.add('notify-modal');
1668			$backdrop.classList.add('notify-modal__backdrop');
1669			$panel.classList.add('notify-modal__panel');
1670			$contents.classList.add('notify-modal__contents');
1671			$iframe.classList.add('notify-modal__iframe');
1672
1673			if (utils.isSidebarMini()) {
1674				$modal.classList.add('notify-modal--narrow');
1675			}
1676
1677			$iframe.referrerPolicy = 'strict-origin';
1678			$iframe.src = utils.getIframeURL(NotifyHandler.CONST.iframeSource, NotifyHandler.CONST.breakpoints);
1679
1680			$contents.appendChild($iframe);
1681
1682			$panel.appendChild($contents);
1683
1684			$modal.appendChild($backdrop);
1685			$modal.appendChild($panel);
1686
1687			return $modal;
1688		}
1689
1690		const attachEvent = ($modal) => {
1691			const $panel = $modal.querySelector('.notify-modal__panel');
1692
1693			$panel.addEventListener('click', () => {
1694				this.closeModal();
1695			});
1696		}
1697
1698		const breakpointKey = utils.getCurrentBreakpointKey(NotifyHandler.CONST.breakpoints);
1699
1700		const $modal = createElement();
1701
1702		const channel = createChannel(
1703				$modal.querySelector('.notify-modal__iframe'),
1704				this.handleMessage,
1705		);
1706
1707		attachEvent($modal);
1708
1709		$button.classList.add('focus');
1710
1711		$provider.appendChild($modal);
1712
1713		this.attachWindowEvents();
1714
1715		if (utils.isMobileBreakpoint(breakpointKey)) {
1716			utils.dismissMobileMenubar();
1717			scrollLock.lock();
1718		}
1719
1720		utils.blurCurrentActiveElement();
1721
1722		this.breakpointKey = breakpointKey;
1723		this.isOpen = true;
1724		this.channel = channel;
1725
1726		this.postMessage('open');
1727
1728		this.interval.stop();
1729	}
1730
1731	NotifyHandler.prototype.closeModal = function() {
1732		const { $provider, $button, channel } = this;
1733
1734		this.postMessage('close');
1735
1736		channel.close();
1737
1738		this.detachWindowEvents();
1739
1740		scrollLock.unlock();
1741
1742		$button.classList.remove('focus');
1743
1744		// channel.postMessage('close') 하기 위함
1745		window.setTimeout(() => {
1746			$provider.innerHTML = '';
1747		}, 0);
1748
1749		this.breakpointKey = '';
1750		this.isOpen = false;
1751		this.channel = null;
1752
1753		this.interval.start();
1754	}
1755
1756	NotifyHandler.prototype.toggleModal = function() {
1757		const { isOpen } = this;
1758
1759		if (isOpen) {
1760			this.closeModal();
1761		} else {
1762			this.openModal();
1763		}
1764	}
1765
1766	NotifyHandler.prototype.setInterval = function(interval) {
1767		this.interval = interval;
1768	}
1769
1770	NotifyHandler.CONST = {
1771		iframeSource: `${location.origin}/admin/notify`,
1772		providerSelector: '._notify-modal-provider',
1773		buttonSelector: '._gnb_notification_button',
1774		breakpoints: {
1775			sm: { key: 'sm', threshold: 587 },
1776			md: { key: 'md', threshold: 992 },
1777			lg: { key: 'lg', threshold: Number.MAX_VALUE },
1778		},
1779	};
1780
1781	let instance = null;
1782
1783	return () => {
1784		if (instance === null) {
1785			const options = {
1786				$provider: document.querySelector(NotifyHandler.CONST.providerSelector),
1787				$button: document.querySelector(NotifyHandler.CONST.buttonSelector),
1788			};
1789
1790			instance = new NotifyHandler(options);
1791		}
1792
1793		return instance;
1794	}
1795})();
1796
1797const getNotifyBadgeInterval = (function() {
1798	const SELECTOR = '_badge';
1799
1800	function fetchBadgeCount() {
1801		return $.ajax({
1802			cache: false,
1803			type: 'GET',
1804			dataType: 'json',
1805			url: '/ajax/notification/notification.cm?apiPath=list&req=count',
1806		}).promise().then(res => res.unReadTotalCount ?? 0);
1807	}
1808
1809	function createBadgeElement(type, text) {
1810		const $badge = document.createElement('span');
1811
1812		$badge.classList.add(SELECTOR);
1813
1814		$badge.textContent = text;
1815
1816		switch(type) {
1817			case 'count':
1818				$badge.classList.add('badge_icon', 'badge_count');
1819				break;
1820			case 'new':
1821				$badge.textContent = '';
1822				break;
1823		}
1824
1825		return $badge;
1826	}
1827
1828	function setBadge(count, $button) {
1829		const $prevBadge = $button.find(`.${SELECTOR}`);
1830
1831		if ($prevBadge.length) {
1832			$prevBadge.remove();
1833		}
1834
1835		const countText = count > 99 ? '99+' : count;
1836
1837		const $badge = count > 0 ? createBadgeElement('count', countText) : createBadgeElement('new', 'New');
1838
1839		$button.append($badge);
1840	}
1841
1842	function createUpdateBadgeCount($button) {
1843		return () => {
1844			return fetchBadgeCount().then((count) => setBadge(count, $button));
1845		}
1846	}
1847
1848	function Interval(fn, interval, $button) {
1849		let timer = null;
1850
1851		const start = () => {
1852			timer = window.setInterval(fn, interval);
1853		}
1854
1855		const stop = () => {
1856			window.clearInterval(timer);
1857		}
1858
1859		const force = () => {
1860			fn();
1861		}
1862
1863		const clearBadge = () => {
1864			setBadge(0, $button);
1865		}
1866
1867		return { start, stop, force, clearBadge };
1868	}
1869
1870	return ($button) => {
1871		const updateBadgeCount = createUpdateBadgeCount($button);
1872
1873		return new Interval(updateBadgeCount, 1000 * 60 * 1, $button);
1874	}
1875})();

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.