PageSourceSearch

https://cdn.edupress.kr/script/ImageViewer.js

js edupress.kr collected 2026-10-05 18:17:56 UTC 9,093 bytes, 208 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.classList.add("bigsize");
30            figure.setAttribute("data-current-index", validIndex++);
31
32            imgElement.addEventListener("click", () => this.initModal(figure));
33        });
34        
35    }
36
37    // 클릭된 이미지에 대한 모달을 염. 모달이 존재하지 않으면 생성
38    initModal(target) {
39        const _this = this;
40        if(!this.#MODAL) {
41            this.createModal();
42            this.initElements();
43        }
44        const { lightBoxImage, prevBtn, nextBtn, caption, number} = this.#ELEMENTS;
45        const index = parseInt(target.getAttribute("data-current-index", 10));
46
47        this.#MODAL.setAttribute("data-current-index", index);
48
49        // 이벤트가 추가되지 않았다면 추가
50        if (!this.#FLAG) {
51            $(this.#MODAL).on("open.zf.reveal", (e) => {
52                this.#FIGURES.forEach(figure => this.getActiveSource(figure));
53                this.#CURRENT_INDEX = parseInt(this.#MODAL.getAttribute("data-current-index"), 10);
54                lightBoxImage.onload = () => {
55                    caption.textContent = this.#CAPTION[this.#CURRENT_INDEX];
56                    number.textContent = `${this.#CURRENT_INDEX + 1} / ${this.#SOURCE.length}`;
57                    this.changeBtnState();
58
59                    // image size for first(캡션을 이미지와 크기를 맞추기 위한 설정)
60                    const _lightBox = this.#ELEMENTS.lightBox;
61                    const _imageWidth = lightBoxImage.offsetWidth;
62                    if(_imageWidth >= 400 && _imageWidth <= 1280) {
63                        _lightBox.style.setProperty("--nd-lightbox-text-max-width", _imageWidth + "px");
64                    } else {
65                        _lightBox.style.setProperty("--nd-lightbox-text-max-width", "");
66                    }
67                };
68                lightBoxImage.src = this.#SOURCE[this.#CURRENT_INDEX]; 
69            });
70
71
72            $(this.#MODAL).on("closed.zf.reveal", (e) => {
73                _this.#ISMODALOPEN = false;
74                _this.#SOURCE = [];
75                _this.#CAPTION = [];
76            });
77            
78            $(this.#MODAL).on("click", ".btn-close", function(e) {
79                $('#edit-image-viewer').foundation('close');
80            });
81
82            document.addEventListener("keydown", (e) => {
83                if (this.#ISMODALOPEN == false || !["ArrowRight", "ArrowLeft", "Escape"].includes(e.code)) return false;
84                switch (e.code) {
85                    case "ArrowRight":
86                        this.next();
87                        break;
88                    case "ArrowLeft":
89                        this.prev();
90                        break;
91                }
92            });
93
94            prevBtn.addEventListener("click", () => this.prev());
95            nextBtn.addEventListener("click", () => this.next());
96            this.#FLAG = true;
97        }
98        
99        $(this.#MODAL).foundation("open");
100        this.#ISMODALOPEN = true;
101    }
102    
103    // 모달 생성하고 foundation 활성화
104    createModal() {
105        const htmlContent = `
106            <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">
107                <div class="lightbox-container">
108                    <article class="lightbox">
109                        <section class="lightbox-body">
110                            <button class="btn-close"></button>
111                            <img class="lightbox-image" alt="viewer Image">
112                            <div class="lightbox-text">
113                                <div class="lightbox-caption"></div>
114                                <div class="lightbox-number"></div>
115                            </div>
116                            <button class="lightbox-btn lightbox-prev"></button>
117                            <button class="lightbox-btn lightbox-next"></button>
118                        </section>
119                    </article>
120                </div>
121            </div>`;
122
123        document.body.insertAdjacentHTML('beforeend', htmlContent);
124        $('#edit-image-viewer').css("outline", "none");
125        $('#edit-image-viewer').foundation();
126        this.#MODAL = document.querySelector("#edit-image-viewer");
127    }
128    
129    initElements() {
130        this.#ELEMENTS = {
131            lightBox: this.#MODAL.querySelector(".lightbox"),
132            lightBoxImage: this.#MODAL.querySelector(".lightbox-image"),
133            prevBtn: this.#MODAL.querySelector(".lightbox-prev"),
134            nextBtn: this.#MODAL.querySelector(".lightbox-next"),
135            caption: this.#MODAL.querySelector(".lightbox-caption"),
136            number: this.#MODAL.querySelector(".lightbox-number"),
137            ...this.#ELEMENTS,
138        };
139    }
140
141    // src,caption 배열 업데이트
142    getActiveSource(figure) {
143        const imgElement = figure.querySelector("picture img") || figure.querySelector("img"),
144            imgSrc = imgElement.src,
145            sources = figure.querySelectorAll("picture source");
146        const caption = figure.querySelector("figcaption");
147
148        for (let source of sources) {
149            if (source.srcset === imgSrc) {
150                this.#SOURCE.push(source.srcset);
151                this.#CAPTION.push(caption.textContent || "");
152                return;
153            }
154        }
155        this.#SOURCE.push(imgSrc);
156        this.#CAPTION.push(caption.textContent);
157    }
158
159    // 이미지를 변경하는 메서드
160    changeImage(direction) {
161        this.#CURRENT_INDEX += direction;
162        if (this.#CURRENT_INDEX < 0 || this.#CURRENT_INDEX > this.#SOURCE.length - 1) {
163            this.#CURRENT_INDEX = Math.min(Math.max(0, this.#CURRENT_INDEX), this.#SOURCE.length - 1)
164            return false;
165        }
166        const { lightBox, lightBoxImage, caption, number } = this.#ELEMENTS;
167        lightBox.style.opacity = 0;
168        setTimeout(() => {
169            lightBoxImage.onload = () => {
170                caption.textContent = this.#CAPTION[this.#CURRENT_INDEX];
171                this.changeBtnState();
172                number.textContent = `${this.#CURRENT_INDEX + 1} / ${this.#SOURCE.length}`;
173                lightBox.style.opacity = 1;
174
175                // image size(캡션을 이미지와 크기를 맞추기 위한 설정)
176                const _imageWidth = lightBoxImage.offsetWidth;
177                if(_imageWidth >= 400 && _imageWidth <= 1280) {
178                    lightBox.style.setProperty("--nd-lightbox-text-max-width", _imageWidth + "px");
179                } else {
180                    lightBox.style.setProperty("--nd-lightbox-text-max-width", "");
181                }
182            };
183            lightBoxImage.src = this.#SOURCE[this.#CURRENT_INDEX];
184        }, 100); 
185    }
186    
187    // 버튼 상태를 변경
188    changeBtnState() {
189        this.#ELEMENTS.prevBtn.classList.toggle("is-hidden", this.#CURRENT_INDEX === 0);
190        this.#ELEMENTS.nextBtn.classList.toggle("is-hidden", this.#CURRENT_INDEX === this.#SOURCE.length - 1);
191        // this.#ELEMENTS.prevBtn.classList.toggle("disabled", this.#CURRENT_INDEX === 0);
192        // this.#ELEMENTS.nextBtn.classList.toggle("disabled", this.#CURRENT_INDEX === this.#SOURCE.length - 1);
193    }
194
195    // 
196    checkState() {
197    }
198
199    // 이전 이미지로 넘기는 메서드
200    prev() {
201        this.changeImage(-1);
202    }
203
204    // 다음 이미지로 넘기는 메서드
205    next() {
206        this.changeImage(1);
207    }
208}

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.