PageSourceSearch

https://metallbude.com/cdn/shop/t/280/assets/wishlist-button.js?v=53172590485267343551789367173

js metallbude.com collected 2026-09-24 11:56:01 UTC 22,995 bytes, 96 lines download raw bytes

1class MetallbudeWishlistButton{constructor(){this.backendUrl="https://metallbude-auth.onrender.com",this.customerId=this.getCustomerId(),this.init()}getCustomerId(){if(console.log("\u{1F50D} Detecting customer ID..."),console.log("\u{1F50D} Available global objects:",{ShopifyCustomer:typeof window.ShopifyCustomer,meta:typeof window.meta,theme:typeof window.theme,Shopify:typeof window.Shopify}),typeof window.ShopifyCustomer<"u"&&window.ShopifyCustomer&&window.ShopifyCustomer.id){const customerId=window.ShopifyCustomer.id.toString();return console.log("\u{1F50D} Found Shopify customer:",customerId),customerId}const scripts=document.querySelectorAll("script");for(const script of scripts)if(script.textContent){const customerMatch=script.textContent.match(/customer.*?id["']?\s*:\s*["']?(\d+)["']?/i);if(customerMatch)return console.log("\u{1F50D} Found customer ID in script:",customerMatch[1]),customerMatch[1];const shopifyMatch=script.textContent.match(/["']customer["']:\s*\{[^}]*["']id["']:\s*["']?(\d+)["']?/i);if(shopifyMatch)return console.log("\u{1F50D} Found customer ID in Shopify object:",shopifyMatch[1]),shopifyMatch[1]}const customerScript=document.querySelector("script[data-customer-id]");if(customerScript){const customerId=customerScript.getAttribute("data-customer-id");return console.log("\u{1F50D} Found customer ID from script tag:",customerId),customerId}if(typeof window.meta<"u"&&window.meta.customer&&window.meta.customer.id){const customerId=window.meta.customer.id.toString();return console.log("\u{1F50D} Found customer in meta:",customerId),customerId}if(typeof window.theme<"u"&&window.theme.customer&&window.theme.customer.id){const customerId=window.theme.customer.id.toString();return console.log("\u{1F50D} Found customer in theme:",customerId),customerId}if(typeof window.Shopify<"u"&&window.Shopify.customer&&window.Shopify.customer.id){const customerId=window.Shopify.customer.id.toString();return console.log("\u{1F50D} Found customer in Shopify global:",customerId),customerId}const customerIdInput=document.querySelector('input[name="customer_id"], input[data-customer-id]');if(customerIdInput&&customerIdInput.value){const customerId=customerIdInput.value;return console.log("\u{1F50D} Found customer ID from input:",customerId),customerId}const customerMeta=document.querySelector('meta[name="customer-id"], meta[property="customer-id"]');if(customerMeta){const customerId=customerMeta.getAttribute("content");return console.log("\u{1F50D} Found customer ID from meta tag:",customerId),customerId}const urlParams=new URLSearchParams(window.location.search),customerIdParam=urlParams.get("customer_id")||urlParams.get("customerId");if(customerIdParam)return console.log("\u{1F50D} Found customer ID from URL param:",customerIdParam),customerIdParam;console.log("\u{1F50D} No customer ID found, checking if user is logged in...");const accountLinks=document.querySelectorAll('a[href*="/account"], a[href*="/orders"], .account-link, .customer-name');if(accountLinks.length>0){console.log("\u{1F50D} User appears to be logged in (found account links), but no customer ID detected"),console.log("\u{1F50D} Account links found:",accountLinks.length);for(const link of accountLinks){const href=link.getAttribute("href");if(href){const customerMatch=href.match(/customer[/_](\d+)/i);if(customerMatch)return console.log("\u{1F50D} Found customer ID in account link:",customerMatch[1]),customerMatch[1]}}}let guestId=localStorage.getItem("metallbude_guest_id");return guestId||(guestId="guest_"+Date.now()+"_"+Math.random().toString(36).substr(2,9),localStorage.setItem("metallbude_guest_id",guestId)),console.log("\u{1F50D} Using guest ID:",guestId),guestId}init(){console.log("\u{1F525} Metallbude Wishlist Button initialized"),console.log("\u{1F194} Customer ID:",this.customerId),console.log("\u{1F310} Backend URL:",this.backendUrl),this.setupEventListeners(),setTimeout(()=>{this.initializeButtons()},100)}setupEventListeners(){document.addEventListener("click",e=>{const wishlistBtn=e.target.closest(".metallbude-wishlist-btn, .wishlist-btn");wishlistBtn&&(e.preventDefault(),e.stopPropagation(),this.handleWishlistClick(wishlistBtn))}),document.addEventListener("change",e=>{console.log("\u{1F504} Change event detected:",e.target),(e.target.name==="id"||e.target.classList.contains("variant-selector")||e.target.getAttribute("data-option")||e.target.getAttribute("data-option-name")||e.target.name.includes("option")||e.target.closest(".option-selector"))&&(console.log("\u{1F504} Variant/option change detected, updating button states..."),setTimeout(()=>{this.updateButtonStates()},50))}),document.addEventListener("click",e=>{e.target.type==="radio"&&(e.target.getAttribute("data-option")||e.target.getAttribute("data-option-name")||e.target.name.includes("option"))&&(console.log("\u{1F504} Radio option click detected, updating button states..."),setTimeout(()=>{this.updateButtonStates()},10))}),document.addEventListener("variantChanged",e=>{console.log("\u{1F504} Custom variantChanged event detected:",e.detail),this.updateButtonStates()}),document.addEventListener("variant:change",e=>{console.log("\u{1F504} Shopify variant:change event detected:",e.detail),this.updateButtonStates()}),document.addEventListener("product:variant-change",e=>{console.log("\u{1F504} Product variant-change event detected:",e.detail),this.updateButtonStates()})}async initializeButtons(){console.log("\u{1F50D} Looking for wishlist buttons..."),console.log("\u{1F50D} DOM ready state:",document.readyState),console.log("\u{1F50D} Total elements in DOM:",document.querySelectorAll("*").length);const specificButtons=document.querySelectorAll(".metallbude-wishlist-btn"),genericButtons=document.querySelectorAll(".wishlist-btn"),allWishlistButtons=[...specificButtons,...genericButtons];if(console.log(`\u{1F527} Found ${specificButtons.length} specific wishlist buttons`),console.log(`\u{1F527} Found ${genericButtons.length} generic wishlist buttons`),console.log(`\u{1F527} Total wishlist buttons: ${allWishlistButtons.length}`),allWishlistButtons.length===0){console.warn("\u26A0\uFE0F No wishlist buttons found! Looking for all buttons...");const allButtons=document.querySelectorAll("button");console.log(`\u{1F50D} Total buttons in DOM: ${allButtons.length}`),allButtons.forEach((btn,index)=>{index<10&&console.log(`  Button ${index}: class="${btn.className}", id="${btn.id}"`)})}for(const button of allWishlistButtons){button.classList.contains("metallbude-wishlist-btn")||button.classList.add("metallbude-wishlist-btn");const productId=this.getProductId(button);console.log("\u{1F527} Initializing button for product:",productId),await this.updateButtonState(button)}}async handleWishlistClick(button){if(button.disabled)return;button.disabled=!0;const originalContent=button.innerHTML;button.innerHTML='<span class="spinner">\u23F3</span>';try{const productId=this.getProductId(button),variantId=this.getCurrentVariantId(button),selectedOptions=this.getSelectedOptions(button),productData=this.getProductData(button),isInWishlist=button.classList.contains("in-wishlist");console.log(`\u{1F504} Handling wishlist click: ${isInWishlist?"REMOVING":"ADDING"} variant ${variantId}`),isInWishlist?(await this.removeFromWishlist(productId,variantId,selectedOptions),console.log("\u2705 Successfully removed from wishlist"),this.setButtonState(button,!1)):(await this.addToWishlist(productId,variantId,selectedOptions,productData),console.log("\u2705 Successfully added to wishlist"),this.setButtonState(button,!0)),setTimeout(async()=>{console.log("\u{1F504} Double-checking button state after operation with fresh data..."),await this.updateButtonState(button,!0)}
1,500),this.showFeedback(isInWishlist?"removed":"added")}catch(error){console.error("Error handling wishlist click:",error),this.showError("Fehler beim Bearbeiten der Wunschliste"),await this.updateButtonState(button)}finally{button.disabled=!1,button.innerHTML.includes("spinner")&&(button.innerHTML=originalContent)}}getProductId(button){return button.getAttribute("data-product-id")||button.closest("[data-product-id]")?.getAttribute("data-product-id")}getCurrentVariantId(button){let variantId=button.getAttribute("data-variant-id");if(variantId)return console.log("\u{1F50D} Found variant ID from button data:",variantId),variantId;const productForm=button.closest('form[action*="/cart/add"]')||button.closest(".product-form")||button.closest("[data-product-id]")||document.querySelector('form[action*="/cart/add"]');if(productForm){const variantSelector=productForm.querySelector('select[name="id"], input[name="id"]:checked, .variant-selector:checked');if(variantSelector&&variantSelector.value)return console.log("\u{1F50D} Found variant ID from form selector:",variantSelector.value),variantSelector.value}const globalVariantSelector=document.querySelector('select[name="id"], input[name="id"]:checked');if(globalVariantSelector&&globalVariantSelector.value)return console.log("\u{1F50D} Found variant ID from global selector:",globalVariantSelector.value),globalVariantSelector.value;if(window.currentVariant&&window.currentVariant.id)return console.log("\u{1F50D} Found variant ID from window.currentVariant:",window.currentVariant.id),window.currentVariant.id.toString();const variantParam=new URLSearchParams(window.location.search).get("variant");return variantParam?(console.log("\u{1F50D} Found variant ID from URL parameter:",variantParam),variantParam):(console.log("\u{1F50D} No variant ID found"),null)}getSelectedOptions(button){const options={},productForm=button.closest('form[action*="/cart/add"]')||button.closest(".product-form")||button.closest("[data-product-id]")||document.querySelector('form[action*="/cart/add"]');if(productForm){console.log("\u{1F50D} Found product form, extracting options...");const optionSelectors=productForm.querySelectorAll(`
2        [name^="properties["], 
3        select[data-option], 
4        input[data-option]:checked,
5        select[data-option-name],
6        input[data-option-name]:checked,
7        .option-selector,
8        .variant-input:checked,
9        select[name*="option"],
10        input[name*="option"]:checked
11      `);console.log(`\u{1F50D} Found ${optionSelectors.length} option selectors`),optionSelectors.forEach((selector,index)=>{console.log(`\u{1F50D} Processing option selector ${index}:`,{name:selector.name,value:selector.value,dataOption:selector.getAttribute("data-option"),dataOptionName:selector.getAttribute("data-option-name")});let optionName=selector.getAttribute("data-option")||selector.getAttribute("data-option-name")||selector.name.replace(/properties\[|\]|option/g,"").trim();optionName&&selector.value&&selector.value!==""&&(options[optionName]=selector.value,console.log(`\u{1F50D} Added option: ${optionName} = ${selector.value}`))}),productForm.querySelectorAll(".product-form__input").forEach(input=>{if(input.type==="radio"&&input.checked){const label=productForm.querySelector(`label[for="${input.id}"]`);if(label){const optionName=label.getAttribute("data-option-name")||"Option";options[optionName]=input.value,console.log(`\u{1F50D} Added Shopify option: ${optionName} = ${input.value}`)}}else if(input.tagName==="SELECT"){const optionName=input.getAttribute("data-option-name")||input.name||"Option";input.value&&(options[optionName]=input.value,console.log(`\u{1F50D} Added Shopify select option: ${optionName} = ${input.value}`))}})}return console.log("\u{1F50D} Final selected options:",options),options}getProductData(button){const productData={title:button.getAttribute("data-product-title")||document.title,handle:button.getAttribute("data-product-handle")||window.location.pathname.split("/").pop(),price:0,imageUrl:"",sku:""},variantId=this.getCurrentVariantId(button);if(variantId&&window.productVariants){const variant=window.productVariants.find(v=>v.id.toString()===variantId);variant&&(productData.price=variant.price,productData.sku=variant.sku||"",productData.imageUrl=variant.featured_image?.url||"")}if(!productData.imageUrl){const featuredImage=document.querySelector(".product-image img, .featured-image img");featuredImage&&(productData.imageUrl=featuredImage.src)}return productData}async addToWishlist(productId,variantId,selectedOptions,productData){console.log("\u2795 Adding to wishlist:",{productId,variantId,selectedOptions});const formattedProductId=productId.includes("gid://")?productId:`gid://shopify/Product/${productId}`;let formattedVariantId=variantId;variantId&&!variantId.includes("gid://")&&(formattedVariantId=`gid://shopify/ProductVariant/${variantId}`);const response=await fetch(`${this.backendUrl}/api/public/wishlist/add`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerId:this.customerId,productId:formattedProductId,variantId:formattedVariantId,selectedOptions,title:productData.title,imageUrl:productData.imageUrl,price:productData.price,sku:productData.sku,handle:productData.handle})});if(!response.ok)throw new Error(`Failed to add to wishlist: ${response.status}`);const data=await response.json();if(!data.success)throw new Error(data.error||"Failed to add to wishlist");return data}async removeFromWishlist(productId,variantId,selectedOptions){console.log("\u2796 Removing from wishlist:",{productId,variantId,selectedOptions});const formattedProductId=productId.includes("gid://")?productId:`gid://shopify/Product/${productId}`;let formattedVariantId=variantId;variantId&&!variantId.includes("gid://")&&(formattedVariantId=`gid://shopify/ProductVariant/${variantId}`);const response=await fetch(`${this.backendUrl}/api/public/wishlist/remove`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({customerId:this.customerId,productId:formattedProductId,variantId:formattedVariantId,selectedOptions})});if(!response.ok)throw new Error(`Failed to remove from wishlist: ${response.status}`);const data=await response.json();if(!data.success)throw new Error(data.error||"Failed to remove from wishlist");return data}async checkWishlistStatus(productId,variantId,selectedOptions,bustCache=!1){try{console.log("\u{1F50D} Checking wishlist status for:",{productId,variantId,selectedOptions,bustCache});const url=new URL(`${this.backendUrl}/api/public/wishlist/items`);
11url.searchParams.set("customerId",this.customerId),bustCache&&url.searchParams.set("_t",Date.now().toString());const response=await fetch(url.toString());if(!response.ok)return console.error("Failed to fetch wishlist:",response.status),!1;const data=await response.json();if(!data.success||!data.items)return console.error("Invalid wishlist response:",data),!1;console.log("\u{1F4E5} Wishlist items:",data.items.length);const isInWishlist=data.items.some(item=>{const itemProductId=this.normalizeId(item.productId),currentProductId=this.normalizeId(productId);if(console.log("\u{1F50D} Comparing products:",{itemProductId,currentProductId}),itemProductId!==currentProductId)return!1;if(variantId){const itemVariantId=this.normalizeId(item.variantId),currentVariantId=this.normalizeId(variantId);if(console.log("\u{1F50D} Comparing variants (STRICT):",{itemVariantId,currentVariantId,itemSKU:item.sku,match:itemVariantId===currentVariantId}),itemVariantId&&currentVariantId)return itemVariantId===currentVariantId;if(currentVariantId&&!itemVariantId)return console.log("\u{1F50D} Current variant exists but item has no variant - NO MATCH"),!1}if(selectedOptions&&Object.keys(selectedOptions).length>0){if(!item.selectedOptions||Object.keys(item.selectedOptions).length===0)return console.log("\u{1F50D}
11 Current has options but item has no options - NO MATCH"),!1;for(const[key,value]of Object.entries(selectedOptions))if(item.selectedOptions[key]!==value)return console.log(`\u{1F50D} Option mismatch: ${key} = "${value}" vs "${item.selectedOptions[key]}" - NO MATCH`),!1;return console.log("\u{1F50D} All options match - MATCH"),!0}return!variantId&&!selectedOptions&&(item.variantId||item.selectedOptions&&Object.keys(item.selectedOptions).length>0)?(console.log("\u{1F50D} Generic product check but item has specific variant/options - NO MATCH"),!1):(console.log("\u{1F50D} Both are product-level items - MATCH"),!0)});return console.log("\u2705 Wishlist check result (variant-specific):",isInWishlist),isInWishlist}catch(error){return console.error("Error checking wishlist status:",error),!1}}extractProductId(shopifyGid){return typeof shopifyGid=="string"&&shopifyGid.includes("gid://shopify/Product/")?shopifyGid.split("/").pop():shopifyGid}extractVariantId(shopifyGid){return typeof shopifyGid=="string"&&shopifyGid.includes("gid://shopify/ProductVariant/")?shopifyGid.split("/").pop():shopifyGid}normalizeId(id){if(!id)return null;const idStr=String(id);return idStr.includes("gid://shopify/")?idStr.split("/").pop():idStr}async updateButtonState(button,bustCache=!1){const productId=this.getProductId(button),variantId=this.getCurrentVariantId(button),selectedOptions=this.getSelectedOptions(button);if(console.log("\u{1F504} Updating button state for:",{productId,variantId,selectedOptions,optionKeys:Object.keys(selectedOptions),hasVariant:!!variantId,bustCache}),!productId){console.warn("\u26A0\uFE0F No product ID found for button");return}try{const isInWishlist=await this.checkWishlistStatus(productId,variantId,selectedOptions,bustCache);console.log(`\u{1F504} Button state result: ${isInWishlist?"IN WISHLIST":"NOT IN WISHLIST"}`),this.setButtonState(button,isInWishlist)}catch(error){console.error("\u274C Error updating button state:",error),this.setButtonState(button,!1)}}async updateButtonStates(){const specificButtons=document.querySelectorAll(".metallbude-wishlist-btn"),genericButtons=document.querySelectorAll(".wishlist-btn"),allButtons=[...specificButtons,...genericButtons];for(const button of allButtons)await this.updateButtonState(button)}setButtonState(button,isInWishlist){if(console.log(`\u{1F3A8} Setting button state: ${isInWishlist?"IN WISHLIST":"NOT IN WISHLIST"}`),button.classList.remove("in-wishlist","not-in-wishlist"),isInWishlist){button.classList.add("in-wishlist"),button.setAttribute("aria-label","Von Wunschliste entfernen");const icon=button.querySelector(".wishlist-icon"),text=button.querySelector(".wishlist-text");if(icon&&(icon.innerHTML="\u2764\uFE0F"),text&&(text.textContent="In Wunschliste"),!icon&&!text){let content=button.innerHTML;content.includes("\u{1F90D}")||content.includes("\u2661")||content.includes("\u{1F5A4}")?(content=content.replace(/🤍|♡|🖤/g,"\u2764\uFE0F"),button.innerHTML=content):content.includes("Zur Wunschliste")&&(content=content.replace("Zur Wunschliste","In Wunschliste"),button.innerHTML=content)}}else{button.classList.add("not-in-wishlist"),button.setAttribute("aria-label","Zur Wunschliste hinzuf\xFCgen");const icon=button.querySelector(".wishlist-icon"),text=button.querySelector(".wishlist-text");if(icon&&(icon.innerHTML="\u{1F90D}"),text&&(text.textContent="Zur Wunschliste"),!icon&&!text){let content=button.innerHTML;content.includes("\u2764\uFE0F")||content.includes("\u2665")||content.includes("\u{1F5A4}")?(content=content.replace(/❤️|♥|🖤/g,"\u{1F90D}"),button.innerHTML=content):content.includes("In Wunschliste")&&(content=content.replace("In Wunschliste","Zur Wunschliste"),button.innerHTML=content)}}const variantId=this.getCurrentVariantId(button),selectedOptions=this.getSelectedOptions(button);(variantId||Object.keys(selectedOptions).length>0)&&(button.setAttribute("data-current-variant",variantId||"options"),button.setAttribute("data-current-options",JSON.stringify(selectedOptions))),console.log("\u{1F3A8} Final button classes:",button.className),console.log("\u{1F3A8} Final button content:",button.innerHTML.substring(0,50)+"...")}showFeedback(action){const message=action==="added"?"Zur Wunschliste hinzugef\xFCgt!":"Von Wunschliste entfernt!",feedback=document.createElement("div");feedback.className="metallbude-wishlist-feedback",feedback.textContent=message,feedback.style.cssText=`
12      position: fixed;
13      top: 20px;
14      right: 20px;
15      background: #000;
16      color: white;
17      padding: 12px 24px;
18      border-radius: 20px;
19      font-size: 14px;
20      font-weight: 500;
21      z-index: 10000;
22      opacity: 0;
23      transform: translateX(100%);
24      transition: all 0.3s ease;
25    `,document.body.appendChild(feedback),requestAnimationFrame(()=>{feedback.style.opacity="1",feedback.style.transform="translateX(0)"}),setTimeout(()=>{feedback.style.opacity="0",feedback.style.transform="translateX(100%)",setTimeout(()=>feedback.remove(),300)},2e3)}showError(message){console.error("Wishlist error:",message);const feedback=document.createElement("div");feedback.className="metallbude-wishlist-error",feedback.textContent=message,feedback.style.cssText=`
26      position: fixed;
27      top: 20px;
28      right: 20px;
29      background: #dc3545;
30      color: white;
31      padding: 12px 24px;
32      border-radius: 20px;
33      font-size: 14px;
34      font-weight: 500;
35      z-index: 10000;
36      opacity: 0;
37      transform: translateX(100%);
38      transition: all 0.3s ease;
39    `,document.body.appendChild(feedback),requestAnimationFrame(()=>{feedback.style.opacity="1",feedback.style.transform="translateX(0)"}),setTimeout(()=>{feedback.style.opacity="0",feedback.style.transform="translateX(100%)",setTimeout(()=>feedback.remove(),300)},3e3)}}document.readyState==="loading"?document.addEventListener("DOMContentLoaded",()=>{window.metallbudeWishlist=new MetallbudeWishlistButton}):window.metallbudeWishlist=new MetallbudeWishlistButton;const wishlistStyles=`
40.metallbude-wishlist-btn {
41  display: inline-flex;
42  align-items: center;
43  gap: 8px;
44  background: transparent;
45  border: 1px solid #000;
46  border-radius: 20px;
47  padding: 8px 16px;
48  font-size: 14px;
49  font-weight: 500;
50  color: #000;
51  cursor: pointer;
52  transition: all 0.2s ease;
53  text-decoration: none;
54  min-height: 40px;
55}
56
57.metallbude-wishlist-btn:hover {
58  background: transparent !important;
59  color: #000 !important;
60  border-color: #000 !important;
61}
62
63.metallbude-wishlist-btn:disabled {
64  opacity: 0.6;
65  cursor: not-allowed;
66}
67
68.wishlist-icon {
69  font-size: 16px;
70  line-height: 1;
71}
72
73.wishlist-text {
74  font-size: 12px;
75  text-transform: uppercase;
76  letter-spacing: 0.5px;
77}
78
79.spinner {
80  animation: spin 1s linear infinite;
81  display: inline-block;
82}
83
84@keyframes spin {
85  from { transform: rotate(0deg); }
86  to { transform: rotate(360deg); }
87}
88
89.metallbude-wishlist-feedback,
90.metallbude-wishlist-error {
91  font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;
92  text-transform: uppercase;
93  letter-spacing: 0.5px;
94}
95`;if(!document.querySelector("#metallbude-wishlist-styles")){const styleSheet=document.createElement("style");styleSheet.id="metallbude-wishlist-styles",styleSheet.textContent=wishlistStyles,document.head.appendChild(styleSheet)}
96//# sourceMappingURL=/cdn/shop/t/280/assets/wishlist-button.js.map?v=53172590485267343551789367173

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.