PageSourceSearch

https://cdn.mediapic.co.kr/script/Bannerpop.js

js mediapic.co.kr collected 2026-10-06 17:56:29 UTC 30,741 bytes, 862 lines download raw bytes

1class Bannerpop {
2    IS_MOBILE_MODE = false;
3    POP_OPTIONS = {
4		bodyWidth:0,
5		bodyLeft:0,
6		screenWidth:0,
7		popname:"popup_",
8		width:{ window:[],layer:[] },
9		height:{ window:[],layer:[] },
10		top:{ window:[],layer:[] },
11		left:{ window:[],layer:[] },
12		order:0,						// z-index용 순차적
13		view:"normal"					// 일반 : normal or 관리자 : manager
14	};
15    constructor() {
16		const userWrap = document.querySelector("#user-wrap");
17		if (userWrap) {
18			this.IS_MOBILE_MODE = userWrap.classList.contains("mobile") ? true : false;
19		}
20    }
21
22    // 배너 append
23	static async append() {
24		const _this = this,
25			status = window.bannerStatus,
26			time = window.___currentTime || window.parent.___currentTime,
27			sTime = window.startTime,
28			eTime = window.endTime,
29			title = typeof window.bannerTitle == "undefined" ? "" : decodeURIComponent(window.bannerTitle),
30			target = document.currentScript,
31			voteUse = typeof bannerVoteUse == "undefined" ? "N" : bannerVoteUse;
32
33		let content = decodeURIComponent(bannerContent);
34
35		if(!target || target.getAttribute("data-append") == true) return false; // 객체가 없거나 시작전,이미 추가되었다면 중단
36
37		// 예정,종료 배너
38		if(sTime > time || time > eTime || status=="0"){
39			const isAdminPage = location.href.includes('/edit/adminSection.html');
40			const isUserView = document.body.classList.contains('ndip_user_body_view');
41			if (!isAdminPage && !isUserView && voteUse === 'Y' && status === "0") {
42				return false; // 종료 메시지 안 띄우기
43			}
44			//
45            let voteAdcClass = "",
46				adcMsg="",
47				adcClass="";
48
49			if(sTime > time && status != "0"){
50				adcMsg = '예약배너';
51				adcClass = "banner-later";
52			}else if(time > eTime || status == "0"){
53				adcMsg = '종료배너';
54				adcClass = "banner-finish";
55				if(voteUse == "Y") voteAdcClass = "vote_banner_box_finish";
56			}
57
58			target.setAttribute("data-append", "true");
59			const div = document.createElement('div');
60
61			if (!isAdminPage) {
62				div.className = "ad-finished " + voteAdcClass;
63			} else {
64				div.className = "ad-finished " + adcClass;
65				div.innerHTML = '<i class="icon-clock-o"></i> ' + adcMsg + ': <strong>"' + title + '"</strong>';
66			}
67			
68			target.insertAdjacentElement('afterend', div);
69
70			bannerTitle = "";  // 초기화'
71
72			return false;
73		}
74
75		const reloadScripts = async (element) => {
76			const scripts = element.querySelectorAll("script[src]");
77			const cacheVersion = Util.getCookie("nx_cache_vm")
78		
79			for (const script of scripts) {
80				await new Promise((resolve, reject) => {
81					const newScript = document.createElement("script");
82					newScript.async = true;
83					let newSrc = script.src;
84					if (cacheVersion === "vnum1") {
85						const separator = newSrc.includes("?") ? "&" : "?";
86						newSrc += separator + "version=" + new Date().getTime();
87					}
88		
89					newScript.src = newSrc;
90					script.replaceWith(newScript);
91					newScript.onload = resolve;
92					newScript.onerror = reject;
93					
94				});
95			}
96		};
97	
98		const setSizeInfo = (_this) => {
99			const idxno = _this.getAttribute("data-idxno"),
100				ul = _this.querySelector(".beb_ul"),
101				boxWidth = _this.clientWidth || _this.closest("#float_side_box").clientWidth; // 윙 사이드의 배너일경우 재측정
102				
103			let lis = ul.querySelectorAll(".beb_li"),
104				liCount = lis.length || 0;
105
106			lis.forEach(li => {
107				const len = li.querySelectorAll(".banner_box").length;
108				if (!len) {
109					li.remove();
110					lis = ul.querySelectorAll(".beb_li");
111					liCount = lis.length;
112				}
113			});
114			
115			// 부모박스 기준으로 ul 최대 넓이 지정
116			ul.style.width = (boxWidth * liCount) + "px";
117	
118			// 배너마다 넓이가 다를수 있으니 부모박스 넓이기준으로함
119			lis.forEach(li => li.style.width = boxWidth + "px");
120			
121			return {
122						idxno : idxno,
123						ul : ul,
124						lis : lis,
125						boxWidth : boxWidth,
126						liCount : liCount
127					};
128		}
129
130		const parser = new DOMParser(),
131			doc = parser.parseFromString(content, "text/html"),
132			contentBox = _this.changeCDNLink(doc.body.firstChild);
133
134		let targetIdxno = null;
135		if (target.src) {
136			const targetMatch = target.src.match(/\/(\d+)\.js/);
137			targetIdxno = (targetMatch!=null)?targetMatch[1]:null;
138		}
139
140		if (targetIdxno!=null && Util.getCookie("delay_banner")==targetIdxno) {
141			setTimeout(()=>{
142				target.after(contentBox);
143				Util.setCookie("delay_banner", "", 0);
144			}, 1000); 
145		} else target.after(contentBox);
146
147		await reloadScripts(contentBox);
148
149		if (!(contentBox.classList.contains("banner_event_box") || contentBox.classList.contains("banner-multi-box"))) return;
150
151		// 기능성 배너
152		if(contentBox.classList.contains("banner_event_box")) {
153			const type = contentBox.getAttribute("data-type");
154			const savePrefix = "banner_order_";
155
156			const ActivityRelatedFunc = {
157				// 순서대로 하나의 배너만 보임
158				order:function(){
159					const info = setSizeInfo(contentBox),
160						idxno = info.idxno,
161						lis = info.lis,
162						liCount = info.liCount - 1,
163						saveKey = savePrefix + idxno,
164						order = Number(localStorage.getItem(saveKey)) || 0;
165					// 랜덤 선택된것만 보임, 버튼 없앰
166					lis.forEach((li, i) => li.style.display = i === order ? "list-item" : "none");
167
168					localStorage.setItem(saveKey, (order >= liCount ? 0 : order + 1));
169				},
170
171				// random banner
172				random:function(){
173					const info = setSizeInfo(contentBox),
174						lis = info.lis,
175						liCount = info.liCount,
176						random = Math.floor(Math.random() * liCount);
177					// 랜덤 선택된것만 보임, 버튼 없앰
178					lis.forEach((li, i) => li.style.display = i === random ? "list-item" : "none");
179				},
180				// random 배너 선택후 슬라이드
181				randomSlide:function(){
182					this.slide(true);
183				},
184
185				// slide banner
186				// rand true or false
187				slide: function(rand){
188					const info = setSizeInfo(contentBox),
189						ul = info.ul,
190						lis = Array.from(info.lis),
191						liCount = info.liCount,
192						btnBox = contentBox.querySelector(".beb_buttons"),
193						btnStop = btnBox.querySelector(".beb_btn_stop"),
194						speed = Number(contentBox.getAttribute("data-speed")) || 0.3,
195						delayInfo = lis.map(li => Number(li.getAttribute("data-delay")) || 5),
196						boxWidth = info.boxWidth;
197					
198					let btnCurrent = null, 														// 현재 선택된 버튼 prev,next,play,stop of buttons
199						count = (rand === true ? Math.floor(Math.random() * (liCount)) : -1),	// 처음부터 +1 하고 시작하기에 -1초기값-random일때는 배너수중 1개
200						left = boxWidth,														// 움직이는 간격
201						delay = null,															// 멈춰잇는 시간(초)
202						si = null,																// setInterval 변수 : animate complete에서 delay로 해결하려 했는데 뭔가 잘 안풀림
203						tmpDelay = null,														// next, prev 시 delay 없이 넘김
204						firstBtnClicked = 0;													// prev,next로 이동하는가 - rotate에서 또한번 +,- 하기에 처음만 실행되어야 함
205
206					/**** function //s ****/
207					/*
208						다음 배너
209						@params
210						 loop : 반복될것인가
211						 mode : + or -
212					*/
213
214					lis.forEach(li => li.style.display = "list-item");
215
216					function rotate(loop, mode){
217						if(!mode) mode = "+";
218						mode === "+" ? count++ : count--;
219
220						if(count >= liCount || count <= 0) count = 0;
221						let front = count + 1;
222						if(count >= (liCount - 1)) front = 0;
223						left = boxWidth * front * -1;
224						delay = tmpDelay === null ? delayInfo[count] : tmpDelay;
225
226						si = setTimeout(() => {
227							ul.style.transition = `margin-left ${speed * 1000}ms`;
228							ul.style.marginLeft = left + "px";
229
230							ul.addEventListener('transitionend', function callback() {
231								if (loop === false) btnCurrent.disabled = false;
232								tmpDelay = null;
233								ul.removeEventListener('transitionend', callback);
234							});
235
236							if (loop !== false) rotate();
237						}, delay * 1000);
238					}
239						
240
241					// stop
242					function stop(){
243						firstBtnClicked++;
244
245						//$ul.stop(true, false);
246						clearTimeout(si);
247						tmpDelay = null;
248						btnStop.classList.add("beb_btn_start");
249						btnStop.textContent = "play";
250					}
251
252					// restart
253					function start(){
254						firstBtnClicked = 0;
255
256						count--;
257						if(count<=0) count=0;
258
259						tmpDelay = null;
260						rotate();
261
262						btnStop.classList.remove("beb_btn_start");
263						btnStop.textContent = "멈춤";
264					}
265
266					// 이전
267					function prev(){
268						stop();
269
270						if(firstBtnClicked === 1) count--;			// 처음만 이미 증가된값을 감소시킴
271						if(count <= 0) count = liCount;
272
273						tmpDelay=0;
274						rotate(false, "-");
275
276						btnCurrent.disabled = true;
277					}
278
279					// 이후
280					function next(){
281						stop();
282
283						if(firstBtnClicked === 1) count--;
284						if(count >= liCount) count=0;
285
286						tmpDelay = 0;
287						rotate(false);
288
289						btnCurrent.disabled = true;
290					}
291					/**** function //e ****/
292
293					// 처음 배너를 보이지 않는다는 것은 랜덤 배너의 슬라이드라는 것-처음보일배너 설정
294					if(count >= 0 && rand === true){
295						let _boxWidth = boxWidth * (count >= liCount ? liCount -1 : count) * -1;
296						count--;
297
298						ul.style.marginLeft = _boxWidth + "px";
299					}
300
301					// 실행
302					rotate();
303
304					// 버튼보임
305					btnBox.addEventListener("click", (e) => {
306						const target = e.target.closest("button.beb_btns");
307						if(!target) return;
308						btnCurrent = target;
309						
310						if(target.classList.contains("beb_btn_prev")) prev();
311						if(target.classList.contains("beb_btn_next")) next();
312						if(target.classList.contains("beb_btn_stop")) target.classList.contains("beb_btn_start") ? start() : stop();
313					});
314
315					// mouseover event
316					if (_this.IS_MOBILE_MODE) {
317						ul.addEventListener('touchstart', stop);
318						ul.addEventListener('touchend', start);
319					} else {
320						ul.addEventListener('mouseenter', stop);
321						ul.addEventListener('mouseleave', start);
322					}
323				},
324			};
325
326			
327			ActivityRelatedFunc[type]();
328
329		}
330
331		// 멀티 기능성 배너
332		if(contentBox.classList.contains("banner-multi-box")) {
333			this.bannerLoad(contentBox);
334		}
335		//$(target).attr("data-append","true").after(content);	// 추가 되었으면 data-append 태그 나김
336	}
337
338	// script, img link cdn 링크로 치환
339	static changeCDNLink(dom) {
340		let cdnDomain = "";
341		if("CDN_DOMAIN" in window) cdnDomain=window.CDN_DOMAIN;
342		else if("CDN_DOMAIN" in window.parent) dnDomain=window.parent.CDN_DOMAIN;
343		else if("CDN_DOMAIN" in window.opener) dnDomain=window.opener.CDN_DOMAIN;
344
345		if(!cdnDomain || !(/^http(s)?:/i.test(cdnDomain) || /^\/\//i.test(cdnDomain))) return dom;
346
347		let els=dom.querySelectorAll("script[src] , img[src]"); //console.log(els)
348		if(els.length<=0) return dom;
349		
350		els.forEach((a)=>{
351			const src = a.getAttribute("src");
352			if(src && !/^http(s)?:/i.test(src) && !/^\/\//i.test(src)) a.src = cdnDomain + src;
353		});
354
355		return dom;
356	}
357
358	static bannerLoad(contentBox) {
359        const effectType = contentBox.getAttribute("data-effect-type"),
360			rowNum = parseInt(contentBox.getAttribute("data-row-num"), 10) || 1,
361			colNum = Number(contentBox.getAttribute("data-column-num")) || 1,
362			scrollNum = parseInt(contentBox.getAttribute("data-scroll-num"), 10) || 1,
363			moveDisplay = contentBox.getAttribute("data-move-display") || 'none',
364			isAutoPlay = contentBox.getAttribute("data-autoplay") == "Y",
365			autoplaySpeed = parseInt(contentBox.getAttribute("data-autoplay-speed"), 10) || 5,
366			speed = parseInt(contentBox.getAttribute("data-speed"), 10) || 300,
367			isRandom = contentBox.getAttribute("data-random") == "Y";
368
369		// 예약배너이거나 종료 배너일 경우 제외 처리.
370		if (location.href.indexOf('/edit/adminSection.html') < 0) {
371			// 슬라이드 사용 시
372			contentBox.querySelectorAll(".slick-slide").forEach(function(slide) {
373				if (slide.querySelectorAll('.banner_box').length <= 0) {
374					slide.remove();
375				}
376			});
377			// 슬라이드 미사용 시
378			$('.banner-multi-box .slick-slide-none .ad-finished').closest('.columns').remove();
379		}
380
381		if(effectType != "none") {
382			const totalSlide = contentBox.querySelectorAll(".slick-slide").length || 0,
383				slideCount = effectType == "horizon" ? (totalSlide / rowNum) || 0 : totalSlide,
384				randomStart = isRandom ? Math.floor(Math.random() * slideCount) : 0;
385			
386			const slickParam = {};
387            slickParam.prevArrow = '<button type="button" class="roll-btns btn-prev"><i class="icon-angle-left-thin"><span class="show-for-sr">prev</span></i></button>';  // 이전버튼
388            slickParam.nextArrow = '<button type="button" class="roll-btns btn-next"><i class="icon-angle-right-thin"><span class="show-for-sr">next</span></i></button>'; // 다음버튼
389            slickParam.infinite = true;                                                                                                                     // 무한 넘기기
390            slickParam.draggable = false;    
391			switch(effectType) {
392				case "vertical" :
393					slickParam.slidesToShow = rowNum;
394					slickParam.vertical = true;
395					slickParam.verticalSwiping = true;
396					if(isRandom && totalSlide > rowNum) slickParam.initialSlide = randomStart;
397					break;
398				case "horizon":
399				default:
400					slickParam.slidesToShow = parseInt(colNum , 10);
401                    if (rowNum > 1) slickParam.rows = rowNum;
402                    if (isRandom) slickParam.initialSlide = randomStart;
403                    break;
404			}
405			slickParam.slidesToScroll = scrollNum;
406            slickParam.arrows = moveDisplay == "arrow" ? true : false;
407            slickParam.dots = moveDisplay == "dot" ? true : false;
408            slickParam.dotsClass = "auto-pager";
409            slickParam.autoplay = isAutoPlay ? true : false;
410            slickParam.autoplaySpeed = isAutoPlay ? autoplaySpeed * 1000 : 0;
411            slickParam.speed = speed;
412            slickParam.centerMode = false;
413            slickParam.variableWidth = false;
414            slickParam.focusOnSelect = true;
415
416            $(contentBox).slick(slickParam);
417		} else {
418			if (isRandom) shuffle($(contentBox));
419		}
420
421		function shuffleArray(d) {
422            for (var c = d.length - 1; c > 0; c--) {
423                var b = Math.floor(Math.random() * (c + 1));
424                var a = d[c];
425                d[c] = d[b];
426                d[b] = a;
427            }
428            return d;
429        }
430        
431		function shuffle(target){
432            var $box = $(target).children(".slick-slide-none")
433                ,$container = $box.children(".row")
434                ,$li = $box.find(".columns")
435                ,total = $li.length
436                ,tmp = [];
437            for(var i=0;i<total;i++) tmp.push(i);
438            $container.empty();
439            setTimeout(
440                shuffleArray(tmp).forEach(function(a,b){
441                    var $bannerBoxs=$li[a].querySelectorAll(".banner_box")
442                        ,length=$bannerBoxs.length
443                        ,div=document.createElement("div");
444                    for(var i=0;i<length;i++){
445                        div.appendChild($bannerBoxs[i]);
446                        div.className = "columns";
447                    }
448                    $container.append(div);
449                })
450            ,0);
451        }
452    }
453
454    float(j) {
455        // json 값
456        const parents = this, 
457			current = window. ___currentTime,
458            start = Number(j.start) || 0,
459            end = Number(j.end) || (new Date().getTime() + 100),
460            status = j.status || "0";
461        
462        if(this.POP_OPTIONS.view != "manager" && (status == "0" || current < start || (current > end && end !== 0))) return false;
463
464        const idxno = j.idxno || "0",
465			popupType = j.popup_type || "window",
466			popupWidth = Number(j.popup_width) || 0,
467			popupHeight = (Number(j.popup_height) || 0),
468			contentType = j.content_type||"FILE",
469			bottomClose = j.bottom_close||"N",
470			bottomCloseExpireCk = j.bottom_close_expire_ck || "none",
471			bottomCloseExpire	= j.bottom_close_expire || 1,
472			layerBorder = j.layer_border || "N",
473			layerMove = j.layer_move || "N",
474			layerScroll = j.layer_scroll || "N",
475			layerClosePosition = j.layer_close_position || "BR",
476			windowScroll = j.window_scroll || "Y",
477			content = decodeURIComponent(j.content) || "",
478			popname = this.POP_OPTIONS.popname + idxno,
479			_type = popupType; 								// default: 각자띄움 - 팝업과 레이어가 위치값을 공유하여 차례로 띄울려면 "window" or 공유하지 않고 각자 띄울려면 popupType;
480
481		let top = 0,
482			left = 0,
483			xPosition = Number(j.x_position) || 0,
484			yPosition = Number(j.y_position) || 0;
485		
486		const type = {
487			calcPosition: function() {
488				// x,y가 0일때는 auto로 간주
489				if((xPosition == "0" && yPosition == "0")) {
490					const maxHeight = parents.arrayMaxMin(parents.POP_OPTIONS.height[_type], "max"),
491						maxTop = parents.arrayMaxMin(parents.POP_OPTIONS.top[_type], "max"),
492						initLeft = popupType == "window" ? 0 : parents.POP_OPTIONS.bodyLeft,
493						prevWidth = parents.POP_OPTIONS.width[_type][parents.POP_OPTIONS.width[_type].length - 1] || 0,
494						prevLeft = parents.POP_OPTIONS.left[_type][parents.POP_OPTIONS.left[_type].length - 1] || initLeft;
495
496					left = prevLeft + prevWidth;
497
498					if(left > parents.POP_OPTIONS[popupType == "window" ? "screenWidth" : "bodyWidth"]) {
499						top += maxHeight;
500						left = initLeft;
501
502						parents.POP_OPTIONS.width[_type] = [];
503						parents.POP_OPTIONS.height[_type] = [];
504						parents.POP_OPTIONS.top[_type] = [];
505						parents.POP_OPTIONS.left[_type] = [];
506					} else {
507						top = (maxTop || 0);
508					}
509
510					// 변화된값 배열에 넣기 위해 조작
511					xPosition = left;
512					yPosition = top;
513
514					// 변수에 담음
515					parents.POP_OPTIONS.width[_type].push(popupWidth || 0);
516					parents.POP_OPTIONS.height[_type].push(popupHeight || 0);
517					parents.POP_OPTIONS.top[_type].push(yPosition || 0);
518					parents.POP_OPTIONS.left[_type].push(xPosition || 0);
519				} else {
520					top = yPosition;
521					left = xPosition;
522				}
523			},
524
525			// 관리자 미리보기시 중앙에
526			setPositionCenter: function(){
527				const _width = (document.body.clientWidth/2)-(popupWidth/2),
528					_scrollTop = document.body.scrollTop;
529
530				top = _scrollTop + 50;
531				left = (_width>0)?_width:0;
532			},
533
534			// 띄울 페이지 applyPage
535			isApplyPage: function() {
536				if(parents.POP_OPTIONS.view == "manager") return true;
537			},
538
539			window:function(){
540				this.calcPosition(); // 위치 계산
541				const winpopupUrl = '/bannerpop/popup.html?win_popup_idxno=' + idxno;
542				window.open(winpopupUrl, popname, "width="+popupWidth+",height="+(popupHeight + ((bottomCloseExpireCk=='none'&&bottomClose=='N')?0:40))+",scrollbars="+(windowScroll=="Y"?"yes":"no")+",resizable=yes,top="+top+",left="+left);
543			},
544
545			layer:function(){
546				this.calcPosition(); // 위치 계산
547
548				// HTML 문자열 DOM 요소로 변환
549				const parser = new DOMParser(),
550					doc = parser.parseFromString(content, 'text/html'),
551					contentBox = parents.constructor.changeCDNLink(doc.body.firstChild);
552
553				contentBox.id = popname;
554				if (layerMove == "Y" && parents.IS_MOBILE_MODE!==true) contentBox.style.setProperty("--nd-popup-transform", "none");
555				contentBox.style.setProperty("--nd-popup-offset-x", left + "px");
556				contentBox.style.setProperty("--nd-popup-offset-y", top + "px");
557				contentBox.style.setProperty("--nd-popup-width", popupWidth + "px");
558				contentBox.style.setProperty("--nd-popup-height", popupHeight + "px");
559				contentBox.style.setProperty("--nd-popup-zindex", "110" + parents.POP_OPTIONS.order);
560				contentBox.classList.add("layer_box");
561
562				if(contentType == "HTML") contentBox.classList.add("for-html");
563
564				if(layerScroll === "Y") contentBox.classList.add("layer_box_fixed");
565
566				if(layerBorder === "Y") contentBox.classList.add("layer_box_border");
567
568				const closeBox = contentBox.querySelector(".popup_close_box");
569				if(closeBox) {
570					closeBox.classList.add("layer_close_box");
571				}
572
573				if(layerMove == "Y" && parents.IS_MOBILE_MODE!==true){
574					contentBox.classList.add("layer_box_cursor_move");
575					parents.enableDraggable(contentBox);
576				}
577
578				// 창닫기-icon 닫기 버튼은 별개다
579				//if(bottomClose=="Y"){
580				const btnClose = contentBox.querySelectorAll(".popup_close, .popup_close_map, .popup_icon_close"),
581					hasPopupCloseElements = contentBox.querySelectorAll(".popup_close, .popup_close_map").length > 0;
582
583				btnClose.forEach(btn => {
584					if (btn.classList.contains("popup_icon_close")) {
585						// 버튼위치 잡는데 불필요해서 주석(2024-05-17)
586						// if ((layerClosePosition == 'BL' || layerClosePosition == 'BR') && hasPopupCloseElements) {
587						// 	btn.style.top = Number(j.popup_height) - 30 + "px";
588						// }
589						if(!hasPopupCloseElements) {
590							contentBox.classList.add("no-footer");
591						}
592						btn.classList.add("layer_position_" + layerClosePosition);
593					}
594				});
595
596				btnClose.forEach(btn => btn.addEventListener("click", () => {
597					parents.evtClose(popupType, idxno);
598					return false;
599				}));
600				//}
601
602				// 이미지내 링크가 창닫기 라면
603				//$content.find("a[href*='javascript:window.close()']").click(function(){
604				const elements = contentBox.querySelectorAll("[href*='javascript:window.close()']");
605				elements.forEach(element => element.addEventListener("click", () => {
606					parents.evtClose(popupType, idxno);
607					return false;
608				}));
609
610				const popupDays = contentBox.querySelectorAll(".popup-days, .popup_day_close_map");
611				// 하루, 몇일동안 열지 않기
612				parents.daysNotOpen(bottomCloseExpireCk, bottomCloseExpire, popupDays, j);
613
614				if(parents.POP_OPTIONS.view == "manager"){
615					document.body.appendChild(contentBox);
616					this.setPositionCenter();
617					contentBox.classList.remove("layer_box_fixed");
618					contentBox.style.setProperty("--nd-popup-transform", "none");
619					contentBox.style.setProperty("--nd-popup-offset-y", top + "px");
620					contentBox.style.setProperty("--nd-popup-offset-x", left + "px");
621				}else {
622					 document.querySelector("#layer-popups").appendChild(contentBox);
623				}
624
625				// 이미지맵 리사이즈
626				$('img[usemap]', contentBox).rwdImageMaps();
627			}
628		};
629
630		type[popupType]();
631    }
632
633	// 팝업띄우기
634	popup() {
635		if(!window.___popup) return;
636		if(Array.isArray(___popup) === false && ___popup.length > 0) return;
637		if(!window.___currentTime) ___currentTime = Math.floor(new Date().getTime() / 1000);
638
639		const content = document.querySelector("#layer-popups");
640
641		if(!content) return false;
642
643		this.POP_OPTIONS.bodyWidth = content.clientWidth;
644		this.POP_OPTIONS.screenWidth = window.screen.width;
645		
646		for(let i = 0, cnt = ___popup.length ; i < cnt ; i++ ){
647			// 쿠키인지 판단후 열기
648			let coo = "";
649			coo = Util.getCookie(this.POP_OPTIONS.popname + ___popup[i].idxno);
650
651			if(!coo){
652				this.POP_OPTIONS.order++;
653				this.float(___popup[i]);
654			}
655		}
656	}
657
658	// 창닫기
659	evtClose(mode, idxno) {
660		if(mode=="layer"){
661			const layerId = this.POP_OPTIONS.popname + idxno;
662
663			$("#"+layerId).fadeOut("fast", function(){
664				$(this).remove();
665			});
666		}else window.close();
667	}
668
669	// cookie
670	evtCookieClose(name, days) {
671		Util.setCookie(name, "popup", days);
672	}
673
674	// 하루, 몇일동안 열지 않기.
675	daysNotOpen(expire, period, btns, p) {
676		const idxno = p.idxno;
677		let isClickedMap = false;
678		const addListenerAndClose = (element, days) => {
679			element.addEventListener("click", () => {
680				this.evtCookieClose(this.POP_OPTIONS.popname + idxno, Number(days)||1);
681				this.evtClose(p.popup_type, idxno);
682				return false;
683			});
684		};
685	
686		if (expire !== "none") {
687			btns.forEach(btn => {
688				if (btn.classList.contains("popup-days")) {
689					// 하루동안 열지 않기
690					if (expire === "day") {
691						addListenerAndClose(btn, "1");
692						if (!btn.hasChildNodes()) {
693							btn.innerHTML = '<a href="#close-to-layer">하루동안 보지 않기</a>';
694						}
695					} 
696					// 지정된 일수 동안 열지 않기
697					else if (expire === "days") {
698						const html = `<a href="#link">${period > 1 ? period + '일 동안 보지 않기' : '하루 동안 보지 않기'}</a>`;
699						btn.insertAdjacentHTML('beforeend', html);
700						addListenerAndClose(btn, period);
701					}
702				} else {
703					addListenerAndClose(btn, "1");
704					isClickedMap = true;
705				}
706			});
707		}
708
709		// expire 값이랑 관계없이 이미지맵을 찾아 처리
710        if (isClickedMap === false) {
711            btns.forEach((btn) =>
712                btn.addEventListener("click", () =>
713                    addListenerAndClose(btn, "1")
714                )
715            );
716        }
717	}
718
719	// window popup control
720	popupControl(){
721		if(!___popup) return ;
722		if((typeof ___popup) !== "object") return ;
723		const p = ___popup,
724			title = decodeURIComponent(p.title)||"",
725			popupType = p.popup_type||"window",
726			bottomClose = p.bottom_close||"Y",
727			bottomCloseExpireCk = p.bottom_close_expire_ck||"day",	// : day,days,none
728			bottomCloseExpire = p.bottom_close_expire||1,	// : 몇일
729			btnClose = document.querySelectorAll(".popup_close, .popup_close_map, .popup_icon_close"),
730			btnDayClose = document.querySelectorAll('.popup-days');
731
732		if(popupType!="window") return ;
733
734		// 윈도우 바 타이틀 교체
735		document.title = title;
736
737		// 창닫기
738		if(bottomClose=="Y"){
739			btnClose.forEach(btn => {
740				btn.addEventListener("click", () => {
741					this.evtClose(p.popup_type, p.idxno);
742					return false;
743				});
744			});
745		}
746
747		// 하루, 몇일동안 열지 않기
748		this.daysNotOpen(bottomCloseExpireCk, bottomCloseExpire, btnDayClose, p);
749	}
750
751	enableDraggable(ele) {
752		let isDragging = false;        
753		let dragStartX, dragStartY;
754		let initTranslateX = 0,  
755			initTranslateY = 0;
756
757		const draggableEle = ele;
758		const linksAndAreas = ele.querySelectorAll('a, area');
759
760		linksAndAreas.forEach(element => {
761			element.style.cursor = 'move';
762		});
763		ele.addEventListener("click", (e) => e.preventDefault());
764		const onPointerDown = (e) => {
765			e.preventDefault();
766			isDragging = true;
767			dragStartX = e.pageX;
768			dragStartY = e.pageY;
769
770			const transform = window.getComputedStyle(draggableEle).transform;
771			if (transform !== 'none') {
772				const matrix = new DOMMatrixReadOnly(transform);
773				initTranslateX = matrix.m41;
774				initTranslateY = matrix.m42;
775			} else {
776				initTranslateX = 0;
777				initTranslateY = 0;
778			}
779		};
780
781		const onPointerMove = (e) => {
782			if (!isDragging) return;                       
783
784			const moveX = e.pageX - dragStartX;
785			const moveY = e.pageY - dragStartY;
786
787			const translateX = initTranslateX + moveX;
788			const translateY = initTranslateY + moveY;
789			draggableEle.style.setProperty("--nd-popup-transform", `translate(${translateX}px, ${translateY}px)`);
790		};
791
792		const onPointerUp = () => {
793			isDragging = false;
794		};
795
796		draggableEle.addEventListener('mousedown', onPointerDown);
797		document.addEventListener('mousemove', onPointerMove);
798		document.addEventListener('mouseup', onPointerUp);
799	}
800
801	// array 최고값
802	arrayMaxMin(arr, mode) {
803		if(Array.isArray(arr) === false) return false;
804
805		if(!mode) mode = "max";
806		arr.sort(function(a, b){
807			return b - a;
808		});
809		return (mode=="max"? arr[0] : arr[arr.length-1]) || 0;
810	}
811
812    // 통계 애널리틱스 이벤트 연결
813    analytics(type, banner, agent) {
814        if (!type||!banner||!agent) return;
815
816        switch (type) {
817            case "view":
818                agent.forEach(function(agt) {
819                    try{
820                        if (agt=="ga" && typeof ga!="undefined") {
821                            ga('send', 'event', '배너뷰', 'NDVIEWBANNER', banner);
822                        }
823                        if (agt=="gtag" && typeof gtag!="undefined") {
824                            gtag('event', 'NDVIEWBANNER', {'event_category': '배너뷰', 'event_label': banner});
825                        }
826                        if (agt=="gtm" && typeof dataLayer!="undefined") {
827                            dataLayer.push({'event':'NDVIEWBANNER','event_category':'배너뷰', 'event_label':banner});
828                        }
829                        if (agt=="wcsnaver" && typeof wcs!="undefined") {
830                            wcs.event(banner, 'NDVIEWBANNER');
831                        }
832                    } catch (e) {}
833                });
834                break;
835
836            case "click":
837                agent.forEach(function(agt) {
838                    try {
839                        if (agt=="ga" && typeof ga!="undefined") {
840                            ga('send', 'event', '배너클릭', 'NDCLICKBANNER', banner);
841                        }
842                        if (agt=="gtag" && typeof gtag!="undefined") {
843                            gtag('event', 'NDCLICKBANNER', {'event_category': '배너클릭', 'event_label': banner});
844                        }
845                        if (agt=="gtm" && typeof dataLayer!="undefined") {
846                            dataLayer.push({'event':'NDCLICKBANNER','event_category':'배너클릭', 'event_label':banner});
847                        }
848                        if (agt=="wcsnaver" && typeof wcs!="undefined") {
849                            wcs.event(banner, 'NDCLICKBANNER');
850                        }
851                    } catch(e) {}
852                });
853                break;
854        }
855
856        // Tip!!
857        // ga('send', 'event', 카테고리, 액션, 라벨)                    // 구글 유니버설 애널리í‹
857±ìФ 태그 사용시 (analytics.js)
858        // gtag('event', event_name, {event_category, event_label})    // 구글 글로벌 사이트 태그 사용시 (gtag.js)
859        // dataLayer.push({var:value});                                // 구글 태그 관리자 데이터 영역 변수 필요시 (GA4 구성 사용)
860        // wcs.event('이벤트카테고리명', '이벤트명')                     // 네이버 애널리틱스 사용시
861    }
862}

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.