/**
* Challenge floor: reads window.CHALLENGE_FLOOR_BOOT { currencyRates, programData, challengeFeeDiscount? },
* drives UI via appState (programType, instantMode, aifoliteMode, currency, feeDiscount).
* List price = round(fee × rate); sale price = round(fee × rate × feeDiscount) to 2 decimal places (cents); integers display without “.00”.
*/
(function ($) {
var boot = window.CHALLENGE_FLOOR_BOOT;
if (!boot || !boot.programData) {
console.warn("CHALLENGE_FLOOR_BOOT missing; load challenge-floor-program-data.js first.");
return;
}
var programData = boot.programData;
var currencyRates = boot.currencyRates;
var defaultFeeDiscount =
boot && boot.challengeFeeDiscount != null && !isNaN(Number(boot.challengeFeeDiscount))
? Number(boot.challengeFeeDiscount)
: 0.5;
var appState = {
programType: "2step",
instantMode: "NoCommission",
aifoliteMode: "lite",
accountSize: "10k",
expandedParam: null,
currency: "USD",
feeDiscount: defaultFeeDiscount,
};
if (typeof window.CHALLENGE_FLOOR_APP_STATE === "object" && window.CHALLENGE_FLOOR_APP_STATE) {
$.extend(appState, window.CHALLENGE_FLOOR_APP_STATE);
}
if (appState.feeDiscount == null || (typeof appState.feeDiscount === "number" && isNaN(appState.feeDiscount))) {
appState.feeDiscount = defaultFeeDiscount;
} else {
appState.feeDiscount = normalizeFeeDiscount(appState.feeDiscount);
}
/** Theme URL for ladder SVGs etc.; set via CHALLENGE_FLOOR_BOOT.assetBase or global assets_url (header.php). */
var CHALLENGE_FLOOR_ASSET_BASE = (function () {
if (boot && boot.assetBase) {
return String(boot.assetBase).replace(/\/?$/, "/");
}
if (typeof assets_url !== "undefined" && assets_url) {
return String(assets_url).replace(/\/?$/, "/") + "assets/images/challenge/";
}
return "../assets/images/challenge/";
})();
var parameterDescriptions = {
"Challenge Fee": "One-time fee to participate in the challenge program",
"Daily Drawdown":
"Maximum percentage loss allowed in a single trading day based on the previous day’s closing balance or equity.",
"Max Drawdown":
"The Maximum Loss Limit is the maximum allowable loss calculated based on the highest equity value your account reaches during the evaluation or trading phase.",
"Profit Target": "The percentage gain required to successfully complete each phase of the challenge.",
"Enhanced Payout Target":
"Higher target threshold used to unlock enhanced payout benefits in this program.",
"Enhanced Payout":
"≥ 6% profit target for applying for 95% profit split and advance settlement.",
"Challenge Window":
"After account activation, users are granted a 24-hour trading window. The countdown begins upon successful challenge activation.",
"Max Floating Loss":
"The total floating loss of all open positions must not exceed 1% of the initial account balance.",
"Consistency Score":
"A profit-distribution metric that measures how evenly your profits are generated over time.",
"withdrawable profit":
"The Profit Target refers to the required percentage of withdrawable profit that the account must achieve before a payout can be requested.",
"Profit split": "Percentage of profits you keep. The firm keeps the remainder.",
"Payout period": "Minimum time required before you can request a withdrawal of profits.",
"Trading days": "Minimum trading days required to pass the phase.",
"News Trading": "",
};
var parameterDescriptionsLite = {
"Challenge Fee": "One-time fee to participate in the challenge program.",
"Profit Target": "The percentage gain required to successfully complete each phase of the challenge.",
"Enhanced Payout":
"≥ 6% profit target for applying for 95% profit split and advance settlement.",
"Challenge Window":
"After account activation, users are granted a 24-hour trading window. The countdown begins upon successful challenge activation.",
"Max Floating Loss":
"The total floating loss of all open positions must not exceed 1% of the initial account balance.",
"Max Drawdown":
"The maximum loss limit follows a static drawdown model. If account equity falls below 98% of the initial balance, the account will be immediately terminated.",
"Profit split": "Percentage of profits you keep. The firm keeps the remainder.",
};
var parameterNames = [
"Challenge Fee",
"Daily Drawdown",
"Max Drawdown",
"Profit Target",
"Profit split",
"Trading days",
"Payout period",
];
var parameterNames_instant = [
"Challenge Fee",
"Daily Drawdown",
"Max Drawdown",
"Consistency Score",
"Profit split",
"Payout period",
];
var parameterNames_lite = [
"Challenge Fee",
"Profit Target",
"Enhanced Payout",
"Challenge Window",
"Max Floating Loss",
"Max Drawdown",
"Profit split",
];
var PARAM_TO_FIELD = {
"Challenge Fee": "fee",
"Daily Drawdown": "daily",
"Max Drawdown": "max",
"Profit Target": "target",
"withdrawable profit": "target",
"Consistency Score": "consistency",
"Enhanced Payout Target": "enhanced_target",
"Enhanced Payout": "enhanced_target",
"Challenge Window": "window",
"Max Floating Loss": "floating",
"Profit split": "split",
"Profit Split": "split",
"Trading days": "tradingDays",
"Payout period": "payout",
};
var ICON_BY_PARAM = {
"Challenge Fee": "Component 26.svg",
"Daily Drawdown": "Component 27.svg",
"Max Drawdown": "Component 28.svg",
"Profit Target": "Component 29.svg",
"Enhanced Payout Target": "Component 29.svg",
"Enhanced Payout": "Component 29.svg",
"Challenge Window": "Component 29.svg",
"Max Floating Loss": "Component 28.svg",
"Consistency Score": "challenge_icon_consistency_score.png",
"withdrawable profit": "Component 29.svg",
"Profit split": "Component 30.svg",
"Profit Split": "Component 30.svg",
"Trading days": "Component 32.svg",
"Payout period": "Component 31.svg",
"News Trading": "Component 32.svg",
};
function isAifoliteEliteMode() {
return appState.programType === "aifolite" && appState.aifoliteMode === "aifo";
}
function getParameterDescription(paramName) {
if (appState.programType === "aifolite" && !isAifoliteEliteMode() && parameterDescriptionsLite[paramName]) {
return parameterDescriptionsLite[paramName];
}
if (
(appState.programType === "instant" || isAifoliteEliteMode()) &&
paramName === "Consistency Score"
) {
return (
"A profit-distribution metric that measures how evenly your profits are generated over time, requiring that no single day contributes 15% or more of total profit and that the two most profitable days combined contribute less than 25% of total profit."
);
}
return parameterDescriptions[paramName] || "";
}
function normalizeInstantMode(mode) {
if (!programData.instant) return "NoCommission";
if (mode && programData.instant[mode]) return mode;
if (programData.instant.NoCommission) return "NoCommission";
if (programData.instant.pro) return "pro";
if (programData.instant.aifo) return "aifo";
var keys = Object.keys(programData.instant);
return keys.length ? keys[0] : "NoCommission";
}
function normalizeAifoliteMode(mode) {
if (mode === "aifo" && programData.instant && programData.instant.aifo) return "aifo";
return "lite";
}
function getCurrentProgramConfig() {
var cur = appState.currency;
var p = appState.programType;
if (p === "instant") {
var mode = normalizeInstantMode(appState.instantMode);
appState.instantMode = mode;
if (!programData.instant[mode] || !programData.instant[mode][cur]) {
return null;
}
return programData.instant[mode][cur];
}
if (p === "aifolite") {
var aMode = normalizeAifoliteMode(appState.aifoliteMode);
appState.aifoliteMode = aMode;
if (aMode === "aifo") {
if (!programData.instant || !programData.instant.aifo || !programData.instant.aifo[cur]) {
return null;
}
return programData.instant.aifo[cur];
}
if (!programData.aifolite || !programData.aifolite[cur]) return null;
return programData.aifolite[cur];
}
if (!programData[p] || !programData[p][cur]) return null;
return programData[p][cur];
}
function ensureValidCurrency() {
var cfg = getCurrentProgramConfig();
if (cfg) return cfg;
var codes = Object.keys(currencyRates);
for (var i = 0; i < codes.length; i++) {
appState.currency = codes[i];
if (getCurrentProgramConfig()) return getCurrentProgramConfig();
}
return null;
}
function escapeHtml(s) {
return String(s)
.replace(/&/g, "&")
.replace(//g, ">")
.replace(/"/g, """);
}
function formatFee(currency, feeDigits) {
var meta = currencyRates[currency] || currencyRates.USD;
var sym = meta.symbol;
var rate = meta.rate != null ? meta.rate : 1;
var n = parseFloat(String(feeDigits).replace(/,/g, ""));
if (isNaN(n)) return sym + feeDigits;
var v = n * rate;
return sym + v.toLocaleString("en-US", { maximumFractionDigits: 0 });
}
function parseFeeNumber(feeDigits) {
var n = parseFloat(String(feeDigits).replace(/,/g, ""));
return isNaN(n) ? NaN : n;
}
var FEE_SALE_DECIMAL_PLACES = 2;
var FEE_AMOUNT_EPS = 1e-9;
function roundToCurrencyDecimals(value, decimalPlaces) {
if (value == null || isNaN(value)) return NaN;
var d =
decimalPlaces == null ? FEE_SALE_DECIMAL_PLACES : Math.max(0, Math.floor(Number(decimalPlaces)));
if (d === 0) return Math.round(Number(value));
var m = Math.pow(10, d);
return Math.round(Number(value) * m) / m;
}
function isEffectivelyIntegerAmount(amount) {
if (amount == null || isNaN(amount)) return false;
var r = Math.round(amount);
return Math.abs(amount - r) < FEE_AMOUNT_EPS * Math.max(1, Math.abs(amount));
}
function stripTrailingZerosFromLocalizedNumber(str) {
if (str.indexOf(".") < 0) return str;
return str.replace(/(\.\d*?)0+$/, "$1").replace(/\.$/, "");
}
function formatFeeCurrencyAmount(currency, amount) {
var meta = currencyRates[currency] || currencyRates.USD;
var sym = meta.symbol;
if (amount == null || isNaN(amount)) return sym + "—";
var neg = amount < 0;
var x = neg ? -amount : amount;
var core;
if (isEffectivelyIntegerAmount(x)) {
core = Math.round(x).toLocaleString("en-US", { maximumFractionDigits: 0 });
} else {
core = stripTrailingZerosFromLocalizedNumber(
x.toLocaleString("en-US", { minimumFractionDigits: 0, maximumFractionDigits: 14 })
);
}
return (neg ? "-" : "") + sym + core;
}
function normalizeFeeDiscount(mult) {
var d = mult == null || isNaN(Number(mult)) ? 1 : Number(mult);
if (d <= 0) return 0.01;
if (d > 1) return 1;
return d;
}
function feeDisplayAmounts(currency, feeDigits, discountMult) {
var meta = currencyRates[currency] || currencyRates.USD;
var rate = meta.rate != null ? meta.rate : 1;
var d = normalizeFeeDiscount(discountMult);
var n = parseFeeNumber(feeDigits);
if (isNaN(n)) return { listNum: NaN, saleNum: NaN };
var listNum = Math.round(n * rate);
var rawSale = n * rate * d;
var saleNum = roundToCurrencyDecimals(rawSale, FEE_SALE_DECIMAL_PLACES);
return { listNum: listNum, saleNum: saleNum };
}
function formatFeeRoundedAmount(currency, roundedInt) {
var meta = currencyRates[currency] || currencyRates.USD;
var sym = meta.symbol;
if (roundedInt == null || isNaN(roundedInt)) return sym + "—";
return sym + roundedInt.toLocaleString("en-US", { maximumFractionDigits: 0 });
}
function formatAccountSize(sizeKey) {
var n = parseInt(String(sizeKey).replace(/k$/i, ""), 10);
if (isNaN(n)) return sizeKey;
return "$" + n.toLocaleString("en-US") + ",000";
}
/** Mobile size pills: "10k" → "10K" (matches design, not currency-prefixed). */
function formatMobileSizeShort(sizeKey) {
var m = String(sizeKey).match(/^(\d+)k$/i);
if (m) return m[1] + "K";
return String(sizeKey).toUpperCase();
}
function normalizeRow(raw) {
var o = $.extend({}, raw);
o.tradingDays = o.miniday != null ? o.miniday : o.tradingDays != null ? o.tradingDays : "—";
return o;
}
function getActiveParameterNames() {
if (appState.programType === "aifolite") {
return isAifoliteEliteMode() ? parameterNames_instant.slice() : parameterNames_lite.slice();
}
if (appState.programType === "instant") return parameterNames_instant.slice();
return parameterNames.slice();
}
function profitTargetRowClass() {
if (appState.programType === "3step") return " phase-target-row phase-target-row--3";
if (appState.programType === "2step") return " phase-target-row phase-target-row--2";
if (appState.programType === "1step") return " phase-target-row phase-target-row--1";
return "";
}
function getLadderDefinitions() {
return getActiveParameterNames()
.filter(function (name) {
return name !== "Challenge Fee";
})
.map(function (paramName, idx) {
return {
paramName: paramName,
label: paramName,
icon: ICON_BY_PARAM[paramName] || "Component 29.svg",
tooltip: getParameterDescription(paramName),
rowClass: paramName === "Profit Target" ? profitTargetRowClass() : "",
ladderIndex: idx,
ladderKey: String(paramName)
.replace(/\s+/g, "-")
.replace(/[^a-zA-Z0-9-]/g, "")
.toLowerCase(),
};
});
}
function getCellRaw(row, paramName) {
var key = PARAM_TO_FIELD[paramName];
if (!key) return "";
if (key === "tradingDays") return row.tradingDays != null ? String(row.tradingDays) : "—";
var v = row[key];
return v == null ? "" : String(v);
}
function cellAllowsHtml(paramName) {
return (
paramName === "Profit Target" ||
paramName === "Consistency Score" ||
paramName === "withdrawable profit"
);
}
function metricHelpMarkup(label, tooltip, opts) {
opts = opts || {};
var mobileCard = !!opts.mobileCard;
var btnClass = "metric-help-btn" + (mobileCard ? " metric-help-btn--sr-only" : "");
var btnTab = mobileCard ? "-1" : "0";
var btnAriaHidden = mobileCard ? ' aria-hidden="true"' : "";
return (
'' +
'' +
' ' +
" " +
'' +
'' +
escapeHtml(tooltip) +
" "
);
}
var LADDER_HEAD = { label: "Account Size", icon: "Component 33.svg" };
var LADDER_FOOT = {
line1: "One-time",
line2: "Challenge Fee",
icon: "Component 34.svg",
};
function renderMetricsFootItem() {
return (
'
"
);
}
function renderMetricsAnchorItem(label, iconFile, anchorClass, sectionClass) {
return (
'' +
'
' +
'
' +
escapeHtml(label) +
" " +
"
"
);
}
function renderMetricsColumn(rows) {
var body = rows
.map(function (m) {
var rowCls = m.rowClass || "";
var dataAttrs =
' data-ladder-row="' +
m.ladderIndex +
'" data-ladder-key="' +
escapeHtml(m.ladderKey) +
'"';
return (
'" +
'
' +
'
' +
escapeHtml(m.label) +
" " +
metricHelpMarkup(m.label, m.tooltip) +
"
"
);
})
.join("");
return (
renderMetricsAnchorItem(LADDER_HEAD.label, LADDER_HEAD.icon, "metric-item--ladder-head", "head") +
'' +
body +
"
" +
'"
);
}
function priceRowHtml(acc) {
var highlight = acc.highlight ? " highlight" : "";
var rowModifier = acc.best ? " price-row--best" : "";
var bestIcon = acc.best
? ' '
: "";
var oldPart = acc.oldPrice
? '' + escapeHtml(acc.oldPrice) + " "
: "";
return (
'' +
bestIcon +
'' +
escapeHtml(acc.priceFormatted) +
" " +
oldPart +
"
"
);
}
function renderMobileMetricRows(acc, rows) {
return rows
.map(function (m) {
var raw = getCellRaw(acc, m.paramName);
var inner = cellAllowsHtml(m.paramName) ? raw : escapeHtml(raw);
var rowCls = m.rowClass || "";
var dataAttrs =
' data-ladder-row="' +
m.ladderIndex +
'" data-ladder-key="' +
escapeHtml(m.ladderKey) +
'"';
return (
'" +
'
' +
' ' +
'' +
escapeHtml(m.label) +
" " +
metricHelpMarkup(m.label, m.tooltip, { mobileCard: true }) +
" " +
'
' +
inner +
" " +
"
"
);
})
.join("");
}
function renderDesktopAccountCard(acc, rows) {
var best = acc.best ? " best" : "";
var tag = acc.best ? 'BEST VALUE
' : "";
var discount =
acc.discountTag ?
' ' :
"";
var values = rows
.map(function (m) {
var raw = getCellRaw(acc, m.paramName);
var inner = cellAllowsHtml(m.paramName) ? raw : escapeHtml(raw);
var rowCls = m.rowClass || "";
var dataAttrs =
' data-ladder-row="' +
m.ladderIndex +
'" data-ladder-key="' +
escapeHtml(m.ladderKey) +
'"';
return '" + inner + "
";
})
.join("");
var pid = acc.product_id != null ? acc.product_id : "";
var vid = acc.variant_id != null ? acc.variant_id : "";
return (
'' +
'
' +
tag +
'Account
' +
discount +
escapeHtml(acc.sizeLabel) +
"
" +
'' +
values +
"
" +
'' +
" "
);
}
function renderMobileAccountCard(acc, rows, cardIndex) {
var best = acc.best ? " best" : "";
var tag = acc.best ? 'BEST VALUE
' : "";
var discount =
acc.discountTag ?
' ' :
"";
var pid = acc.product_id != null ? acc.product_id : "";
var vid = acc.variant_id != null ? acc.variant_id : "";
var idxAttr =
cardIndex != null && !isNaN(Number(cardIndex))
? ' data-challenge-card-index="' + String(Number(cardIndex)) + '"'
: "";
return (
'" +
'
' +
tag +
'Account
' +
discount +
escapeHtml(acc.sizeLabel) +
"
" +
'' +
'Start Challenge ' +
priceRowHtml(acc) +
"
" +
'' +
renderMobileMetricRows(acc, rows) +
"
" +
" "
);
}
function buildAccountsFromConfig(cfg) {
if (!cfg || !cfg.availableSizes) return [];
var best = cfg.best_value_lable || [];
var discount = cfg.discount_lable || [];
return cfg.availableSizes
.map(function (sizeKey) {
var raw = cfg.data[sizeKey];
if (!raw) return null;
var row = normalizeRow(raw);
row.sizeKey = sizeKey;
row.sizeLabel = formatAccountSize(sizeKey);
row.best = best.indexOf(sizeKey) >= 0;
row.discountTag = discount.indexOf(sizeKey) >= 0;
var amounts = feeDisplayAmounts(appState.currency, row.fee, appState.feeDiscount);
if (isNaN(amounts.saleNum)) {
row.priceFormatted = formatFee(appState.currency, row.fee);
row.oldPrice = "";
} else {
row.priceFormatted = formatFeeCurrencyAmount(appState.currency, amounts.saleNum);
var listVal = amounts.listNum;
var saleVal = amounts.saleNum;
var showOld =
!isNaN(listVal) &&
!isNaN(saleVal) &&
Math.abs(listVal - saleVal) > FEE_AMOUNT_EPS * Math.max(1, Math.abs(listVal));
row.oldPrice = showOld ? formatFeeRoundedAmount(appState.currency, listVal) : "";
}
row.highlight = row.best;
row.product_id = raw.product_id;
row.variant_id = raw.variant_id;
return row;
})
.filter(Boolean);
}
function buildAifolite24hNoteHtml() {
return (
'' +
'
' +
'
' +
'
Note:
' +
"
" +
"The challenge begins immediately after purchase. To ensure you have the maximum time to complete the challenge. Please take consider market closing time and holidays." +
"
" +
"
" +
"The instrument used in the first executed trade will be considered the only permitted trading instrument for the account. Any subsequent trading of other instruments will be deemed a violation." +
"
" +
"
" +
"Please refer to the FAQs for more information." +
"
" +
"
"
);
}
function buildFloorHtml() {
var cfg = ensureValidCurrency();
if (!cfg) {
return 'No program data for this selection.
';
}
var rows = getLadderDefinitions();
var accounts = buildAccountsFromConfig(cfg);
if (!accounts.length) {
return 'No tiers available.
';
}
var metricsCol = renderMetricsColumn(rows);
var desktopCards = accounts.map(function (a) {
return renderDesktopAccountCard(a, rows);
}).join("");
var mobileCards = accounts
.map(function (a, i) {
return renderMobileAccountCard(a, rows, i);
})
.join("");
var mobileSizeTabs =
'' +
accounts
.map(function (a, i) {
var activeCls = i === 0 ? " active" : "";
var dot = a.best ? ' ' : "";
return (
'' +
dot +
'' +
escapeHtml(formatMobileSizeShort(a.sizeKey)) +
" "
);
})
.join("") +
"
";
var rowLayoutExtras = "";
if (accounts.length === 1) {
rowLayoutExtras = " challenge-floor-row--single-card";
} else if (accounts.length === 5) {
rowLayoutExtras = " challenge-floor-row--five-cards-pc";
} else if (accounts.length === 4) {
rowLayoutExtras = " challenge-floor-row--four-cards-pc";
} else if (
(appState.programType === "instant" && appState.instantMode === "aifo") ||
isAifoliteEliteMode()
) {
rowLayoutExtras = " challenge-floor-row--instant-aifo-two-pc";
} else if (appState.programType === "aifolite") {
rowLayoutExtras = " challenge-floor-row--aifolite-24h-pc";
}
var mainRow =
'";
var note =
appState.programType === "aifolite" && !isAifoliteEliteMode() ? buildAifolite24hNoteHtml() : "";
return mainRow + note;
}
function bindChallengeCardsScroll($ctx) {
$ctx.find(".js-challenge-cards-scroll").each(function () {
var $el = $(this);
$el.off("scroll.challengeCards").on("scroll.challengeCards", function () {
$el.addClass("is-scrolling");
window.clearTimeout($el.data("challengeScrollTimer"));
$el.data(
"challengeScrollTimer",
window.setTimeout(function () {
$el.removeClass("is-scrolling");
}, 900)
);
});
});
}
/** 992px~1200px:与 CSS 卡片固定宽区间一致 */
function isMidDesktopCardsViewport() {
return window.matchMedia("(min-width: 992px) and (max-width: 1199.98px)").matches;
}
var challengeCardsDragDocBound = false;
var challengeCardsDragActive = null;
function unbindChallengeCardsDragScroll($ctx) {
$ctx.find(".js-challenge-cards-scroll").each(function () {
$(this)
.off(".challengeCardsDrag")
.removeClass("challenge-cards-scroll--drag-enabled is-drag-scrolling")
.css("cursor", "");
});
challengeCardsDragActive = null;
if (challengeCardsDragDocBound) {
$(document).off(".challengeCardsDrag");
challengeCardsDragDocBound = false;
}
}
function bindChallengeCardsDragScroll($ctx) {
unbindChallengeCardsDragScroll($ctx);
if (!isMidDesktopCardsViewport()) return;
function onDocMouseMove(e) {
if (!challengeCardsDragActive) return;
var drag = challengeCardsDragActive;
var dx = e.pageX - drag.startX;
if (Math.abs(dx) > 4) drag.moved = true;
drag.el.scrollLeft = drag.startScroll - dx;
}
function onDocMouseUp() {
if (!challengeCardsDragActive) return;
var drag = challengeCardsDragActive;
drag.$el.removeClass("is-drag-scrolling");
if (drag.moved) {
drag.$el.data("challengeCardsDragMoved", true);
window.setTimeout(function () {
drag.$el.removeData("challengeCardsDragMoved");
}, 0);
}
challengeCardsDragActive = null;
}
if (!challengeCardsDragDocBound) {
$(document).on("mousemove.challengeCardsDrag", onDocMouseMove);
$(document).on("mouseup.challengeCardsDrag", onDocMouseUp);
challengeCardsDragDocBound = true;
}
$ctx.find(".js-challenge-cards-scroll").each(function () {
var el = this;
var $el = $(el);
$el.addClass("challenge-cards-scroll--drag-enabled");
$el.on("mousedown.challengeCardsDrag", function (e) {
if (e.button !== 0) return;
if (
$(e.target).closest(
"button, a, input, select, textarea, label, .metric-help-btn, .metric-label-dotted"
).length
) {
return;
}
challengeCardsDragActive = {
el: el,
$el: $el,
startX: e.pageX,
startScroll: el.scrollLeft,
moved: false,
};
$el.addClass("is-drag-scrolling");
e.preventDefault();
});
$el.on("click.challengeCardsDrag", "button, a", function (e) {
if ($el.data("challengeCardsDragMoved")) {
e.preventDefault();
e.stopImmediatePropagation();
}
});
});
}
function syncMobileSizeTabFromScroll($mount) {
var $scroll = $mount.find(".js-challenge-cards-scroll.d-lg-none");
var $tabs = $mount.find(".js-challenge-mobile-size-tabs");
if (!$scroll.length || !$tabs.length) return;
var $cards = $scroll.find(".challenge-cards-inner > [class*='col-']");
if (!$cards.length) return;
var scrollEl = $scroll[0];
var scRect = scrollEl.getBoundingClientRect();
var mid = scRect.left + scRect.width / 2;
var bestI = 0;
var bestD = Infinity;
$cards.each(function (idx) {
var r = this.getBoundingClientRect();
var c = r.left + r.width / 2;
var d = Math.abs(c - mid);
if (d < bestD) {
bestD = d;
bestI = idx;
}
});
$tabs.find(".challenge-mobile-size-tab").each(function (idx) {
var on = idx === bestI;
$(this).toggleClass("active", on);
$(this).attr("aria-selected", on ? "true" : "false");
});
}
function bindMobileSizeTabSync($mount) {
var $scroll = $mount.find(".js-challenge-cards-scroll.d-lg-none");
var $tabs = $mount.find(".js-challenge-mobile-size-tabs");
if (!$scroll.length || !$tabs.length) return;
function sync() {
syncMobileSizeTabFromScroll($mount);
}
$scroll.off("scroll.mobileSizeTabs").on("scroll.mobileSizeTabs", function () {
window.requestAnimationFrame(sync);
});
$tabs
.off("click.mobileSizeTabs")
.on("click.mobileSizeTabs", ".challenge-mobile-size-tab", function () {
var idx = parseInt(String($(this).attr("data-size-index")), 10);
if (isNaN(idx)) return;
var card = $scroll.find(".challenge-cards-inner > [class*='col-']").get(idx);
if (card) {
card.scrollIntoView({ behavior: "smooth", inline: "center", block: "nearest" });
}
});
$(window).off("resize.mobileSizeTabsChallenge").on("resize.mobileSizeTabsChallenge", sync);
}
/** 防止阶梯行被负 margin / 过大 padding 顶出左列(992~1200 + overflow:hidden 时会整列看似空白) */
function clampMetricsColLadderInView($metricsCol) {
if (!$metricsCol.length || !$metricsCol[0]) return;
var colTop = $metricsCol[0].getBoundingClientRect().top;
var slack = 2;
var $body = $metricsCol.find(".metrics-col__body");
if ($body.length) {
var pt = parseFloat($body.css("padding-top")) || 0;
if (pt > 240) {
$body.css("padding-top", "");
}
}
$metricsCol.find(".metric-item").each(function () {
var rect = this.getBoundingClientRect();
if (rect.height < 1) return;
if (rect.top < colTop - slack) {
var $el = $(this);
var mt = parseFloat($el.css("margin-top")) || 0;
$el.css("margin-top", mt + Math.ceil(colTop - rect.top) + "px");
}
});
}
/** PC:按设计稿对齐——Account Size↔金额、参数行↔value、Fee↔price-row、左右列底↔卡片底;Profit Target 行高逻辑不变。 */
function syncChallengeDesktopLadderHeights() {
if (isCompactChallengeFloorViewport()) return;
var $mount = $("#challenge-floor-mount");
var $row = $mount.children(".row.no-gutters").first();
if (!$row.length) return;
var $metricsCol = $row.find(".metrics-col");
var $head = $metricsCol.find(".metric-item--ladder-head");
var $foot = $metricsCol.find(".metric-item--ladder-foot");
var $footTail = $metricsCol.find(".challenge-ladder__foot-tail");
var $metrics = $metricsCol.find(".metrics-col__body .metric-item");
var $scroll = $row.find(".js-challenge-cards-scroll.d-none.d-lg-block");
if (!$metricsCol.length || !$scroll.length) return;
if (!$scroll.is(":visible")) return;
var $cards = $scroll.find("article.account-card");
var $card = $cards.first();
if (!$card.length) return;
var $accountHead = $card.find("> .account-head");
var $size = $accountHead.find(".size").first();
var $priceRow = $card.find("> .price-area > .price-row").first();
var $valueCols = $scroll.find("article.account-card > .values");
if (!$valueCols.length) return;
$metricsCol.css({ marginTop: "", paddingBottom: "" });
$footTail.css({ height: "", minHeight: "" });
$metricsCol.find(".metrics-col__body").css("padding-top", "");
$metrics.css("margin-top", "");
$head.add($foot).css({
minHeight: "",
height: "",
marginTop: "",
paddingTop: "",
paddingBottom: "",
});
if ($head.length && $accountHead.length && $size.length) {
var headBoxH = Math.ceil($accountHead.outerHeight());
if (headBoxH < 1) headBoxH = 1;
var sizeH = Math.ceil($size.outerHeight());
if (sizeH < 1) sizeH = 1;
$head.css({
boxSizing: "border-box",
display: "flex",
alignItems: "center",
minHeight: headBoxH,
height: headBoxH,
paddingTop: "0",
paddingBottom: "0",
marginTop: "0",
});
if ($head[0]) void $head[0].offsetHeight;
var headTopDelta = Math.round(
$accountHead[0].getBoundingClientRect().top - $head[0].getBoundingClientRect().top
);
if (headTopDelta !== 0) {
$head.css("margin-top", headTopDelta + "px");
}
if ($head[0]) void $head[0].offsetHeight;
var ladderHeadTop = $head[0].getBoundingClientRect().top;
var sizeTop = $size[0].getBoundingClientRect().top;
var padTop = Math.max(0, Math.round(sizeTop - ladderHeadTop));
var padBottom = Math.max(0, headBoxH - padTop - sizeH);
$head.css({
paddingTop: padTop + "px",
paddingBottom: padBottom + "px",
});
function nudgeHeadPaddingToSize() {
if (!$head[0] || !$size[0]) return;
var sizeRect = $size[0].getBoundingClientRect();
var sizeMidY = (sizeRect.top + sizeRect.bottom) / 2;
var $label = $head.find(".metric-label").first();
var alignEl = $label.length ? $label[0] : $head[0];
var alignRect = alignEl.getBoundingClientRect();
var alignMidY = (alignRect.top + alignRect.bottom) / 2;
var centerDelta = Math.round(sizeMidY - alignMidY);
if (centerDelta === 0) return;
var headPadT = parseFloat($head.css("padding-top")) || 0;
var headPadB = parseFloat($head.css("padding-bottom")) || 0;
if (centerDelta > 0 && headPadB >= centerDelta) {
$head.css({
paddingTop: headPadT + centerDelta + "px",
paddingBottom: headPadB - centerDelta + "px",
});
} else if (centerDelta < 0 && headPadT >= -centerDelta) {
$head.css({
paddingTop: headPadT + centerDelta + "px",
paddingBottom: headPadB - centerDelta + "px",
});
}
}
if ($head[0]) void $head[0].offsetHeight;
nudgeHeadPaddingToSize();
if ($head[0]) void $head[0].offsetHeight;
var headBottomFix = Math.round(
$accountHead[0].getBoundingClientRect().bottom - $head[0].getBoundingClientRect().bottom
);
if (headBottomFix !== 0) {
var headPadB = parseFloat($head.css("padding-bottom")) || 0;
$head.css("padding-bottom", Math.max(0, headPadB + headBottomFix) + "px");
if ($head[0]) void $head[0].offsetHeight;
nudgeHeadPaddingToSize();
}
}
var $metricsBody = $metricsCol.find(".metrics-col__body");
var $firstValCell = $valueCols.first().children(".value-item").first();
if ($metricsBody.length && $firstValCell.length && $head.length && $accountHead.length) {
var cardBodyGap = Math.round(
$firstValCell[0].getBoundingClientRect().top -
$accountHead[0].getBoundingClientRect().bottom
);
if (cardBodyGap < 0) cardBodyGap = 0;
$metricsBody.css("padding-top", cardBodyGap + "px");
if ($metrics.length && $head[0]) {
if ($metrics[0]) void $metrics[0].offsetHeight;
var leftBodyGap = Math.round(
$metrics.first()[0].getBoundingClientRect().top - $head[0].getBoundingClientRect().bottom
);
if (leftBodyGap !== cardBodyGap) {
$metricsBody.css("padding-top", cardBodyGap + (cardBodyGap - leftBodyGap) + "px");
}
}
}
var nValues = $valueCols.first().children(".value-item").length;
var rowCount = Math.min($metrics.length, nValues);
if (rowCount >= 1) {
$metrics.css({ minHeight: "", height: "" });
$valueCols.children(".value-item").css({ minHeight: "", height: "" });
var r;
for (r = 0; r < rowCount; r++) {
var $mRow = $metrics.eq(r);
var isPhaseTarget = $mRow.hasClass("phase-target-row");
var maxH = Math.ceil($mRow.outerHeight());
$valueCols.each(function () {
var $cell = $(this).children(".value-item").eq(r);
if ($cell.length) {
var h = Math.ceil($cell.outerHeight());
if (h > maxH) maxH = h;
}
});
if (maxH < 1) maxH = 1;
if (isPhaseTarget) {
$mRow.css({ minHeight: "", height: "" });
$valueCols.each(function () {
$(this).children(".value-item").eq(r).css({ minHeight: "", height: "" });
});
if ($mRow[0]) void $mRow[0].offsetHeight;
maxH = Math.ceil($mRow.outerHeight());
$valueCols.each(function () {
var $cell = $(this).children(".value-item").eq(r);
if ($cell.length) {
var phaseH = Math.ceil($cell.outerHeight());
if (phaseH > maxH) maxH = phaseH;
}
});
if (maxH < 1) maxH = 1;
$mRow.css({ minHeight: maxH, height: "" });
$valueCols.each(function () {
$(this).children(".value-item").eq(r).css({ minHeight: maxH, height: "" });
});
} else {
$mRow.css({ minHeight: maxH, height: maxH });
$valueCols.each(function () {
$(this).children(".value-item").eq(r).css({ minHeight: maxH, height: maxH });
});
}
}
/* 逐行:左侧 label 顶边与首张卡 value 顶边严格对齐 */
for (r = 0; r < rowCount; r++) {
var $mRowAlign = $metrics.eq(r);
var $vRef = $valueCols.first().children(".value-item").eq(r);
if (!$mRowAlign.length || !$vRef.length) continue;
if ($mRowAlign[0]) void $mRowAlign[0].offsetHeight;
var rowDelta = Math.round(
$vRef[0].getBoundingClientRect().top - $mRowAlign[0].getBoundingClientRect().top
);
if (rowDelta !== 0) {
var rowMt = parseFloat($mRowAlign.css("margin-top")) || 0;
$mRowAlign.css("margin-top", rowMt + rowDelta + "px");
}
}
}
if ($foot.length && $priceRow.length) {
$foot.css({ minHeight: "", height: "auto", marginTop: "" });
var $oneTimeLine = $foot.find(".ladder-foot-line--primary").first();
var $priceEl = $priceRow.find(".price").first();
if (!$priceEl.length) {
$priceEl = $priceRow;
}
if ($oneTimeLine.length && $priceEl.length) {
if ($foot[0]) void $foot[0].offsetHeight;
var oneTimeRect = $oneTimeLine[0].getBoundingClientRect();
var priceRect = $priceEl[0].getBoundingClientRect();
var oneTimeMidY = (oneTimeRect.top + oneTimeRect.bottom) / 2;
var priceMidY = (priceRect.top + priceRect.bottom) / 2;
var push = Math.round(priceMidY - oneTimeMidY);
if (push !== 0) {
$foot.css("margin-top", push + "px");
}
}
}
/* 992~1200px:foot-tail 补齐至卡片底,消除左下角空白 */
if ($footTail.length) {
if (isMidDesktopCardsViewport() && $foot.length) {
var cardBottomMid = 0;
$cards.each(function () {
var b = this.getBoundingClientRect().bottom;
if (b > cardBottomMid) cardBottomMid = b;
});
if (cardBottomMid > 0) {
if ($foot[0]) void $foot[0].offsetHeight;
var footBottomMid = $foot[0].getBoundingClientRect().bottom;
var tailH = Math.round(cardBottomMid - footBottomMid);
if (tailH < 0) tailH = 0;
$footTail.css({
display: "block",
minHeight: tailH,
height: tailH > 0 ? tailH : "",
});
}
} else {
$footTail.css({ minHeight: "", height: "" });
}
}
clampMetricsColLadderInView($metricsCol);
syncMetricsColGradientPanel($metricsCol, $cards);
}
/** PC:左侧渐变背景高度 ≈ 卡片高度,顶部略高出卡片顶(不撑满整列) */
function syncMetricsColGradientPanel($metricsCol, $cards) {
if (!$metricsCol.length || !$cards.length) return;
$metricsCol.css({ "--metrics-col-bg-h": "", "--metrics-col-bg-top": "" });
var tallestH = 0;
var cardTop = Infinity;
var cardBottom = 0;
$cards.each(function () {
var rect = this.getBoundingClientRect();
if (rect.height > tallestH) tallestH = Math.ceil(rect.height);
if (rect.top < cardTop) cardTop = rect.top;
if (rect.bottom > cardBottom) cardBottom = rect.bottom;
});
if (tallestH < 1 || !isFinite(cardTop)) return;
if ($metricsCol[0]) void $metricsCol[0].offsetHeight;
var metricsTop = $metricsCol[0].getBoundingClientRect().top;
var overhang = 4;
var $floor = $metricsCol.closest(".challenge-floor");
if ($floor.length) {
var v = getComputedStyle($floor[0]).getPropertyValue("--challenge-metrics-bg-overhang-top");
if (v) overhang = parseFloat(v.trim()) || 4;
}
var bgTop = Math.round(cardTop - metricsTop - overhang);
/* 992~1200px:渐变底边与拉伸后的左列底(≈ 卡片底)对齐 */
if (isMidDesktopCardsViewport()) {
var metricsH = Math.ceil($metricsCol[0].getBoundingClientRect().height);
var bgH = metricsH - bgTop;
if (cardBottom > 0) {
var cardAlignedH = Math.round(cardBottom - metricsTop - bgTop);
if (cardAlignedH > bgH) bgH = cardAlignedH;
}
if (bgH < tallestH + overhang) bgH = tallestH + overhang;
$metricsCol.css({
"--metrics-col-bg-h": bgH + "px",
"--metrics-col-bg-top": bgTop + "px",
});
return;
}
$metricsCol.css({
"--metrics-col-bg-h": tallestH + overhang + "px",
"--metrics-col-bg-top": bgTop + "px",
});
}
var challengeLadderResizeTimer = null;
function scheduleChallengeDesktopLadderSync() {
window.clearTimeout(challengeLadderResizeTimer);
challengeLadderResizeTimer = window.setTimeout(function () {
syncChallengeDesktopLadderHeights();
}, 120);
}
$(window).on("resize.challengeFloorLadder", scheduleChallengeDesktopLadderSync);
function renderCurrencyBar() {
var html = Object.keys(currencyRates)
.map(function (code) {
var active = code === appState.currency ? " active" : "";
return (
'' +
code +
" "
);
})
.join("");
$("#challengeCurrencyTabs").html(html);
}
function updateInstantSubtabsVisibility() {
var show = appState.programType === "instant";
$("#challengeInstantSubtabs").toggleClass("d-none", !show);
if (show) {
$("#challengeInstantSubtabs .challenge-instant-subtab").removeClass("active");
$(
'#challengeInstantSubtabs .challenge-instant-subtab[data-instant-mode="' +
appState.instantMode +
'"]'
).addClass("active");
}
}
function updateAifoliteSubtabsVisibility() {
var show = appState.programType === "aifolite";
$("#challengeAifoliteSubtabs").toggleClass("d-none", !show);
if (show) {
appState.aifoliteMode = normalizeAifoliteMode(appState.aifoliteMode);
$("#challengeAifoliteSubtabs .challenge-instant-subtab").removeClass("active");
$(
'#challengeAifoliteSubtabs .challenge-instant-subtab[data-aifolite-mode="' +
appState.aifoliteMode +
'"]'
).addClass("active");
}
}
function updateAddonsVisibility() {
var hide = appState.programType === "instant" || appState.programType === "aifolite";
$(".challenge-floor-addons").toggleClass("d-none", hide);
}
function renderChallengeFloor() {
ensureValidCurrency();
closeAllMetricTooltips();
var $mount = $("#challenge-floor-mount");
$mount.html(buildFloorHtml());
bindChallengeCardsScroll($mount);
bindChallengeCardsDragScroll($mount);
bindMobileSizeTabSync($mount);
window.requestAnimationFrame(function () {
syncChallengeDesktopLadderHeights();
window.requestAnimationFrame(function () {
syncChallengeDesktopLadderHeights();
syncMobileSizeTabFromScroll($mount);
if (isMidDesktopCardsViewport()) {
window.setTimeout(syncChallengeDesktopLadderHeights, 280);
}
});
});
}
function tabIdToProgramType(tabId) {
if (tabId === "24h") return "aifolite";
return tabId;
}
function programTypeToTabId(p) {
if (p === "aifolite") return "24h";
return p;
}
function syncTabsFromAppState() {
var tab = programTypeToTabId(appState.programType);
$(".challenge-floor .tab-btn[data-tab]").removeClass("active");
$('.challenge-floor .tab-btn[data-tab="' + tab + '"]').addClass("active");
updateInstantSubtabsVisibility();
updateAifoliteSubtabsVisibility();
updateAddonsVisibility();
}
$(document).on("click", ".challenge-floor .tab-btn[data-tab]", function () {
var tab = $(this).data("tab");
appState.programType = tabIdToProgramType(tab);
if (appState.programType === "instant") {
appState.instantMode = normalizeInstantMode(appState.instantMode);
}
if (appState.programType === "aifolite") {
appState.aifoliteMode = normalizeAifoliteMode(appState.aifoliteMode);
}
syncTabsFromAppState();
renderCurrencyBar();
renderChallengeFloor();
});
$(document).on("click", "#challengeInstantSubtabs .challenge-instant-subtab", function () {
appState.instantMode = normalizeInstantMode($(this).data("instant-mode"));
syncTabsFromAppState();
renderCurrencyBar();
renderChallengeFloor();
});
$(document).on("click", "#challengeAifoliteSubtabs .challenge-instant-subtab", function () {
appState.aifoliteMode = normalizeAifoliteMode($(this).data("aifolite-mode"));
syncTabsFromAppState();
renderCurrencyBar();
renderChallengeFloor();
});
$(document).on("click", ".challenge-currency-btn", function () {
appState.currency = $(this).data("currency");
renderCurrencyBar();
renderChallengeFloor();
});
/** Viewport width at/above this uses CSS absolute tooltip (desktop ladder/cards). */
var METRIC_TOOLTIP_PC_MIN_PX = 992;
var metricTooltipSuppressDocClose = false;
/** 手机窄屏(<992):使用卡片内指标 + 横向滑动楼层;992+ 含竖屏平板走 PC 楼层 */
function isCompactChallengeFloorViewport() {
return window.matchMedia("(max-width: 991.98px)").matches;
}
function isMobileMetricTooltipViewport() {
return isCompactChallengeFloorViewport();
}
function isMobileCardMetricHelp($wrap) {
return $wrap && $wrap.length && $wrap.closest(".card-mobile-metrics").length > 0;
}
function getMetricTooltipForWrap($wrap) {
var $portaled = $wrap.data("metricTooltipPortaled");
if ($portaled && $portaled.length && $.contains(document.documentElement, $portaled[0])) {
return $portaled;
}
return $wrap.children(".metric-tooltip").first();
}
function restoreMetricTooltipDom($wrap) {
if (!$wrap || !$wrap.length) return;
var $tip = getMetricTooltipForWrap($wrap);
$wrap.removeClass("metric-help--tooltip-fixed");
$wrap.removeData("metricTooltipPortaled");
if (!$tip.length) return;
$tip.removeClass("metric-tooltip--mobile-portal metric-tooltip--positioned metric-tooltip--flip-below");
$tip[0].style.removeProperty("--metric-tooltip-callout-x");
$tip.css({
position: "",
left: "",
top: "",
right: "",
bottom: "",
width: "",
maxWidth: "",
transform: "",
margin: "",
});
if ($tip.parent()[0] === document.body) {
$wrap.append($tip);
}
}
function closeAllMetricTooltips() {
$(".challenge-floor .metric-help.show").each(function () {
var $w = $(this);
$w.removeClass("show");
restoreMetricTooltipDom($w);
});
$("body > .metric-tooltip.metric-tooltip--mobile-portal").remove();
}
function portalMetricTooltipToBody($wrap) {
var $tip = $wrap.children(".metric-tooltip").first();
if (!$tip.length) return $();
if ($tip.parent()[0] !== document.body) {
$wrap.data("metricTooltipPortaled", $tip);
document.body.appendChild($tip[0]);
}
$tip.addClass("metric-tooltip--mobile-portal");
$tip.removeClass("metric-tooltip--positioned");
return $tip;
}
function clearMobileMetricTooltipPosition($wrap) {
restoreMetricTooltipDom($wrap);
}
function metricHelpToggleFromWrap($wrap) {
var isMobileCard = isMobileCardMetricHelp($wrap);
var isMobileViewport = isMobileMetricTooltipViewport();
var willOpen = !$wrap.hasClass("show");
$(".challenge-floor .metric-help")
.not($wrap)
.each(function () {
var $other = $(this);
$other.removeClass("show");
restoreMetricTooltipDom($other);
});
if (willOpen) {
if (isMobileCard && isMobileViewport) {
metricTooltipSuppressDocClose = true;
window.setTimeout(function () {
metricTooltipSuppressDocClose = false;
}, 0);
$wrap.addClass("metric-help--tooltip-fixed show");
portalMetricTooltipToBody($wrap);
positionMobileMetricTooltip($wrap);
schedulePositionMobileMetricTooltip($wrap);
} else {
$wrap.addClass("show");
}
} else {
$wrap.removeClass("show");
if (isMobileCard) {
restoreMetricTooltipDom($wrap);
}
}
}
function positionMobileMetricTooltip($wrap) {
if (!$wrap || !$wrap.length || !$wrap.hasClass("show")) return;
if (!isMobileCardMetricHelp($wrap)) return;
if (!isMobileMetricTooltipViewport()) {
restoreMetricTooltipDom($wrap);
return;
}
var $tip = getMetricTooltipForWrap($wrap);
if (!$tip.length) {
$tip = portalMetricTooltipToBody($wrap);
}
if (!$tip.length) return;
var $left = $wrap.closest(".card-mobile-metric-left");
var $anchor = $left.length ? $left.find(".metric-label-dotted").first() : $wrap.find(".metric-help-btn");
if (!$anchor.length) return;
var tipEl = $tip[0];
var $card = $wrap.closest(".account-card");
var cardEl = $card.length ? $card[0] : null;
$wrap.addClass("metric-help--tooltip-fixed");
tipEl.classList.remove("metric-tooltip--flip-below", "metric-tooltip--positioned");
var edgePad = 12;
var vw = window.innerWidth;
var vh = window.innerHeight;
var margin = 12;
var gapToAnchor = 8;
var arrowH = 7;
var cardRect = cardEl ? cardEl.getBoundingClientRect() : null;
var maxW = cardRect
? Math.max(120, cardRect.width - edgePad * 2)
: Math.min(320, vw - margin * 2);
$tip.css({
position: "fixed",
transform: "none",
margin: 0,
right: "auto",
bottom: "auto",
width: maxW + "px",
maxWidth: maxW + "px",
});
var anchor = $anchor[0].getBoundingClientRect();
var tipRect = tipEl.getBoundingClientRect();
var tipW = tipRect.width;
var tipH = tipRect.height;
var minLeft = cardRect ? cardRect.left + edgePad : margin;
var maxLeft = cardRect ? cardRect.right - edgePad - tipW : vw - margin - tipW;
var anchorCx = anchor.left + anchor.width / 2;
var left = anchorCx - tipW / 2;
if (maxLeft < minLeft) {
left = minLeft;
} else {
left = Math.max(minLeft, Math.min(left, maxLeft));
}
var tail = gapToAnchor + arrowH;
var top = anchor.top - tipH - tail;
var flipBelow = false;
if (top < margin) {
top = anchor.bottom + tail;
flipBelow = true;
}
if (top + tipH > vh - margin) {
top = Math.max(margin, vh - margin - tipH);
}
if (flipBelow) {
tipEl.classList.add("metric-tooltip--flip-below");
}
$tip.css({ left: left + "px", top: top + "px" });
tipRect = tipEl.getBoundingClientRect();
tipW = tipRect.width;
var arrowCenterX = anchorCx - tipRect.left;
var minCx = 18;
var maxCx = tipW - minCx;
arrowCenterX = Math.max(minCx, Math.min(maxCx, arrowCenterX));
tipEl.style.setProperty("--metric-tooltip-callout-x", arrowCenterX + "px");
$tip.addClass("metric-tooltip--positioned");
}
function schedulePositionMobileMetricTooltip($wrap) {
if (!$wrap || !$wrap.length || !$wrap.hasClass("show")) return;
requestAnimationFrame(function () {
requestAnimationFrame(function () {
positionMobileMetricTooltip($wrap);
});
});
}
var metricTooltipRepositionTimer = null;
function repositionOpenMetricTooltips() {
clearTimeout(metricTooltipRepositionTimer);
metricTooltipRepositionTimer = setTimeout(function () {
$(".challenge-floor .metric-help.show").each(function () {
positionMobileMetricTooltip($(this));
});
}, 40);
}
$(document).on("click", ".challenge-floor .metric-help-btn", function (e) {
e.preventDefault();
e.stopPropagation();
if ($(this).attr("aria-hidden") === "true") return;
metricHelpToggleFromWrap($(this).closest(".metric-help"));
});
$(document).on("keydown", ".challenge-floor .metric-help-btn", function (e) {
if (e.key !== "Enter" && e.key !== " ") return;
e.preventDefault();
if ($(this).attr("aria-hidden") === "true") return;
metricHelpToggleFromWrap($(this).closest(".metric-help"));
});
function metricHelpWrapFromMobileMetricLeft($left) {
var $label = $left.find(".metric-label-dotted").first();
if (!$label.length) return $();
return $label.siblings(".metric-help");
}
$(document).on("click", ".challenge-floor .card-mobile-metrics .metric-label-dotted", function (e) {
e.preventDefault();
e.stopPropagation();
var $wrap = $(this).siblings(".metric-help");
if (!$wrap.length) return;
metricHelpToggleFromWrap($wrap);
});
$(document).on("click", ".challenge-floor .card-mobile-metrics .card-mobile-metric-left", function (e) {
if ($(e.target).closest(".metric-label-dotted").length) return;
var $wrap = metricHelpWrapFromMobileMetricLeft($(this));
if (!$wrap.length) return;
e.preventDefault();
e.stopPropagation();
metricHelpToggleFromWrap($wrap);
});
$(document).on("keydown", ".challenge-floor .card-mobile-metrics .metric-label-dotted", function (e) {
if (e.key !== "Enter" && e.key !== " ") return;
e.preventDefault();
var $wrap = $(this).siblings(".metric-help");
if (!$wrap.length) return;
metricHelpToggleFromWrap($wrap);
});
$(document).on("click", function (e) {
if (metricTooltipSuppressDocClose) return;
var $t = $(e.target);
if ($t.closest(".metric-tooltip--mobile-portal").length) return;
if ($t.closest(".challenge-floor .metric-help").length) return;
if ($t.closest(".challenge-floor .card-mobile-metrics .card-mobile-metric-left").length) return;
$(".challenge-floor .metric-help.show").each(function () {
var $w = $(this);
$w.removeClass("show");
restoreMetricTooltipDom($w);
});
});
var CHALLENGE_CHECKOUT_COUPON = "AIFO50";
var CHALLENGE_CHECKOUT_BASE = "https://dashboard.aifo.com/checkout";
function buildChallengeCheckoutUrl(productId, variantId) {
return (
CHALLENGE_CHECKOUT_BASE +
"?product_id=" +
encodeURIComponent(productId) +
"&variant_id=" +
encodeURIComponent(variantId)
);
}
function copyChallengeCouponCode(code) {
var text = String(code || "");
if (!text) return Promise.resolve();
if (navigator.clipboard && typeof navigator.clipboard.writeText === "function") {
return navigator.clipboard.writeText(text);
}
return new Promise(function (resolve, reject) {
var $ta = $("