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.