ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8873.jpg?v=1720881004","width":1080},{"alt":null,"id":34242103214306,"position":9,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8874.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8874.jpg?v=1720881004","width":1080},{"alt":null,"id":34242103247074,"position":10,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8875.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8875.jpg?v=1720881004","width":1080},{"alt":null,"id":34242103279842,"position":11,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8876.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektr
ide.co.uk\/cdn\/shop\/files\/8876.jpg?v=1720881004","width":1080},{"alt":null,"id":34242103312610,"position":12,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8877.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8877.jpg?v=1720881004","width":1080},{"alt":null,"id":34242103345378,"position":13,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8878.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8878.jpg?v=1720881004","width":1080},{"alt":null,"id":34242103378146,"position":14,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8879.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8879.jpg?v=1720881004","width":1080
- },{"alt":null,"id":34242103410914,"position":15,"preview_image":{"aspect_ratio":1.0,"height":1080,"width":1080,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8880.jpg?v=1720881004"},"aspect_ratio":1.0,"height":1080,"media_type":"image","src":"\/\/projektride.co.uk\/cdn\/shop\/files\/8880.jpg?v=1720881004","width":1080}],"requires_selling_plan":false,"selling_plan_groups":[],"content":"\u003cp\u003e \u003c\/p\u003e\n\u003cp\u003eThe RECON is our first sustainable outdoor water bottle series, made from 50% recycled content in both the cap and bottle. Topped with our new patented twist cap that provides an experience like drinking out of a glass. RECON was born out of the collective belief that great gear for the trail can and should be lower impact on the environment. An enlightened way to hydrate.\u003c\/p\u003e\n\u003cul\u003e\n\u003cli\u003eSmooth flow twist cap is quick to open\/close – 180° turn delivers the perfect flow rate for a glass-like drinking experience; twist back to close for leak-proof tra
nsport\u003c\/li\u003e\n\u003cli\u003eThe wide-mouth design includes an integrated TPU bail handle that is comfortable to carry, lightweight, and nearly indestructible\u003c\/li\u003e\n\u003cli\u003eSlender bottle shape, designed to fit in your backpack pocket, but versatile enough for everyday use – fits in most cup holders\u003c\/li\u003e\n\u003cli\u003eIndented finger groove provides a better grip including with gloves and in all weather conditions\u003c\/li\u003e\n\u003cli\u003eCap and bottle made utilizing Eastman™ Tritan Renew™ Technology, 50% recycled plastic content comes from a variety of sources with x9 PET single use bottles being the primary source\u003c\/li\u003e\n\u003cli\u003eMinimally designed with only 2-parts, the cap and inner gasket separate for easy cleaning, and along with the bottle, are all dishwasher safe\u003c\/li\u003e\n\u003cli\u003eImpact, stain, and odour resistant, as well as 100% BPA, BPS and BPF free with no residual taste\u003c\/li\u003e\n\u003c\/ul\u003e\n\u003c!----\u003e"};
window._RestockRocketConfig.variantsInventoryPolicy = {45694293803234 : "deny",45694293836002 : "deny",45694294327522 : "deny",};
window._RestockRocketConfig.variantsInventoryQuantity = {45694293803234 : parseInt("1"),45694293836002 : parseInt("1"),45694294327522 : parseInt("0"),};
window._RestockRocketConfig.variantsPreorderCount = {45694293803234 : parseInt(""),45694293836002 : parseInt(""),45694294327522 : parseInt(""),};
window._RestockRocketConfig.variantsPreorderCountForMarket = {45694293803234 : null,45694293836002 : null,45694294327522 : null,};
window._RestockRocketConfig.variantsPreorderMaxCount = {45694293803234 : parseInt(""),45694293836002 : parseInt(""),45694294327522 : parseInt(""),};
window._RestockRocketConfig.variantsPreorderMaxCountForMarket = {45694293803234 : null,45694293836002 : null,45694294327522 : null,};
window._RestockRocketConfig.variantsShippingText = {45694293803234 : "",45694293836002 : "",45694294327522 : "",};
window._RestockRocketConfiHandlebarsg.variantsShippingTextForMarket = {45694293803234 : null,45694293836002 : null,45694294327522 : null,};
window._RestockRocketConfig.selected_variant_id = 45694293803234;
window._RestockRocketConfig.selected_variant_available = window._RestockRocketConfig.product.variants.find(function(variant) { return variant.id == window._RestockRocketConfig.selected_variant_id }).available;window._RestockRocketConfig.scriptUrlProduct = 'https://cdn.shopify.com/extensions/019bf9e0-3016-7510-aaea-4318e35b6645/restock-rocket-shopify-436/assets/restockrocket-product.js'
window._RestockRocketConfig.scriptUrlCollection = 'https://cdn.shopify.com/extensions/019bf9e0-3016-7510-aaea-4318e35b6645/restock-rocket-shopify-436/assets/restockrocket-collection.js'
window._RestockRocketConfig.scriptHost = window._RestockRocketConfig.scriptUrlProduct.substring(0, window._RestockRocketConfig.scriptUrlProduct.lastIndexOf('/') + 1)
window._RestockRocketConfig.host = 'https://app.restockrocket.io'
const SETTINGS_CACHE_DURATION = 15 * 60 * 1000; // 15 minutes in milliseconds
function checkSettingsExpiry(settings) {
try {
if (!settings || !settings.updated_at) {
console.debug('STOQ - Invalid settings data structure');
return null;
}
if (!settings.cache) {
console.debug('STOQ - settings caching disabled');
return null;
}
// Check if translations are enabled but missing from cache
// This handles the backfill period where DB has translations but metafield doesn't
if (settings.multi_language_enabled) {
if (!settings.translations) {
// Translations enabled but no translation data in metafield
// Metafield hasn't been backfilled yet - force refresh
console.debug('STOQ - multi-language enabled but no translation data in cache, fetching fresh');
return null;
}
// Translations object exists in metafield - cache is valid
// If current locale isn't translated, applyTranslations will gracefu
lly use default locale from base fields
if (window._RestockRocketConfig.normalizedLocale &&
!Object.prototype.hasOwnProperty.call(settings.translations, window._RestockRocketConfig.normalizedLocale)) {
console.debug('STOQ - locale not explicitly translated, will use default language from cache');
}
// Don't return null - continue using cache even for untranslated locales
}
const updatedAt = new Date(settings.updated_at);
if (isNaN(updatedAt.getTime())) {
console.debug('STOQ - Invalid updated_at date format in settings');
return null;
}
const age = Date.now() - updatedAt.getTime();
if (age
- <
SETTINGS_CACHE_DURATION) {
console.debug('STOQ - settings changed recently, skipping cache');
return null;
}
return settings;
} catch (error) {
console.debug('STOQ - Error checking settings cache:', error);
return null;
}
}
function createRestockRocketContainer() {
const restockRocketContainer = document.createElement('div');
restockRocketContainer.id = 'restock-rocket';
document.body.appendChild(restockRocketContainer);
}
function createRestockRocketScript(scriptUrl) {
const restockRocketScriptElement = document.createElement('script');
restockRocketScriptElement.setAttribute('defer', 'defer');
restockRocketScriptElement.src = scriptUrl;
document.body.appendChild(restockRocketScriptElement);
}
createRestockRocketContainer()
console.debug('STOQ - extension activated')
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 = settinStemsgs.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:', normalizedLocale);
}
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) {
if (!settings || !settings.preorder_enabled) {
return;
}
// Listen for stoq:preorder-api-ready event dispatched by preorder.js
window.addEventListener('stoq:preorder-api-ready', function(event) {
console.debug('STOQ - Preorder API ready, updating cart selling plans');
if (window._RestockRocket && window._RestockRocket.updateCartSellingPlans) {
window._RestockRocket.updateCartSellingPlans()
.then(hasUpdates => {
if (hasUpdates) {
console.debug('STOQ - cart selling plans updated successfully');
} else {
console.debug('STOQ - no cart selling plan updates needed');
}
})
.catch(error => {
con
sole.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-Schema-Version'] = window.Shopify.theme.schema_version;
headers['X-Shopify-Theme-Store-Id'] = window.Shopify.theme.theme_store_id;
}
fetch(
`${window._RestockRocketCon
- fig.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 initializeScripts(settings) {
settings = applyTranslations(settings);
window._RestockRocketConfig.settings = settings;
console.debug(`STOQ - settings configured for ${window._RestockRocketConfig.pageType}`);
// Setup
cart selling plan updater BEFORE loading any scripts to avoid race conditions
setupCartSellingPlanUpdater(settings);
if(settings.enable_app) {
const hijackIntegration = window._RestockRocketConfig.integrations.find(function(integration) {
return integration.type === 'hijack' && integration.enabled && integration.page_types.includes(window._RestockRocketConfig.pageType);
})
if(window._RestockRocketConfig.pageType === 'collection' && (settings.show_button_on_collection || settings.preorder_collection_enabled)) {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else if(window._RestockRocketConfig.pageType === 'index' && (settings.show_button_on_index || settings.preorder_index_enabled)) {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else if(window._RestockRocketConfig.pageType === 'search' && (settings.show_button_on_search || settings.preorder_search_enabled)) {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else if(window._RestockRocketConfig.pageType === 'page' && (settings.show_button_on_page || settings.preorder_page_enabled)) {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else if(window._RestockRocketConfig.pageType === 'product') {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlProduct);
} else if(hijackIntegration) {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else {
console.debug(`STOQ - no scripts enabled for ${window._RestockRocketConfig.pageType}`);
}
// Dispatch custom event when app is loaded
// Cart selling plan updates will be triggered by stoq:preorder-api-ready event
const appLoadedEvent = new CustomEvent('stoq:loaded', {
detail: {
pageType: window._RestockRocketConfig.pageType,
enabled: settings.enable_app,
settings: settings,
Grips & Bar-TapepreorderEnabled: settings.preorder_enabled
}
});
console.debug('STOQ - dispatching app loaded event');
window.dispatchEvent(appLoadedEvent);
}
}
-
Headsets
.restock-rocket-button,.restock-rocket-button-float{opacity:1!important;border:none!important;cursor:pointer!important;background-image:none!important;box-shadow:none!important;padding:15px 20px;font-size:16px;width:100%;font-family:inherit}@font-face{font-family:OpenSans;font-weight:200;src:url(https://d382hokyqag45a.cloudfront.net/assets/OpenSans-Light.woff)}@font-face{font-family:OpenSans;font-weight:300;src:url(https://d382hokyqag45a.cloudfront.net/assets/OpenSans-Regular.woff)}@font-face{font-family:OpenSans;font-weight:600;src:url(https://d382hokyqag45a.cloudfront.net/assets/OpenSans-SemiBold.woff)}.restock-rocket-button-container{position:relative;z-index:1;width:100%}.restock-rocket-button-container-float-right{position:fixed;z-index:123123;top:c
alc(50% - 200px);right:0;transform:rotate(270deg);transform-origin:bottom right}.restock-rocket-button-container-float-left{position:fixed;z-index:123123;top:calc(50% - 200px);left:40px;transform:rotate(90deg);transform-origin:top left}.restock-rocket-button-container-float-left:hover,.restock-rocket-button-container-float-right:hover,.restock-rocket-button-container:hover,.restock-rocket-button-float:hover,.restock-rocket-button:hover{opacity:.8}.restock-rocket-button{min-height:50px;margin-top:10px;margin-bottom:10px}.restock-rocket-button-collection{position:relative;font-size:13px;line-height:1;padding:7px;height:auto;z-index:3}.restock-rocket-wrapper{background-color:rgba(0,0,0,.5);z-index:123123123;width:100%;height:100%;overflow:auto;position:fixed;right:0;top:0;transition-property:all;transition-duration:.3s;display:flex;flex-direction:column;justify-content:center;}.restock-rocket-wrapper-inline{width:100%;height:100%;margin-top:20px}.restock-rocket-preorder-description{padding:10px 15px;margin-top:2