1import { initCategoryArticleFaceCrop } from "/js/common/face_crop.js"; 2// /js/pages/category/category.js 3document.addEventListener("DOMContentLoaded", () => { 4 // ========================= 5 // 0) breadcrumb (hidden input -> nav-thispage) 6 // ========================= 7 const d1Name = document.querySelector("#__paramDepth1Name")?.value || ""; 8 const d2Name = document.querySelector("#__paramDepth2Name")?.value || ""; 9 const d1 = document.querySelector("#__paramDepth1")?.value || ""; 10 const d2 = document.querySelector("#__paramDepth2")?.value || ""; 11 const cc = document.querySelector("#__categoryCode")?.value || "000000"; 12 13 const nav = document.querySelector(".nav-thispage"); 14 if (nav) { 15 // HOMEë§ ë¨ê¸°ê³ ëë¨¸ì§ ì ê±°(ì¤ë³µ ë°©ì§) 16 nav.querySelectorAll("a.nav-depth:not(:first-child)").forEach((el) => el.remove()); 17 18 if (d1Name && d1) { 19 const a1 = document.createElement("a"); 20 a1.className = "nav-depth"; 21 a1.tabIndex = 0; 22 a1.href = `/categories/${d1}000`; 23 a1.textContent = d1Name; 24 nav.appendChild(a1); 25 } 26 27 if (d2Name && d1 && d2 && d2 !== "000") { 28 const a2 = document.createElement("a"); 29 a2.className = "nav-depth"; 30 a2.tabIndex = 0; 31 a2.href = `/categories/${d1}${d2}`; 32 a2.textContent = d2Name; 33 nav.appendChild(a2); 34 } 35 } 36 37 // ========================= 38 // 1) state + URL helpers 39 // ========================= 40 const $list = document.querySelector(".mnm-news-list"); 41 const $alignBtns = document.querySelectorAll(".align-list-btns .align-list"); 42 43 function getCategoryCodeFromPath() { 44 const m = window.location.pathname.match(/^\/categories\/(\d{6})$/); 45 return m ? m[1] : "000000"; 46 } 47 48 function getQueryParam(name) { 49 return new URLSearchParams(window.location.search).get(name); 50 } 51 52 // URL 쿼리 -> state ì´ê¸°í 53 const initialSort = getQueryParam("sort") || ""; 54 const initialStartDt = getQueryParam("startDt"); 55 const initialEndDt = getQueryParam("endDt"); 56 const hasDateSearch = Boolean( 57 (initialStartDt && String(initialStartDt).trim()) || 58 (initialEndDt && String(initialEndDt).trim()) 59 ); 60 const initialPage1Base = parseInt(getQueryParam("page") || "1", 10); 61 const initialPage0Base = Number.isFinite(initialPage1Base) && initialPage1Base > 0 ? initialPage1Base - 1 : 0; 62 63 const state = { 64 categoryCode: getCategoryCodeFromPath(), 65 page: initialPage0Base, 66 size: 20, 67 sort: initialSort, 68 startDt: initialStartDt, 69 endDt: initialEndDt, 70 }; 71 72 const $oldestSortBtn = document.querySelector('.align-list-btns .align-list[data-sort="reversed"]'); 73 if ($oldestSortBtn && !hasDateSearch) { 74 $oldestSortBtn.remove(); 75 } 76 77 if (!hasDateSearch && state.sort === "reversed") { 78 state.sort = ""; 79 } 80 81 // ì ë ¬ ë²í¼ active ìí URLê³¼ ë기í 82 $alignBtns.forEach((btn) => { 83 const s = btn.dataset.sort || ""; 84 btn.classList.toggle("active", s === state.sort); 85 }); 86 87 const DATE_FMT = "YYYY-MM-DD"; 88 89 function toYmdOrNull(v) { 90 if (!v) return null; 91 if (!window.moment) return String(v); 92 const m = window.moment(v, [DATE_FMT, window.moment.ISO_8601], true); 93 return m.isValid() ? m.format(DATE_FMT) : null; 94 } 95 96 function setOrDeleteParam(params, key, value) { 97 if (value === null || value === undefined || value === "") params.delete(key); 98 else params.set(key, String(value)); 99 } 100 101 function applyFiltersAndReload({ sort, startDt, endDt, page } = {}) { 102 const url = new URL(window.location.href); 103 104 const sStart = toYmdOrNull(startDt !== undefined ? startDt : state.startDt); 105 const sEnd = toYmdOrNull(endDt !== undefined ? endDt : state.endDt); 106 const s = sort !== undefined ? sort : (state.sort ?? ""); 107 const page0 = typeof page === "number" ? page : state.page; 108 const page1 = page0 + 1; 109 110 // â 기존 쿼리ì¤í¸ë§ ì ë¶ ì´ê¸°í í ìíë ììë¡ ì¬ì¤ì 111 url.search = ""; 112 113 if (sStart) url.searchParams.set("startDt", sStart); 114 if (sEnd) url.searchParams.set("endDt", sEnd); 115 if (s) url.searchParams.set("sort", s); 116 if (page1 > 1) url.searchParams.set("page", String(page1)); 117 118 window.location.href = url.toString(); 119 } 120 121 // ========================= 122 // 2) API fetch - HTML ë ëë§ ë²ì 123 // ========================= 124 async function fetchCategoryNews() { 125 const params = new URLSearchParams(); 126 const isMobileDevice = window.isMobile === true || window.isMobile === "true"; 127 params.set("categoryCode", state.categoryCode);
128 params.set("page", String(state.page)); 129 params.set("size", String(state.size)); 130 params.set("pageBlockSize", isMobileDevice ? "5" : "10"); 131 if (state.sort) params.set("sort", state.sort); 132 133 const start = toYmdOrNull(state.startDt); 134 const end = toYmdOrNull(state.endDt); 135 if (start) params.set("startDt", start); 136 if (end) params.set("endDt", end); 137 138 // â HTML ë ëë§ API í¸ì¶ 139 const res = await fetch(`/api/articles/categoryNews?${params.toString()}`, { 140 method: "GET", 141 headers: { Accept: "application/json" }, 142 }); 143 144 if (!res.ok) throw new Error(`API error: ${res.status}`); 145 const json = await res.json(); 146 return { 147 articlesHtml: json.articlesHtml, 148 paginationHtml: json.paginationHtml 149 }; 150 } 151 152 // ========================= 153 // 3) render helpers 154 // ========================= 155 function initMemberSwiper() { 156 if (!document.querySelector(".mbs-swiper")) return; 157 if (window.__mbsSwiperInited) return; 158 159 window.__mbsSwiperInited = true; 160 161 if (typeof Swiper !== "function") return; 162 163 new Swiper(".mbs-swiper", { 164 slidesPerView: 1.5, 165 spaceBetween: 12, 166 loop: false, 167 on: { 168 reachEnd: function () { 169 const lastIndex = this.slides.length - 1; 170 this.slideTo(lastIndex, 500, false); 171 }, 172 }, 173 touchRatio: 1, 174 }); 175 } 176 177 function isMobileCategoryView() { 178 return window.matchMedia("(max-width: 640px)").matches 179 || window.isMobile === true 180 || window.isMobile === "true"; 181 } 182 183 function placeMemberNewsMoAfterFirstArticle() { 184 if (!isMobileCategoryView()) return; 185 186 const listRoot = document.querySelector(".mnm-news-list"); 187 const firstItem = listRoot?.querySelector(".mnm-news"); 188 if (!listRoot || !firstItem) return; 189 190 const existsInList = Array.from(listRoot.children).some((child) => 191 child.classList?.contains("membership-cont-mo") 192 && child.classList?.contains("mbs-news-cont-mo") 193 ); 194 if (existsInList) return; 195 196 const widgetContainer = document.querySelector('#side-root [data-widget="MEMBER_NEWS_MO"]'); 197 const memberBlock = widgetContainer?.querySelector(".membership-cont-mo.mbs-news-cont-mo"); 198 if (!memberBlock) return; 199 200 firstItem.insertAdjacentElement("afterend", memberBlock); 201 202 const swiperEl = memberBlock.querySelector(".mbs-swiper"); 203 if (swiperEl?.dataset.swiperInited !== "1") { 204 initMemberSwiper?.(); 205 } 206 } 207 208 // ========================= 209 // 4) ë ì§ ë¤ë¹ê²ì´ì ì ë°ì´í¸ 210 // ========================= 211 function updateDateNavigation() { 212 const $prevBtn = document.querySelector("#prev-date"); 213 const $nextBtn = document.querySelector("#next-date"); 214 const $selectedDate = document.querySelector("#selected-date"); 215 216 if (!$prevBtn || !$nextBtn || !$selectedDate) return; 217 218 if (state.startDt && state.endDt) { 219 $selectedDate.textContent = `${state.startDt} ~ ${state.endDt}`; 220 } else if (state.endDt) { 221 $selectedDate.textContent = `~ ${state.endDt}`; 222 } else { 223 $selectedDate.textContent = `~ ${window.moment().format(DATE_FMT)}`; 224 } 225 226 if (window.moment) { 227 const endMoment = state.endDt ? window.moment(state.endDt, DATE_FMT) : window.moment(); 228 const today = window.moment(); 229 230 const prevDate = endMoment.clone().subtract(1, 'days'); 231 $prevBtn.className = "prev-date active"; 232 $prevBtn.onclick = (e) => { 233 e.preventDefault(); 234 applyFiltersAndReload({ 235 startDt: state.startDt ? window.moment(state.startDt, DATE_FMT).subtract(1, 'days').format(DATE_FMT) : null, 236 endDt: prevDate.format(DATE_FMT), 237 page: 0 238 }); 239 }; 240 241 if (endMoment.isBefore(today, 'day')) { 242 const nextDate = endMoment.clone().add(1, 'days'); 243 $nextBtn.className = "next-date active"; 244 $nextBtn.onclick = (e) => { 245 e.preventDefault();
246 applyFiltersAndReload({ 247 startDt: state.startDt ? window.moment(state.startDt, DATE_FMT).add(1, 'days').format(DATE_FMT) : null, 248 endDt: nextDate.format(DATE_FMT), 249 page: 0 250 }); 251 }; 252 } else { 253 $nextBtn.className = "next-date"; 254 $nextBtn.onclick = null; 255 $nextBtn.style.cursor = "default"; 256 } 257 } 258 } 259 260 // â íì´ì§ë¤ì´ì ì´ë²¤í¸ ë°ì¸ë© 261 function bindPaginationEvents() { 262 document.querySelectorAll("#pagination .paging-btns a[data-page]").forEach(btn => { 263 btn.addEventListener("click", (e) => { 264 e.preventDefault(); 265 const page = parseInt(btn.dataset.page) - 1; // 0-baseë¡ ë³í 266 applyFiltersAndReload({ page }); 267 }); 268 }); 269 } 270 271 // ========================= 272 // 5) â load - ì¹´í ê³ ë¦¬ 먼ì ë¡ë 273 // ========================= 274 async function load() { 275 try { 276 // â 1. ì¹´í ê³ ë¦¬ ê¸°ì¬ HTML ë°ìì¤ê¸° 277 const { articlesHtml, paginationHtml } = await fetchCategoryNews(); 278 279 // â 2. HTML ì½ì 280 if ($list) { 281 $list.innerHTML = articlesHtml; 282 initCategoryArticleFaceCrop($list); 283 window.packSize?.apply?.(); 284 } 285 286 const $paginationRoot = document.querySelector("#pagination .paging-btns"); 287 if ($paginationRoot) { 288 $paginationRoot.innerHTML = paginationHtml; 289 290 // â 3. íì´ì§ë¤ì´ì ì´ë²¤í¸ ë°ì¸ë© 291 bindPaginationEvents(); 292 } 293 294 updateDateNavigation(); 295 296 } catch (err) { 297 console.error("ë¡ë ì¤ ìë¬:", err); 298 if ($list) $list.innerHTML = `<div class="mnm-news-blank">ì¤ë¥ê° ë°ìíìµëë¤.</div>`; 299 } 300 301 try { 302 // ì¹´í ê³ ë¦¬ 본문 ë¡ë ì´í ì¬ì´ë ìì ¯ì êµì²´í©ëë¤. 303 // 본문 APIê° ì¤í¨í´ë ì¬ì´ë ì¤ì¼ë í¤ì´ ê³ì ë¨ì§ ìëë¡ ë³ë tryë¡ ë¶ë¦¬í©ëë¤. 304 const { initSideItems } = await import("/js/pages/side-item/side_item.js"); 305 await initSideItems(); 306 307 // 모ë°ì¼ íì ê¸°ì¬ ìì ¯ì ì¤ì ê¸°ì¬ ëª©ë¡ì´ ì긴 ë¤ ì²« ê¸°ì¬ ìëë¡ ì´ëí©ëë¤. 308 placeMemberNewsMoAfterFirstArticle(); 309 } catch (sideErr) { 310 console.error("ì¬ì´ë ë¡ë ì¤ ìë¬:", sideErr); 311 } 312 } 313 314 // ========================= 315 // 6) ì ë ¬ í´ë¦ 316 // ========================= 317 $alignBtns.forEach((btn) => { 318 btn.addEventListener("click", (e) => { 319 e.preventDefault(); 320 e.stopPropagation(); 321 322 const newSort = btn.dataset.sort || ""; 323 applyFiltersAndReload({ sort: newSort, page: 0 }); 324 }); 325 }); 326 327 // ========================= 328 // 7) ë ì§/ê¸°ê° ë²í¼ 329 // ========================= 330 const $selectDateBtn = $('.select-date-btn'); 331 const $selectPeriodBtn = $('.select-period-btn'); 332 333 if (window.moment && window.$ && typeof $.fn.daterangepicker === "function") { 334 335 if ($selectDateBtn.data('daterangepicker')) { 336 $selectDateBtn.data('daterangepicker').remove(); 337 } 338 if ($selectPeriodBtn.data('daterangepicker')) { 339 $selectPeriodBtn.data('daterangepicker').remove(); 340 } 341 342 function safeMoment(val, fallback) { 343 if (!val) return fallback || window.moment(); 344 const m = window.moment(val, [DATE_FMT, window.moment.ISO_8601], true); 345 return m.isValid() ? m : (fallback || window.moment()); 346 } 347 348 $selectDateBtn.daterangepicker({ 349 singleDatePicker: true, 350 autoApply: true, 351 startDate: safeMoment(state.endDt, window.moment()), 352 endDate: safeMoment(state.endDt, window.moment()), 353 maxDate: window.moment(), 354 showDropdowns: true, 355 showWeekNumbers: false, 356 locale: { 357 format: "YYYY-MM-DD", 358 applyLabel: "íì¸", 359 cancelLabel: "ì·¨ì", 360 daysOfWeek: ['ì¼','ì','í','ì','목','ê¸','í '], 361 monthNames: ['01','02','03','04','05','06','07','08','09','10','11','12'], 362 firstDay: 0 363 } 364 }, function(start) {
365 applyFiltersAndReload({ 366 startDt: null, 367 endDt: start.format('YYYY-MM-DD'), 368 page: 0 369 }); 370 }); 371 372 // â ìì : endDate 먼ì ê³ì°íê³ , startDateë endDate ê¸°ì¤ -7ì¼ë¡ ì¤ì 373 const endDate = safeMoment(state.endDt, window.moment()); 374 const startDate = safeMoment(state.startDt, endDate.clone().subtract(7, 'days')); 375 376 $selectPeriodBtn.daterangepicker({ 377 showDropdowns: true, 378 showWeekNumbers: false, 379 startDate: startDate, 380 endDate: endDate, 381 maxDate: window.moment(), 382 locale: { 383 format: "YYYY-MM-DD", 384 separator: " ~ ", 385 applyLabel: "íì¸", 386 cancelLabel: "ì·¨ì", 387 fromLabel: "From", 388 toLabel: "To", 389 daysOfWeek: ['ì¼','ì','í','ì','목','ê¸','í '], 390 monthNames: ['01','02','03','04','05','06','07','08','09','10','11','12'], 391 firstDay: 0 392 } 393 }, function(start, end) { 394 applyFiltersAndReload({ 395 startDt: start.format('YYYY-MM-DD'), 396 endDt: end.format('YYYY-MM-DD'), 397 page: 0 398 }); 399 }); 400 } 401 402 // ========================= 403 // 8) ìµì´ ë¡ë© 404 // ========================= 405 load(); 406});
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.