httpv://youtu.be/mf9CnaFtpW8
Understanding audio sample rates
/** * Geolocation-based product restrictions * Compatible with all Shopify plans (no checkout scripts required) * Version 3.2 - Improved variant selector cleaning for Tinker theme */ // 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'; this.cleaningInterval = null; } parseCountries(countriesString) { return countriesString .toUpperCase() .split(',') .map(c => c.trim()) .filter(c => c.length === 2); } async init() { console.log('=== GEO RESTRICTIONS INIT ==='); 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; } if (window.Shopify?.country) { console.log('Using Shopify country:', window.Shopify.country); this.userCountry = window.Shopify.country; this.setCachedCountry(this.userCountry); this.applyRestrictions(); return; } 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 = localStorage.getItem(this.storageExpiry); const country = localStorage.getItem(this.storageKey); if (expiry && country && Date.now() < parseInt(expiry)) { console.log(`Using cached country: ${country}`); return country; } } catch (e) { console.warn('localStorage not available:', e); } return null; } setCachedCountry(country) { try { const expiry = Date.now() + (24 * 60 * 60 * 1000); localStorage.setItem(this.storageKey, country); localStorage.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'); } } // Helper function to clean unavailable/sold out text cleanText(text) { return text .replace(/\s*-\s*Unavailable/gi, '') .replace(/\s*—\s*Unavailable/gi, '') .replace(/\s*-\s*Sold out/gi, '') .replace(/\s*—\s*Sold out/gi, '') .replace(/-\s*Unavailable/gi, '') .replace(/-\s*Sold out/gi, '') .trim(); } // Check if text needs cleaning needsCleaning(text) { if (!text) return false; const lower = text.toLowerCase(); return lower.includes('unavailable') || lower.includes('sold out'); } // Remove "- Unavailable" and "- Sold out" from variant selectors cleanVariantSelectors() { console.log('=== CLEANING VARIANT SELECTORS ==='); // Clean all select options - comprehensive selector document.querySelectorAll('select option, .variant-option__select option, variant-picker select option').forEach(option => { const text = option.textContent; if (this.needsCleaning(text)) { const cleaned = this.cleanText(text); if (cleaned !== text) { option.setAttribute('data-original-text', text); option.textContent = cleaned; console.log('Cleaned option:', text, '->', cleaned); } } }); // Clean variant picker displayed values - expanded selectors for Tinker theme const spanSelectors = [ 'variant-picker span', 'variant-picker div', '.variant-option span', '.variant-option__select-wrapper span', '[class*="variant"] span', '[class*="variant"] div:not([class])', '.variant-option__label', 'fieldset span', 'fieldset label' ].join(', '); document.querySelectorAll(spanSelectors).forEach(el => { if (el.children.length === 0) { const text = el.textContent; if (this.needsCleaning(text) && text.length < 100) { const cleaned = this.cleanText(text); if (cleaned !== text) { el.setAttribute('data-original-text', text); el.textContent = cleaned; console.log('Cleaned span:', text, '->', cleaned); } } } }); // Run again after delays to catch dynamic updates setTimeout(() => this.cleanVariantSelectorsDelayed(), 100); setTimeout(() => this.cleanVariantSelectorsDelayed(), 300); setTimeout(() => this.cleanVariantSelectorsDelayed(), 500); setTimeout(() => this.cleanVariantSelectorsDelayed(), 1000); setTimeout(() => this.cleanVariantSelectorsDelayed(), 2000); // Set up MutationObserver for dynamic changes on variant pickers AND select elements this.setupVariantObservers(); // Also run periodic cleaning for a few seconds after page load // This catches any late-loading JavaScript that modifies options this.startPeriodicCleaning(); } setupVariantObservers() { // Watch variant-picker elements document.querySelectorAll('variant-picker, .variant-picker, .variant-option').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 }); } }); // Also watch individual select elements directly document.querySelectorAll('select.variant-option__select, variant-picker select, [class*="variant"] select').forEach(select => { if (!select.hasAttribute('data-geo-observed')) { select.setAttribute('data-geo-observed', 'true'); const observer = new MutationObserver(() => { this.cleanVariantSelectorsDelayed(); }); observer.observe(select, { childList: true, subtree: true, characterData: true }); // Also listen for change events on the select select.addEventListener('change', () => { setTimeout(() => this.cleanVariantSelectorsDelayed(), 50); }); } }); // Watch the product form for any changes document.querySelectorAll('product-form, .product-form, form[action*="/cart/add"]').forEach(form => { if (!form.hasAttribute('data-geo-observed')) { form.setAttribute('data-geo-observed', 'true'); const observer = new MutationObserver(() => { this.cleanVariantSelectorsDelayed(); }); observer.observe(form, { childList: true, subtree: true, characterData: true }); } }); } startPeriodicCleaning() { // Clear any existing interval if (this.cleaningInterval) { clearInterval(this.cleaningInterval); } let cleanCount = 0; const maxCleans = 20; // Run for about 5 seconds (20 * 250ms) this.cleaningInterval = setInterval(() => { this.cleanVariantSelectorsDelayed(); cleanCount++; if (cleanCount >= maxCleans) { clearInterval(this.cleaningInterval); this.cleaningInterval = null; console.log('Periodic variant cleaning complete'); } }, 250); } cleanVariantSelectorsDelayed() { // Clean select options document.querySelectorAll('select option').forEach(option => { const text = option.textContent; if (this.needsCleaning(text)) { option.textContent = this.cleanText(text); } }); // Clean displayed spans/divs in variant areas const spanSelectors = [ 'variant-picker span', 'variant-picker div', '.variant-option span', '.variant-option__select-wrapper span', '[class*="variant"] span' ].join(', '); document.querySelectorAll(spanSelectors).forEach(el => { if (el.children.length === 0 && el.textContent) { const text = el.textContent; if (this.needsCleaning(text) && text.length < 100) { el.textContent = this.cleanText(text); } } }); } 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(); if (!this.config.isCollectionPage) { this.showRestrictionMessage(); } } 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'); }); // Clean variant selectors (remove "- Unavailable") this.cleanVariantSelectors(); } showAllowedElements() { // Stop periodic cleaning if running if (this.cleaningInterval) { clearInterval(this.cleaningInterval); this.cleaningInterval = null; } 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'); }); } showRestrictionMessage() { const insertionSelectors = [ '.product-form__buttons', '.buy-buttons-block', 'variant-picker', 'variant-selects', 'variant-radios', '[class*="variant"]', '.product-form', '.product__info-wrapper' ]; let insertAfterElement = null; for (const selector of insertionSelectors) { const el = document.querySelector(selector); if (el) { insertAfterElement = el; break; } } if (!insertAfterElement || document.querySelector('.geo-restriction-message')) return; const restrictionContainer = document.createElement('div'); restrictionContainer.className = 'geo-restriction-message'; restrictionContainer.style.cssText = 'width: 100%; margin: 1.5rem 0; text-align: center;'; const dealerButton = document.createElement('a'); dealerButton.href = '/pages/find-a-dealer'; dealerButton.className = 'geo-dealer-button'; dealerButton.style.cssText = ` display: block; width: 100%; padding: 18px 30px; background-color: #2563eb; color: white; text-decoration: none; border-radius: 4px; font-weight: 500; font-size: 16px; text-align: center; transition: background-color 0.2s ease; box-sizing: border-box; `; dealerButton.textContent = 'Find a Dealer or Distributor in Your Country'; dealerButton.addEventListener('mouseenter', () => dealerButton.style.backgroundColor = '#1d4ed8'); dealerButton.addEventListener('mouseleave', () => dealerButton.style.backgroundColor = '#2563eb'); restrictionContainer.appendChild(dealerButton); if (insertAfterElement.nextSibling) { insertAfterElement.parentNode.insertBefore(restrictionContainer, insertAfterElement.nextSibling); } else { insertAfterElement.parentNode.appendChild(restrictionContainer); } } setTestCountry(countryCode) { this.userCountry = countryCode.toUpperCase(); this.setCachedCountry(this.userCountry); // Stop any ongoing periodic cleaning if (this.cleaningInterval) { clearInterval(this.cleaningInterval); this.cleaningInterval = null; } 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 { 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'); 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/mf9CnaFtpW8
Loading comments...
Share your thoughts below and get our latest posts in your inbox
We respect your privacy. Unsubscribe anytime.
0 comments