PageSourceSearch

https://phfd.co/core-content/js/projects.js?d017c21ceddd

js phfd.co collected 2026-09-30 13:57:37 UTC 10,534 bytes, 297 lines download raw bytes

1class Projects {
2    SEARCH_PARAM = "i";
3    RES_ATTRIBUTE = "full-resolution";
4    ID_ATTRIBUTE = "data-id";
5    constructor() {
6        this.display = document.querySelector(".image img");
7        this.loading = document.querySelector("windmill-spinner");
8        this.cached = {};
9
10        // this.expand  = document.querySelector("#expand");
11        this.galleryPanel = document.querySelector(".panel");
12        this.gallery = document.querySelector(".cfs--picture-gallery");
13
14        // Let's grab our first image and ensure it's cached at first load
15        const first_image = this.gallery.querySelector(`[${this.RES_ATTRIBUTE}="${(new URL(this.display.src)).pathname}"]`);
16        if(first_image) this.cached[first_image.getAttribute(this.ID_ATTRIBUTE)] = this.display.src;
17
18        this.backButton = document.querySelectorAll(".nav-button.left");
19        this.nextButton = document.querySelectorAll(".nav-button.right");
20        this.shareButton = document.querySelector("#share");
21        this.selectedClass = "project--individual--current";
22        this.pin = document.querySelector('[is="button-toggle"]');
23        this.softPin = false;
24        
25        this.name = document.querySelector("[name='name']").value;
26        this.blurb = document.querySelector('[name="blurb"]').value;
27
28        this.timeout = null;
29        this.timeoutLength = 1500;
30        this.closePanel = 0;
31        this.pinPreference = "project-pin-preference";
32        this.pin.value = pref(this.pinPreference) || false;
33
34        
35        this.setUpCSSVars();
36
37        this.listeners()
38        this.updateImage();
39    }
40
41    get currentImage() {
42        let current = new URL(location.href).searchParams.get(this.SEARCH_PARAM);
43
44        if(current === "null") {
45            current = document.querySelector(`img[${this.ID_ATTRIBUTE}]`).getAttribute(this.ID_ATTRIBUTE);
46            this.updateHistoryState(current);
47        }
48        if(!current) current = document.querySelector(`img[${this.ID_ATTRIBUTE}]`).getAttribute(this.ID_ATTRIBUTE);
49        return current;
50    }
51
52    listeners() {
53        // this.expand.addEventListener("click", this.toggleGallery.bind(this));
54        for(const img of this.gallery.querySelectorAll("img")) {
55            img.addEventListener("click", event => {
56                this.updateHistoryState(event.target.getAttribute(this.ID_ATTRIBUTE));
57                this.updateImage();
58            });
59        }
60        for(const btn of this.nextButton) {
61            btn.addEventListener("click", () => {
62                this.incrementTo(1);
63                this.updateImage();
64            });
65        }
66        for(const btn of this.backButton) {
67            btn.addEventListener("click", () => {
68                this.incrementTo(-1);
69                this.updateImage();
70            });
71        }
72
73        this.pin.addEventListener("click", this.pinSetup.bind(this));
74
75        this.share(this.shareButton);
76        // this.shareButton.addEventListener("click", (event) => {
77        //     this.action.toggle();
78        // });
79
80        this.galleryPanel.addEventListener("mouseover", () => {
81            this.softPin = true;
82        });
83
84        this.galleryPanel.addEventListener("mouseout", () => {
85            this.softPin = false;
86        });
87    }
88
89    updateHistoryState(target) {
90        let current = new URL(location.href);
91        current.searchParams.set(this.SEARCH_PARAM, target);
92        // history.replaceState({url: `#${target}`},'',`#${target}`);
93        // history.replaceState({url: current, '', ""})
94        // Cobalt.router.replaceState(current.toString(), {skipRequest: true, skipUpdate: true});
95        history.replaceState({url: current.toString()}, null, current.toString());
96    }
97
98    async updateImage(){
99        clearTimeout(this.timeout);
100        this.loading.style.opacity = 1;
101        this.closePanel += 1;
102        if(this.closePanel > 1) {
103            this.pin.value = true;
104            this.closePanel = 1;
105        }
106        const selected = this.gallery.querySelectorAll(`.${this.selectedClass}`);
107
108        if(selected) {
109            for(const i of selected) {
110                i.classList.remove(this.selectedClass);
111            }
112        }
113
114        const id = this.currentImage;
115        let link = this.gallery.querySelector(`[${this.ID_ATTRIBUTE}="${id}"]`);
116        if(!link) link = this.gallery.children[0].querySelector(`[${this.ID_ATTRIBUTE}]`);
117        link.classList.add(this.selectedClass);
118
119        link.scrollIntoView({behavior: "smooth", block: "nearest", inl
119ine: "nearest"});
120        this.loadingLock = link.getAttribute(this.RES_ATTRIBUTE);
121        const progress = this.loading.querySelector("progress");
122        if(progress) progress.value = 0;
123        let blob = null;
124        if( id in this.cached == false) {
125            const promises = [
126                await wait_for_transition(this.display, "opaque", false, 300),
127                await getBlobWithLoadingBar(this.loadingLock, "", "#image-download-bar")
128            ];
129            await Promise.all(promises);
130            this.cached[id] = promises[1];
131        }
132        blob = this.cached[id];
133        if(this.loadingLock !== link.getAttribute(this.RES_ATTRIBUTE)) return;
134        this.display.classList.remove("opaque");
135        this.loadingLock = null;
136
137        // const image = link.querySelector("img");
138        this.display.src = blob; // link.getAttribute(this.RES_ATTRIBUTE);
139        this.timeout = setTimeout(() => {
140            this.closeGallery();
141            this.closePanel -= 1;
142            this.timeoutLength = 600;
143        }, this.timeoutLength);
144        this.loading.style.opacity = 0;
145    }
146
147    toggleGallery() {
148        this.galleryPanel.classList.add("open")
149        // this.galleryPanel.classList.toggle('open');
150        // this.closePanel = 0;
151        // if(this.pin.value !== pref(this.pinPreference)) this.pin.value = pin(this.pinPreference);
152    }
153
154    closeGallery() {
155        // if(this.pin.value === false && this.softPin === false) 
156        return this.galleryPanel.classList.add('open');
157        // this.closePanel = 0;
158    }
159
160    pinSetup() {
161        pref(this.pinPreference, this.pin.value);
162    }
163
164    setUpCSSVars() {
165        this.galleryPanel.style.setProperty('--margin-width', this.changeStyle(getComputedStyle(this.gallery).marginLeft));
166    }
167
168    changeStyle(val) {
169        val = val.substring(0, val.length - 2);
170        console.log(val);
171        val = Math.round(parseFloat(val) * 10) / 10;
172        console.log(val);
173        return `${val}px`
174    }
175
176    hashListener(event) {
177        event.preventDefault();
178    }
179
180    cleanup() {
181        // window.removeEventListener("hash", this.hashListener);
182    }
183
184    incrementTo(value) {
185        let index = null;
186        let maxLength = this.gallery.children.length - 1
187
188        this.backButton.disabled = false;
189        this.nextButton.disabled = false;
190
191        Array.from(this.gallery.children).forEach((e,i) => {
192            if(this.getTarget(e).getAttribute(this.ID_ATTRIBUTE) === this.currentImage) {
193                index = i;
194                return 0;
195            }
196        });
197
198        if(index === null) index = -1;
199        index += value;
200        if(index < 0) {
201            // this.backButton.disabled = true;
202            index = maxLength;
203        }
204        if(index > maxLength){
205            // this.nextButton.disabled = true;
206            index = 0;
207        }
208
209        const target = this.getTarget(this.gallery.children[index]);
210        this.updateHistoryState(target.getAttribute(this.ID_ATTRIBUTE));
211    }
212
213    share(event) {
214        this.action = new ActionMenu(this.shareButton, "modal");
215        // {
216        //     event,
217        //     title: "Share This",
218        //     mode: "modal",
219        //     withIcons: true
220        // }
221        const copy = this.action.registerAction();
222        copy.label = "Copy link to clipboard";
223        copy.icon = "content-copy";
224        copy.callback = () => {
225            const input = document.createElement("input");
226            input.value = location.toString();
227            document.body.appendChild(input);
228            input.select();
229            input.setSelectionRange(0,location.toString().length + 100);
230
231            navigator.clipboard.writeText(input.value);
232            new StatusMessage({message:"Copied to clipboard!"});
233            input.parentNode.removeChild(input);
234            return true;
235        }
236
237        if("share" in navigator) {
238            const share = this.action.registerAction();
239            share.label = "Share with an app";
240            share.icon = "share";
241            share.callback = () => {
242                navigator.share({
243                    text: `"${this.name}" by Perry's Home Furnishings & Design: ${this.blurb} ${location.toString()}`,
244                });
245                return true;
246            }
247        }
248        const pinterest = this.action.registerAction();
249        pinterest.label = "Share to Pinterest";
250        pinterest.icon = "pinterest";
251        pinterest.callback = () => {
252            window.open(`http://www.pinterest.com/pin/create/bookmarklet/?url=${location.toString()}&media=${this.display.src}&description=${encodeURIComponent("@perryshome "+this.blurb)}`);
253            return true;
254        }
255
256        const fb = this.action.registerAction();
257        fb.label = "Share to Facebook",
258        fb.icon = "facebook",
259        fb.callback = () => {
260            window.open(`https://www.facebook.com/sharer/sharer.php?u=${location.toString()}`)
261            return true;
262        }
263
264        const dl = this.action.registerAction();
265        dl.label = "Download image",
266        dl.icon = "download",
267        dl.callback = () => {
268            this.downloadImage(this.currentImage);
269            return true;
270        }
271        
272        if(this.gallery.children.length > 1) {
273            const dla = this.action.registerAction();
274            dla.label = "Download all images",
275            dla.icon = "download-multiple",
276            dla.callback = () => {
277                for(const img of this.gallery.children) {
278                    this.downloadImage(this.getTarget(img).getAttribute(this.RES_ATTRIBUTE));
279                }
280                return true;
281            }
282        }
283    }
284
285    getTarget(div) {
286        return div.querySelector(`img[${this.RES_ATTRIBUTE}]`);
287    }
288
289    downloadImage(href) {
290        var link = document.createElement("a");
291        link.href = href;
292        link.download = href;
293        document.body.appendChild(link);
294        link.click();
295        document.body.removeChild(link);
296    }
297}

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.