PageSourceSearch

https://cdn.the-pr.co.kr/script/Bannerpop.js

js the-pr.co.kr collected 2026-10-02 10:08:33 UTC 32,501 bytes, 917 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			analysisUse = j.analysis_use ? "Y" : "N",
478			analysisCode = j.analysis_code || "",
479			content = decodeURIComponent(j.content) || "",
480			popname = this.POP_OPTIONS.popname + idxno,
481			_type = popupType; 								// default: 각자띄움 - 팝업과 레이어가 위치값을 공유하여 차례로 띄울려면 "window" or 공유하지 않고 각자 띄울려면 popupType;
482
483		let top = 0,
484			left = 0,
485			xPosition = Number(j.x_position) || 0,
486			yPosition = Number(j.y_position) || 0;
487		
488		const type = {
489			calcPosition: function() {
490				// x,y가 0일때는 auto로 간주
491				if((xPosition == "0" && yPosition == "0")) {
492					const maxHeight = parents.arrayMaxMin(parents.POP_OPTIONS.height[_type], "max"),
493						maxTop = parents.arrayMaxMin(parents.POP_OPTIONS.top[_type], "max"),
494						initLeft = popupType == "window" ? 0 : parents.POP_OPTIONS.bodyLeft,
495						prevWidth = parents.POP_OPTIONS.width[_type][parents.POP_OPTIONS.width[_type].length - 1] || 0,
496						prevLeft = parents.POP_OPTIONS.left[_type][parents.POP_OPTIONS.left[_type].length - 1] || initLeft;
497
498					left = prevLeft + prevWidth;
499
500					if(left > parents.POP_OPTIONS[popupType == "window" ? "screenWidth" : "bodyWidth"]) {
501						top += maxHeight;
502						left = initLeft;
503
504						parents.POP_OPTIONS.width[_type] = [];
505						parents.POP_OPTIONS.height[_type] = [];
506						parents.POP_OPTIONS.top[_type] = [];
507						parents.POP_OPTIONS.left[_type] = [];
508					} else {
509						top = (maxTop || 0);
510					}
511
512					// 변화된값 배열에 넣기 위해 조작
513					xPosition = left;
514					yPosition = top;
515
516					// 변수에 담음
517					parents.POP_OPTIONS.width[_type].push(popupWidth || 0);
518					parents.POP_OPTIONS.height[_type].push(popupHeight || 0);
519					parents.POP_OPTIONS.top[_type].push(yPosition || 0);
520					parents.POP_OPTIONS.left[_type].push(xPosition || 0);
521				} else {
522					top = yPosition;
523					left = xPosition;
524				}
525			},
526
527			// 관리자 미리보기시 중앙에
528			setPositionCenter: function(){
529				const _width = (document.body.clientWidth/2)-(popupWidth/2),
530					_scrollTop = document.body.scrollTop;
531
532				top = _scrollTop + 50;
533				left = (_width>0)?_width:0;
534			},
535
536			// 띄울 페이지 applyPage
537			isApplyPage: function() {
538				if(parents.POP_OPTIONS.view == "manager") return true;
539			},
540
541			window:function(){
542				this.calcPosition(); // 위치 계산
543				const winpopupUrl = '/bannerpop/popup.html?win_popup_idxno=' + idxno;
544				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);
545			},
546
547			layer:function(){
548				this.calcPosition(); // 위치 계산
549
550				// HTML 문자열 DOM 요소로 변환
551				const parser = new DOMParser(),
552					doc = parser.parseFromString(content, 'text/html'),
553					contentBox = parents.constructor.changeCDNLink(doc.body.firstChild);
554
555				contentBox.id = popname;
556				if (layerMove == "Y" && parents.IS_MOBILE_MODE!==true) contentBox.style.setProperty("--nd-popup-transform", "none");
557				contentBox.style.setProperty("--nd-popup-offset-x", left + "px");
558				contentBox.style.setProperty("--nd-popup-offset-y", top + "px");
559				contentBox.style.setProperty("--nd-popup-width", popupWidth + "px");
560				contentBox.style.setProperty("--nd-popup-height", popupHeight + "px");
561				contentBox.style.setProperty("--nd-popup-zindex", "110" + parents.POP_OPTIONS.order);
562				contentBox.classList.add("layer_box");
563
564				if(contentType == "HTML") contentBox.classList.add("for-html");
565
566				if(layerScroll === "Y") contentBox.classList.add("layer_box_fixed");
567
568				if(layerBorder === "Y") contentBox.classList.add("layer_box_border");
569
570				const closeBox = contentBox.querySelector(".popup_close_box");
571				if(closeBox) {
572					closeBox.classList.add("layer_close_box");
573				}
574
575				if(layerMove == "Y" && parents.IS_MOBILE_MODE!==true){
576					contentBox.classList.add("layer_box_cursor_move");
577					parents.enableDraggable(contentBox);
578				}
579
580				// 창닫기-icon 닫기 버튼은 별개다
581				//if(bottomClose=="Y"){
582				const btnClose = contentBox.querySelectorAll(".popup_close, .popup_close_map, .popup_icon_close"),
583					hasPopupCloseElements = contentBox.querySelectorAll(".popup_close, .popup_close_map").length > 0;
584
585				btnClose.forEach(btn => {
586					if (btn.classList.contains("popup_icon_close")) {
587						// 버튼위치 잡는데 불필요해서 주석(2024-05-17)
588						// if ((layerClosePosition == 'BL' || layerClosePosition == 'BR') && hasPopupCloseElements) {
589						// 	btn.style.top = Number(j.popup_height) - 30 + "px";
590						// }
591						if(!hasPopupCloseElements) {
592							contentBox.classList.add("no-footer");
593						}
594						btn.classList.add("layer_position_" + layerClosePosition);
595					}
596				});
597
598				btnClose.forEach(btn => btn.addEventListener("click", () => {
599					parents.evtClose(popupType, idxno);
600					return false;
601				}));
602				//}
603
604				// 이미지내 링크가 창닫기 라면
605				//$content.find("a[href*='javascript:window.close()']").click(function(){
606				const elements = contentBox.querySelectorAll("[href*='javascript:window.close()']");
607				elements.forEach(element => element.addEventListener("click", () => {
608					parents.evtClose(popupType, idxno);
609					return false;
610				}));
611
612				const popupDays = contentBox.querySelectorAll(".popup-days, .popup_day_close_map");
613				// 하루, 몇일동안 열지 않기
614				parents.daysNotOpen(bottomCloseExpireCk, bottomCloseExpire, popupDays, j);
615
616				if(parents.POP_OPTIONS.view == "manager"){
617					document.body.appendChild(contentBox);
618					this.setPositionCenter();
619					contentBox.classList.remove("layer_box_fixed");
620					contentBox.style.setProperty("--nd-popup-transform", "none");
621					contentBox.style.setProperty("--nd-popup-offset-y", top + "px");
622					contentBox.style.setProperty("--nd-popup-offset-x", left + "px");
623				}else {
624					 document.querySelector("#layer-popups").appendChild(contentBox);
625				}
626
627				// 팝업 뷰 통계 
628				if (analysisUse == "Y" && analysisCode != "") {
629					if (contentType == "HTML") bannerpop.analytics('view', analysisCode);
630				}
631
632				// 이미지맵 리사이즈
633				$('img[usemap]', contentBox).rwdImageMaps();
634			}
635		};
636
637		type[popupType]();
638    }
639
640	// 팝업띄우기
641	popup() {
642		if(!window.___popup) return;
643		if(Array.isArray(___popup) === false && ___popup.length > 0) return;
644		if(!window.___currentTime) ___currentTime = Math.floor(new Date().getTime() / 1000);
645
646		const content = document.querySelector("#layer-popups");
647
648		if(!content) return false;
649
650		this.POP_OPTIONS.bodyWidth = content.clientWidth;
651		this.POP_OPTIONS.screenWidth = window.screen.width;
652		
653		for(let i = 0, cnt = ___popup.length ; i < cnt ; i++ ){
654			// 쿠키인지 판단후 열기
655			let coo = "";
656			coo = Util.getCookie(this.POP_OPTIONS.popname + ___popup[i].idxno);
657
658			if(!coo){
659				this.POP_OPTIONS.order++;
660				this.float(___popup[i]);
661			}
662		}
663	}
664
665	// 창닫기
666	evtClose(mode, idxno) {
667		if(mode=="layer"){
668			const layerId = this.POP_OPTIONS.popname + idxno;
669
670			$("#"+layerId).fadeOut("fast", function(){
671				$(this).remove();
672			});
673		}else window.close();
674	}
675
676	// cookie
677	evtCookieClose(name, days) {
678		Util.setCookie(name, "popup", days);
679	}
680
681	// 하루, 몇일동안 열지 않기.
682	daysNotOpen(expire, period, btns, p) {
683		const idxno = p.idxno;
684		let isClickedMap = false;
685		const addListenerAndClose = (element, days) => {
686			element.addEventListener("click", () => {
687				this.evtCookieClose(this.POP_OPTIONS.popname + idxno, Number(days)||1);
688				this.evtClose(p.popup_type, idxno);
689				return false;
690			});
691		};
692	
693		if (expire !== "none") {
694			btns.forEach(btn => {
695				if (btn.classList.contains("popup-days")) {
696					// 하루동안 열지 않기
697					if (expire === "day") {
698						addListenerAndClose(btn, "1");
699						if (!btn.hasChildNodes()) {
700							btn.innerHTML = '<a href="#close-to-layer">하루동안 보지 않기</a>';
701						}
702					} 
703					// 지정된 일수 동안 열지 않기
704					else if (expire === "days") {
705						const html = `<a href="#link">${period > 1 ? period + '일 동안 보지 않기' : '하루 동안 보지 않기'}</a>`;
706						btn.insertAdjacentHTML('beforeend', html);
707						addListenerAndClose(btn, period);
708					}
709				} else {
710					addListenerAndClose(btn, "1");
711					isClickedMap = true;
712				}
713			});
714		}
715
716		// expire 값이랑 관계없이 이미지맵을 찾아 처리
717        if (isClickedMap === false) {
718            btns.forEach((btn) =>
719                btn.addEventListener("click", () =>
720                    addListenerAndClose(btn, "1")
721                )
722            );
723        }
724	}
725
726	// window popup control
727	popupControl(){
728		if(!___popup) return ;
729		if((typeof ___popup) !== "object") return ;
730		const p = ___popup,
731			title = decodeURIComponent(p.title)||"",
732			popupType = p.popup_type||"window",
733			bottomClose = p.bottom_close||"Y",
734			bottomCloseExpireCk = p.bottom_close_expire_ck||"day",	// : day,days,none
735			bottomCloseExpire = p.bottom_close_expire||1,	// : 몇일
736			btnClose = document.querySelectorAll(".popup_close, .popup_close_map, .popup_icon_close"),
737			btnDayClose = document.querySelectorAll('.popup-days');
738
739		if(popupType!="window") return ;
740
741		// 윈도우 바 타이틀 교체
742		document.title = title;
743
744		// 창닫기
745		if(bottomClose=="Y"){
746			btnClose.forEach(btn => {
747				btn.addEventListener("click", () => {
748					this.evtClose(p.popup_type, p.idxno);
749					return false;
750				});
751			});
752		}
753
754		// 하루, 몇일동안 열지 않기
755		this.daysNotOpen(bottomCloseExpireCk, bottomCloseExpire, btnDayClose, p);
756	}
757
758	enableDraggable(ele) {
759		let isDragging = false;        
760		let dragStartX, dragStartY;
761		let initTranslateX = 0,  
762			initTranslateY = 0;
763
764		const draggableEle = ele;
765		const linksAndAreas = ele.querySelectorAll('a, area');
766
767		linksAndAreas.forEach(element => {
768			element.style.cursor = 'move';
769		});
770		ele.addEventListener("click", (e) => e.preventDefault());
771		const onPointerDown = (e) => {
772			e.preventDefault();
773			isDragging = true;
774			dragStartX = e.pageX;
775			dragStartY = e.pageY;
776
777			const transform = window.getComputedStyle(draggableEle).transform;
778			if (transform !== 'none') {
779				const matrix = new DOMMatrixReadOnly(transform);
780				initTranslateX = matrix.m41;
781				initTranslateY = matrix.m42;
782			} else {
783				initTranslateX = 0;
784				initTranslateY = 0;
785			}
786		};
787
788		const onPointerMove = (e) => {
789			if (!isDragging) return;                       
790
791			const moveX = e.pageX - dragStartX;
792			const moveY = e.pageY - dragStartY;
793
794			const translateX = initTranslateX + moveX;
795			const translateY = initTranslateY + moveY;
796			draggableEle.style.setProperty("--nd-popup-transform", `translate(${translateX}px, ${translateY}px)`);
797		};
798
799		const onPointerUp = () => {
800			isDragging = false;
801		};
802
803		draggableEle.addEventListener('mousedown', onPointerDown);
804		document.addEventListener('mousemove', onPointerMove);
805		document.addEventListener('mouseup', onPointerUp);
806	}
807
808	// array 최고값
809	arrayMaxMin(arr, mode) {
810		if(Array.isArray(arr) === false) return false;
811
812		if(!mode) mode = "max";
813		arr.sort(function(a, b){
814			return b - a;
815		});
816		return (mode=="max"? arr[0] : arr[arr.length-1]) || 0;
817	}
818
819    // 통계 애널리틱스 이벤트 연결 
820    analytics(type, banner, agent = "") {
821        if (!type||!banner) return;
822
823        // 비교 값이 없는 경우 전부 셋업
824        if (!agent) agent = ['ga', 'gtag', 'gtm', 'wcsnaver'];
825
826        // 데이터 타입 확인 
827        const dataType = banner.match(/[a-zA-Z]+/)[0]||'BANNER';
828        let dataEventName = '',
829            dataCategory = (dataType=='POPUP')?'팝업':'배너';
830
831        switch (type) {
832            case "view":
833                dataEventName = `NDVIEW${dataType}`;
834                dataCategory = `${dataCategory}ë·°`;
835                agent.forEach(function(agt) {
836                    try{
837                        if (agt=="ga" && typeof ga!="undefined") {
838                            ga('send', 'event', dataCategory, dataEventName, banner);
839                        }
840                        if (agt=="gtag" && typeof gtag!="undefined") {
841                            gtag('event', dataEventName, {'event_category': dataCategory, 'event_label': banner});
842                        }
843                        if (agt=="gtm" && typeof dataLayer!="undefined") {
844                            dataLayer.push({'event':dataEventName,'event_category':dataCategory, 'event_label':banner});
845                        }
846                        if (agt=="wcsnaver" && typeof wcs!="undefined") {
847                            wcs.event(banner, dataEventName);
848                        }
849                    } catch (e) {}
850                });
851                break;
852
853            case "click":
854                dataEventName = `NDCLICK${dataType}`;
855                dataCategory = `${dataCategory}클릭`;
856                agent.forEach(function(agt) {
857                    try {
858                        if (agt=="ga" && typeof ga!="undefined") {
859                            ga('send', 'event', dataCategory, dataEventName, banner);
860                        }
861                        if (agt=="gtag" && typeof gtag!="undefined") {
862                            gtag('event', dataEventName, {'event_category': dataCategory, 'event_label': banner});
863                        }
864                        if (agt=="gtm" && typeof dataLayer!="undefined") {
865                            dataLayer.push({'event':dataEventName,'event_category':dataCategory, 'event_label':banner});
866                        }
867                        if (agt=="wcsnaver" && typeof wcs!="undefined") {
868                            wcs.event(banner, dataEventName);
869                        }
870                    } catch(e) {}
871                });
872                break;
873        }
874
875        // Tip!!
876        // ga('send', 'event', 카테고리, 액션, 라벨)                    // 구글 유니버설 애널리í‹
876±ìФ 태그 사용시 (analytics.js)
877        // gtag('event', event_name, {event_category, event_label})    // 구글 글로벌 사이트 태그 사용시 (gtag.js)
878        // dataLayer.push({var:value});                                // 구글 태그 관리자 데이터 영역 변수 필요시 (GA4 구성 사용)
879        // wcs.event('이벤트카테고리명', '이벤트명')                     // 네이버 애널리틱스 사용시
880    }
881}
882
883// 레이어 팝업 이동옵션 부여 시 링크 작동하지 않는 현상 보완
884$(function () {
885	let isDragging = false;
886	let startX, startY;
887
888	$('.popup-container').on('mousedown', function (e) {
889		isDragging = false;
890		startX = e.pageX;
891		startY = e.pageY;
892
893		$(document).on('mousemove.drag', function (e2) {
894			let dx = e2.pageX - startX;
895			let dy = e2.pageY - startY;
896
897			if (Math.abs(dx) > 5 || Math.abs(dy) > 5) {
898				isDragging = true;
899			}
900		});
901
902			$(document).on('mouseup.drag', function () {
903			$(document).off('.drag');
904		});
905	});
906
907	// 클릭시 드래그가 아니라고 판단되면 링크 연결
908	$('.popup-container a').on('click', function (e) {
909		const isMoveCursor = $(this).css('cursor') === 'move';
910if (!isDragging && isMoveCursor) {
911			const url = $(this).attr('href');
912			const target = $(this).attr('target');
913			if(target == "_blank") window.open(url, '_blank');
914			else window.location.href = url;
915		}
916	});
917});

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.