PageSourceSearch

https://moonpd.ai/assets/front/js/ui.js?v=1791211049197

js moonpd.ai collected 2026-10-05 16:26:41 UTC 11,000 bytes, 540 lines download raw bytes

1const VIEWPORT_PC = "PC";
2const VIEWPORT_TABLET = "TABLET";
3const VIEWPORT_MOBILE = "MOBILE";
4
5
6$(function() {
7	viewportCheck();
8	gnbUi().init();
9});
10
11
12/**
13 * Viewport check
14 *
15 * @return viewport
16 **/
17function viewportCheck() {
18	const checkPc = window.matchMedia("screen and (max-width: 1023px)");
19	const checkTablet = window.matchMedia("screen and (max-width: 767px)");
20	let viewport = VIEWPORT_MOBILE;
21
22	if(!checkPc.matches) {
23		viewport = VIEWPORT_PC;
24	} else if(checkPc.matches && !checkTablet.matches) {
25		viewport = VIEWPORT_TABLET;
26	}
27
28	return viewport;
29}
30
31
32/**
33 * Responsive GNB Animation
34 *
35 **/
36function gnbUi() {
37	const menuList = $(".js-gnb-menu");
38	const menuTarget = $(".js-gnb-hover");
39	const gnb = $(".js-gnb");
40	const body = $("body");
41
42	return {
43		init: function() {
44			// 하위 메뉴 없을 경우 체크
45			menuTarget.each(function(i, el) {
46				if($(el).siblings().length <= 0 && $(el).siblings(menuList).children().length <= 0) {
47					$(el).addClass("depth-empty");
48				}
49			});
50
51			if(viewportCheck() === VIEWPORT_PC) {
52				gnbUi().hoverOpen();
53				gnbUi().hoverClose();
54			} else {
55				gnbUi().clickOpen();
56				gnbUi().clickClose();
57				gnbUi().menuClick();
58			}
59		},
60		hoverOpen: function () {
61			menuTarget.on("mouseenter", function() {
62				$(this).parent().addClass("active");
63			})
64		},
65		hoverClose: function() {
66			$(".js-gnb-list").on("mouseleave", function() {
67				$(this).removeClass("active");
68			});
69		},
70		clickOpen: function() {
71			$(".js-m-gnb-open").on("click", function() {
72				gnb.addClass("active");
73				body.addClass("opt-prevent opt-gnb-open");
74			});
75		},
76		clickClose: function() {
77			$(".js-m-gnb-close").on("click", function() {
78				gnb.removeClass("active");
79				body.removeClass("opt-prevent opt-gnb-open");
80			});
81		},
82		menuClick: function() {
83			menuTarget.on("click", function(e) {
84				if(!$(this).hasClass("depth-empty")) {
85					e.preventDefault();
86					$(this).toggleClass("active");
87					$(this).siblings(menuList).stop().slideToggle();
88				}
89			});
90		},
91		resize: function () {
92			gnb.removeClass("active").removeAttr("style");
93			body.removeClass("opt-prevent opt-gnb-open");
94			menuList.removeAttr("style");
95			menuTarget.removeClass("active");
96		}
97	}
98}
99
100
101/**
102 * Toggle Animation
103 *
104 **/
105function toggleUi(el) {
106	const toggleName = $(el).attr("data-toggle");
107	$(el).toggleClass("active");
108	$(el).parent().find("[data-toggle='" + toggleName + "']").not($(el)).stop().slideToggle().toggleClass("active");
109}
110
111
112/**
113 * Layer Popup
114 *
115 **/
116function layerPopupUi(el) {
117	const dataLayer = $(el).attr("data-layer");
118	const objLayer = $(el).closest(".js-layer").find("[data-layer='" + dataLayer + "']").not($(el));
119
120	return {
121		toggle: function() {
122			$(el).toggleClass("active");
123			objLayer.toggleClass("active");
124		},
125		open: function () {
126			$(el).addClass("active");
127			objLayer.addClass("active");
128		},
129		close: function () {
130			$(el).removeClass("active");
131			objLayer.removeClass("active");
132		}
133	}
134}
135
136
137/**
138 * Modal Popup
139 *
140 **/
141function modalPopupUi(el) {
142	const dataModal = $(el).attr("data-modal");
143	const objModal = $("[data-modal='" + dataModal + "']").not($(el));
144	const body = $("body");
145
146	return {
147		open: function () {
148			objModal.addClass("active");
149			body.addClass("opt-prevent");
150		},
151		close: function () {
152			$(el).closest(".js-modal").removeClass("active");
153			body.removeClass("opt-prevent");
154		}
155	}
156}
157
158
159/**
160 * Top Scroll
161 *
162 **/
163function scrollTopUi() {
164	event.preventDefault();
165	$("body, html").stop().animate({scrollTop : 0}, 0);
166}
167
168
169/**
170 * Slide
171 *
172 **/
173function slideUi() {
174	const option = {
175		speed: 1000,
176		loop: true,
177	};
178	return {
179		// channel slide
180		channel: function() {
181			const slide = ".js-channel-slide";
182			const swiper = new Swiper(`${slide} .swiper`, Object.assign(option, {
183				loop: false,
184				navigation: {
185					nextEl: `${slide} .swiper-btn-next`,
186					prevEl: `${slide} .swiper-btn-prev`
187				},
188				breakpoints: {
189					768: {
190						slidesPerView: 3,
191					},
192					1024: {
193						slidesPerView: 5,
194					},
195				},
196			}));
197		}
198	}
199}
200
201/**
202 * Toggle Switch
203 *
204 * @param el{Object} switch input 객체
205 * @param checkText{String} 체크 시 노출 텍스트
206 * @param unCheckText{String} 미체크 시 노출 텍스트
207 **/
208function toggleSwitchUi(el, checkText, unCheckText) {
209	const objText = $(el).siblings(".js-switch-text");
210	if($(el).prop("checked"))
211		objText.text(checkText);
212	else
213		objText.text(unCheckText);
214}
215
216
217
218/**
219 * Chart
220 *
221 **/
222function chart() {
223	const charOpt = {
224		font: {
225			size: viewportCheck() === VIEWPORT_MOBILE ? 10 : 15,
226			family: "Noto Sans KR",
227			lineHeight: 1
228		},
229		backgroundColor:["#00b7ee", "#c766ff", "#5fea82", "#fbca6b", "#f773d3", "#ff6f48", "#b7b7b7"],
230		animation: {
231			duration: 600
232		}
233	}
234
235	return {
236		// 차트 유형 변경
237		type: function (elem) {
238			const idx = $(elem).attr("data-graph-idx");
239			const elemWrap = $(elem).closest(`[data-graph="wrap"]`);
240			const elemCont = elemWrap.find(`[data-graph="cont"][data-graph-idx="${idx}"]`);
241
242			$(elem).siblings().removeClass("active");
243			$(elem).addClass("active");
244
245			elemWrap.find(`[data-graph="cont"]`).removeClass("active");
246			elemCont.addClass("active");
247		},
248
249		// 이미지 저장
250		download: function (elem) {
251			const elemChart = $(elem).closest(`[data-graph="wrap"]`).find(`.active[data-graph="cont"] .chart`);
252
253			$(elem).attr("href", elemChart[0].toDataURL());
254		},
255
256		// 막대 형태
257		bar: function (elem, labelArr, dataArr) {
258			const sum = dataArr.reduce((a,b) => a+b ,0);
259			const prevChart = Chart.getChart(elem[0]);
260
261			if(prevChart)
262				prevChart.destroy();
263
264			const chart = new Chart(elem[0].getContext("2d"), {
265				plugins: [
266					ChartDataLabels,
267					{
268						afterLayout: chart => {
269							const yAxis = chart.scales["y"];
270
271							chart.data.datasets[0].arrH = [];
272
273							chart.data.datasets[0].data.forEach(value => {
274								const elemH = Math.round(yAxis.bottom - yAxis.getPixelForValue(value));
275								chart.data.datasets[0].arrH.push(elemH);
276							});
277						}
278					}
279				],
280				type: "bar",
281				data: {
282					labels: labelArr,
283					datasets: [
284						{
285							maxBarThickness:70,
286							borderRadius:20,
287							backgroundColor: charOpt.backgroundColor,
288							data: dataArr,
289						},
290					]
291				},
292				options: {
293					layout: {
294						padding: {
295							top:30
296						}
297					},
298					animation: charOpt.animation,
299					plugins: {
300						legend: {
301							display: false
302						},
303						tooltip: {
304							enabled: false
305						},
306						datalabels: {
307							formatter: function (value, context) {
308								const percent = (value / sum * 100).toFixed(1);
309								const elemH = context.dataset.arrH[context.dataIndex];
310								let result;
311
312								if(elemH > 30) {
313									result = result = `${value}\n\n${percent}%`;
314								} else {
315									if(elemH <= 0) {
316										result = `${value}\n`;
317									} else {
318										result = `${value}\n${percent}%`;
319									}
320								}
321
322								return result;
323							},
324							font: {
325								...charOpt.font,
326								weight:600,
327								lineHeight: 1.3
328							},
329							color: "#414141",
330							anchor: "end",
331							clamp: true,
332							textAlign:"center",
333							display: "auto"
334						},
335					},
336					responsive: true,
337					scales: {
338						x: {
339							display: true,
340							title: {
341								display: false
342							},
343							ticks: {
344								font: charOpt.font,
345								padding: 15
346							},
347							grid: {
348								display: false
349							},
350						},
351						y: {
352							display: true,
353							min:0,
354							title: {
355								display: false
356							},
357							ticks: {
358								font: charOpt.font
359							}
360						}
361					}
362				},
363			});
364		},
365
366		// 원형 차트
367		pie: function (elem, labelArr, dataArr) {
368			const sum = dataArr.reduce((a,b) => a+b ,0);
369			let prevChart = Chart.getChart(elem[0]);
370
371			if(prevChart)
372				prevChart.destroy();
373
374			const chart = new Chart(elem[0].getContext("2d"), {
375				plugins: [ChartDataLabels],
376				type: "doughnut",
377				data: {
378					labels: labelArr,
379					datasets: [
380						{
381							borderColor:"#fff",
382							backgroundColor: charOpt.backgroundColor,
383							data: dataArr,
384						},
385					]
386				},
387				options: {
388					animation: {...charOpt.animation, duration: 0},
389					plugins: {
390						legend: {
391							display: false,
392						},
393						tooltip: {
394							enabled: false
395						},
396						datalabels: {
397							formatter: function (value, context) {
398								const percent = (value / sum * 100).toFixed(1);
399								const title = context.chart.data.labels[context.dataIndex];
400								let result;
401
402								value > 0 ? result = `${title}\n${value}\n${percent}%` : result = "";
403
404								return result;
405							},
406							font: {
407								...charOpt.font,
408								weight:600,
409								lineHeight: 1.3
410							},
411							color: "#414141",
412							textAlign:"center",
413						}
414					},
415					responsive: true,
416				}
417			});
418		},
419
420		// 바 + 라인
421		mix: function (elem, labelArr, dataTotalArr, dataUseArr) {
422			const chart = new Chart(elem[0].getContext("2d"), {
423				plugins: [ChartDataLabels],
424				type: "scatter",
425				data: {
426					labels: labelArr,
427					datasets: [
428						{
429							type: "line",
430							borderColor:"#d907be",
431							pointBackgroundColor: "#d907be",
432							pointRadius:3,
433							data: dataTotalArr,
434							borderWidth:1
435						},
436						{
437							type: "bar",
438							maxBarThickness:20,
439							borderRadius:20,
440							backgroundColor: "#19c8fd",
441							data: dataUseArr,
442						},
443					]
444				},
445				options: {
446					layout: {
447						padding: {
448							top:20
449						}
450					},
451					plugins: {
452						legend: {
453							display: false,
454						},
455						tooltip: {
456							enabled: false
457						},
458						datalabels: {
459							formatter: function (value, context) {
460								if(context.dataset.type !== "bar")
461									return "";
462								else
463									return `${value}\n\n`;
464							},
465							font: {
466								...charOpt.font,
467								weight:600,
468							},
469							color: "#000",
470							textAlign:"center",
471							anchor: "end",
472						}
473					},
474					responsive: true,
475				}
476			});
477		}
478	}
479}
480
481
482/**
483 * Service
484 *
485 **/
486function serviceUi() {
487	return {
488		init: function() {
489			serviceUi().with();
490			serviceUi().detail();
491		},
492
493		with: function() {
494			const slide = ".js-with-slide";
495			const swiper = new Swiper(`${slide} .swiper`, {
496				slidesPerView: 2,
497				spaceBetween: 8,
498				speed: 500,
499				grid: {
500					rows: 6,
501				},
502				navigation: {
503					nextEl: `${slide} .swiper-btn-next`,
504					prevEl: `${slide} .swiper-btn-prev`
505				},
506				breakpoints: {
507					768: {
508						slidesPerView: 4,
509						spaceBetween: 15,
510						grid: {
511							rows: 4,
512						},
513					},
514					1024: {
515						slidesPerView: 5,
516						spaceBetween: 15,
517						grid: {
518							rows: 4,
519						},
520					},
521				},
522			});
523		},
524
525		detail: function() {
526			$(".js-detail-slide").each(function(idx, el) {
527				$(this).addClass(`opt-slide${idx}`);
528
529				const swiper = new Swiper(`.opt-slide${idx} .swiper`, {
530					effect: "fade",
531					slidesPerView: 1,
532					navigation: {
533						nextEl: `.opt-slide${idx} .swiper-btn-next`,
534						prevEl: `.opt-slide${idx} .swiper-btn-prev`
535					},
536				});
537			});
538		},
539	}
540}

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.