Sienna tafellamp — sculpturale vorm, sfeervol licht

Sienna tafellamp — sculpturale vorm, sfeervol licht

∅ 8.7″ x H 9.8″ / Walnootkleur & Amber / Amerikaanse stekker
€123,54
Aanbiedingsprijs  €123,54 Reguliere prijs 
Ga naar productinformatie
Sienna tafellamp — sculpturale vorm, sfeervol licht

Sienna tafellamp — sculpturale vorm, sfeervol licht

Gratis verzending en eenvoudig retourneren
10% korting bij aankoop van 2+ artikelen

€123,54
Aanbiedingsprijs  €123,54 Reguliere prijs 
Maat
KleurWalnootkleur & Amber
Stekkeropties
Gegevens

Gevormde contouren en een glaskap in warme tinten zorgen samen voor een zachte, gouden gloed die ideaal is voor bureaus, nachtkastjes of consoletafels. Het sierlijk gebogen houten frame vermijdt zware versieringen, waardoor de lamp moeiteloos past in eigentijdse, neutrale en aardse interieurstijlen.



Ontwerp en constructie

Een gebogen houten basis in walnoottint verankert de armatuur en benadrukt organische houtbewerkingstechnieken. De bijbehorende glazen diffuser wordt gevormd in een handmatig gestookte oven, waardoor microscopische luchtbelletjes en subtiele variaties in de oppervlaktestructuur natuurlijke productiekenmerken zijn en niet als fabricagefouten worden beschouwd.

  • Vloeiend houten silhouet met een rijke walnootafwerking
  • Getinte glazen koepel die het licht filtert tot een diepgele tint
  • Gebruiksklare elektrische uitvoering met een kabel van 59,1 inch en een tastbare tuimelschakelaar

Verlichting en afmetingen

Het interne verlichtingssysteem verbruikt ongeveer 12 watt via ingebouwde halfgeleiderdiodes die zijn ontworpen voor langdurig dagelijks gebruik. Standaardseries worden geleverd met een warme lichtkleur van 3000 kelvin; klanten die een uitgebalanceerde uitvoering van 4000K of een heldere uitvoering van 6000K wensen, moeten vóór het afrekenen rechtstreeks contact opnemen met de klantenservice. Twee hoogtemodellen zorgen voor flexibiliteit in verschillende ruimte-indelingen. Beide versies hebben dezelfde doorsnede van 8,7 inch en gebruiken exact dezelfde glazen kap. De lagere variant is 9,8 inch hoog, terwijl het hogere model 12,6 inch bereikt.

Sienna-tafellamp — kort H9.8"
Sienna-tafellamp — hoog H12.6"</div><br>
<h2>Technische specificaties</h2>
<table>
<thead>
<tr>
<th>Specificatie</th>
<th>Details</th>
</tr>
</thead>
<tbody>
<tr>
<td>Primaire materialen</td>
<td>Hout, structurele metalen onderdelen, getint glas</td>
</tr>
<tr>
<td>Verlichtingsmodule</td>
<td>Ingebouwde ledmodule, ongeveer 12 watt</td>
</tr>
<tr>
<td>Spanningscompatibiliteit</td>
<td>AC 110–240</td>
</tr>
<tr>
<td>Stroomaansluiting</td>
<td>Rechtstreeks in het stopcontact, kabel van 59,1 inch, handmatige tuimelschakelaar</td>
</tr>
<tr>
<td>Beschermingsklasse</td>
<td>IP20, uitsluitend geclassificeerd voor gecontroleerde binnenomgevingen</td>
</tr>
<tr>
<td>Marktconformiteit</td>
<td>Goedgekeurd voor regio { resetProtectionAfterCheckoutReturn(); }); (async function ShipCheckCartExtension() { const CONFIG = { EXTENSION_DISABLED: false, EXTENSION_LAYOUT: "user_consent", TIMESTAMP: "1790215566", SHOP_PERMANENT_DOMAIN: "rpejan-3j.myshopify.com", PRODUCT_HANDLE: "shipping-protection-1", PRODUCT_ID: 15652463411573, AUTO_ACCEPT: true, AUTO_TRANSLATE: true, DISABLE_FOR_DIGITAL_PRODUCTS: false, EXCLUDE_DIGITAL_PRODUCTS: false, IMAGE_GRAYSCALE: false, CHECKOUT_SELECTOR: "button[name=\"checkout\"]", CART_TOTAL_SELECTOR: ".totals__total-value", EXTENSION_TARGET_SELECTOR: "", CURRENT_COUNTRY: "DE", }; const CHECKOUT_BUTTONS = [ CONFIG.CHECKOUT_SELECTOR, 'button[name="checkout"],.rebuy-cart__checkout-button,#mu-checkout-button,.cd-checkout-section-button,.ocu-checkout-button,.slide-cart-checkout-btn,.boost-sd__btn-checkout,input[name="checkout"]:not([type="hidden"]),a[id="cart__checkout_btn"],button[data-href="/checkout"],a[href="/checkout"]:not(#checkout-xx-aa),a[href="/en/checkout"],.xb_slide_cart_button_checkout,.scd__checkout,.AOV-CartDrawer-Checkout,button[upnova-action="checkout"],#cart-sidebar-checkout,a[data-instant-action-type="redirect-to-checkout"],.amp-cart__footer-checkout-button,button[name="icartCheckout"],.unicorn_checkout_button,#corner-cowi-cart-summary-card-cta-button,button[name="waio_addToCart"],.checkout-button,.upcart-checkout-button,button[data-essential-cart-element="cart-drawer-checkout-button"],#AOV-CDMinimal__ViewCartButton,.easc-checkoutBtn,.pp-checkout-button,button[href^="https://www.shop.carcover.com/cart/"],.kaching-cart__checkout-button,.qsc2-checkout-button', ].filter((s) => String(s || '').trim()).join(','); const CUSTOMER_TAGS = null; const LOCALES_JSON = {"en":{"cartTickerTitle":"Secure Checkout","cartTickerSubtitleOn":"Your order is protected from loss or damage","cartTickerSubtitleOff":"Your order isn't protected from loss or damage ","cartTickerPolicy":"Terms and Conditions","cartBtnLayoutLabel":"Protected Checkout","cartBtnLayoutTitle":"Secure Checkout","cartBtnLayoutSubtitle":"Your order is protected from loss or damage","cartBtnLayoutPolicy":"Terms","checkoutTitle":"Shipping Protection","checkoutSubtitleOn":"Order is protected from loss or damage","checkoutSubtitleOff":"Order is not protected from loss or damage","checkoutPolicy":"Terms and Conditions","thankYouTitle":"Shipping Protection","thankYouSubtitle":"Protect your order from loss or damage","thankYouPolicy":"Terms and Conditions","thankYouBuyBtn":"Add to order","thankYouBuyBtnError":"Retry","thankYouPaymentLink":"Pay now"},"ar":{"cartTickerTitle":"الدفع الآمن","cartTickerSubtitleOn":"طلبك محمي من الفقدان أو التلف","cartTickerSubtitleOff":"طلبك غير محمي من الفقدان أو التلف","cartBtnLayoutLabel":"دفع محمي","cartBtnLayoutTitle":"الدفع الآمن","cartBtnLayoutSubtitle":"طلبك محمي من الضياع أو التلف"},"cs":{"cartTickerTitle":"Bezpečná pokladna","cartTickerSubtitleOn":"Vaše objednávka je chráněna proti ztrátě nebo poškození","cartTickerSubtitleOff":"Vaše objednávka není chráněna proti ztrátě nebo poškození","cartBtnLayoutLabel":"Chráněná pokladna","cartBtnLayoutTitle":"Bezpečný pokladní výstup","cartBtnLayoutSubtitle":"Vaše objednávka je chráněna před ztrátou nebo pošk"},"da":{"cartTickerTitle":"Sikker betaling","cartTickerSubtitleOn":"Din ordre er beskyttet mod tab eller skade","cartTickerSubtitleOff":"Din ordre er ikke beskyttet mod tab eller skade","cartBtnLayoutLabel":"Beskyttet betaling","cartBtnLayoutTitle":"Sikker checkout","cartBtnLayoutSubtitle":"Din ordre er beskyttet mod tab eller beskadigelse"},"de":{"cartTickerTitle":"Sicherer Checkout","cartTickerSubtitleOn":"Ihre Bestellung ist gegen Verlust oder Beschädigung geschützt","cartTickerSubtitleOff":"Ihre Bestellung ist nicht gegen Verlust oder Beschädigung geschützt","cartBtnLayoutLabel":"Geschützter Checkout","cartBtnLayoutTitle":"Sicherer Checkout","cartBtnLayoutSubtitle":"Ihre Bestellung ist vor Verlust oder Beschädigung "},"es":{"cartTickerTitle":"Pago seguro","cartTickerSubtitleOn":"Su pedido está protegido contra pérdida o daño","cartTickerSubtitleOff":"Su pedido no está protegido contra pérdida o daño","cartBtnLayoutLabel":"Pago Protegido","cartBtnLayoutTitle":"Proceso de pago seguro","cartBtnLayoutSubtitle":"Su pedido está protegido contra pérdidas o daños"},"fi":{"cartTickerTitle":"Turvallinen kassa","cartTickerSubtitleOn":"Tilauksesi on suojattu katoamista tai vahinkoa vastaan","cartTickerSubtitleOff":"Tilauksesi ei ole suojattu katoamista tai vahinkoa vastaan","cartBtnLayoutLabel":"Suojattu kassa","cartBtnLayoutTitle":"Turvallinen maksu","cartBtnLayoutSubtitle":"Tilauksesi on suojattu katoamiselta tai vahingoitt"},"fr":{"cartTickerTitle":"Paiement sécurisé","cartTickerSubtitleOn":"Votre commande est protégée contre la perte ou les dommages","cartTickerSubtitleOff":"Votre commande n'est pas protégée contre la perte ou les dommages","cartBtnLayoutLabel":"Paiement sécurisé","cartBtnLayoutTitle":"Validation sécurisée","cartBtnLayoutSubtitle":"Votre commande est protégée contre la perte ou les"},"he":{"cartTickerTitle":"צ'קאאוט מאובטח","cartTickerSubtitleOn":"ההזמנה שלך מוגנת מפני אובדן או נזק","cartTickerSubtitleOff":"ההזמנה שלך אינה מוגנת מפני אובדן או נזק","cartBtnLayoutLabel":"קופה מוגנת","cartBtnLayoutTitle":"תשלום מאובטח","cartBtnLayoutSubtitle":"ההזמנה שלך מוגנת מפני אובדן או נזק"},"hu":{"cartTickerTitle":"Biztonságos fizetés","cartTickerSubtitleOn":"A rendelése védett elvesztés vagy sérülés ellen","cartTickerSubtitleOff":"A rendelése nincs védve elvesztés vagy sérülés ellen","cartBtnLayoutLabel":"Védett pénztár","cartBtnLayoutTitle":"Biztonságos pénztár","cartBtnLayoutSubtitle":"A rendelése védve van az elvesztéstől vagy sérülés"},"it":{"cartTickerTitle":"Checkout sicuro","cartTickerSubtitleOn":"Il tuo ordine è protetto da smarrimento o danni","cartTickerSubtitleOff":"Il tuo ordine non è protetto da smarrimento o danni","cartBtnLayoutLabel":"Pagamento protetto","cartBtnLayoutTitle":"Checkout sicuro","cartBtnLayoutSubtitle":"Il tuo ordine è protetto da smarrimento o danneggi"},"ja":{"cartTickerTitle":"安全なチェックアウト","cartTickerSubtitleOn":"ご注文は紛失や損傷から保護されています","cartTickerSubtitleOff":"ご注文は紛失や損傷から保護されていません","cartBtnLayoutLabel":"保護されたチェックアウト","cartBtnLayoutTitle":"安全なチェックアウト","cartBtnLayoutSubtitle":"ご注文は紛失や破損から保護されます"},"nb":{"cartTickerTitle":"Sikker betaling","cartTickerSubtitleOn":"Bestillingen din er beskyttet mot tap eller skade","cartTickerSubtitleOff":"Bestillingen din er ikke beskyttet mot tap eller skade","cartBtnLayoutLabel":"Beskyttet betaling","cartBtnLayoutTitle":"Sikker utsjekk","cartBtnLayoutSubtitle":"Bestillingen din er beskyttet mot tap eller skade"},"nl":{"cartTickerTitle":"Veilige checkout","cartTickerSubtitleOn":"Je bestelling is beschermd tegen verlies of schade","cartTickerSubtitleOff":"Je bestelling is niet beschermd tegen verlies of schade","cartBtnLayoutLabel":"Beveiligde betaling","cartBtnLayoutTitle":"Veilig afrekenen","cartBtnLayoutSubtitle":"Uw bestelling is beschermd tegen verlies of bescha"},"pl":{"cartTickerTitle":"Bezpieczna płatność","cartTickerSubtitleOn":"Twoje zamówienie jest chronione przed zagubieniem lub uszkodzeniem","cartTickerSubtitleOff":"Twoje zamówienie nie jest chronione przed zagubieniem lub uszkodzeniem","cartBtnLayoutLabel":" Zabezpieczona płatność","cartBtnLayoutTitle":"Bezpieczne finalizowanie zamówienia","cartBtnLayoutSubtitle":"Twoje zamówienie jest chronione przed zgubieniem l"},"pt-PT":{"cartTickerTitle":"Checkout seguro","cartTickerSubtitleOn":"A sua encomenda está protegida contra perda ou danos","cartTickerSubtitleOff":"A sua encomenda não está protegida contra perda ou danos","cartBtnLayoutLabel":"Checkout Protegido","cartBtnLayoutTitle":"Checkout seguro","cartBtnLayoutSubtitle":"O seu pedido está protegido contra perdas ou danos"},"ro":{"cartTickerTitle":"Finalizare comandă sigură","cartTickerSubtitleOn":"Comanda dvs. este protejată împotriva pierderii sau deteriorării","cartTickerSubtitleOff":"Comanda dvs. nu este protejată împotriva pierderii sau deteriorării","cartBtnLayoutLabel":"Finalizare comandă protejată","cartBtnLayoutTitle":"Plată securizată","cartBtnLayoutSubtitle":"Comanda dvs. este protejată împotriva pierderii sa"},"sv":{"cartTickerTitle":"Säker utcheckning","cartTickerSubtitleOn":"Din beställning är skyddad mot förlust eller skada","cartTickerSubtitleOff":"Din beställning är inte skyddad mot förlust eller skada","cartBtnLayoutLabel":"Skyddad utcheckning","cartBtnLayoutTitle":"Säker utcheckning","cartBtnLayoutSubtitle":"Din beställning är skyddad mot förlust eller skada"},"zh-CN":{"cartTickerSubtitleOn":"您的订单受到丢失或损坏的保护","cartTickerSubtitleOff":"您的订单未受到丢失或损坏的保护","cartBtnLayoutLabel":"安全结账","cartBtnLayoutTitle":"安全结账"}}; const TS = CONFIG.TIMESTAMP; const CLASSES = { ICON: `asc-widget-image-${TS}`, ROOT: `asc-root-${TS}`, SUBTITLE: 'asc-shipping-protection-subtitle-text', SWITCH: 'asc-widget-switch', PRICE: 'asc-shipping-protection-price', }; const DEBOUNCE_MS = 500; const CART_UPDATE_DEBOUNCE_MS = 150; // Avoid /\/cart\// — some theme minifiers treat the trailing // as a comment. const CART_REQUEST_URL_RE = new RegExp('/cart/'); const STOREFRONT_CART_EVENTS = [ 'shopify:cart:lines-update', 'shopify:cart:note-update', 'shopify:cart:discount-update', ]; const SPINNER_HTML = ''; const INJECT_INDEX = { 'resource-electrical.myshopify.com': 0, 'trimove-fi.myshopify.com': 1, 'mvmf3z-uc.myshopify.com': 2, 'mycubecraft.myshopify.com': 3, 'barrett-fragrances-6447.myshopify.com': 3, }; function each(selector, fn) { document.querySelectorAll(selector).forEach(fn); } function debounce(fn, ms) { let timer = null; return (...args) => { if (timer) clearTimeout(timer); timer = setTimeout(() => { timer = null; fn(...args); }, ms); }; } const EMBEDDED_PLANS = [{"id":57592685789557,"title":"0T50","price":199},{"id":57592685822325,"title":"50T100","price":299},{"id":57592684740981,"title":"100T200","price":449},{"id":56499832258933,"title":"200TN","price":699}]; function currencyRate() { const rate = Number(Shopify?.currency?.rate); return Number.isFinite(rate) && rate > 0 ? rate : 1; } function normalizePlanVariant(variant, { fromShopMajorUnits = false } = {}) { const id = variant.id; const title = variant.title || variant.name || ''; let price = Number(variant.price); if (!Number.isFinite(price)) price = 0; if (fromShopMajorUnits) { price = Math.round(price * 100 * currencyRate()); } return { id, title, price }; } async function loadProtectionPlans() { if (EMBEDDED_PLANS?.length) { return EMBEDDED_PLANS.map((v) => normalizePlanVariant(v)); } if (!CONFIG.PRODUCT_HANDLE) { console.warn('ShipCheck: protection product handle is missing'); return null; } const root = String(window.Shopify?.routes?.root || '/').replace(/\/?$/, '/'); const productBase = `${root}products/${encodeURIComponent(CONFIG.PRODUCT_HANDLE)}`; try { const response = await fetch(`${productBase}.js`); if (response.ok) { const product = await response.json(); if (product?.id && CONFIG.PRODUCT_ID == null) CONFIG.PRODUCT_ID = product.id; const variants = product?.variants; if (variants?.length) { return variants.map((v) => normalizePlanVariant(v)); } return null; } if (response.status === 404) { console.warn( 'Shipcheck protection product not found or not active', CONFIG.PRODUCT_HANDLE, ); return null; } } catch {} try { const response = await fetch(`${productBase}.json`); if (!response.ok) return null; const product = (await response.json())?.product; if (product?.id && CONFIG.PRODUCT_ID == null) CONFIG.PRODUCT_ID = product.id; const variants = product?.variants; if (!variants?.length) return null; return variants.map((v) => normalizePlanVariant(v, { fromShopMajorUnits: true })); } catch { return null; } } const plans = await loadProtectionPlans(); if (!plans) return; const TLD_LOCALE_SHOPS = new Set([ 'rg9hrw-4v.myshopify.com', 'acproductsnl.myshopify.com', 'ffepey-pg.myshopify.com', ]); const baseLocale = "en"; const shopLocale = TLD_LOCALE_SHOPS.has(CONFIG.SHOP_PERMANENT_DOMAIN) ? window.location.hostname.split('.').pop() : "nl".split('-')[0]; const ctx = { shopLocale, shopLangLocale: CONFIG.AUTO_TRANSLATE ? shopLocale : baseLocale, baseLocale, plans, moneyWithCurrencyFormat: "€{{amount_with_comma_separator}} EUR", moneyFormat: "€{{amount_with_comma_separator}}", }; const PROTECTION_VARIANT_IDS = new Set(plans.map((p) => String(p.id))); const translation = { userConsent: { titles: {}, subtitles: { on: {}, off: {} }, policy: {} }, checkoutButton: { buttonLabel: {}, title: {}, subtitle: {}, policy: {} }, }; (function hydrateTranslation() { if (!LOCALES_JSON || typeof LOCALES_JSON !== 'object') return; const set = (target, locale, value) => { if (typeof value === 'string') target[locale] = value; }; for (const [locale, strings] of Object.entries(LOCALES_JSON)) { if (!strings || typeof strings !== 'object') continue; set(translation.userConsent.titles, locale, strings.cartTickerTitle); set(translation.userConsent.subtitles.on, locale, strings.cartTickerSubtitleOn); set(translation.userConsent.subtitles.off, locale, strings.cartTickerSubtitleOff); set(translation.userConsent.policy, locale, strings.cartTickerPolicy); set(translation.checkoutButton.buttonLabel, locale, strings.cartBtnLayoutLabel); set(translation.checkoutButton.title, locale, strings.cartBtnLayoutTitle); set(translation.checkoutButton.subtitle, locale, strings.cartBtnLayoutSubtitle); set(translation.checkoutButton.policy, locale, strings.cartBtnLayoutPolicy); } })(); function t(map) { if (!map || typeof map !== 'object') return ''; const value = map[ctx.shopLangLocale] ?? map[ctx.baseLocale] ?? map.en; return typeof value === 'string' ? value : ''; } function formatAmount(amount, template = '') { let locale = ctx.shopLocale; if (CONFIG.SHOP_PERMANENT_DOMAIN === 'avatechwear.myshopify.com' && Shopify.currency.active === 'EUR') { locale = 'de-DE'; } else if (CONFIG.SHOP_PERMANENT_DOMAIN === 'u0x8b1-xu.myshopify.com') { locale = 'en-US'; } const digits = template.includes('amount_no_decimals') ? 0 : 2; return new Intl.NumberFormat(locale, { minimumFractionDigits: digits, maximumFractionDigits: digits, }).format(amount); } function money(amount, template) { const formatted = formatAmount(amount, template); return template.replace(/\{\{(.*?)\}\}/g, () => formatted); } const state = { cart: {"note":null,"attributes":{},"original_total_price":0,"total_price":0,"total_discount":0,"total_weight":0.0,"item_count":0,"items":[],"requires_shipping":false,"currency":"EUR","items_subtotal_price":0,"cart_level_discount_applications":[],"checkout_charge_amount":0}, consent: CONFIG.EXTENSION_DISABLED ? false : CONFIG.AUTO_ACCEPT, }; let selectedPlan = null; let hasProtectionInCart = false; let checkoutBusy = false; let interceptBusy = false; let interceptorBound = false; let addInProgress = false; let desiredVariantId = null; let addStartedAt = 0; let usingStorefrontEvents = false; let mounting = false; let mountRetryTimer = null; const extensionRoots = new Set(); const detachedRoots = []; const isEnabled = () => !CONFIG.EXTENSION_DISABLED; const isCheckoutLayout = () => CONFIG.EXTENSION_LAYOUT === 'checkout_button'; const cartItems = () => state.cart?.items || []; const EXCLUDED_PRODUCT_IDS = [7648135217198]; function numericId(value) { if (value == null || value === '') return null; if (typeof value === 'number' && Number.isFinite(value)) return value; const match = String(value).match(/(\d+)\s*$/); return match ? Number(match[1]) : null; } function isProtectionItem(item) { if (!item) return false; const productId = numericId(item.product_id); if (CONFIG.PRODUCT_ID != null && productId === Number(CONFIG.PRODUCT_ID)) return true; const variantId = numericId(item.variant_id) ?? numericId(item.id); if (variantId != null && PROTECTION_VARIANT_IDS.has(String(variantId))) return true; if (desiredVariantId && variantId != null && String(variantId) === desiredVariantId) return true; if (item.handle && item.handle === CONFIG.PRODUCT_HANDLE) return true; const props = item.properties; return Boolean(props && (props._shipcheck || props['_shipcheck'])); } function isExcludedProduct(item) { const productId = numericId(item?.product_id); return productId != null && EXCLUDED_PRODUCT_IDS.includes(productId); } function findProtectionLine() { return cartItems().find(isProtectionItem) || null; } function syncProtectionFlag() { hasProtectionInCart = cartItems().some(isProtectionItem); } function merchandiseTotals() { let physical = 0; let merchandise = 0; for (const item of cartItems()) { if (isProtectionItem(item) || isExcludedProduct(item)) continue; const line = Number(item.final_line_price) / 100; if (!Number.isFinite(line)) continue; merchandise += line; if (item.requires_shipping) physical += line; } return { merchandise, physical }; } function planByVariantId(variantId) { const id = String(numericId(variantId) ?? ''); if (!id) return null; return ctx.plans.find((plan) => String(plan.id) === id) || null; } function selectPlan() { if (addInProgress && selectedPlan) return; const line = findProtectionLine(); if (line) { const locked = planByVariantId(line.variant_id ?? line.id); if (locked) { selectedPlan = locked; return; } if (selectedPlan) return; } const { merchandise, physical } = merchandiseTotals(); const total = (CONFIG.EXCLUDE_DIGITAL_PRODUCTS ? physical : merchandise) / currencyRate(); selectedPlan = null; let fallback = null; for (const variant of ctx.plans) { const range = String(variant.title || '').split(' ').pop() || ''; const parts = range.split('T'); if (parts.length !== 2) continue; const min = parseFloat(parts[0]); const max = parts[1] === 'N' ? Infinity : parseFloat(parts[1]); if (!Number.isFinite(min)) continue; if (parts[1] === 'N') fallback = variant; if (total >= min && total <= max) { selectedPlan = variant; break; } } if (!selectedPlan) selectedPlan = fallback; } function protectionPrice() { if (!selectedPlan) return 0; const cents = Number(selectedPlan.price); if (!Number.isFinite(cents)) return 0; return cents / 100; } function protectionDisplayAmount() { const raw = money(protectionPrice(), ctx.moneyFormat); const tmp = document.createElement('span'); tmp.innerHTML = raw; return (tmp.textContent || raw).trim(); } const DISPLAY_RULES = [ { type: 'country_restriction', shop: 'pet-tags-com-au.myshopify.com', evaluate: () => ['NZ'].includes(CONFIG.CURRENT_COUNTRY), }, { type: 'customer_tag_restriction', shop: 'threeworlds.myshopify.com', evaluate: () => { const excluded = ['wholesale', 'jasonure']; return Array.isArray(CUSTOMER_TAGS) && excluded.some((tag) => CUSTOMER_TAGS.includes(tag)); }, }, { type: 'product_exclusion', global: true, evaluate: () => { const merchandise = cartItems().filter((item) => !isProtectionItem(item)); return merchandise.length > 0 && merchandise.every(isExcludedProduct); }, }, { type: 'product_inclusion', shop: 'art-fever.myshopify.com', evaluate: () => { const allowed = ['Photo Roller Blinds', 'Google Blinds', 'Shopping 2025 Best Sellers']; return !cartItems().some((item) => allowed.includes(item.product_type)); }, }, { type: 'config_flag', global: true, evaluate: () => CONFIG.DISABLE_FOR_DIGITAL_PRODUCTS && !state.cart.requires_shipping, }, ]; function shouldHideExtension() { return DISPLAY_RULES.some((rule) => { if (!rule.global && rule.shop && rule.shop !== CONFIG.SHOP_PERMANENT_DOMAIN) return false; return rule.evaluate(); }); } function canAddProtection() { return ( isEnabled() && !hasProtectionInCart && !shouldHideExtension() && cartItems().length > 0 && selectedPlan ); } function shouldInflateCartTotal() { return state.consent && !hasProtectionInCart && !addInProgress; } function cartTotalText() { if (!selectedPlan) return null; const protection = protectionPrice(); const inflate = shouldInflateCartTotal(); if (window.SLIDECART_STATE) { const base = SLIDECART_STATE().cart.total_price / 100; return money( inflate ? base + protection : base, window.SLIDECART_FORMAT || ctx.moneyFormat, ); } if (inflate) { return money(merchandiseTotals().merchandise + protection, ctx.moneyWithCurrencyFormat); } return money((state.cart?.total_price || 0) / 100, ctx.moneyWithCurrencyFormat); } function writeCartTotal() { if (!CONFIG.CART_TOTAL_SELECTOR || isCheckoutLayout()) return; if (hasProtectionInCart) return; const text = cartTotalText(); if (!text) return; each(`${CONFIG.CART_TOTAL_SELECTOR}, .cart__total-value`, (target) => { const match = target.innerText.match(/^(.*?)\s*•\s*.+$/); const next = match ? `${match[1]} • ${text}` : text; if (target.innerHTML !== next) target.innerHTML = next; }); } function moneyToCents(value) { if (value == null) return 0; if (typeof value === 'number') return Math.round(value * 100); const amount = typeof value === 'object' ? value.amount : value; return Math.round(parseFloat(amount || 0) * 100); } function normalizeCart(cart) { if (!cart) return null; if (Array.isArray(cart.items)) return cart; const rawLines = Array.isArray(cart.lines) ? cart.lines : cart.lines?.nodes; if (!Array.isArray(rawLines)) return null; const items = rawLines.map((line) => { const merchandise = line.merchandise || {}; const product = merchandise.product || {}; const variantId = numericId(merchandise.id ?? line.merchandiseId); return { id: numericId(line.id) ?? variantId, variant_id: variantId, key: line.id != null ? String(line.id) : undefined, quantity: line.quantity ?? 0, final_line_price: moneyToCents(line.cost?.totalAmount ?? line.cost?.amount), requires_shipping: merchandise.requiresShipping ?? product.requiresShipping ?? line.requiresShipping ?? true, product_type: product.productType || product.type || line.productType || '', handle: product.handle || line.handle || '', product_id: numericId(product.id), }; }); return { items, item_count: cart.totalQuantity ?? items.reduce((n, i) => n + i.quantity, 0), total_price: moneyToCents(cart.cost?.totalAmount ?? cart.cost?.amount), requires_shipping: items.some((item) => item.requires_shipping), }; } async function mutateCart(action, variantId) { try { const isAdd = action === 'add'; const response = await fetch( isAdd ? '/cart/add.js?shipcheck' : '/cart/change.js?shipcheck', { method: 'POST', headers: { 'Content-Type': 'application/json' }, body: JSON.stringify( isAdd ? { items: [{ id: String(variantId), quantity: 1, properties: { _shipcheck: 'true', _attributed_extension: 'cart', }, }], } : { id: String(variantId), quantity: 0 }, ), }, ); return response.ok; } catch { return false; } } async function fetchAjaxCart() { try { return await (await fetch('/cart.js?shipcheck')).json(); } catch { return null; } } function sync(cart) { if (cart) { const normalized = normalizeCart(cart); if (!normalized) return; state.cart = normalized; } syncProtectionFlag(); selectPlan(); ensureMounted(); paint(); } let cartRefreshInFlight = false; let cartRefreshQueued = false; async function refreshCart() { if (cartRefreshInFlight) { cartRefreshQueued = true; return; } cartRefreshInFlight = true; try { do { cartRefreshQueued = false; const cart = await fetchAjaxCart(); if (cart) { sync(cart); if (addInProgress) await reconcileProtectionLine(); } } while (cartRefreshQueued); } finally { cartRefreshInFlight = false; } } const scheduleCartRefresh = debounce(refreshCart, CART_UPDATE_DEBOUNCE_MS); async function reconcileProtectionLine() { const line = findProtectionLine(); if (line) { const lineId = String(line.variant_id || line.id || ''); if (desiredVariantId && lineId !== desiredVariantId) { await mutateCart('remove', lineId); await mutateCart('add', desiredVariantId); cartRefreshQueued = true; } return; } if (Date.now() - addStartedAt > 2000) endAddWindow(); } function beginAddWindow(variantId) { addInProgress = true; desiredVariantId = String(variantId); addStartedAt = Date.now(); localStorage.setItem(STORAGE_KEY_PROTECTION_ID, String(variantId)); localStorage.removeItem(STORAGE_KEY_PROTECTION_ID_LEGACY); } function endAddWindow() { addInProgress = false; desiredVariantId = null; } function onStorefrontCartEvent(event) { usingStorefrontEvents = true; stopCartRequestObserver(); const run = event.promise ? event.promise.then(() => refreshCart()).catch(() => scheduleCartRefresh()) : refreshCart(); Promise.resolve(run).catch(() => scheduleCartRefresh()); } function subscribeStorefrontCartEvents() { for (const name of STOREFRONT_CART_EVENTS) { document.addEventListener(name, onStorefrontCartEvent); } } let cartRequestObserver = null; function startCartRequestObserver() { if (cartRequestObserver || usingStorefrontEvents) return; if (typeof PerformanceObserver === 'undefined') return; cartRequestObserver = new PerformanceObserver((list) => { if (usingStorefrontEvents) return; for (const entry of list.getEntries()) { if (!['xmlhttprequest', 'fetch'].includes(entry.initiatorType)) continue; if (entry.duration <= 0) continue; if (!CART_REQUEST_URL_RE.test(entry.name)) continue; if (entry.name.includes('shipcheck')) continue; scheduleCartRefresh(); } }); cartRequestObserver.observe({ entryTypes: ['resource'] }); } function stopCartRequestObserver() { if (!cartRequestObserver) return; cartRequestObserver.disconnect(); cartRequestObserver = null; } function paintPrice() { const amount = protectionDisplayAmount(); each(`.${CLASSES.PRICE}`, (el) => { el.setAttribute('translate', 'no'); el.textContent = amount; }); } function paintConsent() { const html = (state.consent ? t(translation.userConsent.subtitles.on) : t(translation.userConsent.subtitles.off) ).trim(); each(`.${CLASSES.SUBTITLE}`, (node) => { node.innerHTML = html; }); each(`.${CLASSES.SWITCH}`, (el) => { el.setAttribute('aria-checked', state.consent ? 'true' : 'false'); }); if (CONFIG.IMAGE_GRAYSCALE) { const value = state.consent ? '0' : '1'; each(`.${CLASSES.ICON}`, (icon) => { icon.style.filter = `grayscale(${value})`; }); } } function paint() { paintPrice(); const hide = shouldHideExtension(); for (const root of extensionRoots) { root.style.display = hide ? 'none' : ''; } if (state.consent && (!isEnabled() || (hide && !CONFIG.AUTO_ACCEPT))) { setConsent(false); return; } writeCartTotal(); } function setConsent(consent) { const next = isEnabled() ? Boolean(consent) : false; if (state.consent === next) return; state.consent = next; paintConsent(); if (isCheckoutLayout()) return; writeCartTotal(); requestAnimationFrame(writeCartTotal); } function onConsentSwitchClick(event) { event?.preventDefault?.(); event?.stopPropagation?.(); if (!isEnabled()) return; setConsent(!state.consent); } function checkoutButtonMarkup() { return `

${t(translation.checkoutButton.title)}

${t(translation.checkoutButton.subtitle)}

`; } function userConsentMarkup() { const subtitle = (state.consent ? t(translation.userConsent.subtitles.on) : t(translation.userConsent.subtitles.off) ).trim(); return `

${t(translation.userConsent.titles)}

${subtitle}

`; } function extensionMarkup() { return isCheckoutLayout() ? checkoutButtonMarkup() : userConsentMarkup(); } const ROOT_SELECTOR = `shipcheck-cart-widget[data-shipcheck-extension="true"], .${CLASSES.ROOT}[data-shipcheck-extension="true"]`; const MOUNT_SELECTOR = CHECKOUT_BUTTONS ; const MOUNT_RETRY_MS = 200; const BOOT_RETRY_MS = [100, 400, 1200]; function isRootIntact(root) { return Boolean( root?.querySelector?.(`.${CLASSES.SWITCH}, .${CLASSES.PRICE}, .asc-checkout-button-${TS}`), ); } function pruneRoots() { for (const root of [...extensionRoots]) { if (document.documentElement.contains(root)) { if (isRootIntact(root)) continue; extensionRoots.delete(root); root.remove(); continue; } extensionRoots.delete(root); if (isRootIntact(root) && !detachedRoots.includes(root)) detachedRoots.push(root); } while (detachedRoots.length > 4) detachedRoots.shift(); } function stashRoot(root) { if (!root) return; if (document.documentElement.contains(root)) { extensionRoots.add(root); return; } extensionRoots.delete(root); if (isRootIntact(root) && !detachedRoots.includes(root)) detachedRoots.push(root); while (detachedRoots.length > 4) detachedRoots.shift(); } function takeStashedRoot() { while (detachedRoots.length) { const root = detachedRoots.pop(); if (root && !document.documentElement.contains(root) && isRootIntact(root)) { root.querySelectorAll(`.${CLASSES.PRICE}`).forEach((el) => { el.textContent = ''; }); return root; } } return null; } function findRootIn(container) { if (!container) return null; for (const root of extensionRoots) { if (!container.contains(root)) continue; if (isRootIntact(root)) return root; extensionRoots.delete(root); root.remove(); } const found = container.querySelector?.(ROOT_SELECTOR); if (!found) return null; if (!isRootIntact(found)) { found.remove(); return null; } extensionRoots.add(found); const idx = detachedRoots.indexOf(found); if (idx >= 0) detachedRoots.splice(idx, 1); return found; } function createRoot(hide) { const root = document.createElement('shipcheck-cart-widget'); root.className = CLASSES.ROOT; root.setAttribute('data-shipcheck-extension', 'true'); root.innerHTML = extensionMarkup(); root.style.display = hide ? 'none' : ''; extensionRoots.add(root); return root; } function insertAt(parent, node, index) { const ref = parent.children[index]; if (ref) parent.insertBefore(node, ref); else parent.appendChild(node); } function mountRoot(parent, beforeNode, index) { const hide = shouldHideExtension(); const existing = findRootIn(parent); if (existing) { existing.style.display = hide ? 'none' : ''; return false; } const reused = takeStashedRoot(); const root = reused || createRoot(hide); if (reused) { root.style.display = hide ? 'none' : ''; extensionRoots.add(root); } if (index !== undefined) insertAt(parent, root, index); else if (beforeNode && beforeNode.parentNode === parent) parent.insertBefore(root, beforeNode); else parent.prepend(root); return true; } function injectExtension() { pruneRoots(); const shop = CONFIG.SHOP_PERMANENT_DOMAIN; const index = INJECT_INDEX[shop]; let changed = false; const seenParents = new Set(); for (const button of document.querySelectorAll(CHECKOUT_BUTTONS)) { const parent = button.parentNode; const grandparent = parent?.parentNode; if (!parent || !grandparent || seenParents.has(grandparent)) continue; seenParents.add(grandparent); const did = index !== undefined ? mountRoot(grandparent, null, index) : mountRoot(grandparent, parent); if (did) changed = true; } return changed; } function needsMount() { pruneRoots(); for (const root of extensionRoots) { if (!document.documentElement.contains(root) || !isRootIntact(root)) return true; } const seenParents = new Set(); for (const button of document.querySelectorAll(CHECKOUT_BUTTONS)) { const parent = button.parentNode; const grandparent = parent?.parentNode; if (!parent || !grandparent || seenParents.has(grandparent)) continue; seenParents.add(grandparent); if (!findRootIn(grandparent)) return true; } return false; } function ensureMounted() { if (mounting) return false; mounting = true; let changed = false; try { changed = injectExtension(); attachCheckoutInterceptor(); if (changed) paintConsent(); } finally { mounting = false; } return changed; } function remountAndPaint() { ensureMounted(); paint(); if (needsMount()) scheduleMountRetry(); } function scheduleMountRetry() { if (mountRetryTimer) return; mountRetryTimer = setTimeout(() => { mountRetryTimer = null; if (!needsMount()) return; remountAndPaint(); }, MOUNT_RETRY_MS); } function captureElementState(el) { const tag = el.tagName.toLowerCase(); const rect = el.getBoundingClientRect(); return { tag, content: tag === 'input' ? el.value : el.innerHTML, disabled: el.disabled, height: rect.height, width: rect.width, }; } function setButtonLoading(el, loading, original) { if (loading) { if (el.hasAttribute('data-asc-loading')) return; el.setAttribute('data-asc-loading', JSON.stringify({ content: original.tag === 'input' ? el.value : el.innerHTML, styles: { position: el.style.position, height: el.style.height, width: el.style.width, minHeight: el.style.minHeight, minWidth: el.style.minWidth, pointerEvents: el.style.pointerEvents, color: el.style.color, }, })); const computed = window.getComputedStyle(el).position; el.style.position = computed === 'static' || !computed ? 'relative' : computed; el.style.height = `${original.height}px`; el.style.width = `${original.width}px`; el.disabled = true; if (original.tag === 'input') { el.value = ''; el.style.color = 'transparent'; el.insertAdjacentHTML( 'afterend', `${SPINNER_HTML}`, ); } else { el.style.pointerEvents = 'none'; el.innerHTML = `${original.content}${SPINNER_HTML}`; } return; } const raw = el.getAttribute('data-asc-loading'); if (!raw) return; const saved = JSON.parse(raw); if (original.tag === 'input') { el.value = saved.content; const spinner = el.nextElementSibling; if (spinner?.classList.contains('asc-spinner-wrapper')) spinner.remove(); } else { el.innerHTML = saved.content; } el.disabled = original.disabled; Object.assign(el.style, saved.styles); el.removeAttribute('data-asc-loading'); } function checkoutUrl(el) { if (el?.tagName === 'A' && el.href) return el.href; const dataHref = el?.getAttribute?.('data-href'); if (dataHref) { try { return new URL(dataHref, location.origin).href; } catch { return dataHref; } } const root = window.Shopify?.routes?.root || '/'; return root.endsWith('/') ? `${root}checkout` : `${root}/checkout`; } async function addProtectionIfNeeded() { if (!canAddProtection()) return false; beginAddWindow(selectedPlan.id); try { let ok = await mutateCart('add', selectedPlan.id); if (!ok) ok = await mutateCart('add', selectedPlan.id); if (ok) hasProtectionInCart = true; return ok; } catch { endAddWindow(); return false; } } async function onCheckoutExtensionClick(event) { if (!isEnabled() || checkoutBusy) return; event.preventDefault(); const button = event.target?.closest?.('button, a, input') || event.target; if (!button) return; const original = captureElementState(button); button.disabled = true; checkoutBusy = true; setButtonLoading(button, true, original); try { if (isCheckoutLayout()) await addProtectionIfNeeded(); } finally { setButtonLoading(button, false, original); checkoutBusy = false; setTimeout(endAddWindow, DEBOUNCE_MS); window.location.href = checkoutUrl(button); } } function attachCheckoutInterceptor() { if (!isEnabled() || interceptorBound) return; interceptorBound = true; let resumeClick = false; document.addEventListener('click', async (event) => { if (resumeClick) return; if (interceptBusy) return; if (!state.consent || !canAddProtection() || isCheckoutLayout()) return; let control; try { control = event.target?.closest?.(CHECKOUT_BUTTONS); } catch { return; } if (!control) return; if (control.closest(`.asc-checkout-button-${TS}`)) return; event.preventDefault(); event.stopImmediatePropagation(); interceptBusy = true; beginAddWindow(selectedPlan.id); const original = captureElementState(control); setButtonLoading(control, true, original); let ok = await mutateCart('add', selectedPlan.id); if (!ok) ok = await mutateCart('add', selectedPlan.id); if (ok) hasProtectionInCart = true; setButtonLoading(control, false, original); interceptBusy = false; setTimeout(endAddWindow, DEBOUNCE_MS); const anchorHref = control.tagName === 'A' ? control.href : ''; resumeClick = true; try { control.click(); if (anchorHref) window.location.href = anchorHref; } finally { resumeClick = false; } }, true); } function mutationRemovedExtension(mutation) { for (const node of mutation.removedNodes) { if (node.nodeType !== 1) continue; try { if (node.matches?.(ROOT_SELECTOR)) { stashRoot(node); continue; } const nested = node.querySelector?.(ROOT_SELECTOR); if (nested) stashRoot(nested); } catch { } for (const root of [...extensionRoots]) { if (node === root || node.contains?.(root)) stashRoot(root); } } } function mutationAddedMountTarget(mutation) { if (!MOUNT_SELECTOR) return false; for (const node of mutation.addedNodes) { if (node.nodeType !== 1) continue; try { if (node.matches?.(MOUNT_SELECTOR) || node.querySelector?.(MOUNT_SELECTOR)) return true; } catch { } } return false; } function onThemeCartUpdate() { queueMicrotask(() => { if (needsMount()) remountAndPaint(); else paint(); scheduleCartRefresh(); }); } const mutationObserver = new MutationObserver((mutations) => { if (mounting) return; let addedTarget = false; for (const mutation of mutations) { if (mutation.type !== 'childList') continue; mutationRemovedExtension(mutation); let insideOurs = false; for (const root of extensionRoots) { if (root === mutation.target || root.contains?.(mutation.target)) { insideOurs = true; break; } } if (insideOurs) continue; if (mutationAddedMountTarget(mutation)) addedTarget = true; } if (!needsMount()) { if (addedTarget) attachCheckoutInterceptor(); return; } remountAndPaint(); }); window.shipcheck = { ...window.shipcheck, update_consent_1790215566: onConsentSwitchClick, on_checkout_click_1790215566: onCheckoutExtensionClick, reset_protection_fn: resetProtectionAfterCheckoutReturn, }; syncProtectionFlag(); selectPlan(); ensureMounted(); paintConsent(); paint(); mutationObserver.observe(document.body, { childList: true, subtree: true }); BOOT_RETRY_MS.forEach((ms) => { setTimeout(() => { if (needsMount()) remountAndPaint(); }, ms); }); document.addEventListener('cart:update', onThemeCartUpdate); subscribeStorefrontCartEvents(); startCartRequestObserver(); })(); })();