PageSourceSearch

https://kluboapp.com/www/js/common/menuNotiDot.js?d=202609251099

js kluboapp.com collected 2026-09-25 03:41:21 UTC 4,482 bytes, 106 lines download raw bytes

1/**
2 * menuNotiDot.js
3 * 관리자 좌측 슬라이드 메뉴(#klubo-myclub-navigation-container)의 미처리 알림 배지를
4 * 스캔하여, 헤더 "메뉴 열기" 버튼(#klubo-myclub-navigation-openBtn)과 좌측 메뉴 내부의
5 * "관리자/개인" 탭 버튼(.leftpanel-tab__btn[data-tab])에 레드닷(has-noti)을 표시한다.
6 *
7 * - 집계 대상(스코프 내부):
8 *   1) .leftpanel-submenu__item.new            (가입 신청 접수 등 "New" 배지)
9 *   2) .submenu-count[data-count] (data-count >= 1)  (참석 승인 등 카운트 배지)
10 * - 헤더 버튼은 컨테이너 전체를 스코프로, 탭 버튼은 각 탭의 .leftpanel-tab__content
11 *   (data-tab-content 로 매칭)만 스코프로 삼아 그 탭 안에 알림이 있을 때만 점을 찍는다.
12 * - 하나라도 있으면(total > 0) 버튼에 .has-noti 추가 → CSS 의사요소로 레드닷 노출.
13 * - 비운영자/게시판 페이지는 해당 컨테이너나 배지가 없으므로 자연히 닷이 뜨지 않는다.
14 *   (게시판 좌측 메뉴는 #klubo_board_navigation_container 로 별도 id → 스코프 밖)
15 * - 좌측 메뉴가 AJAX 등으로 재렌더되는 경우를 대비해 window.KluboMenuNotiDot.refresh() 를 노출한다.
16 */
17(function (window, document) {
18    'use strict';
19
20    var MENU_CONTAINER_ID = 'klubo-myclub-navigation-container';
21    var OPEN_BTN_ID = 'klubo-myclub-navigation-openBtn';
22    var HAS_NOTI_CLASS = 'has-noti';
23
24    /**
25     * 주어진 스코프 엘리먼트 안의 미처리 알림 총합을 계산한다.
26     * @returns {number} 0 이면 알림 없음
27     */
28    function countNotificationsWithin(scopeEl) {
29        var total = 0;
30
31        // 1) "New" 배지 (가입 신청 접수 등) — 존재 개수
32        total += scopeEl.querySelectorAll('.leftpanel-submenu__item.new').length;
33
34        // 2) 카운트 배지 (참석 승인 등) — data-count 값이 1 이상인 요소를 알림으로 취급
35        var countEls = scopeEl.querySelectorAll('.submenu-count[data-count]');
36        for (var i = 0; i < countEls.length; i++) {
37            var n = parseInt(countEls[i].getAttribute('data-count'), 10);
38            if (!isNaN(n) && n > 0) {
39                total += n;
40            }
41        }
42
43        return total;
44    }
45
46    /**
47     * 관리자 좌측 메뉴 전체(헤더 버튼 스코프)의 미처리 알림 총합을 계산한다.
48     * @returns {number} 0 이면 알림 없음
49     */
50    function countNotifications() {
51        var container = document.getElementById(MENU_CONTAINER_ID);
52        if (!container) {
53            return 0;
54        }
55        return countNotificationsWithin(container);
56    }
57
58    /**
59     * "관리자/개인" 탭 버튼 각각에, 그 탭 콘텐츠 안의 알림 유무에 따라 레드닷을 토글한다.
60     */
61    function refreshTabDots(container) {
62        var tabBtns = container.querySelectorAll('.leftpanel-tab__btn[data-tab]');
63        for (var i = 0; i < tabBtns.length; i++) {
64            var btn = tabBtns[i];
65            var tabKey = btn.getAttribute('data-tab');
66            var content = container.querySelector('.leftpanel-tab__content[data-tab-content="' + tabKey + '"]');
67            var hasNoti = content ? (countNotificationsWithin(content) > 0) : false;
68            if (hasNoti) {
69                btn.classList.add(HAS_NOTI_CLASS);
70            } else {
71                btn.classList.remove(HAS_NOTI_CLASS);
72            }
73        }
74    }
75
76    /**
77     * 알림 유무에 따라 메뉴 버튼의 레드닷 클래스를 토글한다.
78     * id 는 유일하지만 방어적으로 모든 매칭 요소에 적용한다.
79     */
80    function refresh() {
81        var hasNoti = countNotifications() > 0;
82        var btns = document.querySelectorAll('#' + OPEN_BTN_ID);
83        for (var i = 0; i < btns.length; i++) {
84            if (hasNoti) {
85                btns[i].classList.add(HAS_NOTI_CLASS);
86            } else {
87                btns[i].classList.remove(HAS_NOTI_CLASS);
88            }
89        }
90
91        var container = document.getElementById(MENU_CONTAINER_ID);
92        if (container) {
93            refreshTabDots(container);
94        }
95    }
96
97    // 외부(재렌더 시) 재실행용 노출
98    window.KluboMenuNotiDot = { refresh: refresh };
99
100    if (document.readyState === 'loading') {
101        document.addEventListener('DOMContentLoaded', refresh);
102    } else {
103        // head 로드 후 이미 DOM 준비된 경우
104        refresh();
105    }
106})(window, document);

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.