PageSourceSearch

https://cdn.ccreview.co.kr/script/ImageViewer.js

js ccreview.co.kr collected 2026-10-02 19:09:54 UTC 8,040 bytes, 190 lines download raw bytes

1/**
2 * 기사 뷰 페이지(articleView.html) 에서 이미지 클릭 시, 해당 이미지에 대한 lightbox를 제공.
3 * 사용자가 설정한 최소 너비를 충족하는 이미지만 표시. default 0
4 */
5class ImageViewer {
6    #CURRENT_INDEX = 0; // 현재 보고 있는 이미지의 인덱스
7    #SELECTOR;          
8    #SOURCE = [];       // lightbox에서 보여줄 이미지의 src 배열
9    #CAPTION = [];      // 각 이미지에 대한 캡션 배열
10    #ELEMENTS = {};
11    #MIN_WIDTH = 0;     // ImageViewer에 포함될 이미지의 최소 너비. 기본 값 0
12    #MODAL = null;      // lightbox 모달 DOM 요소
13    #FIGURES = [];      // lightbox에 포함될 figure 요소들의 배열
14    #FLAG = false;
15    #ISMODALOPEN = false;
16    constructor(selector, minWidth = 0) {
17        this.#SELECTOR = selector;
18        this.#MIN_WIDTH = minWidth;
19        this.init();
20    }
21
22    init() {
23        const figures = document.querySelectorAll(this.#SELECTOR);
24        let validIndex = 0;
25        figures.forEach(figure => {
26            const imgElement = figure.querySelector("picture img") || figure.querySelector("img");
27            if(imgElement.naturalWidth < this.#MIN_WIDTH || !imgElement) return;
28            this.#FIGURES.push(figure);
29            figure.setAttribute("data-current-index", validIndex++);
30
31            imgElement.addEventListener("click", () => this.initModal(figure));
32        });
33        
34    }
35
36    // 클릭된 이미지에 대한 모달을 염. 모달이 존재하지 않으면 생성
37    initModal(target) {
38        const _this = this;
39        if(!this.#MODAL) {
40            this.createModal();
41            this.initElements();
42        }
43        const { lightBoxImage, prevBtn, nextBtn, caption, number} = this.#ELEMENTS;
44        const index = parseInt(target.getAttribute("data-current-index", 10));
45
46        this.#MODAL.setAttribute("data-current-index", index);
47
48        // 이벤트가 추가되지 않았다면 추가
49        if (!this.#FLAG) {
50            $(this.#MODAL).on("open.zf.reveal", (e) => {
51                this.#FIGURES.forEach(figure => this.getActiveSource(figure));
52                this.#CURRENT_INDEX = parseInt(this.#MODAL.getAttribute("data-current-index"), 10);
53                lightBoxImage.onload = () => {
54                    caption.textContent = this.#CAPTION[this.#CURRENT_INDEX];
55                    number.textContent = `${this.#CURRENT_INDEX + 1} / ${this.#SOURCE.length}`;
56                    this.changeBtnState();
57                };
58                lightBoxImage.src = this.#SOURCE[this.#CURRENT_INDEX];
59            });
60
61
62            $(this.#MODAL).on("closed.zf.reveal", (e) => {
63                _this.#ISMODALOPEN = false;
64                _this.#SOURCE = [];
65                _this.#CAPTION = [];
66            });
67            
68            $(this.#MODAL).on("click", ".btn-close", function(e) {
69                $('#edit-image-viewer').foundation('close');
70            });
71
72            document.addEventListener("keydown", (e) => {
73                if (this.#ISMODALOPEN == false || !["ArrowRight", "ArrowLeft", "Escape"].includes(e.code)) return false;
74                switch (e.code) {
75                    case "ArrowRight":
76                        this.next();
77                        break;
78                    case "ArrowLeft":
79                        this.prev();
80                        break;
81                }
82            });
83
84            prevBtn.addEventListener("click", () => this.prev());
85            nextBtn.addEventListener("click", () => this.next());
86            this.#FLAG = true;
87        }
88        
89        $(this.#MODAL).foundation("open");
90        this.#ISMODALOPEN = true;
91    }
92    
93    // 모달 생성하고 foundation 활성화
94    createModal() {
95        const htmlContent = `
96            <div id="edit-image-viewer" class="reveal lightbox-reveal" data-reveal="" data-v-offset="0" data-h-offset="0" data-close-on-click="false" data-close-on-esc="true" data-animation-in="fade-in" data-animation-out="fade-out" data-additional-overlay-classes="lightbox-overlay">
97                <div class="lightbox-container">
98                    <article class="lightbox">
99                        <section class="lightbox-body">
100                            <button class="btn-close"></button>
101                            <img class="lightbox-image" alt="viewer Image">
102                            <div class="lightbox-text">
103                                <div class="lightbox-caption"></div>
104                                <div class="lightbox-number"></div>
105                            </div>
106                            <button class="lightbox-btn lightbox-prev"></button>
107                            <button class="lightbox-btn lightbox-next"></button>
108                        </section>
109                    </article>
110                </div>
111            </div>`;
112
113        document.body.insertAdjacentHTML('beforeend', htmlContent);
114        $('#edit-image-viewer').css("outline", "none");
115        $('#edit-image-viewer').foundation();
116        this.#MODAL = document.querySelector("#edit-image-viewer");
117    }
118    
119    initElements() {
120        this.#ELEMENTS = {
121            lightBox: this.#MODAL.querySelector(".lightbox"),
122            lightBoxImage: this.#MODAL.querySelector(".lightbox-image"),
123            prevBtn: this.#MODAL.querySelector(".lightbox-prev"),
124            nextBtn: this.#MODAL.querySelector(".lightbox-next"),
125            caption: this.#MODAL.querySelector(".lightbox-caption"),
126            number: this.#MODAL.querySelector(".lightbox-number"),
127            ...this.#ELEMENTS,
128        };
129    }
130
131    // src,caption 배열 업데이트
132    getActiveSource(figure) {
133        const imgElement = figure.querySelector("picture img") || figure.querySelector("img"),
134            imgSrc = imgElement.src,
135            sources = figure.querySelectorAll("picture source");
136        const caption = figure.querySelector("figcaption");
137
138        for (let source of sources) {
139            if (source.srcset === imgSrc) {
140                this.#SOURCE.push(source.srcset);
141                this.#CAPTION.push(caption.textContent || "");
142                return;
143            }
144        }
145        this.#SOURCE.push(imgSrc);
146        this.#CAPTION.push(caption.textContent);
147    }
148
149    // 이미지를 변경하는 메서드
150    changeImage(direction) {
151        this.#CURRENT_INDEX += direction;
152        if (this.#CURRENT_INDEX < 0 || this.#CURRENT_INDEX > this.#SOURCE.length - 1) {
153            this.#CURRENT_INDEX = Math.min(Math.max(0, this.#CURRENT_INDEX), this.#SOURCE.length - 1)
154            return false;
155        }
156        const { lightBox, lightBoxImage, caption, number } = this.#ELEMENTS;
157        lightBox.style.opacity = 0;
158        setTimeout(() => {
159            lightBoxImage.onload = () => {
160                caption.textContent = this.#CAPTION[this.#CURRENT_INDEX];
161                this.changeBtnState();
162                number.textContent = `${this.#CURRENT_INDEX + 1} / ${this.#SOURCE.length}`;
163                lightBox.style.opacity = 1;
164            };
165            lightBoxImage.src = this.#SOURCE[this.#CURRENT_INDEX];
166        }, 100); 
167    }
168    
169    // 버튼 상태를 변경
170    changeBtnState() {
171        this.#ELEMENTS.prevBtn.classList.toggle("is-hidden", this.#CURRENT_INDEX === 0);
172        this.#ELEMENTS.nextBtn.classList.toggle("is-hidden", this.#CURRENT_INDEX === this.#SOURCE.length - 1);
173        // this.#ELEMENTS.prevBtn.classList.toggle("disabled", this.#CURRENT_INDEX === 0);
174        // this.#ELEMENTS.nextBtn.classList.toggle("disabled", this.#CURRENT_INDEX === this.#SOURCE.length - 1);
175    }
176
177    // 
178    checkState() {
179    }
180
181    // 이전 이미지로 넘기는 메서드
182    prev() {
183        this.changeImage(-1);
184    }
185
186    // 다음 이미지로 넘기는 메서드
187    next() {
188        this.changeImage(1);
189    }
190}

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.