Marazzi Castellina Stone Mosaic 13" X 17" Elongated Hexagon Gray And Fawn
Marazzi Castellina Stone Mosaic 13" X 17" Elongated Hexagon White And Midnight Gray

Marazzi Castellina Stone Mosaic 13" X 17" Elongated Hexagon

Marazzi

Save $3.88
$17.6 /Sq Ft $ 21.48
SKU: CT69 ELONHEXMS1P
Limited-Time Offers, End in: 151D 8H 23M 0S

Specification

Surface Type:Honed

Coverage Area (sq ft/box):--

Length:--

Width:--

Height:--

Application:
Wall and Floor
Composition:
Natural Stone
Design:
Mosaic Tile

Color Shade:

Pcs Per box:--

Price UOM:--

Usage:
  • Residential and Commercial
Order Summary
$0.00
Color: Gray And Fawn
// ============================================ // ISOLATED COVERAGE CALCULATOR // Badge positioned above Add to Cart button // ============================================ (function() { 'use strict'; // Create unique namespace window.ShopifyCoverageCalculator = window.ShopifyCoverageCalculator || {}; // Private variables - completely isolated const PRIVATE = { initialized: false, currentVariant: null, currentQuantity: 1, productData: null, coverageData: null, elements: {}, addToCartButton: null, config: { debug: false, coveragePerBox: 10.64, defaultPrice: 0 } }; // ===== HELPER FUNCTIONS ===== const Helpers = { // Safe console log log: function() { if (PRIVATE.config.debug && console && console.log) { console.log('[CoverageCalc]', ...arguments); } }, // Parse price from text parsePrice: function(priceText) { if (!priceText || typeof priceText !== 'string') return 0; const match = priceText.match(/[\d,]+(?:\.\d{2})?/); if (match) { return parseFloat(match[0].replace(/,/g, '')); } return 0; }, // Format price formatPrice: function(price) { return price.toFixed(2); }, // Safe event handler wrapper safeHandler: function(fn) { return function() { try { return fn.apply(this, arguments); } catch (e) { Helpers.log('Error in handler:', e); } }; }, // Find Add to Cart button findAddToCartButton: function() { // Common selectors for Add to Cart button const selectors = [ 'button[type="submit"][name="add"]', 'button[name="add"]', '.product-form__submit', '.shopify-payment-button__button', 'button.add-to-cart', '.add-to-cart-button', 'form[action*="/cart/add"] button[type="submit"]', '#add-to-cart-button', '.product-form__add-to-cart' ]; for (let selector of selectors) { const button = document.querySelector(selector); if (button && button.offsetParent !== null) { // Check if visible return button; } } // Fallback: try to find any submit button in cart form const cartForm = document.querySelector('form[action*="/cart/add"]'); if (cartForm) { const submitButton = cartForm.querySelector('button[type="submit"], input[type="submit"]'); if (submitButton) return submitButton; } return null; }, // Insert badge above Add to Cart button insertBadgeAboveAddToCart: function(badgeElement) { const addToCartBtn = PRIVATE.addToCartButton || Helpers.findAddToCartButton(); if (addToCartBtn && addToCartBtn.parentNode) { // Insert badge before the Add to Cart button addToCartBtn.parentNode.insertBefore(badgeElement, addToCartBtn); Helpers.log('Badge inserted above Add to Cart button'); return true; } // Fallback: try to find product form const productForm = document.querySelector('form[action*="/cart/add"]'); if (productForm) { productForm.insertBefore(badgeElement, productForm.firstChild); Helpers.log('Badge inserted at top of product form'); return true; } // Last resort: append to price info const priceInfo = document.querySelector('.price-info'); if (priceInfo) { priceInfo.appendChild(badgeElement); Helpers.log('Badge appended to price-info'); return true; } return false; } }; // ===== DOM ELEMENT CACHE ===== const Elements = { cache: function() { PRIVATE.elements = { area: document.getElementById('area'), boxQty: document.getElementById('box-quantity'), totalPrice: document.getElementById('total-price'), dynamicTotal: document.querySelector('.total-price-dynamic'), pricePerBox: document.getElementById('productPrice'), pricePerSqFt: document.getElementById('discountedProductPrice'), savedAmount: document.querySelector('.saved_amount span'), coverageCalculator: document.querySelector('.coverage-calculator'), productPriceText: document.getElementById('product-price') }; // Find Add to Cart button PRIVATE.addToCartButton = Helpers.findAddToCartButton(); Helpers.log('Elements cached:', Object.keys(PRIVATE.elements).filter(k => PRIVATE.elements[k])); Helpers.log('Add to Cart button found:', !!PRIVATE.addToCartButton); }, update: function(selector, content) { const element = PRIVATE.elements[selector]; if (element && element !== document) { element.textContent = content; return true; } return false; }, html: function(selector, html) { const element = PRIVATE.elements[selector]; if (element) { element.innerHTML = html; return true; } return false; }, val: function(selector, value) { const element = PRIVATE.elements[selector]; if (element) { if (value !== undefined) { element.value = value; return true; } return element.value; } return null; } }; // ===== DATA MANAGEMENT ===== const DataManager = { loadProductData: function() { try { const productJson = document.getElementById('ProductJson'); const coverageJson = document.getElementById('VariantCoverageData'); if (productJson && productJson.textContent) { PRIVATE.productData = JSON.parse(productJson.textContent); Helpers.log('Product data loaded:', PRIVATE.productData.title); } if (coverageJson && coverageJson.textContent) { PRIVATE.coverageData = JSON.parse(coverageJson.textContent); Helpers.log('Coverage data loaded'); } if (PRIVATE.productData && PRIVATE.productData.variants && PRIVATE.productData.variants.length > 0) { PRIVATE.currentVariant = PRIVATE.productData.variants[0]; PRIVATE.config.defaultPrice = PRIVATE.currentVariant.price / 100; // Update coverage per box from data attribute if (PRIVATE.elements.coverageCalculator && PRIVATE.elements.coverageCalculator.dataset.coveragePerBox) { PRIVATE.config.coveragePerBox = parseFloat(PRIVATE.elements.coverageCalculator.dataset.coveragePerBox); } // Or get from coverage data if (PRIVATE.coverageData && PRIVATE.coverageData[PRIVATE.currentVariant.id]) { PRIVATE.config.coveragePerBox = parseFloat(PRIVATE.coverageData[PRIVATE.currentVariant.id]); if (PRIVATE.elements.coverageCalculator) { PRIVATE.elements.coverageCalculator.dataset.coveragePerBox = PRIVATE.config.coveragePerBox; } } return true; } } catch (e) { Helpers.log('Error loading product data:', e); } return false; }, getCurrentPrice: function() { if (PRIVATE.currentVariant && PRIVATE.currentVariant.price) { return PRIVATE.currentVariant.price / 100; } // Try to get from DOM if (PRIVATE.elements.pricePerBox) { return Helpers.parsePrice(PRIVATE.elements.pricePerBox.textContent); } return PRIVATE.config.defaultPrice; }, getCoveragePerBox: function() { if (PRIVATE.elements.coverageCalculator && PRIVATE.elements.coverageCalculator.dataset.coveragePerBox) { return parseFloat(PRIVATE.elements.coverageCalculator.dataset.coveragePerBox); } return PRIVATE.config.coveragePerBox; }, getQuantity: function() { const qty = Elements.val('boxQty'); if (qty && !isNaN(qty)) { return parseInt(qty) > 0 ? parseInt(qty) : 1; } return 1; } }; // ===== CALCULATION ENGINE ===== const Calculator = { // Calculate boxes from area calculateBoxesFromArea: function(area) { const coverage = DataManager.getCoveragePerBox(); if (coverage <= 0) return 1; return Math.ceil(area / coverage); }, // Calculate area from boxes calculateAreaFromBoxes: function(boxes) { const coverage = DataManager.getCoveragePerBox(); return boxes * coverage; }, // Calculate total price calculateTotal: function(price, quantity) { return price * quantity; }, // Main calculation and update update: function() { try { const price = DataManager.getCurrentPrice(); const quantity = DataManager.getQuantity(); const total = this.calculateTotal(price, quantity); const coverage = DataManager.getCoveragePerBox(); const area = this.calculateAreaFromBoxes(quantity); // Update all displays this.updateDisplays(price, quantity, total, area, coverage); // Update dynamic badge this.updateDynamicBadge(price, quantity, total, area); Helpers.log('Calculation updated:', { price, quantity, total, area }); return total; } catch (e) { Helpers.log('Error in calculation:', e); return 0; } }, updateDisplays: function(price, quantity, total, area, coverage) { // Update total price span Elements.update('totalPrice', Helpers.formatPrice(total)); // Update dynamic total div if (PRIVATE.elements.dynamicTotal) { PRIVATE.elements.dynamicTotal.innerHTML = `Total Price: $${Helpers.formatPrice(total)}`; } // Update area input if needed const currentArea = parseFloat(Elements.val('area')); if (isNaN(currentArea) || Math.abs(currentArea - area) > 0.01) { Elements.val('area', area.toFixed(2)); } // Update price per sq ft const pricePerSqFt = price / coverage; Elements.update('pricePerSqFt', `$${Helpers.formatPrice(pricePerSqFt)}`); }, updateDynamicBadge: function(price, quantity, total, area) { let badge = document.getElementById('coverage-calc-badge'); let badgeContainer = document.getElementById('coverage-calc-badge-container'); // Create container if it doesn't exist if (!badgeContainer) { badgeContainer = document.createElement('div'); badgeContainer.id = 'coverage-calc-badge-container'; badgeContainer.style.marginBottom = '15px'; // Insert above Add to Cart button Helpers.insertBadgeAboveAddToCart(badgeContainer); } // Create or update badge if (!badge) { badge = document.createElement('div'); badge.id = 'coverage-calc-badge'; badgeContainer.appendChild(badge); } // const pricePerSqFt = price / DataManager.getCoveragePerBox(); badge.innerHTML = `
Order Summary
$${Helpers.formatPrice(total)}
${quantity} box${quantity !== 1 ? 'es' : ''} × $${Helpers.formatPrice(price)}/box
Covers ${Helpers.formatPrice(area)} sq ft @ $${Helpers.formatPrice(pricing.unit_price)}/sq ft
`; // Add animation styles if not already added if (!document.getElementById('coverage-calc-styles')) { const styles = document.createElement('style'); styles.id = 'coverage-calc-styles'; styles.textContent = ` @keyframes fadeIn { from { opacity: 0; transform: translateY(-10px); } to { opacity: 1; transform: translateY(0); } } `; document.head.appendChild(styles); } }, // Update from area input updateFromArea: function(areaValue) { if (!areaValue || areaValue <= 0) { Elements.val('boxQty', '1'); this.update(); return; } const boxes = this.calculateBoxesFromArea(areaValue); const actualArea = this.calculateAreaFromBoxes(boxes); // Update box quantity Elements.val('boxQty', boxes.toString()); // Update area to actual coverage if (Math.abs(areaValue - actualArea) > 0.01) { Elements.val('area', actualArea.toFixed(2)); } this.update(); }, // Update from box quantity updateFromBoxes: function(boxesValue) { if (!boxesValue || boxesValue <= 0) { Elements.val('boxQty', '1'); boxesValue = 1; } const area = this.calculateAreaFromBoxes(boxesValue); Elements.val('area', area.toFixed(2)); this.update(); } }; // ===== VARIANT MANAGEMENT ===== const VariantManager = { updateVariant: function(variantId) { if (!PRIVATE.productData || !PRIVATE.productData.variants) return false; const variant = PRIVATE.productData.variants.find(v => v.id == variantId); if (!variant) return false; PRIVATE.currentVariant = variant; // Update coverage per box if (PRIVATE.coverageData && PRIVATE.coverageData[variantId]) { PRIVATE.config.coveragePerBox = parseFloat(PRIVATE.coverageData[variantId]); if (PRIVATE.elements.coverageCalculator) { PRIVATE.elements.coverageCalculator.dataset.coveragePerBox = PRIVATE.config.coveragePerBox; } } // Update price displays const pricePerBox = variant.price / 100; const coverage = DataManager.getCoveragePerBox(); const pricePerSqFt = pricing.unit_price; Elements.update('pricePerBox', `$${Helpers.formatPrice(pricePerBox)}`); Elements.update('pricePerSqFt', `$${Helpers.formatPrice(pricePerSqFt)}`); // Update savings if (variant.compare_at_price) { const comparePrice = variant.compare_at_price / 100; const save = comparePrice - pricePerBox; if (save > 0 && PRIVATE.elements.savedAmount) { PRIVATE.elements.savedAmount.textContent = `Save $${Helpers.formatPrice(save)} per box`; } } // Reset calculator Elements.val('boxQty', '1'); Elements.val('area', coverage.toFixed(2)); Calculator.update(); Helpers.log('Variant updated:', variant.title, pricePerBox); return true; }, getSelectedVariant: function() { // Try to get from form const variantInput = document.querySelector('input[name="id"]:checked, select[name="id"]'); if (variantInput && variantInput.value) { const variant = PRIVATE.productData?.variants?.find(v => v.id == variantInput.value); if (variant) return variant; } // Try from swatches const selectedSwatch = document.querySelector('.swatch input[type="radio"]:checked, .product-form__input input[type="radio"]:checked'); if (selectedSwatch) { const variantId = selectedSwatch.dataset.variantId || selectedSwatch.value; const variant = PRIVATE.productData?.variants?.find(v => v.id == variantId); if (variant) return variant; } return PRIVATE.currentVariant; } }; // ===== EVENT HANDLERS ===== const EventManager = { setup: function() { // Area input events const areaEl = PRIVATE.elements.area; if (areaEl) { areaEl.addEventListener('input', Helpers.safeHandler(function(e) { const value = parseFloat(e.target.value); if (!isNaN(value) && value > 0) { Calculator.updateFromArea(value); } })); areaEl.addEventListener('blur', Helpers.safeHandler(function(e) { let value = parseFloat(e.target.value); if (isNaN(value) || value <= 0) { value = DataManager.getCoveragePerBox(); Elements.val('area', value.toFixed(2)); } Calculator.updateFromArea(value); })); } // Box quantity events const boxEl = PRIVATE.elements.boxQty; if (boxEl) { boxEl.addEventListener('input', Helpers.safeHandler(function(e) { const value = parseInt(e.target.value); if (!isNaN(value) && value > 0) { Calculator.updateFromBoxes(value); } })); boxEl.addEventListener('blur', Helpers.safeHandler(function(e) { let value = parseInt(e.target.value); if (isNaN(value) || value <= 0) { value = 1; Elements.val('boxQty', '1'); } Calculator.updateFromBoxes(value); })); } // Listen to variant changes this.setupVariantListeners(); // Watch for DOM changes to reposition badge if needed this.watchForAddToCartButton(); }, setupVariantListeners: function() { // Watch for swatch clicks document.addEventListener('click', Helpers.safeHandler(function(e) { const target = e.target.closest('.swatch input[type="radio"], .product-form__input input[type="radio"], [data-variant-id]'); if (target) { setTimeout(() => { const variant = VariantManager.getSelectedVariant(); if (variant && variant.id !== PRIVATE.currentVariant?.id) { VariantManager.updateVariant(variant.id); } }, 50); } })); // Listen to select changes document.addEventListener('change', Helpers.safeHandler(function(e) { const target = e.target; if (target.matches('select[name="id"], select.single-option-selector')) { setTimeout(() => { const variant = VariantManager.getSelectedVariant(); if (variant) { VariantManager.updateVariant(variant.id); } }, 50); } })); // Listen to Shopify events if (typeof $ !== 'undefined') { $(document).on('variant:change', Helpers.safeHandler(function(e, variant) { if (variant && variant.id) { VariantManager.updateVariant(variant.id); } })); } }, watchForAddToCartButton: function() { // Watch for Add to Cart button if it's loaded dynamically const observer = new MutationObserver(function(mutations) { const button = Helpers.findAddToCartButton(); if (button && button !== PRIVATE.addToCartButton) { PRIVATE.addToCartButton = button; Helpers.log('Add to Cart button found, repositioning badge...'); // Reposition badge above button const badgeContainer = document.getElementById('coverage-calc-badge-container'); if (badgeContainer && button.parentNode) { button.parentNode.insertBefore(badgeContainer, button); } } }); observer.observe(document.body, { childList: true, subtree: true }); } }; // ===== PUBLIC API ===== const PublicAPI = { // Update total update: function() { return Calculator.update(); }, // Update variant setVariant: function(variantId) { return VariantManager.updateVariant(variantId); }, // Get current state getState: function() { return { variant: PRIVATE.currentVariant, pricePerBox: DataManager.getCurrentPrice(), quantity: DataManager.getQuantity(), total: DataManager.getCurrentPrice() * DataManager.getQuantity(), coveragePerBox: DataManager.getCoveragePerBox() }; }, // Enable debug debug: function(enabled) { PRIVATE.config.debug = enabled === true; Helpers.log('Debug mode:', PRIVATE.config.debug); }, // Reset calculator reset: function() { Elements.val('boxQty', '1'); Elements.val('area', DataManager.getCoveragePerBox().toFixed(2)); Calculator.update(); }, // Force reposition badge repositionBadge: function() { const badgeContainer = document.getElementById('coverage-calc-badge-container'); if (badgeContainer) { Helpers.insertBadgeAboveAddToCart(badgeContainer); } } }; // ===== INITIALIZATION ===== function init() { if (PRIVATE.initialized) { Helpers.log('Already initialized'); return; } Helpers.log('Initializing...'); // Cache elements Elements.cache(); // Load data if (!DataManager.loadProductData()) { Helpers.log('Waiting for product data...'); setTimeout(init, 500); return; } // Setup event handlers EventManager.setup(); // Initial calculation setTimeout(() => { Calculator.update(); }, 100); PRIVATE.initialized = true; Helpers.log('✅ Calculator initialized successfully'); Helpers.log('Badge positioned above Add to Cart button'); Helpers.log('State:', PublicAPI.getState()); } // Start when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Expose public API window.ShopifyCoverageCalculator = PublicAPI; })(); // ============================================ // VARIANT HANDLER - SKU, Name & Image Updates // Completely separate from calculator // ============================================ (function() { 'use strict'; // Create unique namespace window.ShopifyVariantHandler = window.ShopifyVariantHandler || {}; // Private variables const PRIVATE = { initialized: false, productData: null, currentVariant: null, elements: { sku: null, variantName: null, productImage: null, variantTitle: null }, config: { debug: false, imageSelector: '.product__media img, .product-single__photo img, .product-featured-img, .product-image-main img, [data-product-image] img', skuSelector: '.sku, .product-sku, [data-sku], .variant-sku', variantNameSelector: '.variant-name, .product-variant, .selected-variant, [data-variant-name]', variantTitleSelector: '.variant-title, .product-title-variant' } }; // ===== HELPER FUNCTIONS ===== const Helpers = { log: function() { if (PRIVATE.config.debug && console) { console.log('[VariantHandler]', ...arguments); } }, // Find all variant selectors on page findVariantSelectors: function() { const selectors = [ 'input[name="id"]:checked', 'select[name="id"]', '.swatch input[type="radio"]:checked', '.product-form__input input[type="radio"]:checked', '[data-variant-id]' ]; for (let selector of selectors) { const element = document.querySelector(selector); if (element && element.value) { return { element, value: element.value }; } } return null; }, // Find variant by ID findVariantById: function(variantId) { if (!PRIVATE.productData || !PRIVATE.productData.variants) return null; return PRIVATE.productData.variants.find(v => v.id == variantId); }, // Format price formatPrice: function(price) { return price.toFixed(2); }, // Safe event handler safeHandler: function(fn) { return function() { try { return fn.apply(this, arguments); } catch (e) { Helpers.log('Error in handler:', e); } }; } }; // ===== ELEMENT MANAGER ===== const ElementManager = { // Find and cache all relevant elements cacheElements: function() { // Find SKU element PRIVATE.elements.sku = document.querySelector(PRIVATE.config.skuSelector); if (!PRIVATE.elements.sku) { // Try to find any element containing SKU text const allElements = document.querySelectorAll('[class*="sku"], [id*="sku"]'); PRIVATE.elements.sku = allElements[0] || null; } // Find variant name elements PRIVATE.elements.variantName = document.querySelector(PRIVATE.config.variantNameSelector); PRIVATE.elements.variantTitle = document.querySelector(PRIVATE.config.variantTitleSelector); // Find product image PRIVATE.elements.productImage = document.querySelector(PRIVATE.config.imageSelector); // If no image found, try common Shopify image selectors if (!PRIVATE.elements.productImage) { const imageSelectors = [ '.product__media img', '.product-single__photo img', '.product-featured-img', '.product-image-main img', '[data-product-image] img', '.product-single__image img', '.product-image img', '#ProductPhotoImg', '.product-photo-container img' ]; for (let selector of imageSelectors) { const img = document.querySelector(selector); if (img) { PRIVATE.elements.productImage = img; break; } } } Helpers.log('Elements found:', { sku: !!PRIVATE.elements.sku, variantName: !!PRIVATE.elements.variantName, variantTitle: !!PRIVATE.elements.variantTitle, productImage: !!PRIVATE.elements.productImage }); }, // Update SKU updateSku: function(sku) { if (!PRIVATE.elements.sku) return false; const skuText = sku || 'N/A'; // Handle different SKU element types if (PRIVATE.elements.sku.tagName === 'INPUT' || PRIVATE.elements.sku.tagName === 'SELECT') { PRIVATE.elements.sku.value = skuText; } else { PRIVATE.elements.sku.textContent = skuText; } Helpers.log('SKU updated:', skuText); return true; }, // Update variant name updateVariantName: function(variant) { if (!PRIVATE.elements.variantName && !PRIVATE.elements.variantTitle) return false; // Get variant name let variantName = variant.title || ''; // If title is like "Color / Size", format nicely if (variantName.includes('/')) { const parts = variantName.split('/').map(p => p.trim()); variantName = parts.join(' • '); } // Update variant name element if (PRIVATE.elements.variantName) { if (PRIVATE.elements.variantName.tagName === 'INPUT') { PRIVATE.elements.variantName.value = variantName; } else { PRIVATE.elements.variantName.textContent = variantName; } } // Update variant title element if (PRIVATE.elements.variantTitle) { if (PRIVATE.elements.variantTitle.tagName === 'INPUT') { PRIVATE.elements.variantTitle.value = variantName; } else { PRIVATE.elements.variantTitle.textContent = variantName; } } Helpers.log('Variant name updated:', variantName); return true; }, // Update product image updateImage: function(variant) { if (!PRIVATE.elements.productImage) return false; // Get variant image let imageUrl = null; if (variant.featured_image && variant.featured_image.src) { imageUrl = variant.featured_image.src; } else if (variant.image && variant.image.src) { imageUrl = variant.image.src; } else if (variant.featured_media && variant.featured_media.preview_image) { imageUrl = variant.featured_media.preview_image.src; } if (!imageUrl) { Helpers.log('No variant image found'); return false; } // Update image const oldSrc = PRIVATE.elements.productImage.src; if (oldSrc !== imageUrl) { // Add fade effect const container = PRIVATE.elements.productImage.closest('.product__media, .product-single__photo, .product-image-container'); if (container) { container.style.opacity = '0.5'; container.style.transition = 'opacity 0.2s ease'; } PRIVATE.elements.productImage.src = imageUrl; PRIVATE.elements.productImage.alt = variant.title || PRIVATE.productData?.title || 'Product image'; // Also update any thumbnail images if they exist ElementManager.updateThumbnails(variant); setTimeout(() => { if (container) { container.style.opacity = '1'; } }, 100); Helpers.log('Image updated:', imageUrl); return true; } return false; }, // Update thumbnail images updateThumbnails: function(variant) { const thumbnails = document.querySelectorAll('.thumbnail img, .product-thumbnail img, [data-thumbnail] img'); if (thumbnails.length > 0 && variant.featured_image && variant.featured_image.src) { thumbnails.forEach(thumb => { const thumbSrc = thumb.src; if (thumbSrc && thumbSrc.includes(variant.featured_image.src)) { thumb.classList.add('active'); } else { thumb.classList.remove('active'); } }); } }, // Update all variant info updateAll: function(variant) { if (!variant) return false; this.updateSku(variant.sku); this.updateVariantName(variant); this.updateImage(variant); // Also update any meta data this.updateMetaData(variant); return true; }, // Update meta data (like price, compare price) updateMetaData: function(variant) { // Update any price comparison elements const comparePriceElements = document.querySelectorAll('.compare-price, .compare-at-price, [data-compare-price]'); if (comparePriceElements.length > 0 && variant.compare_at_price) { const comparePrice = variant.compare_at_price / 100; comparePriceElements.forEach(el => { el.textContent = `$${Helpers.formatPrice(comparePrice)}`; }); } // Update availability const availabilityElements = document.querySelectorAll('.product-availability, .stock-status, [data-availability]'); if (availabilityElements.length > 0) { const isAvailable = variant.available; const statusText = isAvailable ? 'In Stock' : 'Out of Stock'; const statusColor = isAvailable ? '#27ae60' : '#e74c3c'; availabilityElements.forEach(el => { el.textContent = statusText; el.style.color = statusColor; }); } } }; // ===== IMAGE PRELOADER ===== const ImagePreloader = { preload: function(imageUrl) { if (!imageUrl) return; const img = new Image(); img.src = imageUrl; } }; // ===== VARIANT MANAGER ===== const VariantManager = { // Load product data loadProductData: function() { try { const productJson = document.getElementById('ProductJson'); if (productJson && productJson.textContent) { PRIVATE.productData = JSON.parse(productJson.textContent); Helpers.log('Product data loaded:', PRIVATE.productData.title); return true; } } catch (e) { Helpers.log('Error loading product data:', e); } return false; }, // Update variant updateVariant: function(variantId) { if (!PRIVATE.productData) return false; const variant = Helpers.findVariantById(variantId); if (!variant) { Helpers.log('Variant not found:', variantId); return false; } // Check if it's the same variant if (PRIVATE.currentVariant && PRIVATE.currentVariant.id === variant.id) { Helpers.log('Same variant, skipping update'); return false; } PRIVATE.currentVariant = variant; // Preload image if (variant.featured_image && variant.featured_image.src) { ImagePreloader.preload(variant.featured_image.src); } // Update all elements ElementManager.updateAll(variant); // Dispatch custom event for other scripts const event = new CustomEvent('variant:updated', { detail: { variant: variant }, bubbles: true }); document.dispatchEvent(event); Helpers.log('Variant updated:', { id: variant.id, title: variant.title, sku: variant.sku, hasImage: !!variant.featured_image }); return true; }, // Get current selected variant getSelectedVariant: function() { const selected = Helpers.findVariantSelectors(); if (selected && selected.value) { const variant = Helpers.findVariantById(selected.value); if (variant) return variant; } return PRIVATE.currentVariant; }, // Force update from DOM forceUpdate: function() { const selectedVariant = this.getSelectedVariant(); if (selectedVariant) { this.updateVariant(selectedVariant.id); } } }; // ===== EVENT HANDLERS ===== const EventManager = { setup: function() { // Listen to swatch clicks this.setupSwatchListeners(); // Listen to select changes this.setupSelectListeners(); // Listen to Shopify variant:change event this.setupShopifyEvents(); // Watch for dynamically added elements this.watchForDynamicElements(); }, setupSwatchListeners: function() { // Click handler for swatches document.addEventListener('click', Helpers.safeHandler(function(e) { // Check if clicked on a swatch radio or its label const target = e.target.closest('.swatch input, .product-form__input input, [data-variant-id]'); if (target) { setTimeout(() => { const variant = VariantManager.getSelectedVariant(); if (variant) { VariantManager.updateVariant(variant.id); } }, 50); } })); // Change handler for radio inputs document.addEventListener('change', Helpers.safeHandler(function(e) { if (e.target.matches('.swatch input[type="radio"], .product-form__input input[type="radio"]')) { const variantId = e.target.dataset.variantId || e.target.value; if (variantId) { VariantManager.updateVariant(variantId); } } })); }, setupSelectListeners: function() { document.addEventListener('change', Helpers.safeHandler(function(e) { if (e.target.matches('select[name="id"], select.single-option-selector')) { setTimeout(() => { const variant = VariantManager.getSelectedVariant(); if (variant) { VariantManager.updateVariant(variant.id); } }, 50); } })); }, setupShopifyEvents: function() { // Listen to Shopify's variant:change event if (typeof $ !== 'undefined') { $(document).on('variant:change', Helpers.safeHandler(function(e, variant) { if (variant && variant.id) { VariantManager.updateVariant(variant.id); } })); } // Listen to section reload events document.addEventListener('shopify:section:load', Helpers.safeHandler(function() { setTimeout(() => { ElementManager.cacheElements(); VariantManager.forceUpdate(); }, 100); })); }, watchForDynamicElements: function() { // Watch for dynamically added variant selectors const observer = new MutationObserver(function(mutations) { let shouldUpdate = false; mutations.forEach(function(mutation) { if (mutation.type === 'childList') { // Check if new swatches were added const newSwatches = document.querySelectorAll('.swatch input:not([data-handler-added])'); if (newSwatches.length > 0) { newSwatches.forEach(swatch => { swatch.setAttribute('data-handler-added', 'true'); shouldUpdate = true; }); } // Check if new images were added const newImages = document.querySelectorAll(PRIVATE.config.imageSelector); if (newImages.length > 0 && !PRIVATE.elements.productImage) { ElementManager.cacheElements(); shouldUpdate = true; } } }); if (shouldUpdate) { setTimeout(() => { VariantManager.forceUpdate(); }, 100); } }); observer.observe(document.body, { childList: true, subtree: true }); } }; // ===== PUBLIC API ===== const PublicAPI = { // Update variant by ID setVariant: function(variantId) { return VariantManager.updateVariant(variantId); }, // Get current variant getCurrentVariant: function() { return PRIVATE.currentVariant; }, // Force update from current selection refresh: function() { VariantManager.forceUpdate(); }, // Enable debug mode debug: function(enabled) { PRIVATE.config.debug = enabled === true; Helpers.log('Debug mode:', PRIVATE.config.debug); }, // Get product data getProductData: function() { return PRIVATE.productData; }, // Get all variants getVariants: function() { return PRIVATE.productData?.variants || []; } }; // ===== INITIALIZATION ===== function init() { if (PRIVATE.initialized) { Helpers.log('Already initialized'); return; } Helpers.log('Initializing Variant Handler...'); // Cache elements ElementManager.cacheElements(); // Load product data if (!VariantManager.loadProductData()) { Helpers.log('Waiting for product data...'); setTimeout(init, 500); return; } // Setup event handlers EventManager.setup(); // Initial variant update setTimeout(() => { VariantManager.forceUpdate(); }, 100); PRIVATE.initialized = true; Helpers.log('✅ Variant Handler initialized successfully'); Helpers.log('Initial variant:', PRIVATE.currentVariant?.title); } // Start when DOM is ready if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } // Expose public API window.ShopifyVariantHandler = PublicAPI; })(); (function() { 'use strict'; function updateUIFromVariant(variant) { if (!variant) return; // ✅ Update custom color name places let name = variant.title || ''; if (name.includes('/')) { name = name.split('/').map(v => v.trim()).join(' • '); } document.querySelectorAll('#selectedValue, #otherPlace').forEach(el => { el.textContent = name; }); // ✅ Optional: Thumbnail → slider sync (ONLY if your theme needs it) const thumbnails = document.querySelectorAll('.productView-thumbnail'); thumbnails.forEach(thumb => { thumb.classList.remove('active'); thumb.classList.forEach(cls => { if (cls.includes(variant.id)) { thumb.classList.add('active'); const link = thumb.querySelector('.productView-thumbnail-link'); if (link) link.click(); } }); }); } // 🔥 Listen to YOUR VariantHandler (BEST WAY) document.addEventListener('variant:updated', function(e) { updateUIFromVariant(e.detail.variant); }); // 🔥 Backup: Shopify event if (typeof jQuery !== 'undefined') { jQuery(document).on('variant:change', function(e, variant) { updateUIFromVariant(variant); }); } })(); // ============================================ // VARIANT HANDLER (FIXED FOR SLICK SLIDER) // ============================================ (function() { 'use strict'; window.ShopifyVariantHandler = {}; const PRIVATE = { productData: null, currentVariant: null, config: { debug: false, slickMain: '.product__media, .product-gallery, .product-images', slickSlides: 'img' } }; // ===== HELPERS ===== const Helpers = { log: function(...args) { if (PRIVATE.config.debug) { console.log('[VariantHandler]', ...args); } }, getProductData: function() { const el = document.getElementById('ProductJson'); if (el) { PRIVATE.productData = JSON.parse(el.textContent); } }, findVariant: function(id) { return PRIVATE.productData?.variants?.find(v => v.id == id); }, getSelectedVariantId: function() { const el = document.querySelector('input[name="id"]:checked') || document.querySelector('select[name="id"]'); return el ? el.value : null; } }; // ===== SLICK IMAGE HANDLER ===== const SlickHandler = { goToVariantImage: function(variant) { if (!variant || !variant.featured_image) return; const imageSrc = variant.featured_image.src; const slider = $(PRIVATE.config.slickMain); if (!slider.length || !slider.hasClass('slick-initialized')) { Helpers.log('Slick not initialized'); return; } let targetIndex = -1; slider.find('.slick-slide').each(function(index) { const img = $(this).find('img'); if (img.length && img.attr('src')?.includes(imageSrc)) { targetIndex = $(this).data('slick-index'); } }); if (targetIndex !== -1) { slider.slick('slickGoTo', targetIndex); Helpers.log('Moved to slide:', targetIndex); } else { Helpers.log('Image not found in slider'); } } }; // ===== UI UPDATES ===== const UI = { updateSKU: function(variant) { const el = document.querySelector('.sku, [data-sku]'); if (el) el.textContent = variant.sku || 'N/A'; }, updateVariantName: function(variant) { const el = document.querySelector('.variant-name, [data-variant-name]'); if (el) el.textContent = variant.title; } }; // ===== MAIN UPDATE ===== function updateVariant() { const id = Helpers.getSelectedVariantId(); if (!id) return; const variant = Helpers.findVariant(id); if (!variant) return; if (PRIVATE.currentVariant?.id === variant.id) return; PRIVATE.currentVariant = variant; UI.updateSKU(variant); UI.updateVariantName(variant); // 🔥 Slick Image Fix setTimeout(() => { SlickHandler.goToVariantImage(variant); }, 100); } // ===== EVENTS ===== function bindEvents() { document.addEventListener('change', function(e) { if ( e.target.matches('select[name="id"]') || e.target.matches('input[type="radio"]') ) { setTimeout(updateVariant, 50); } }); document.addEventListener('click', function(e) { if (e.target.closest('[data-variant-id], .swatch')) { setTimeout(updateVariant, 50); } }); if (typeof $ !== 'undefined') { $(document).on('variant:change', function(e, variant) { if (variant) { PRIVATE.currentVariant = variant; UI.updateSKU(variant); UI.updateVariantName(variant); SlickHandler.goToVariantImage(variant); } }); } } // ===== INIT ===== function init() { Helpers.getProductData(); bindEvents(); setTimeout(updateVariant, 200); Helpers.log('✅ Variant Handler Ready'); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', init); } else { init(); } })(); // Simple and direct - updates image on swatch click $(document).ready(function() { // When any color swatch is clicked $('input[name="Color"]').on('change', function() { var selectedColor = $(this).val(); // Update the variation name $('#selectedValue').text(selectedColor); $('#otherPlace').text(selectedColor); // Find the clicked swatch label to get the image var $selectedLabel = $('label[for="' + $(this).attr('id') + '"]'); var $expandImg = $selectedLabel.find('.expand img'); var newImgSrc = $expandImg.attr('srcset') || $expandImg.attr('src'); if (newImgSrc) { // Convert to large image size newImgSrc = newImgSrc.replace('_compact', '_720x').replace('_75x', '_720x').replace('_130x', '_720x'); // Update the main product image var $mainImage = $('.productView-nav .slick-current .productView-img-container img'); if ($mainImage.length) { $mainImage.attr('src', newImgSrc); $mainImage.attr('srcset', newImgSrc); } // Also update the zoom image $('.productView-nav .slick-current .media').attr('href', newImgSrc); } }); // Trigger on page load for initial state $('input[name="Color"]:checked').trigger('change'); });

Total Price: $0

$95.92 per box or $17.6 per 2Sq Ft
Save $21.14 per box
  • Transform your space with the exquisite Marazzi Castellina Stone Mosaic
  • Elongated hexagon shape adds a unique touch to your flooring
  • Measuring 13" X 17", perfect for creating stunning patterns
  • Explore the beauty of natural stone at Genix Flooring

Elevate your interior with the timeless charm of stone mosaic tiles. The Marazzi Castellina collection offers a blend of sophistication and durability, ideal for both residential and commercial spaces. Create a luxurious ambiance with these elongated hexagon tiles, available for purchase online at GenixFlooring.com.

Related Products