1document.addEventListener('DOMContentLoaded', () => { 2 const COOKIE_NAME = 'bookmarked_products'; 3 const COOKIE_DAYS = 7; 4 5 let popupElement = null; 6 let popupHideTimer = null; 7 let popupRemoveTimer = null; 8 9 //ããã¤ã¹ãåå¾ 10 function setShareDeviceClass() { 11 const ua = navigator.userAgent || ''; 12 const platform = navigator.platform || ''; 13 14 const isAndroid = /Android/i.test(ua); 15 16 const isIOS = 17 /iPhone|iPad|iPod/i.test(ua) || 18 ( 19 platform === 'MacIntel' && 20 navigator.maxTouchPoints > 1 21 ); 22 23 document.body.classList.remove( 24 'is-ios', 25 'is-android', 26 'is-other-device' 27 ); 28 29 if (isIOS) { 30 document.body.classList.add('is-ios'); 31 } else if (isAndroid) { 32 document.body.classList.add('is-android'); 33 } else { 34 document.body.classList.add('is-other-device'); 35 } 36 } 37 38 setShareDeviceClass(); 39 40 function getCookie(name) { 41 const cookies = document.cookie ? document.cookie.split('; ') : []; 42 for (const cookie of cookies) { 43 const [key, ...rest] = cookie.split('='); 44 if (key === name) { 45 return decodeURIComponent(rest.join('=')); 46 } 47 } 48 return null; 49 } 50 51 function setCookie(name, value, days) { 52 const expires = new Date(); 53 expires.setTime(expires.getTime() + days * 24 * 60 * 60 * 1000); 54 document.cookie = 55 `${name}=${encodeURIComponent(value)}; expires=${expires.toUTCString()}; path=/; SameSite=Lax`; 56 } 57 58 function getBookmarks() { 59 const value = getCookie(COOKIE_NAME); 60 if (!value) return []; 61 62 try { 63 const parsed = JSON.parse(value); 64 if (!Array.isArray(parsed)) return []; 65 return parsed.map(String); 66 } catch (e) { 67 return []; 68 } 69 } 70 71 function saveBookmarks(bookmarks) { 72 const unique = [...new Set(bookmarks.map(String))];
73 setCookie(COOKIE_NAME, JSON.stringify(unique), COOKIE_DAYS); 74 } 75 76 function isBookmarked(productId) { 77 return getBookmarks().includes(String(productId)); 78 } 79 80 function addBookmark(productId) { 81 const bookmarks = getBookmarks(); 82 const id = String(productId); 83 84 if (!bookmarks.includes(id)) { 85 bookmarks.push(id); 86 saveBookmarks(bookmarks); 87 } 88 } 89 90 function removeBookmark(productId) { 91 const id = String(productId); 92 const bookmarks = getBookmarks().filter(bookmarkId => bookmarkId !== id); 93 saveBookmarks(bookmarks); 94 } 95 96 function updateBookmarkClass(productLoop) { 97 if (!productLoop) return; 98 99 const productId = productLoop.dataset.productId; 100 if (!productId) return; 101 102 if (isBookmarked(productId)) { 103 productLoop.classList.remove('not-bookmarked'); 104 productLoop.classList.add('bookmarked'); 105 } else { 106 productLoop.classList.remove('bookmarked'); 107 productLoop.classList.add('not-bookmarked'); 108 } 109 } 110 111 function updateAllBookmarkClasses() { 112 document.querySelectorAll('.product-loop-full').forEach(updateBookmarkClass); 113 } 114 115 function setInitialSaveOrder() { 116 const productItems = document.querySelectorAll('.product-list .product-loop-full'); 117 productItems.forEach((item, index) => { 118 item.dataset.saveOrder = String(index); 119 }); 120 } 121 122 function sortProductList(sortType = 'save') { 123 const productList = document.querySelector('.product-list'); 124 if (!productList) return; 125 126 const items = Array.from(productList.querySelectorAll('.product-loop-full')); 127 if (!items.length) return; 128 129 items.sort((a, b) => { 130 if (sortType === 'popularity') { 131 const rankA = parseInt(a.dataset.productRank || '999999', 10); 132 const rankB = parseInt(b.dataset.productRank || '999999', 10); 133 return rankA - rankB; 134 } 135 136 const saveOrderA = parseInt(a.dataset.saveOrder || '999999', 10); 137 const saveOrderB = parseInt(b.dataset.saveOrder || '999999', 10); 138 return saveOrderA - saveOrderB; 139 }); 140 141 items.forEach(item => { 142 productList.appendChild(item); 143 }); 144 } 145 146 function initBookmarkSort() { 147 const orderSelect = document.querySelector('#order'); 148 if (!orderSelect) return; 149 150 setInitialSaveOrder(); 151 sortProductList(orderSelect.value); 152 153 orderSelect.addEventListener('change', () => { 154 sortProductList(orderSelect.value); 155 }); 156 } 157 158 function clearBookmarkPopup() { 159 if (popupHideTimer) { 160 clearTimeout(popupHideTimer); 161 popupHideTimer = null; 162 } 163 164 if (popupRemoveTimer) { 165 clearTimeout(popupRemoveTimer); 166 popupRemoveTimer = null; 167 } 168 169 if (popupElement) { 170 popupElement.remove(); 171 popupElement = null; 172 } 173 } 174 175 function showBookmarkPopup(message) { 176 clearBookmarkPopup(); 177 178 const popup = document.createElement('div'); 179 popup.className = 'bookmark-popup'; 180 popup.innerHTML = `<div class="bookmark-popup__inner">${message}</div>`; 181 182 document.body.appendChild(popup); 183 popupElement = popup; 184 185 requestAnimationFrame(() => { 186 if (popupElement) { 187 popupElement.classList.add('is-show'); 188 } 189 }); 190 191 popupHideTimer = setTimeout(() => { 192 if (!popupElement) return; 193 popupElement.classList.remove('is-show'); 194 popupElement.classList.add('is-hide'); 195 }, 2000); 196 197 popupRemoveTimer = setTimeout(() => { 198 clearBookmarkPopup(); 199 }, 2600); 200 } 201 202 // åæè¡¨ç¤ºæã«Cookieã®å 容ãåæ 203 updateAllBookmarkClasses(); 204 205 // 並ã³é åæå 206 initBookmarkSort(); 207 208 // ã¯ãªãã¯ã§ããã¯ãã¼ã¯åãæ¿ã 209 document.addEventListener('click', (event) => { 210 const button = event.target.closest('.bookmark-button'); 211 if (!button) return; 212 213 const productLoop = button.closest('.product-loop-full'); 214 if (!productLoop) return; 215 216 const productId = productLoop.dataset.productId; 217 if (!productId) return; 218 219 if (productLoop.classList.contains('bookmarked')) { 220 removeBookmark(productId); 221 updateBookmarkClass(productLoop); 222 showBookmarkPopup('ååãä¿åãªã¹ãããåé¤ãã¾ãã'); 223 } else { 224 addBookmark(productId); 225 updateBookmarkClass(productLoop); 226 showBookmarkPopup('ååãä¿åãªã¹ãã«è¿½å ãã¾ãã'); 227 } 228 }); 229 230 (() => { 231 const sheet = document.getElementById('product-share-sheet'); 232 const toast = document.getElementById('share-toast'); 233 234 if (!sheet) return; 235 236 const previewImage = document.getElementById('share-preview-image'); 237 const previewCompany = document.getElementById('share-preview-company'); 238 const previewTitle = document.getElementById('share-preview-title'); 239 const previewSummary = document.getElementById('share-preview-summary'); 240 241 let activeTrigger = null; 242 let toastTimer = null; 243 244 function getShareData() { 245 return { 246 url: sheet.dataset.shareUrl || window.location.href, 247 title: sheet.dataset.shareTitle || document.title, 248 text: sheet.dataset.shareText || document.title, 249 image: sheet.dataset.shareImage || '', 250 company: sheet.dataset.shareCompany || '', 251 summary: sheet.dataset.shareSummary || '' 252 }; 253 } 254 255 function buildShareBody(data) {
256 return [data.text, data.url].filter(Boolean).join('\n'); 257 } 258 259 function showToast(message) { 260 if (!toast) return; 261 262 clearTimeout(toastTimer); 263 toast.textContent = message; 264 toast.hidden = false; 265 266 requestAnimationFrame(() => { 267 toast.classList.add('is-show'); 268 }); 269 270 toastTimer = window.setTimeout(() => { 271 toast.classList.remove('is-show'); 272 window.setTimeout(() => { 273 toast.hidden = true; 274 }, 200); 275 }, 2000); 276 } 277 278 async function copyText(text, message = 'ãªã³ã¯ãã³ãã¼ãã¾ãã') { 279 try { 280 await navigator.clipboard.writeText(text); 281 showToast(message); 282 return true; 283 } catch (error) { 284 const textarea = document.createElement('textarea'); 285 textarea.value = text; 286 textarea.setAttribute('readonly', ''); 287 textarea.style.position = 'absolute'; 288 textarea.style.left = '-9999px'; 289 document.body.appendChild(textarea); 290 textarea.select(); 291 292 try { 293 document.execCommand('copy'); 294 showToast(message); 295 document.body.removeChild(textarea); 296 return true; 297 } catch (e) { 298 document.body.removeChild(textarea); 299 showToast('ã³ãã¼ã§ãã¾ããã§ãã'); 300 return false; 301 } 302 } 303 } 304 305 function openSheet(button) { 306 activeTrigger = button; 307 308 sheet.dataset.shareUrl = button.dataset.shareUrl || window.location.href; 309 sheet.dataset.shareTitle = button.dataset.shareTitle || document.title; 310 sheet.dataset.shareText = button.dataset.shareText || document.title; 311 sheet.dataset.shareImage = button.dataset.shareImage || ''; 312 sheet.dataset.shareCompany = button.dataset.shareCompany || ''; 313 sheet.dataset.shareSummary = button.dataset.shareSummary || ''; 314 315 const data = getShareData(); 316 317 if (previewImage) { 318 previewImage.src = data.image || ''; 319 previewImage.alt = data.title || ''; 320 previewImage.style.display = data.image ? 'block' : 'none'; 321 } 322 323 if (previewCompany) previewCompany.textContent = data.company; 324 if (previewTitle) previewTitle.textContent = data.title; 325 if (previewSummary) previewSummary.textContent = data.summary; 326 327 sheet.hidden = false; 328 document.body.classList.add('is-share-open'); 329 330 requestAnimationFrame(() => { 331 sheet.classList.add('is-open'); 332 }); 333 } 334 335 function closeSheet() { 336 sheet.classList.remove('is-open'); 337 document.body.classList.remove('is-share-open'); 338 339 window.setTimeout(() => { 340 sheet.hidden = true; 341 if (activeTrigger) { 342 activeTrigger.focus(); 343 } 344 }, 280); 345 } 346 347 async function runShareAction(action) { 348 const data = getShareData(); 349 const body = buildShareBody(data); 350 351 switch (action) { 352 case 'messages': { 353 const isMobile = /Android|iPhone|iPad|iPod/i.test(navigator.userAgent); 354 355 if (isMobile) { 356 window.location.href = `sms:?&body=${encodeURIComponent(body)}`; 357 } else { 358 await copyText(body, 'SMSå ±æç¨ã®æ¬æãã³ãã¼ãã¾ãã'); 359 } 360 break; 361 } 362 363 case 'line': { 364 window.open( 365 `https://social-plugins.line.me/lineit/share?url=${encodeURIComponent(data.url)}`, 366 '_blank', 367 'noopener' 368 ); 369 break; 370 } 371 372 case 'x': { 373 window.open( 374 `https://twitter.com/intent/tweet?text=${encodeURIComponent(data.text)}&url=${encodeURIComponent(data.url)}`, 375 '_blank', 376 'noopener' 377 ); 378 break; 379 } 380 381 case 'instagram': { 382 if (navigator.share) { 383 try { 384 await navigator.share({ 385 title: data.title, 386 text: data.text, 387 url: data.url 388 }); 389 } catch (error) { 390 // ãã£ã³ã»ã«æã¯ä½ãããªã 391 } 392 } else { 393 await copyText(data.url, 'Instagramå ±æç¨ã«ãªã³ã¯ãã³ãã¼ãã¾ãã'); 394 395 window.open( 396 'https://www.instagram.com/', 397 '_blank', 398 'noopener' 399 ); 400 } 401 break; 402 } 403 404 case 'whatsapp': { 405 window.open( 406 `https://wa.me/?text=${encodeURIComponent(body)}`, 407 '_blank', 408 'noopener' 409 ); 410 break; 411 } 412 413 case 'email': { 414 const subject = encodeURIComponent(data.title); 415 const mailBody = encodeURIComponent(body); 416 window.location.href = `mailto:?subject=${subject}&body=${mailBody}`; 417 break; 418 } 419 420 case 'copy': { 421 await copyText(data.url); 422 break; 423 } 424 425 case 'more': { 426 if (navigator.share) { 427 try { 428 await navigator.share({ 429 title: data.title, 430 text: data.text, 431 url: data.url 432 }); 433 } catch (error) { 434 // ãã£ã³ã»ã«æã¯ä½ãããªã 435 } 436 } else { 437 await copyText(data.url, 'å ±ææ©è½ã使ããªãããããªã³ã¯ãã³ãã¼ãã¾ãã'); 438 } 439 break; 440 } 441 } 442 } 443 444 document.addEventListener('click', async (event) => { 445 const openButton = event.target.closest('.js-share-open'); 446 if (openButton) { 447 event.preventDefault(); 448 openSheet(openButton); 449 return; 450 } 451 452 const closeButton = event.target.closest('.js-share-close'); 453 if (closeButton) { 454 event.preventDefault(); 455 closeSheet(); 456 return; 457 } 458 459 const actionButton = event.target.closest('.js-share-action'); 460 if (actionButton) { 461 event.preventDefault();
462 await runShareAction(actionButton.dataset.shareAction || ''); 463 } 464 }); 465 466 document.addEventListener('keydown', (event) => { 467 if (event.key === 'Escape' && sheet.classList.contains('is-open')) { 468 closeSheet(); 469 } 470 }); 471 })(); 472});
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.