rst, or clear your cart to continue.","b2b_enabled":true,"preorder_progress_bar_enabled":false,"preorder_progress_bar_text":"{{ sold }} of {{ total }} claimed","preorder_progress_bar_fill_color":"#000000","preorder_progress_bar_background_color":"#e5e5e5","preorder_progress_bar_text_color":"#FFFFFF","preorder_progress_bar_border_radius":4,"preorder_progress_bar_show_percentage":false}];(function() {
const cachedData = {"plans":[{"shopify_selling_plan_group_id":98590196095,"shopify_selling_plan_id":713071886719,"enabled":true,"variant_ids":[55569712382335,55569712415103,55569712480639,55570017616255,55570017550719,55569712447871,55570017583487],"product_variants_source":"custom","name":"Preorder","preorder_button_text":"Preorder","preorder_button_description":"Note: This is a preorder. Items will ship based on the estimated delivery date.","preorder_button_description_enabled":true,"preorder_button_description_background_color":"#ebebeb","preorder_button_description_text_color":"#000000","preorder_button
_description_border_radius":10,"preorder_button_description_show_quantity_limit":false,"preorder_button_description_quantity_limit_suffix":" units available for preorder","preorder_button_description_shipping_text_prefix":"Shipping: ","delivery_exact_time":null,"delivery_after_n_intervals":null,"delivery_at":"2025-07-26T09:20:18.169Z","delivery_type":"asap","quantity_limit_text":"{{ quantity }} units available for preorder","preorder_button_description_show_shipping":true,"preorder_button_description_icons_enabled":true,"preorder_shipping_text":"Shipping: {{ date }}","shipping_applies_to_all_products":true,"shipping_text":"Estimated to ship within 2 months","payment_type":"full","billing_checkout_charge_type":"percentage","billing_checkout_charge_amount":null,"billing_checkout_charge_percentage":"100.0","pricing_type":"no_discount","pricing_amount":null,"pricing_percentage":null,"discount_text":"Save {{ discount }}","billing_title":"Full payment","billing_description":null,"enable_billing_widget":false,"inven
e\n\u003cdiv data-v-05762449=\"\" id=\"comp_sizingTabB2BInstructionsHeightInseam_Wrapper\" data-magellan-target=\"comp_sizingTabB2BInstructionsHeightInseam_Wrapper\"\u003e\n\u003cdiv data-v-05762449=\"\" class=\"vertical-spacer-for-editorial row expand\"\u003e\n\u003csection data-v-05762449=\"\" class=\"editorial-group twoup\"\u003e\n\u003cdiv data-v-05762449=\"\" class=\"yCmsComponent small-12 medium-6 vertical-spacer-1\"\u003e\n\u003carticle data-v-05762449=\"\" id=\"comp_sizingTabB2BInstructions_Height\" class=\"editorial-small text-left\" qaid=\"editorial-component\"\u003e\n\u003cdiv data-v-05762449=\"\" class=\"editorial-small__content\"\u003e\n\u003ch2 data-v-05762449=\"\" class=\"editorial-small__header header-small\" qaid=\"editorial-component-header\"\u003eHow tall are you?\u003c\/h2\u003e\n\u003cp data-v-05762449=\"\" class=\"editorial-small__text text-weak\" qaid=\"editorial-component-body\"\u003eTo measure your height, stand up straight, barefoot, with your back, heels, shoulders and head all toucRidgeback
Wireless Only Frameight and against a wall. Tuck a ruler or something that extends to the floor between your legs. Using both hands, and keeping it level, pull the ruler up into your crotch as if you were sitting on the saddle. Measure the distance from the top edge of the ruler to the ground to find your inside leg measurement. Your inside leg is very important to your final bike size so please measure carefully.\u003c\/p\u003e\n\u003c\/div\u003e\n\u003c\/article\u003e\n\u003c\/div\u003e\n\u003c\/section\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e\n\u003csection data-v-05762449=\"\" id=\"SizingComponent\" data-magellan-target=\"SizingComponent\" class=\"sizing-and-fit-aftermarket-component sizing-table grid-container mb-6\"\u003e\n\u003ch2 data-v-05762449=\"\" class=\"header--lg mb-1 cell small-12\"\u003eSizing\u003c\/h2\u003e\n\u003cdiv data-v-05762449=\"\" class=\"rider-sizing mb-2\"\u003e\n\u003cdiv data-v-4052fbf6=\"\" data-v-05762449=\"\"\u003e\n\u003cdiv data-v-4052fbf6=\"\" class=\"size-table\"\u003e\n\u003ctable data-v
ct.substring(0, window._RestockRocketConfig.scriptUrlProduct.lastIndexOf('/') + 1)
// Canary override (STOQ-1287): prefer the Render host emitted into the
// metafield-cached settings (api_host) when present, mirroring the same
// override in assets/js/api.js. cachedSettings is populated above. Falls
// back to the build-baked host for shops without the use_render_api_host
// toggle (or environments where THEME_EXTENSION_API_HOST is unset), so the
// setting.json + fetchEmbedConfig calls below route to the same host as the
// rest of the extension's API traffic instead of staying on the baked host.
window._RestockRocketConfig.host = (window._RestockRocketConfig.cachedSettings && window._RestockRocketConfig.cachedSettings.api_host) || 'https://app.restockrocket.io'
// Deployed extension build number, read from the CDN asset host Shopify generates:
// https://cdn.shopify.com/extensions/
Malta (EUR€)
Electric Bikes
FRAME HIGHLIGHTSuuid>/
Insure Your Bike
handle>-UTO (ex Eovolt)
version>/assets/...
// Trailing digits (e.g. ".../restockrocket-1-521/assets/" -> "521"). Kept numeric to
// match ParseStoqData, so funnel app_version lines up with the order-attribution
// app_version. Reflects the ACTUAL deployed build. This is the SINGLE source of the
// parsed version — preorder.js getAppVersion() reads it back off config rather than
// re-parsing, so the regex lives in exactly one place.
try {
const _stoqVersionMatch = window._RestockRocketConfig.scriptHost.match(/(\d+)\/?(?:assets\/?)?$/);
window._RestockRocketConfig.appVersion = (_stoqVersionMatch && _stoqVersionMatch[1]) || '';
} catch (e) {
window._RestockRocketConfig.appVersion = '';
}
const SETTINGS_CACHE_DURATION = 15 * 60 * 1000; // 15 minutes in milliseconds
const LIQUID_CACHE_MAX_AGE = 15 * 60; // 15 minutes in seconds
// Calculate Liquid cache freshness once at initialization
const liquidRenderedAt = window._RestockRocketConfig.liquidRenderedAt;
// Validate timestamp and calculate ca
Book a Service66.5 - DEGREE head TUBE angle<
75.5 - DEGREE SEAT TUBE ANGLEtranslation data in metafield
115MM REAR TRAVEL PAIRED WITH A 120MM FORKArgon 18
Threaded BB
See More28-36t Max (38t MD-XL) chainring compatability
Custom Builds
See More
THE NEW ASR MARKS THE MOST EXTENSIVE CARBON PROJECT WE’VE EVER TACKLED. YIELDING THE LIGHTEST FULL SUSPENSION BIKE IN OUR HISTORY, CONTEMPORISING OUR FLEX STAY DESIGN TO CHECK TODAY’S KINEMATICS STANDARDS, KILLER SMALL - BUMP SENSITIVITY, INSANE TRACTION AND 100% USABLE TRAVEL.Custom Builds for saleTHE ASR IS BACK. WE’VE NEVER MADE A LIGHTER FULL - SUSPENSION BIKE, BUT THAT’S JUST HALF THE STORY. IT BRINGS PROPER SUSPENSION TO PROPER MOUNTAIN BIKE RACING. THE KIND THAT TAKES SKILL, NOT JUST LUNGS.
But that’s just XC table stakes. The bike for modern XC needs modern geometry that’s just as stable tipping it over at 45 km/h as it excels on chest high drops and hugging the inside line on the steepest switchback climb.
Puts riders in a power position to efficiently spin, evenly weight the wheels for traction, and transition more smoothly from saddle to full-throttle sprinting.See More
{"@context":"http:\/\/schema.org\/","@id":"\/products\/trek-fx-sport-11#product","@type":"ProductGroup","brand":{"@type":"Brand","name":"Trek"},"category":"Road Bikes","description":"\n\nFX Sport 5 is a carbon fibre fitness bike for riders who want the speed of a lightweight road bike with the comfort and control of a flat handlebar. It has an upgraded high-quality drivetrain, hydraulic disc brakes and tubeless-ready rims making it perfect for everything from solo workouts to speedy group rides.\n\nIt's right for you if...\nYou're ready to log big miles, and you prefer the feel of a flat handlebar to the drop bars that come stock on most road bikes. You prioritise comfort and performance equally, and expect your bike to grow with you as you advance in speed and distance.\n\n\nThe tech you get\nA lightweight 400 Series OCLV Carbon frame and fork, a 1x SRAM Apex drivetrain with a wide range of gearing, hydraulic disc brakes, hidden mudguard mounts, a lightweight tubeless-ready Bontrager Paradigm alloy wheel set, wide, fast-rolling Bontrager GR1 Expert gravel tyres and a vibration-absorbing handlebar with ergonomic grips.\n\n\nThe final word\nThe FX Sport 5 has a light and fast carbon frame and quality parts that will hold up over serious miles, including an upgraded SRAM Apex 1x drivetrain.\n\n\n\n\n\n\nHow tall are you?\nTo measure your height, stand up straight, barefoot, with your back, heels, shoulders and head all touching a wall. While looking straight ahead, place a book or straight edge on your head and slowly push it against the wall. Your straight edge should be parallel with the floor. Mark the spot where the bottom of the book is touching the wall. The distance from the marked spot to the floor is your height.\n\n\n\n\n\n\nWhat is your inside leg?\nHow to measure: Stand barefoot with your back straight and against a wall. Tuck a ruler or something that extends to the floor between your legs. Using both hands, and keeping it level, pull the ruler up into your crotch as if you were sitting on the saddle. Measure the distance from the top edge of the ruler to the ground to find your inside leg measurement. Your inside leg is very important to your final bike size so please measure carefully.\n\n\n\n\n\n\n\nSizing\n\n\n\n\nsize-table\n\n\nSize\nRider Height\nInseam\n\n\n\n\nXS\n\n147 - 155 cm\n4'10\" - 5'1\"\n\n\n69 - 73 cm\n27\" - 29\"\n\n\n\nS\n\n155 - 165 cm\n5'1\" - 5'5\"\n\n\n72 - 78 cm\n28\" - 31\"\n\n\n\nM\n\n165 - 175 cm\n5'5\" - 5'9\"\n\n\n77 - 83 cm\n30\" - 33\"\n\n\n\nL\n\n175 - 186 cm\n5'9\" - 6'1\"\n\n\n82 - 88 cm\n32\" - 35\"\n\n\n\nXL\n\n186 - 197 cm\n6'1\" - 6'6\"\n\n\n87 - 93 cm\n34\" - 37\"\n\n\n\n\n\n\n\n\n\n\nGeometry\n\n\n\n\n\n\nAll measurements provided in cm unless otherwise stated.\n \n\nSizing table\n\n\nFrame size letter\nWheel size\n\nA — Seat tube\n\nB — Seat tube angle\n\nC — Head tube length\n\nD — Head angle\n\nE — Effective top tube\n\nF — Bottom bracket height\n\nG — Bottom bracket drop\n\nH — Chainstay length\n\nI — Offset\n\nJ — Trail\n\nK — Wheelbase\n\nL — Standover\n\nM — Frame reach\n\nN — Frame stack\n\n\n\n\nXS\n700c\n35.6\n75.0°\n11.5\n70.0°\n52.8\n28.5\n6.5\n44.2\n5.0\n7.4\n104.7\n70.8\n38.3\n54.3\n\n\nS\n700c\n40.6\n74.5°\n14.0\n70.5°\n54.3\n28.5\n6.5\n44.2\n5.0\n7.1\n105.4\n75.1\n38.5\n56.8\n\n\nM\n700c\n45.7\n74.0°\n16.5\n71.0°\n56.3\n28.5\n6.5\n44.2\n5.0\n6.8\n106.5\n78.7\n39.3\n59.4\n\n\nL\n700c\n50.8\n73.5°\n19.0\n71.5°\n58.3\n28.5\n6.5\n44.2\n5.0\n6.4\n107.5\n81.2\n40.0\n62.0\n\n\nXL\n700c\n55.9\n72.5°\n21.5\n72.5°\n61.3\n28.5\n6.5\n44.2\n5.0\n5.8\n108.2\n84.8\n41.0\n64.8\n\n\n\n\n\n\n\n\n\n","hasVariant":[{"@id":"\/products\/trek-fx-sport-11?variant=46025438560482#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/files\/FXSport6Carbon-25-47256-A-Primary.webp?v=1728052385\u0026width=1920","name":"Trek FX Sport 6 - Extra Small","offers":{"@id":"\/products\/trek-fx-sport-11?variant=46025438560482#offer","@type":"Offer","availability":"http:\/\/schema.org\/InStock","price":"2565.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/trek-fx-sport-11?variant=46025438560482"},"sku":"5323995"},{"@id":"\/products\/trek-fx-sport-11?variant=46025438593250#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/files\/FXSport6Carbon-25-47256-A-Primary.webp?v=1728052385\u0026width=1920","name":"Trek FX Sport 6 - Small","offers":{"@id":"\/products\/trek-fx-sport-11?variant=46025438593250#offer","@type":"Offer","availability":"http:\/\/schema.org\/OutOfStock","price":"2565.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/trek-fx-sport-11?variant=46025438593250"},"sku":"5323996"},{"@id":"\/products\/trek-fx-sport-11?variant=46025438626018#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/files\/FXSport6Carbon-25-47256-A-Primary.webp?v=1728052385\u0026width=1920","name":"Trek FX Sport 6 - Medium","offers":{"@id":"\/products\/trek-fx-sport-11?variant=46025438626018#offer","@type":"Offer","availability":"http:\/\/schema.org\/OutOfStock","price":"2565.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/trek-fx-sport-11?variant=46025438626018"},"sku":"5323997"},{"@id":"\/products\/trek-fx-sport-11?variant=46025438658786#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/files\/FXSport6Carbon-25-47256-A-Primary.webp?v=1728052385\u0026width=1920","name":"Trek FX Sport 6 - Large","offers":{"@id":"\/products\/trek-fx-sport-11?variant=46025438658786#offer","@type":"Offer","availability":"http:\/\/schema.org\/InStock","price":"2565.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/trek-fx-sport-11?variant=46025438658786"},"sku":"5323998"},{"@id":"\/products\/trek-fx-sport-11?variant=46025438691554#variant","@type":"Product","image":"https:\/\/projektride.co.uk\/cdn\/shop\/files\/FXSport6Carbon-25-47256-A-Primary.webp?v=1728052385\u0026width=1920","name":"Trek FX Sport 6 - Extra Large","offers":{"@id":"\/products\/trek-fx-sport-11?variant=46025438691554#offer","@type":"Offer","availability":"http:\/\/schema.org\/OutOfStock","price":"2565.00","priceCurrency":"GBP","url":"https:\/\/projektride.co.uk\/products\/trek-fx-sport-11?variant=46025438691554"},"sku":"5323999"}],"name":"Trek FX Sport 6","productGroupID":"8729395855586","url":"https:\/\/projektride.co.uk\/products\/trek-fx-sport-11"}
e":"Trek Madone SLR Gen 8 Frame Set - Black \/ S","public_title":"Black \/ S","options":["Black","S"],"price":425125,"weight":0,"compare_at_price":447500,"inventory_management":"shopify","barcode":null,"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":54945483522431,"title":"Black \/ M","option1":"Black","option2":"M","option3":null,"sku":"5321424","requires_shipping":true,"taxable":true,"featured_image":null,"available":true,"name":"Trek Madone SLR Gen 8 Frame Set - Black \/ M","public_title":"Black \/ M","options":["Black","M"],"price":425125,"weight":0,"compare_at_price":447500,"inventory_management":"shopify","barcode":null,"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}},{"id":54945483555199,"title":"Black \/ ML","option1":"Black","option2":"ML","option3":null,"sku":"5321425","requires_shipping":true,"taxable":true,"featured_image":null,"available":true,"name":"Trek Madone SLet - Black \/ XL","public_title":"Black \/ XL","options":["Black","XL"],"price":425125,"weight":0,"compare_at_price":447500,"inventory_management":"shopify","barcode":null,"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}}],"images":["\/\/projektride.co.uk\/cdn\/shop\/files\/MadoneSLRDiscFrameset-25-46775-B-Primary_1.webp?v=1736608593","\/\/projektride.co.uk\/cdn\/shop\/files\/MadoneSLRDiscFrameset-25-46775-A-Primary_1.webp?v=1736608602","\/\/projektride.co.uk\/cdn\/shop\/files\/MadoneSLRDISCFrameset-25-46775-A-Alt1_1.webp?v=1736608609"],"featured_image":"\/\/projektride.co.uk\/cdn\/shop\/files\/MadoneSLRDiscFrameset-25-46775-B-Primary_1.webp?v=1736608593","options":["Colour","Size"],"media":[{"alt":null,"id":63842299216255,"position":1,"preview_image":{"aspect_ratio":1.333,"height":1440,"width":1920,"src":"\/\/projektride.co.uk\/cdn\/shop\/files\/MadoneSLRDiscFrameset-25-46775-B-Primary_1.webp?v=1736608593"},"aspect_ratio":1.333,"height":1440,"mediae82=\"\" class=\"b2b-buying-zone__info cell medium-6 large-12\"\u003e\n\u003cdiv data-v-e224ae82=\"\" class=\"buying-zone__positioning-statement mb-2 pt-2 border-t border-gray-10\"\u003e\n\u003cp data-v-e224ae82=\"\"\u003eThe Madone SLR Gen 8 Frame Set is ready for your dream build and the top step of the podium. It's crafted with our highest-level 900 Series OCLV Carbon that's not only insanely light, but also delivers free speed with aerodynamic shapes for cutting through wind. Plus, IsoFlow frame technology adds comfort on long rides without sacrificing speed or aerodynamics.\u003c\/p\u003e\n\u003cp\u003e*Please note – spec applies to all sizes unless listed separately\u003c\/p\u003e\n\u003cp\u003e\u003cbr\u003eFrameset\u003cbr\u003eFrame 900 Series OCLV Carbon, Full System Foil tube shaping, IsoFlow seat tube, RCS Headset System, electronic-only routing, removable aero chain keeper, T47 BB, flat mount disc, UDH, 142x12 mm thru axle\u003cbr\u003eFork Madone Gen 8 one-piece carbon, tapered carbcations, models, colours, materials and pricing. Due to supply chain issues, compatible parts may be substituted at any time without notice.\u003c\/p\u003e\n\u003cp\u003eBike and frame weights are based on pre-production painted frames at time of publication. Weights may vary in final production.\u003c\/p\u003e\n\u003c\/div\u003e\n\u003c\/div\u003e"};
window._RestockRocketConfig.variantsInventoryPolicy = {54945483260287 : "deny",54945483293055 : "deny",54945483325823 : "deny",54945483358591 : "deny",54945483391359 : "deny",54945483424127 : "deny",54945483456895 : "deny",54945483489663 : "deny",54945483522431 : "deny",54945483555199 : "deny",54945483587967 : "deny",54945483620735 : "deny",};
window._RestockRocketConfig.variantsInventoryQuantity = {54945483260287 : parseInt("13"),54945483293055 : parseInt("14"),54945483325823 : parseInt("20"),54945483358591 : parseInt("20"),54945483391359 : parseInt("20"),54945483424127 : parseInt("13"),54945483456895 : parseInt("9"),54945483489663 : parseInt("20"),5494
che age
if (!liquidRenderedAt || typeof liquidRenderedAt !== 'number' || isNaN(liquidRenderedAt)) {
console.debug('STOQ - Invalid or missing liquidRenderedAt timestamp, assuming fresh');
window._RestockRocketConfig.isLiquidCacheFresh = true;
window._RestockRocketConfig.liquidCacheAge = null;
} else {
const now = Math.floor(Date.now() / 1000); // Current time in seconds
const liquidCacheAge = now - liquidRenderedAt; // Age in seconds
// Surfaced into funnel events: a stale cache means the app rendered with
// outdated inventory/selling-plan data — a real "had the opportunity but
// failed" cause. Negative (client clock ahead) clamps to 0.
window._RestockRocketConfig.liquidCacheAge = Math.max(0, liquidCacheAge);
// Handle client clock ahead of server
if (liquidCacheAge
0) {
console.debug(`STOQ - Client clock appears ahead of server by ${Math.abs(Math.round(liquidCacheAge / 60))} minutes, assuming cache fresh`);
window._RestockRocketConfig.isLiquidCacheFresh = true;
} else if (liquidCacheAge
= LIQUID_CACHE_MAX_AGE) {
console.debug(`STOQ - Liquid cache is fresh (${Math.round(liquidCacheAge / 60)} minutes old)`);
window._RestockRocketConfig.isLiquidCacheFresh = true;
} else {
console.debug(`STOQ - Liquid cache is stale (${Math.round(liquidCacheAge / 60)} minutes old, max ${Math.round(LIQUID_CACHE_MAX_AGE / 60)} minutes)`);
window._RestockRocketConfig.isLiquidCacheFresh = false;
}
}
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
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')
// Fire stoq_initialized once per page load so the funnel pipeline has a definitive
// "our code ran on this page" signal independent of any cusctedVariantId || '',
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);
}
// STOQ-1595: publish stoq_accelerated_checkout_clicked for every accelerated-checkout
// click (Buy Now / dynamic checkout / Shop Pay installments) on ALL surfaces, before our
// bundle loads, via one delegated capture-phase listener (catches late-rendered buttons).
// Only on shops with preorders configured (shared `preordersConfigured` signal from the
// derivatist('form');
var vidEl = form && form.querySelector('[name="id"]');
var variantId = (vidEl && vidEl.value) || _sqTrack.selected_variant_id || '';
// The enabled STOQ selling plan the clicked variant belongs to (if any), found once and
// reused for in_enabled_plan / enrolled_plan_id / enrolled_plan_provider below.
var _sqVidNum = Number(variantId);
var _sqPlan = null;
// Array.isArray guard (not `|| []`) so a truthy non-array sellingPlans can't throw and
// silently drop the whole publish.
var _sqPlans = Array.isArray(_sqTrack.sellingPlans) ? _sqTrack.sellingPlans : [];
_sqPlans.forEach(function (pl) {
if (!_sqPlan && pl && pl.enabled && Array.isArray(pl.variant_ids) &&
pl.variant_ids.indexOf(_sqVidNum) !== -1) { _sqPlan = pl; }
});
Shopify?.analytics?.publish?.('stoq_accelerated_checkout_clicked', {
variant_id: variantId,
product_id: (_sqTr
a like-typed compare.
is_preorder_variant: (function () {
var vid = Number(variantId);
var has = function (a) { return Array.isArray(a) && a.indexOf(vid) !== -1; };
return has(_sqTrack.preorderVariantIds) ||
has(_sqTrack.cachedPreorderVariantIds &&
_sqTrack.cachedPreorderVariantIds.preorder_variant_ids);
})(),
// Narrow, authoritative preorder signal: enrolled in an ENABLED STOQ selling plan
// (is_preorder_variant above is the broad OOS+continue heuristic — non-STOQ too).
in_enabled_plan: !!_sqPlan,
// In-stock preorder signal: enrolled but currently IN STOCK (in the inStockVariantIds
// set, not the OOS preorderVariantIds set). Same live-OR-cached pattern as
// is_preorder_variant. is_preorder_variant OR is_instock_variant = "should sell via
// the plan" regardless of stock, so an in-stock preordcceleratedGateArmed,
gate_active_at_click: !!(document.documentElement &&
document.documentElement.classList.contains('stoq-buy-now-pending')),
},
});
} catch (err) { /* never break checkout */ }
}, true);
}
} catch (e) { /* fail open */ }
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 || {})
tion 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 updated successfully');
} else {
console.debug('STOQ - no cart selling plan updates needed');
}
})
.catch(error => {
console.error('STOQ - error updating cart selling plans:', error);
});
}
});
}
// ---- Live settings via the Storefront API (ad< "settings") { value } } }';
fetch(
`https://${cfg.shop}/api/2025-07/graphql.json`,
{
method: 'POST',
headers: {
'Content-Type': 'application/json',
'X-Shopify-Storefront-Access-Token': cfg.storefrontAccessToken,
},
body: JSON.stringify({ query: query, variables: { namespace: cfg.metafieldNamespace } }),
}
)
.then(function(response) {
if (!response.ok) { throw new Error('Storefront API HTTP ' + response.status); }
return response.json();
})
.then(function(body) {
if (body && body.errors && body.errors.length) {
throw new Error('Storefront API errors: ' + body.errors.map(function(e) { return e && e.message; }).join(', '));
}
const value = body && body.data && body.data.shop && body.data.shop.metafield && body.data.shop.metafield.value;
if (!value) { throw new Error('Storefront API returned no settings metafield value'); }
cfg.stop'
};
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._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('S
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 scr }
}),
fetchEmbedConfig('integrations', function(data) {
if (Array.isArray(data)) {
window._RestockRocketConfig.integrations = data;
}
})
]),
new Promise(function(resolve) { setTimeout(resolve, 1000); })
]).then(function() { loadScripts(settings); });
return;
}
loadScripts(settings);
}
function loadScripts(settings) {
// 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);
})
// STOQ-1520: serve the lean back-in-stock-only build (no preorder/hijack code)
// only to shops with NO preorder plans. Use the full build if preorder is o
onPageTypes = ['collection', 'index', 'search', 'page'];
if(collectionPageTypes.indexOf(pageType) !== -1 && (settings[`show_button_on_${pageType}`] || settings[`preorder_${pageType}_enabled`])) {
createRestockRocketScript(collectionScriptUrl);
} else if(pageType === 'product') {
createRestockRocketScript(productScriptUrl);
} else if(hijackIntegration) {
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else if(usePreorderBuild) {
// cart/article/blog/list-collections: full build so the cart sweep runs.
createRestockRocketScript(window._RestockRocketConfig.scriptUrlCollection);
} else {
console.debug(`STOQ - no scripts enabled for ${pageType}`);
}
// Dispatch custom event when app is loaded
// Cart selling plan updates will be triggered by stoq:inventory-data-loaded event
const appLoadedEvent = new CustomEvent('stoq:loaded', {
detail: {
pageType: window._RestockR