1class Wishlist { 2 constructor() { 3 this.storageKey = 'wishlist_samples'; 4 this.buttonsSelector = '.wishlist-btn'; 5 6 this.init(); 7 } 8 9 // Storage 10 11 getItems() { 12 //return Cookies.get(this.storageKey) || []; 13 return JSON.parse(localStorage.getItem(this.storageKey)) || []; 14 } 15 16 saveItems(items) { 17 //Cookies.set(this.storageKey, JSON.stringify(items)); 18 localStorage.setItem(this.storageKey, JSON.stringify(items)); 19 } 20 21 // Helpers 22 23 exists(id) { 24 return this.getItems().some((item) => item.id === id); 25 } 26 27 add(product) { 28 const items = this.getItems(); 29 30 if (!this.exists(product.id)) { 31 items.push(product); 32 this.saveItems(items); 33 } 34 } 35 36 remove(id) { 37 const items = this.getItems().filter((item) => item.id !== id); 38 this.saveItems(items); 39 this.removeDisplay(id); 40 } 41 42 toggle(product) { 43 if (this.exists(product.id)) { 44 this.remove(product.id); 45 } else { 46 this.add(product); 47 } 48 49 this.updateButtons(); 50 this.updateHeaderCounter(); 51 } 52 53 clear() { 54 localStorage.removeItem(this.storageKey); 55 this.updateButtons(); 56 } 57 58 // UI 59 60 updateButtons() { 61 const items = this.getItems(); 62 63 document.querySelectorAll(this.buttonsSelector).forEach((button) => { 64 const id = button.dataset.id; 65 66 const active = items.some((item) => item.id === id); 67 68 button.classList.toggle('fav', active); 69 }); 70 } 71 72 bindEvents() { 73 document.querySelectorAll(this.buttonsSelector).forEach((button) => { 74 button.addEventListener('click', (event) => { 75 event.preventDefault(); 76 const product = { 77 id: button.dataset.id, 78 name: button.dataset.name, 79 image: button.dataset.image, 80 code: button.dataset.code, 81 href: button.dataset.href, 82 }; 83 84 const exists = this.exists(product.id); 85 86 /*if (!exists) { 87 //this.flyToWishlist(button); 88 //this.color(button); 89 this.colorCorner(button, false); 90 }*/ 91 92 if (exists) { 93 console.log('exists reverse'); 94 this.colorCorner(button, true); 95 } else { 96 this.colorCorner(button, false); 97 } 98 99 this.toggle(product); 100 }); 101 }); 102 } 103 104 removeDisplay(id) { 105 console.log('remove display', id); 106 const container = document.querySelector('#wishlist-products .wishlist-inner'); 107 108 if (container) { 109 container.querySelectorAll('.colours-block').forEach((item) => { 110 if (item.dataset.id == id) { 111 item.remove(); 112 } 113 }); 114 115 this.displayCount(); 116 } 117 } 118 119 displayCount() { 120 const header = document.querySelector('#wishlist-products'); 121 122 if (!header) { 123 return; 124 } 125 126 const counter = this.getItems().length; 127 128 if (counter) { 129 const selectedSamples = header.querySelector('.wishlist-counter').dataset.content; 130 header.querySelector('.wishlist-counter').innerHTML = `${counter} ${selectedSamples}`; 131 } else { 132 header.querySelector('.wishlist-counter').innerHTML = `${selectedSamples}`; 133 } 134 } 135 136 updateHeaderCounter() { 137 const badge = document.querySelectorAll('.wishlist-bookmark-counter'); 138 139 if (!badge) { 140 return; 141 } 142 143 badge.forEach((item) => { 144 const count = this.getItems().length; 145 if (count > 0) { 146 item.classList.add('show'); 147 } else { 148 item.classList.remove('show'); 149 } 150 item.textContent = count; 151 152 item.textContent = count; 153 154 item.classList.remove('bump'); 155 156 requestAnimationFrame(() => { 157 item.classList.add('bump'); 158 }); 159 }); 160 } 161 async colorCorner(button, reverse = false) { 162 const rect = button.getBoundingClientRect(); 163 const bookmark = document.querySelector('.fixed-nav .wishlist-bookmark'); 164 165 const buttonX = rect.left + rect.width / 2; 166 const buttonY = rect.top + rect.height / 2; 167 168 // Centro da bola 169 const bookmarkRect = bookmark.getBoundingClientRect(); 170 171 const centerX = bookmarkRect.left + bookmarkRect.width / 2; 172 //const centerY = bookmarkRect.top + bookmarkRect.height / 2; 173 174 /*const centerX = window.innerWidth;*/ 175 const centerY = 0; 176 177 const dx = buttonX - centerX; 178 const dy = buttonY - centerY; 179 180 const radius = Math.sqrt(dx * dx + dy * dy); 181 182 /*const img = new Image(); 183 img.crossOrigin = 'anonymous'; 184 img.src = button.dataset.image; 185 186 await img.decode();*/ 187 188 const img = button.closest('.aCor').querySelector('img'); 189 console.log(img); 190 const color = ColorThief.getColorSync(img); 191 const colorHex = color.hex(); 192 const colorHexOpacity = colorHex + '4C'; 193 const ball = document.createElement('div'); 194 195 ball.className = 'wishlist-burst'; 196 ball.style.setProperty('--color', colorHex); 197 ball.style.setProperty('--colorOpacity', colorHexOpacity); 198 ball.style.setProperty('--width', `${radius * 2}px`); 199 ball.style.setProperty('--height', `${radius * 2}px`); 200 ball.style.setProperty('--left', `${centerX}px`); 201 ball.style.setProperty('--top', `${centerY}px`); 202 ball.style.setProperty('--buttonX', `${buttonX}px`); 203 ball.style.setProperty('--buttonY', `${buttonY}px`); 204 205 if (reverse) { 206 ball.classList.add('reverse'); 207 } 208 209 document.body.appendChild(ball); 210 211 setTimeout(() => { 212 ball.classList.add('animate'); 213 }, 100); 214 215 ball.addEventListener('transitionend', () => ball.remove(), { once: true });
216 } 217 218 async color(button) { 219 const rect = button.getBoundingClientRect(); 220 221 const startX = rect.left + rect.width / 2; 222 const startY = rect.top + rect.height / 2; 223 224 const burst = document.createElement('div'); 225 const url = button.dataset.image; 226 227 const img = new Image(); 228 img.crossOrigin = 'anonymous'; 229 img.src = url; 230 await img.decode(); 231 232 const color = ColorThief.getColorSync(img); 233 const colorHex = color.hex(); 234 235 //const color = '#838485'; 236 burst.className = 'color-burst'; 237 burst.style.background = colorHex; 238 239 burst.style.setProperty('--x', `${startX}px`); 240 burst.style.setProperty('--y', `${startY}px`); 241 burst.style.setProperty('--color', colorHex); 242 243 document.body.appendChild(burst); 244 245 burst.addEventListener('animationend', () => { 246 burst.remove(); 247 }); 248 } 249 flyToWishlist(button) { 250 const bookmark = document.querySelector('.wishlist-bookmark'); 251 252 if (!bookmark) return; 253 254 const start = button.getBoundingClientRect(); 255 const end = bookmark.getBoundingClientRect(); 256 257 const dot = document.createElement('div'); 258 dot.className = 'wishlist-dot'; 259 260 document.body.appendChild(dot); 261 262 dot.style.position = 'fixed'; 263 dot.style.left = start.left + start.width / 2 + 'px'; 264 dot.style.top = start.top + start.height / 2 + 'px'; 265 dot.style.opacity = '1'; 266 267 requestAnimationFrame(() => { 268 dot.classList.add('show'); 269 270 dot.style.left = end.left + end.width / 2 + 'px'; 271 dot.style.top = end.top + end.height / 2 + 'px'; 272 dot.style.transform = 'scale(.2)'; 273 dot.style.opacity = '0'; 274 }); 275 276 dot.addEventListener( 277 'transitionend', 278 () => { 279 dot.remove(); 280 281 const counter = document.querySelector('.wishlist-bookmark-counter'); 282 283 if (counter) { 284 counter.classList.remove('bump'); 285 requestAnimationFrame(() => counter.classList.add('bump')); 286 } 287 }, 288 { once: true }, 289 ); 290 } 291 292 displayProducts() { 293 const container = document.querySelector('#wishlist-products .wishlist-inner'); 294 const template = document.querySelector('#colours-item'); 295 const header = document.querySelector('#wishlist-products'); 296 const productsField = document.querySelector('input.requested-samples'); 297 298 if (!container || !template) { 299 return; 300 } 301 302 this.displayCount(); 303 304 const products = this.getItems(); 305 306 productsField.value = products.map((product) => `${product.id} - ${product.name}`).join('\n'); 307 308 this.clearProductsValidation(productsField); 309 310 products.forEach((product) => { 311 if (!template) return; 312 const clone = template.content.cloneNode(true); 313 314 clone.querySelector('.colours-block').dataset.id = product.id; 315 clone.querySelector('.product-link').href = product.href; 316 317 clone.querySelector('.aCor').style.backgroundImage = `url(${product.image})`; 318 319 clone.querySelector('.product-code').textContent = product.code; 320 clone.querySelector('.product-name').textContent = product.name; 321 322 const button = clone.querySelector('.wishlist-btn'); 323 324 button.dataset.id = product.id; 325 button.dataset.name = product.name; 326 button.dataset.image = product.image; 327 button.dataset.code = product.code; 328 button.dataset.href = product.href; 329 330 container.appendChild(clone); 331 }); 332 333 this.bindEvents(); 334 } 335 336 // Validation 337 338 showProductsValidation(field) { 339 field.classList.add('wpcf7-not-valid'); 340 field.setAttribute('aria-invalid', 'true'); 341 342 // Não duplicar a mensagem 343 let errorTip = field.parentElement.querySelector('.wpcf7-not-valid-tip'); 344 345 if (!errorTip) { 346 errorTip = document.createElement('span'); 347 errorTip.className = 'wpcf7-not-valid-tip'; 348 field.parentElement.appendChild(errorTip); 349 } 350 } 351 352 clearProductsValidation(field) { 353 field.classList.remove('wpcf7-not-valid'); 354 field.removeAttribute('aria-invalid'); 355 356 const errorTip = field.parentElement.querySelector('.wpcf7-not-valid-tip'); 357 358 if (errorTip) { 359 errorTip.remove(); 360 } 361 } 362 363 // Start 364 365 init() { 366 this.bindEvents(); 367 this.updateButtons(); 368 this.updateHeaderCounter(); 369 } 370} 371 372document.addEventListener('DOMContentLoaded', () => { 373 const wishlist = new Wishlist(); 374 375 wishlist.displayProducts(); 376});
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.