httpv://youtu.be/ZrKeC8Cx1js
Do speaker grille cloths damage sound?
/** * Geolocation-based product restrictions * Compatible with all Shopify plans (no checkout scripts required) * Version 3.4 - Clean rebuild with essential fixes only */ // Prevent duplicate initialization if (!window.geoRestrictionsInitialized) { window.geoRestrictionsInitialized = true; class GeolocationRestrictions { constructor() { this.config = { enabled: window.geoRestrictionSettings?.enabled || false, allowedCountries: this.parseCountries(window.geoRestrictionSettings?.allowedCountries || 'US'), restrictedMessage: window.geoRestrictionSettings?.restrictedMessage || 'This product is not available in your region.', showContactLink: window.geoRestrictionSettings?.showContactLink || true, productTemplate: window.geoRestrictionSettings?.productTemplate || 'default', isCollectionPage: window.geoRestrictionSettings?.isCollectionPage || false, cloudflareCountry: window.geoRestrictionSettings?.cloudflareCountry || null }; this.userCountry = null; this.storageKey = 'shopify_user_country'; this.storageExpiry = 'shopify_country_expiry'; } parseCountries(countriesString) { return countriesString .toUpperCase() .split(',') .map(c => c.trim()) .filter(c => c.length === 2); } async init() { console.log('=== GEO RESTRICTIONS INIT v3.4 ==='); console.log('Enabled:', this.config.enabled); console.log('Product Template:', this.config.productTemplate); console.log('Is Collection Page:', this.config.isCollectionPage); if (!this.config.enabled) { console.log('Geolocation restrictions disabled'); this.removeLoadingClass(); return; } if (this.config.productTemplate === 'records') { console.log('Records template - no restrictions, showing all content'); this.removeLoadingClass(); this.showAllowedElements(); return; } if (window.CLOUDFLARE_COUNTRY) { console.log('Using Cloudflare Worker country:', window.CLOUDFLARE_COUNTRY); this.userCountry = window.CLOUDFLARE_COUNTRY; this.setCachedCountry(this.userCountry); this.applyRestrictions(); return; } // Note: Removed Shopify.country check - it reflects market selection, not actual visitor location const cachedCountry = this.getCachedCountry(); if (cachedCountry) { this.userCountry = cachedCountry; this.applyRestrictions(); return; } await this.detectCountry(); this.applyRestrictions(); } removeLoadingClass() { document.documentElement.classList.remove('geo-checking'); document.documentElement.classList.add('geo-checked'); } getCachedCountry() { try { const expiry = sessionStorage.getItem(this.storageExpiry); const country = sessionStorage.getItem(this.storageKey); if (expiry && country && Date.now() < parseInt(expiry)) { console.log(`Using cached country: ${country}`); return country; } } catch (e) { console.warn('sessionStorage not available:', e); } return null; } setCachedCountry(country) { try { // Cache for 1 hour const expiry = Date.now() + (60 * 60 * 1000); sessionStorage.setItem(this.storageKey, country); sessionStorage.setItem(this.storageExpiry, expiry.toString()); } catch (e) { console.warn('Could not cache country:', e); } } async detectCountry() { try { const response = await fetch('https://ipapi.co/json/', { method: 'GET', headers: { 'Accept': 'application/json' } }); if (!response.ok) throw new Error('Geolocation API failed'); const data = await response.json(); this.userCountry = data.country_code || 'US'; console.log(`Detected country: ${this.userCountry}`); this.setCachedCountry(this.userCountry); } catch (error) { console.warn('Country detection failed, defaulting to US:', error); this.userCountry = 'US'; } } isCountryAllowed() { return this.config.allowedCountries.includes(this.userCountry); } collapseParentIfEmpty(el) { el.style.margin = '0'; el.style.padding = '0'; el.style.height = '0'; el.style.minHeight = '0'; const parent = el.parentElement; if (!parent) return; const tagName = parent.tagName.toLowerCase(); const className = (parent.className || '').toLowerCase(); if (tagName !== 'div' && tagName !== 'span') return; if (className.includes('product')) return; if (className.includes('info')) return; if (className.includes('content')) return; if (className.includes('wrapper')) return; if (className.includes('container')) return; if (className.includes('form')) return; if (className.includes('main')) return; if (className.includes('section')) return; if (className.includes('block') && !className.includes('text-block') && !className.includes('group-block')) return; const children = Array.from(parent.children); if (children.length === 1 && children[0] === el) { parent.style.display = 'none'; parent.style.margin = '0'; parent.style.padding = '0'; parent.setAttribute('data-geo-restricted', 'true'); } } // Remove "- Unavailable" and "- Sold out" from variant selectors cleanVariantSelectors() { console.log('=== CLEANING VARIANT SELECTORS ==='); const cleanText = (text) => { return text .replace(/ - Unavailable/gi, '') .replace(/ - Sold out/gi, '') .replace(/ — Unavailable/gi, '') .replace(/ — Sold out/gi, '') .replace(/- Unavailable/gi, '') .replace(/- Sold out/gi, ''); }; // Clean all select options - with whitespace normalization document.querySelectorAll('select option').forEach(option => { const originalText = option.textContent; const normalizedText = originalText.replace(/\s+/g, ' ').trim(); if (normalizedText.toLowerCase().includes('unavailable') || normalizedText.toLowerCase().includes('sold out')) { const cleaned = normalizedText .replace(/\s*-\s*Unavailable/gi, '') .replace(/\s*—\s*Unavailable/gi, '') .replace(/\s*-\s*Sold out/gi, '') .replace(/\s*—\s*Sold out/gi, '') .trim(); if (cleaned !== normalizedText) { option.setAttribute('data-original-text', originalText); option.textContent = cleaned; console.log('Cleaned option:', normalizedText, '->', cleaned); } } }); // Clean variant picker displayed values document.querySelectorAll('variant-picker span, variant-picker div, .variant-option span, [class*="variant"] span').forEach(el => { if (el.children.length === 0) { const text = el.textContent; if ((text.includes('Unavailable') || text.includes('Sold out')) && text.length < 100) { const cleaned = cleanText(text); if (cleaned !== text) { el.setAttribute('data-original-text', text); el.textContent = cleaned; console.log('Cleaned span:', text, '->', cleaned); } } } }); // Run again after delays setTimeout(() => this.cleanVariantSelectorsDelayed(), 100); setTimeout(() => this.cleanVariantSelectorsDelayed(), 500); setTimeout(() => this.cleanVariantSelectorsDelayed(), 1500); // Set up MutationObserver for dynamic changes document.querySelectorAll('variant-picker, .variant-picker').forEach(picker => { if (!picker.hasAttribute('data-geo-observed')) { picker.setAttribute('data-geo-observed', 'true'); const observer = new MutationObserver(() => { this.cleanVariantSelectorsDelayed(); }); observer.observe(picker, { childList: true, subtree: true, characterData: true }); } }); } cleanVariantSelectorsDelayed() { document.querySelectorAll('select option').forEach(option => { const originalText = option.textContent; const normalizedText = originalText.replace(/\s+/g, ' ').trim(); if (normalizedText.toLowerCase().includes('unavailable') || normalizedText.toLowerCase().includes('sold out')) { option.textContent = normalizedText .replace(/\s*-\s*Unavailable/gi, '') .replace(/\s*—\s*Unavailable/gi, '') .replace(/\s*-\s*Sold out/gi, '') .replace(/\s*—\s*Sold out/gi, '') .trim(); } }); document.querySelectorAll('variant-picker span, variant-picker div, .variant-option span').forEach(el => { if (el.children.length === 0 && el.textContent) { const text = el.textContent; if ((text.includes('Unavailable') || text.includes('Sold out')) && text.length < 100) { el.textContent = text .replace(/ - Unavailable/gi, '') .replace(/ - Sold out/gi, '') .replace(/ — Unavailable/gi, '') .replace(/ — Sold out/gi, '') .replace(/- Unavailable/gi, '') .replace(/- Sold out/gi, ''); } } }); } applyRestrictions() { this.removeLoadingClass(); if (this.config.productTemplate === 'records') { console.log('Records template - skipping restrictions'); document.documentElement.classList.add('geo-allowed'); this.showAllowedElements(); return; } const allowed = this.isCountryAllowed(); console.log(`Country: ${this.userCountry}, Allowed: ${allowed}`); if (!allowed) { document.documentElement.classList.add('geo-restricted'); this.hideRestrictedElements(); // Note: showRestrictionMessage removed - geo-dealer-display.liquid handles this now } else { document.documentElement.classList.add('geo-allowed'); this.showAllowedElements(); } } hideRestrictedElements() { console.log('=== HIDING RESTRICTED ELEMENTS ==='); console.log('Is Collection Page:', this.config.isCollectionPage); // Hide "Sold Out" badges on collection pages if (this.config.isCollectionPage) { document.querySelectorAll('.product-badges__badge, .product-badges, [class*="badge"], .badge').forEach(el => { const text = el.textContent.trim().toLowerCase(); if (text === 'sold out' || text === 'soldout' || text.includes('sold out')) { el.style.display = 'none'; el.setAttribute('data-geo-hidden-badge', 'true'); } }); } // Hide add to cart text metafield blocks (short pricing notes) document.querySelectorAll('.text-block, .group-block, rte-formatter, [class*="text-block"], [class*="rich-text"], .rte').forEach(el => { const text = el.textContent.trim().toLowerCase(); if (text.length < 100) { if (text.includes('price is per') || text.includes('price per') || text.includes('priced per') || text.includes('sold as pair') || text.includes('sold as a pair') || text === 'per pair' || text.includes('each unit') || text.includes('price includes')) { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); this.collapseParentIfEmpty(el); } } }); // Hide prices const priceSelectors = [ '.product__price', '.price', '.product-price', '[data-price]', '.money', '.price--on-sale', '.price-item--sale', '.price-item--regular', '.price__sale', '.price__regular', '.compare-at-price', '.was-price' ]; priceSelectors.forEach(selector => { document.querySelectorAll(selector).forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); }); // Collection page: hide prices in cards if (this.config.isCollectionPage) { document.querySelectorAll('[class*="card"] .price, [class*="grid"] .price, .price s, .price del, s, del').forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); } // Hide add to cart buttons const cartSelectors = [ 'button[name="add"]', '.product-form__submit', '.add-to-cart', 'form[action*="/cart/add"]', '.shopify-payment-button', '.product-form__buttons', '.trade-in-buttons-wrapper', '.trade-in-button-group', '.trade-in-btn', '.buy-buttons-block', '.product-form-buttons', '.add-to-cart-button', 'button[data-open-trade-modal]', 'button[data-add-to-cart-normal]', 'button[data-add-with-trade]' ]; cartSelectors.forEach(selector => { document.querySelectorAll(selector).forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); }); // Hide quantity selectors document.querySelectorAll('.product-form__quantity, .quantity-selector').forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); // Hide trade-in promo text document.querySelectorAll('strong, p, div, span, section, aside').forEach(el => { const text = el.textContent.trim(); if (text.length < 300) { if ((text.startsWith('Receive up to') && text.includes('trade in')) || (text.includes('Now available') && text.includes('trade in')) || text.includes('Receive up to $') || text.includes('trade in your')) { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); this.collapseParentIfEmpty(el); } } if (text === '-%}' || text.trim() === '-%}') { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); } }); // Hide trade-in class elements document.querySelectorAll('[class*="trade-in"], [class*="tradein"], [class*="trade-up"], [class*="tradeup"]').forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); // Hide accordion sections document.querySelectorAll('accordion-custom, details').forEach(el => { const text = el.textContent; if (text.includes('Talk to a Hi-Fi Specialist') || text.includes('Home Audition Made Easy') || (text.includes('Shipping') && !text.includes('Shipping policy'))) { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); } }); // Hide menu prices document.querySelectorAll('.menu .price, .dropdown .price, .mega-menu .price, nav .price, header .price').forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); // Hide waiting list / waitlist buttons (for international visitors) document.querySelectorAll('[class*="waiting"], [class*="waitlist"], [class*="Waiting"], [class*="Waitlist"]').forEach(el => { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); }); document.querySelectorAll('button, a.button, a[class*="btn"]').forEach(el => { const text = el.textContent.toLowerCase(); if (text.includes('waiting list') || text.includes('waitlist') || text.includes('notify me')) { el.style.display = 'none'; el.setAttribute('data-geo-restricted', 'true'); } }); // Clean variant selectors (remove "- Unavailable") this.cleanVariantSelectors(); } showAllowedElements() { const restrictionMsg = document.querySelector('.geo-restriction-message'); if (restrictionMsg) restrictionMsg.remove(); document.querySelectorAll('[data-geo-restricted]').forEach(el => { el.style.display = ''; el.style.margin = ''; el.style.padding = ''; el.style.height = ''; el.style.minHeight = ''; el.style.overflow = ''; el.removeAttribute('data-geo-restricted'); }); document.querySelectorAll('[data-geo-hidden-badge]').forEach(el => { el.style.display = ''; el.removeAttribute('data-geo-hidden-badge'); }); document.querySelectorAll('[data-original-text]').forEach(el => { el.textContent = el.getAttribute('data-original-text'); el.removeAttribute('data-original-text'); }); } setTestCountry(countryCode) { this.userCountry = countryCode.toUpperCase(); this.setCachedCountry(this.userCountry); document.documentElement.classList.remove('geo-restricted', 'geo-allowed'); document.querySelectorAll('[data-geo-restricted]').forEach(el => { el.style.display = ''; el.style.margin = ''; el.style.padding = ''; el.style.height = ''; el.style.minHeight = ''; el.style.overflow = ''; el.removeAttribute('data-geo-restricted'); }); document.querySelectorAll('[data-geo-hidden-badge]').forEach(el => { el.style.display = ''; el.removeAttribute('data-geo-hidden-badge'); }); document.querySelectorAll('[data-original-text]').forEach(el => { el.textContent = el.getAttribute('data-original-text'); el.removeAttribute('data-original-text'); }); const msg = document.querySelector('.geo-restriction-message'); if (msg) msg.remove(); this.applyRestrictions(); this.removeLoadingClass(); } clearCache() { try { sessionStorage.removeItem(this.storageKey); sessionStorage.removeItem(this.storageExpiry); // Also clear old localStorage entries localStorage.removeItem(this.storageKey); localStorage.removeItem(this.storageExpiry); console.log('Country cache cleared'); } catch (e) { console.warn('Could not clear cache:', e); } } } function initGeoRestrictions() { if (!window.geoRestrictionSettings) { console.warn('Waiting for geoRestrictionSettings...'); setTimeout(initGeoRestrictions, 50); return; } console.log('Initializing geo restrictions v3.4'); window.geoRestrictions = new GeolocationRestrictions(); window.geoRestrictions.init(); } if (document.readyState === 'loading') { document.addEventListener('DOMContentLoaded', initGeoRestrictions); } else { initGeoRestrictions(); } window.testGeoRestriction = function(countryCode) { if (window.geoRestrictions) { window.geoRestrictions.setTestCountry(countryCode); } }; window.clearGeoCache = function() { if (window.geoRestrictions) { window.geoRestrictions.clearCache(); location.reload(); } }; }
Skip to content
httpv://youtu.be/ZrKeC8Cx1js
Loading comments...
Share your thoughts below and get our latest posts in your inbox
We respect your privacy. Unsubscribe anytime.
0 comments