tomer interaction.
// Detected variants: the variants present in this page's Liquid context (product page has them;
// collection/index/etc. don't expose variants from Liquid). Used to disambiguate "embed didn't
// load" vs "embed loaded but the variant wasn't a preorder/BIS candidate" in order debug.
try {
const _stoqInitConfig = window._RestockRocketConfig;
const _stoqDetectedVariantIds = (_stoqInitConfig.product && Array.isArray(_stoqInitConfig.product.variants))
? _stoqInitConfig.product.variants.map(function(v) { return v.id })
: [];
const _stoqSelectedVariantId = _stoqInitConfig.selected_variant_id;
Shopify?.analytics?.publish?.('stoq_initialized', {
cart_token: _stoqInitConfig.cartToken || '',
page_url: window.location.href,
page_type: _stoqInitConfig.pageType || '',
shop_domain: _stoqInitConfig.shop || '',
market_id: _stoqInitConfig.marketId || '',
detected_variant_ids: _stoqDetectedVariantIds,
selected_variant_id: _stoqSeletomer interaction.
// Detected variants: the variants present in this page's Liquid context (product page has them;
// collection/index/etc. don't expose variants from Liquid). Used to disambiguate "embed didn't
// load" vs "embed loaded but the variant wasn't a preorder/BIS candidate" in order debug.
try {
const _stoqInitConfig = window._RestockRocketConfig;
const _stoqDetectedVariantIds = (_stoqInitConfig.product && Array.isArray(_stoqInitConfig.product.variants))
? _stoqInitConfig.product.variants.map(function(v) { return v.id })
: [];
const _stoqSelectedVariantId = _stoqInitConfig.selected_variant_id;
Shopify?.analytics?.publish?.('stoq_initialized', {
cart_token: _stoqInitConfig.cartToken || '',
page_url: window.location.href,
page_type: _stoqInitConfig.pageType || '',
shop_domain: _stoqInitConfig.shop || '',
market_id: _stoqInitConfig.marketId || '',
detected_variant_ids: _stoqDetectedVariantIds,
selected_variant_id: _stoqSele
ctedVariantId || '',
liquid_rendered_at: _stoqInitConfig.liquidRenderedAt || 0,
app_version: _stoqInitConfig.appVersion || '',
liquid_cache_age: _stoqInitConfig.liquidCacheAge,
// Selected variant's stock posture as our app saw it at render — explains
// whether we *should* have treated it as a preorder candidate.
inventory_policy: (_stoqInitConfig.variantsInventoryPolicy || {})[_stoqSelectedVariantId] || '',
inventory_quantity: (_stoqInitConfig.variantsInventoryQuantity || {})[_stoqSelectedVariantId],
});
} catch (e) {
console.debug('STOQ - stoq_initialized publish failed:', e);
}
function applyTranslations(settings) {
try {
// Skip translation logic entirely if multi-language is not enabled
if (!settings || !settings.multi_language_enabled) {
return settings;
}
if (!settings.translations) {
console.debug('STOQ - No translations found, skipping translation');
return settings;
}
const nctedVariantId || '',
liquid_rendered_at: _stoqInitConfig.liquidRenderedAt || 0,
app_version: _stoqInitConfig.appVersion || '',
liquid_cache_age: _stoqInitConfig.liquidCacheAge,
// Selected variant's stock posture as our app saw it at render — explains
// whether we *should* have treated it as a preorder candidate.
inventory_policy: (_stoqInitConfig.variantsInventoryPolicy || {})[_stoqSelectedVariantId] || '',
inventory_quantity: (_stoqInitConfig.variantsInventoryQuantity || {})[_stoqSelectedVariantId],
});
} catch (e) {
console.debug('STOQ - stoq_initialized publish failed:', e);
}
function applyTranslations(settings) {
try {
// Skip translation logic entirely if multi-language is not enabled
if (!settings || !settings.multi_language_enabled) {
return settings;
}
if (!settings.translations) {
console.debug('STOQ - No translations found, skipping translation');
return settings;
}
const normalizedLocale = window._RestockRocketConfig.normalizedLocale;
const translations = settings.translations;
if (!normalizedLocale) {
// No matching locale has translations; drop payload to save memory
console.debug('STOQ - No matching locale for translations. Available:', Object.keys(translations || {}));
delete settings.translations;
return settings;
}
console.debug(`STOQ - Applying translations for normalized locale: ${normalizedLocale} (original: ${window._RestockRocketConfig.locale})`);
const translatedFields = translations[normalizedLocale];
if (translatedFields && typeof translatedFields === 'object') {
Object.keys(translatedFields).forEach(function(key) {
const value = translatedFields[key];
if (value !== null && value !== undefined && value !== '') {
settings[key] = value;
}
});
} else {
console.debug('STOQ - No translated fields found for locale:', normalizedLormalizedLocale = window._RestockRocketConfig.normalizedLocale;
const translations = settings.translations;
if (!normalizedLocale) {
// No matching locale has translations; drop payload to save memory
console.debug('STOQ - No matching locale for translations. Available:', Object.keys(translations || {}));
delete settings.translations;
return settings;
}
console.debug(`STOQ - Applying translations for normalized locale: ${normalizedLocale} (original: ${window._RestockRocketConfig.locale})`);
const translatedFields = translations[normalizedLocale];
if (translatedFields && typeof translatedFields === 'object') {
Object.keys(translatedFields).forEach(function(key) {
const value = translatedFields[key];
if (value !== null && value !== undefined && value !== '') {
settings[key] = value;
}
});
} else {
console.debug('STOQ - No translated fields found for locale:', normalizedL
ocale);
}
delete settings.translations;
return settings;
} catch (e) {
console.debug('STOQ - error applying translations:', e);
return settings;
}
}
// Setup event listener for cart selling plan updates
// This must be called before any scripts are loaded to avoid race conditions
function setupCartSellingPlanUpdater(settings) {
// Setup listener regardless - updateCartSellingPlans has its own guards
// This ensures cleanup happens even when preorders are disabled globally
// Listen for stoq:inventory-data-loaded event dispatched by api.js
window.addEventListener('stoq:inventory-data-loaded', function(event) {
console.debug('STOQ - Inventory data loaded, updating cart selling plans');
if (window._RestockRocket && window._RestockRocket.updateCartSellingPlans) {
window._RestockRocket.updateCartSellingPlans()
.then(hasUpdates => {
if (hasUpdates) {
console.debug('STOQ - cart selling plans updateocale);
}
delete settings.translations;
return settings;
} catch (e) {
console.debug('STOQ - error applying translations:', e);
return settings;
}
}
// Setup event listener for cart selling plan updates
// This must be called before any scripts are loaded to avoid race conditions
function setupCartSellingPlanUpdater(settings) {
// Setup listener regardless - updateCartSellingPlans has its own guards
// This ensures cleanup happens even when preorders are disabled globally
// Listen for stoq:inventory-data-loaded event dispatched by api.js
window.addEventListener('stoq:inventory-data-loaded', function(event) {
console.debug('STOQ - Inventory data loaded, updating cart selling plans');
if (window._RestockRocket && window._RestockRocket.updateCartSellingPlans) {
window._RestockRocket.updateCartSellingPlans()
.then(hasUpdates => {
if (hasUpdates) {
console.debug('STOQ - cart selling plans update
d successfully');
} else {
console.debug('STOQ - no cart selling plan updates needed');
}
})
.catch(error => {
console.error('STOQ - error updating cart selling plans:', error);
});
}
});
}
// First try to get settings from metafields with expiry check
const cachedSettings = window._RestockRocketConfig.cachedSettings;
const validCachedSettings = cachedSettings ? checkSettingsExpiry(cachedSettings) : null;
if (validCachedSettings) {
console.debug('STOQ - using cached settings');
initializeScripts(validCachedSettings);
} else {
console.debug('STOQ - fetching fresh settings');
const headers = {
'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop,
'ngrok-skip-browser-warning': 'skip'
};
if (window.Shopify?.theme?.role === 'main') {
headers['X-Shopify-Theme-Schema-Name'] = window.Shopify.theme.schema_name;
headers['X-Shopify-Theme-Sched successfully');
} else {
console.debug('STOQ - no cart selling plan updates needed');
}
})
.catch(error => {
console.error('STOQ - error updating cart selling plans:', error);
});
}
});
}
// First try to get settings from metafields with expiry check
const cachedSettings = window._RestockRocketConfig.cachedSettings;
const validCachedSettings = cachedSettings ? checkSettingsExpiry(cachedSettings) : null;
if (validCachedSettings) {
console.debug('STOQ - using cached settings');
initializeScripts(validCachedSettings);
} else {
console.debug('STOQ - fetching fresh settings');
const headers = {
'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop,
'ngrok-skip-browser-warning': 'skip'
};
if (window.Shopify?.theme?.role === 'main') {
headers['X-Shopify-Theme-Schema-Name'] = window.Shopify.theme.schema_name;
headers['X-Shopify-Theme-Sche
ma-Version'] = window.Shopify.theme.schema_version;
headers['X-Shopify-Theme-Store-Id'] = window.Shopify.theme.theme_store_id;
}
fetch(
`${window._RestockRocketConfig.host}/api/v1/setting.json?translation_locale=${window._RestockRocketConfig.normalizedLocale}`,
{ headers }
)
.then(function(response) {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(function(settings) {
initializeScripts(settings);
})
.catch(function(error) {
// If request failed and we have cached settings (even if expired), use them as fallback
if (cachedSettings) {
console.debug('STOQ - using expired cached settings as fallback');
initializeScripts(cachedSettings);
} else {
console.error('STOQ - failed to load settings:', error);
}
})
.catch(function(e) {
console.error(e)
})
}
function fetchEmbedConfig(endpoint, apply) {
return fetma-Version'] = window.Shopify.theme.schema_version;
headers['X-Shopify-Theme-Store-Id'] = window.Shopify.theme.theme_store_id;
}
fetch(
`${window._RestockRocketConfig.host}/api/v1/setting.json?translation_locale=${window._RestockRocketConfig.normalizedLocale}`,
{ headers }
)
.then(function(response) {
if (!response.ok) {
throw new Error('Network response was not ok');
}
return response.json();
})
.then(function(settings) {
initializeScripts(settings);
})
.catch(function(error) {
// If request failed and we have cached settings (even if expired), use them as fallback
if (cachedSettings) {
console.debug('STOQ - using expired cached settings as fallback');
initializeScripts(cachedSettings);
} else {
console.error('STOQ - failed to load settings:', error);
}
})
.catch(function(e) {
console.error(e)
})
}
function fetchEmbedConfig(endpoint, apply) {
return fetch(
`${window._RestockRocketConfig.host}/api/v1/embed/${endpoint}.json`,
{
headers: {
'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop,
'ngrok-skip-browser-warning': 'skip'
}
}
)
.then(function(response) {
if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`);
return response.json();
})
.then(function(data) {
try {
apply(data);
} catch (applyError) {
// Apply failures are programming bugs (e.g. response shape changed
// server-side and the assignment threw). Surface them as console.error
// so they're visible in browser logs, then re-throw to fall through
// to the same Liquid-cached fallback as a fetch failure.
console.error('STOQ - apply failed for ' + endpoint + ':', applyError);
throw applyError;
}
})
.catch(function(error) {
console.debug(`STOQ - using cached ${endpoint}:`, error.message);
}ch(
`${window._RestockRocketConfig.host}/api/v1/embed/${endpoint}.json`,
{
headers: {
'X-Shopify-Shop-Domain': window._RestockRocketConfig.shop || window.Shopify.shop,
'ngrok-skip-browser-warning': 'skip'
}
}
)
.then(function(response) {
if (!response.ok) throw new Error(`Failed to fetch ${endpoint}`);
return response.json();
})
.then(function(data) {
try {
apply(data);
} catch (applyError) {
// Apply failures are programming bugs (e.g. response shape changed
// server-side and the assignment threw). Surface them as console.error
// so they're visible in browser logs, then re-throw to fall through
// to the same Liquid-cached fallback as a fetch failure.
console.error('STOQ - apply failed for ' + endpoint + ':', applyError);
throw applyError;
}
})
.catch(function(error) {
console.debug(`STOQ - using cached ${endpoint}:`, error.message);
}
);
}
function initializeScripts(settings) {
settings = applyTranslations(settings);
window._RestockRocketConfig.settings = settings;
console.debug(`STOQ - settings configured for ${window._RestockRocketConfig.pageType}`);
// Stale-Liquid resilience (default-on, per-shop opt-out via the
// `disable_refresh_on_stale_liquid` Toggle, surfaced as the negative
// `disable_refresh_on_stale_liquid` flag in settings.json so that
// `undefined` -- in CDN-cached metafield payloads that predate this
// key -- reads as `!undefined === true` and gets default-on behavior
// immediately, no metafield rewrite required).
// When the Liquid CDN cache is older than LIQUID_CACHE_MAX_AGE the in-page
// selling_plans / integrations metafields can be wrong; refresh both from
// the API before launching scripts. Race against a 1000ms timeout so a slow
// API can't block init indefinitely. If the timeout wins, the in-flight
// fetches still complete and update window._Resto);
}
function initializeScripts(settings) {
settings = applyTranslations(settings);
window._RestockRocketConfig.settings = settings;
console.debug(`STOQ - settings configured for ${window._RestockRocketConfig.pageType}`);
// Stale-Liquid resilience (default-on, per-shop opt-out via the
// `disable_refresh_on_stale_liquid` Toggle, surfaced as the negative
// `disable_refresh_on_stale_liquid` flag in settings.json so that
// `undefined` -- in CDN-cached metafield payloads that predate this
// key -- reads as `!undefined === true` and gets default-on behavior
// immediately, no metafield rewrite required).
// When the Liquid CDN cache is older than LIQUID_CACHE_MAX_AGE the in-page
// selling_plans / integrations metafields can be wrong; refresh both from
// the API before launching scripts. Race against a 1000ms timeout so a slow
// API can't block init indefinitely. If the timeout wins, the in-flight
// fetches still complete and update window._Resto