Files
aifo-portal-web/assets/js/theme.js
T
steven 4d30803b60 Initial commit: AIFO official portal web.
Add static site, shared layout, and Node dev server with standard .gitignore.

Co-authored-by: Cursor <cursoragent@cursor.com>
2026-06-30 15:29:50 +08:00

928 lines
32 KiB
JavaScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
"use strict";
(function($){
$(window).on("load", function() {
}),
$(window).on('load resize', function() {
// Background image holder - Static hero with fullscreen autosize
if ($('.spotlight').length) {
$('.spotlight').each(function() {
var $this = $(this);
var holderHeight;
if ($this.data('spotlight') == 'fullscreen') {
if ($this.data('spotlight-offset')) {
var offsetHeight = $('body').find($this.data('spotlight-offset')).height();
holderHeight = $(window).height() - offsetHeight;
}
else {
holderHeight = $(window).height();
}
if ($(window).width() > 991) {
$this.find('.spotlight-holder').css({
'height': holderHeight + 'px'
});
}
else {
$this.find('.spotlight-holder').css({
'height': 'auto'
});
}
}
})
}
}),
$(document).ready(function() {
// Plugins init
$(".scrollbar-inner")[0] && $(".scrollbar-inner").scrollbar().scrollLock();
$('[data-stick-in-parent="true"]')[0] && $('[data-stick-in-parent="true"]').stick_in_parent();
$('.selectpicker')[0] && $('.selectpicker').selectpicker();
$('.textarea-autosize')[0] && autosize($('.textarea-autosize'));
$('[data-toggle="tooltip"]').tooltip();
$('[data-toggle="popover"]').each(function() {
var popoverClass = '';
if($(this).data('color')) {
popoverClass = 'popover-'+$(this).data('color');
}
$(this).popover({
trigger: 'focus',
template: '<div class="popover '+ popoverClass +'" role="tooltip"><div class="arrow"></div><h3 class="popover-header"></h3><div class="popover-body"></div></div>'
})
});
// Floating label
$('.form-control').on('focus blur', function(e) {
$(this).parents('.form-group').toggleClass('focused', (e.type === 'focus' || this.value.length > 0));
}).trigger('blur');
// Custom input file
$('.custom-input-file').each(function() {
var $input = $(this),
$label = $input.next('label'),
labelVal = $label.html();
$input.on('change', function(e) {
var fileName = '';
if (this.files && this.files.length > 1)
fileName = (this.getAttribute('data-multiple-caption') || '').replace('{count}', this.files.length);
else if (e.target.value)
fileName = e.target.value.split('\\').pop();
if (fileName)
$label.find('span').html(fileName);
else
$label.html(labelVal);
});
// Firefox bug fix
$input.on('focus', function() {
$input.addClass('has-focus');
})
.on('blur', function() {
$input.removeClass('has-focus');
});
});
// NoUI Slider
if ($(".input-slider-container")[0]) {
$('.input-slider-container').each(function() {
var slider = $(this).find('.input-slider');
var sliderId = slider.attr('id');
var minValue = slider.data('range-value-min');
var maxValue = slider.data('range-value-max');
var sliderValue = $(this).find('.range-slider-value');
var sliderValueId = sliderValue.attr('id');
var startValue = sliderValue.data('range-value-low');
var c = document.getElementById(sliderId),
d = document.getElementById(sliderValueId);
noUiSlider.create(c, {
start: [parseInt(startValue)],
connect: [true, false],
//step: 1000,
range: {
'min': [parseInt(minValue)],
'max': [parseInt(maxValue)]
}
});
c.noUiSlider.on('update', function(a, b) {
d.textContent = a[b];
});
})
}
if ($("#input-slider-range")[0]) {
var c = document.getElementById("input-slider-range"),
d = document.getElementById("input-slider-range-value-low"),
e = document.getElementById("input-slider-range-value-high"),
f = [d, e];
noUiSlider.create(c, {
start: [parseInt(d.getAttribute('data-range-value-low')), parseInt(e.getAttribute('data-range-value-high'))],
connect: !0,
range: {
min: parseInt(c.getAttribute('data-range-value-min')),
max: parseInt(c.getAttribute('data-range-value-max'))
}
}), c.noUiSlider.on("update", function(a, b) {
f[b].textContent = a[b]
})
}
// Scroll to anchor with animation
$('.scroll-me, .toc-entry a').on('click', function(event) {
var hash = $(this).attr('href');
var offset = $(this).data('scroll-to-offset') ? $(this).data('scroll-to-offset') : 0;
// Animate scroll to the selected section
$('html, body').stop(true, true).animate({
scrollTop: $(hash).offset().top - offset
}, 600);
event.preventDefault();
});
}),
$(document).ready(function() {
$("body").on("click", "[data-action]", function(e) {
e.preventDefault();
var $this = $(this);
var action = $this.data('action');
var target = '';
switch (action) {
case "offcanvas-open":
target = $this.data("target"), $(target).addClass("open"), $("body").append('<div class="body-backdrop" data-action="offcanvas-close" data-target=' + target + " />");
break;
case "offcanvas-close":
target = $this.data("target"), $(target).removeClass("open"), $("body").find(".body-backdrop").remove();
break;
case 'aside-open':
target = $this.data('target');
$this.data('action', 'aside-close');
$this.addClass('toggled');
$(target).addClass('toggled');
$('.content').append('<div class="body-backdrop" data-action="aside-close" data-target='+target+' />');
break;
case 'aside-close':
target = $this.data('target');
$this.data('action', 'aside-open');
$('[data-action="aside-open"], '+target).removeClass('toggled');
$('.content, .header').find('.body-backdrop').remove();
break;
}
})
});
// nav
// Check if top code bar is visible
function isTopCodeBarVisible() {
const $topCodeBar = $('.top-code-bar');
// If element doesn't exist
if (!$topCodeBar.length) {
return false;
}
// Use jQuery method to check if visible
return $topCodeBar.is(':visible') && $topCodeBar.css('visibility') !== 'hidden' && $topCodeBar.height() > 0;
}
function isMobileNavViewport() {
if (window.matchMedia("(max-width: 991.98px)").matches) {
return true;
}
if (window.matchMedia("(min-width: 992px)").matches) {
return false;
}
return /Android|webOS|iPhone|iPad|iPod|BlackBerry|IEMobile|Opera Mini/i
.test(navigator.userAgent);
}
function initDesktopFixedNav() {
if (isMobileNavViewport()) return;
const $nav = $('#nav');
if (!$nav.length) return;
const threshold = 35;
const defaultTop = isTopCodeBarVisible() ? '35px' : '0';
const fixedTop = '0';
$nav.css({
'position': 'fixed',
'top': defaultTop,
'left': '0',
'width': '100%',
'z-index': '1500',
'background-color': 'rgba(0, 0, 0, 0.4)',
'box-shadow': '0 2px 10px rgba(0,0,0,0.1)',
'transition': 'none'
});
function updateNavPosition() {
const isdefaultTop = isTopCodeBarVisible();
if (!isdefaultTop) {
$nav.css('top', fixedTop);
return;
}
const scrollTop = $(window).scrollTop();
$nav.css('top', scrollTop > threshold ? fixedTop : defaultTop);
}
$(window).off('scroll.aifoNav resize.aifoNav');
$(window).on('scroll.aifoNav resize.aifoNav', updateNavPosition);
updateNavPosition();
}
$(document).ready(initDesktopFixedNav);
document.addEventListener('aifo:layout-ready', initDesktopFixedNav);
// Seaction-2
$(function() {
// Check if jQuery UI is loaded (for easing functions)
const hasJQueryUI = typeof $.easing !== 'undefined';
const $section2 = $('#section-2');
// Counter data
const counters = [
{ index: 0, start: 1000, end: 50000, suffix: '+' },
{ index: 1, start: 1, end: 20, suffix: 'M+' },
{ index: 2, start: 1000, end: 70000, suffix: '+' }
];
// Listen to elements entering viewport
const observer = new IntersectionObserver(function(entries) {
entries.forEach(function(entry) {
if (entry.isIntersecting) {
// Add fade-in class (if not already)
if (!$section2.hasClass('fade-in')) {
$section2.addClass('fade-in');
}
triggerCounters();
// Listen to animation end
// $section2.one('animationend webkitAnimationEnd oanimationend MSAnimationEnd', function() {
// });
observer.unobserve(entry.target);
}
});
}, { threshold: 0.2 });
// Check if element exists
if ($section2.length) {
observer.observe($section2[0]);
}
function triggerCounters() {
setTimeout(function() {
counters.forEach(function(counter) {
const $counter = $('#section-2 .counter').eq(counter.index);
if ($counter.length) {
animateValue(
$counter,
counter.start,
counter.end,
counter.suffix,
2000,
hasJQueryUI ? 'easeOutElastic' : 'easeOutCubic'
);
}
});
}, 600);
}
// Animation function
function animateValue($element, start, end, suffix, duration, easing) {
let startTimestamp = null;
// Easing function
function getEasing(progress) {
switch(easing) {
case 'easeOutCubic':
return 1 - Math.pow(1 - progress, 3);
case 'easeOutElastic':
const c4 = (2 * Math.PI) / 3;
return progress === 0 ? 0 :
progress === 1 ? 1 :
Math.pow(2, -10 * progress) * Math.sin((progress * 10 - 0.75) * c4) + 1;
default:
return progress;
}
}
// Format number
function formatNumber(num) {
return num.toLocaleString('en-US');
}
// Animation step
function step(timestamp) {
if (!startTimestamp) startTimestamp = timestamp;
const elapsed = timestamp - startTimestamp;
const progress = Math.min(elapsed / duration, 1);
const easedProgress = getEasing(progress);
const current = Math.floor(start + (end - start) * easedProgress);
// Update display
if (suffix === 'M+') {
$element.text('$'+current + suffix);
} else {
$element.text(formatNumber(current) + suffix);
}
if (progress < 1) {
requestAnimationFrame(step);
}
}
requestAnimationFrame(step);
}
});
/* =========================
Intersection fade-in (pixel-precise)
and lazy loaders for floor loading
========================= */
const io = new IntersectionObserver((entries, obs) => {
entries.forEach(e=>{
if(e.isIntersecting){
$(e.target).addClass('show');
obs.unobserve(e.target);
}
});
},{threshold:0.12});
$(function(){
$('.fade-in').each(function(){ io.observe(this); });
});
// ============================================
// challenge
// ============================================
const appState = {
programType: '1step',
instantMode: 'pro',
accountSize: '10k',
expandedParam: null
};
const programData = {
'1step': {
defaultSize: '10k',
availableSizes: ['10k', '25k', '50k', '100k', '200k'],
data: {
'10k': { fee: '$89', daily: '3%', max: '5%', target: '8%', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'25k': { fee: '$179', daily: '3%', max: '5%', target: '8%', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'50k': { fee: '$359', daily: '3%', max: '5%', target: '8%', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'100k': { fee: '$579', daily: '3%', max: '5%', target: '8%', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'200k': { fee: '$1,239', daily: '3%', max: '5%', target: '8%', split: '70%~95%', payout: '14 days', news: 'Allowed' }
}
},
'2step': {
defaultSize: '10k',
availableSizes: ['10k', '25k', '50k', '100k', '200k'],
data: {
'10k': { fee: '$109', daily: ' 3%', max: '8%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span><br> Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 8%</span>', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'25k': { fee: '$219', daily: ' 3%', max: '8%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span><br> Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 8%</span>', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'50k': { fee: '$359', daily: ' 3%', max: '8%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span><br> Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 8%</span>', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'100k': { fee: '$589', daily: ' 3%', max: '8%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span><br> Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 8%</span>', split: '70%~95%', payout: '14 days', news: 'Allowed' },
'200k': { fee: '$1,249', daily: ' 3%', max: '8%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span><br> Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 8%</span>', split: '70%~95%', payout: '14 days', news: 'Allowed' }
}
},
'3step': {
defaultSize: '10k',
availableSizes: ['10k', '25k', '50k', '100k', '200k'],
data: {
'10k': { fee: '$69', daily: ' 3%', max: '5%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 3&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span>', split: '70%~95%', payout: '14 days', news: 'Allow' },
'25k': { fee: '$149', daily: ' 3%', max: '5%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 3&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span>', split: '70%~95%', payout: '14 days', news: 'Allow' },
'50k': { fee: '$299', daily: ' 3%', max: '5%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 3&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span>', split: '70%~95%', payout: '14 days', news: 'Allow' },
'100k': { fee: '$459', daily: ' 3%', max: '5%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 3&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span>', split: '70%~95%', payout: '14 days', news: 'Allow' },
'200k': { fee: '$909', daily: ' 3%', max: '5%', target: 'Phase 1&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 2&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 3%</span><br>Phase 3&nbsp;&nbsp;&nbsp;&nbsp;<span class="text-orange"> 5%</span>', split: '70%~95%', payout: '14 days', news: 'Allow' }
}
},
'instant': {
pro: {
defaultSize: '10k',
availableSizes: ['10k', '25k', '50k'],
data: {
'10k': { fee: '$1091', daily: '2%', max: '5%', target: '4%', split: '70%~95%', payout: 'payout on demand', news: 'Allow' },
'25k': { fee: '$219', daily: '2%', max: '5%', target: '4%', split: '70%~95%', payout: 'payout on demand', news: 'Allow' },
'50k': { fee: '$399', daily: '2%', max: '5%', target: '4%', split: '70%~95%', payout: 'payout on demand', news: 'Allow' },
}
},
aifo: {
defaultSize: '100k',
availableSizes: ['100k', '200k'],
data: {
'100k': { fee: '$749', daily: '2%', max: '4%', target: '4%', split: '70%~95%', payout: 'payout on demand', news: 'Allow' },
'200k': { fee: '$1,199', daily: '2%', max: '4%', target: '4%', split: '70%~95%', payout: 'payout on demand', news: 'Allow' }
}
}
}
};
const 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 days 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.',
'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.',
'News Trading': ''
};
// const parameterNames = ['Challenge Fee', 'Daily Drawdown', 'Max Drawdown', 'Profit Target', 'Profit split', 'Payout period', 'News Trading'];
const parameterNames = ['Challenge Fee', 'Daily Drawdown', 'Max Drawdown', 'Profit Target', 'Profit split', 'Payout period'];
const parameterNames_instant = ['Challenge Fee', 'Daily Drawdown', 'Max Drawdown', 'withdrawable profit', 'Profit split', 'Payout period'];
// ============================================
// 工具函数
// ============================================
function getIconSVG(paramName) {
const icons = {
'Challenge Fee': '<img src="./assets/images/backgrounds/svg/Component 26.svg" alt="Funded Trading" class="chall-icon">',
'Daily Drawdown': '<img src="./assets/images/backgrounds/svg/Component 27.svg" alt="Funded Trading" class="chall-icon">',
'Max Drawdown': '<img src="./assets/images/backgrounds/svg/Component 28.svg" alt="Funded Trading" class="chall-icon">',
'Profit Target': '<img src="./assets/images/backgrounds/svg/Component 29.svg" alt="Funded Trading" class="chall-icon">',
'withdrawable profit': '<img src="./assets/images/backgrounds/svg/Component 29.svg" alt="Funded Trading" class="chall-icon">',
'Profit split': '<img src="./assets/images/backgrounds/svg/Component 30.svg" alt="Funded Trading" class="chall-icon">',
'Payout period': '<img src="./assets/images/backgrounds/svg/Component 31.svg" alt="Funded Trading" class="chall-icon">',
'News Trading': '<img src="./assets/images/backgrounds/svg/Component 32.svg" alt="Funded Trading" class="chall-icon">'
};
return icons[paramName] || '';
}
function getCurrentProgramConfig() {
if (appState.programType === 'instant') {
return programData.instant[appState.instantMode];
}
return programData[appState.programType];
}
function getDataForSize(size) {
const config = getCurrentProgramConfig();
return config.data[size];
}
function formatAccountSize(size) {
const formats = {
'10k': '$10K',
'25k': '$25K',
'50k': '$50K',
'100k': '$100K',
'200k': '$200K'
};
return formats[size] || size;
}
function getParamKey(paramName) {
const keyMap = {
'Challenge Fee': 'fee',
'Daily Drawdown': 'daily',
'Max Drawdown': 'max',
'Profit Target': 'target',
'withdrawable profit':'target',
'Profit split': 'split',
'Payout period': 'payout',
'News Trading': 'news'
};
return keyMap[paramName];
}
// ============================================
// 桌面版渲染
// ============================================
function renderDesktopTable(type) {
const config = getCurrentProgramConfig();
const sizes = config.availableSizes;
let html = '';
var param = parameterNames;
if(type=="aifo" || type=="pro" || type=="instant"){
param = parameterNames_instant;
}
param.forEach(paramName => {
const paramKey = getParamKey(paramName);
const rowId = paramName.replace(/\s+/g, '-').toLowerCase();
// 参数行
html += `<tr class="param-data-row" data-param="${paramName}">`;
html += `<td>
<div class="param-name-cell">
<div class="param-icon">${getIconSVG(paramName)}</div>
<div class="param-name">${paramName}</div>
<div class="expand-btn ${appState.expandedParam === paramName ? 'expanded' : ''}">
<img src="./assets/images/backgrounds/arraw-icon.png" alt="Funded Trading" class="arraw-icon-table">
</div>
</div>
</td>`;
// 各账户大小的数值
sizes.forEach(size => {
const data = getDataForSize(size);
html += `<td>${data[paramKey]}</td>`;
});
html += `</tr>`;
// 展开说明行
html += `<tr class="challenge-row-expand ${appState.expandedParam === paramName ? 'show' : ''}" data-expand="${paramName}">`;
html += `<td colspan="6">${parameterDescriptions[paramName]}</td>`;
html += `</tr>`;
});
$('#desktopTableBody').html(html);
}
// ============================================
// 移动版渲染
// ============================================
function renderMobileAccountSizes() {
const config = getCurrentProgramConfig();
const sizes = config.availableSizes;
const buttons = sizes.map(size => {
const label = formatAccountSize(size);
const activeClass = size === appState.accountSize ? 'active' : '';
return `<button class="mobile-size-btn ${activeClass}" data-size="${size}">${label}</button>`;
}).join('');
$('#mobileAccountSizes').html(buttons);
if (!sizes.includes(appState.accountSize)) {
appState.accountSize = config.defaultSize;
}
}
function renderMobileParams(type) {
const data = getDataForSize(appState.accountSize);
let html = '';
var param = parameterNames;
if(type=="aifo" || type=="pro" || type=="instant"){
param = parameterNames_instant;
}
param.forEach(paramName => {
const paramKey = getParamKey(paramName);
html += `<div>
<div class="mobile-param-row" data-param="${paramName}">
<div class="mobile-param-left">
<div class="param-icon">${getIconSVG(paramName)}</div>
<div class="param-name">${paramName}</div>
<div class="expand-btn ${appState.expandedParam === paramName ? 'expanded' : ''}">
<img src="./assets/images/backgrounds/arraw-icon.png" alt="Funded Trading" class="arraw-icon-table">
</div>
</div>
<div class="mobile-param-value">${data[paramKey]}</div>
</div>
<div class="mobile-param-description ${appState.expandedParam === paramName ? 'show' : ''}" data-desc="${paramName}">
${parameterDescriptions[paramName]}
</div>
</div>`;
});
$('#mobileParamsList').html(html);
}
function updateMobileAmount() {
$('#mobileAmount').text(formatAccountSize(appState.accountSize));
}
// ============================================
// 状态切换
// ============================================
function changeProgramType(newType) {
appState.programType = newType;
const config = getCurrentProgramConfig();
appState.accountSize = config.defaultSize;
appState.expandedParam = null;
renderDesktopTable(newType);
renderMobileAccountSizes();
renderMobileParams(newType);
updateMobileAmount();
}
function changeInstantMode(newMode) {
appState.instantMode = newMode;
const config = getCurrentProgramConfig();
if (!config.availableSizes.includes(appState.accountSize)) {
appState.accountSize = config.defaultSize;
}
appState.expandedParam = null;
renderDesktopTable(newMode);
renderMobileAccountSizes();
renderMobileParams(newMode);
updateMobileAmount();
}
function changeMobileAccountSize(newSize) {
appState.accountSize = newSize;
appState.expandedParam = null;
updateMobileAmount();
$('#mobileParamsList').fadeOut(150, function() {
const tabs = document.querySelectorAll('.challenge-tab');
var type = "step";
tabs.forEach(tab => {
if (tab.classList.contains('active')) {
const program = tab.dataset.program;
if(program == "instant"){
type = "instant";
}
}
});
renderMobileParams(type);
$(this).fadeIn(150);
});
}
// ============================================
// 事件监听
// ============================================
$(document).on('click', '.challenge-tab', function() {
$('.challenge-tab').removeClass('active');
$(this).addClass('active');
const newProgram = $(this).data('program');
const addons = document.querySelector('.challenge-addons');
if (newProgram === 'instant') {
$('#instantSubTabs').addClass('show');
document.querySelectorAll('.ch-pro')
.forEach(el => el.classList.remove('d-none'));
document.querySelectorAll('.ch-aifo')
.forEach(el => el.classList.add('d-none'));
if (addons) {
addons.classList.add('is-hidden');
}
} else {
document.querySelectorAll('.ch-pro')
.forEach(el => el.classList.remove('d-none'));
document.querySelectorAll('.ch-aifo')
.forEach(el => el.classList.remove('d-none'));
$('#instantSubTabs').removeClass('show');
if (addons) {
addons.classList.remove('is-hidden');
}
}
changeProgramType(newProgram);
});
$(document).on('click', '.instant-tab', function() {
$('.instant-tab').removeClass('active');
$(this).addClass('active');
const newMode = $(this).data('instant-type');
if (newMode === "pro") {
document.querySelectorAll('.ch-pro')
.forEach(el => el.classList.remove('d-none'));
document.querySelectorAll('.ch-aifo')
.forEach(el => el.classList.add('d-none'));
} else {
document.querySelectorAll('.ch-pro')
.forEach(el => el.classList.add('d-none'));
document.querySelectorAll('.ch-aifo')
.forEach(el => el.classList.remove('d-none'));
}
changeInstantMode(newMode);
});
$(document).on('click', '.mobile-size-btn', function() {
$('.mobile-size-btn').removeClass('active');
$(this).addClass('active');
const newSize = $(this).data('size');
changeMobileAccountSize(newSize);
});
$(document).on('click', '.param-data-row', function() {
const paramName = $(this).data('param');
if(paramName=="News Trading"){
return;
}
if (appState.expandedParam === paramName) {
appState.expandedParam = null;
} else {
appState.expandedParam = paramName;
}
$('.challenge-row-expand').removeClass('show');
$('.param-data-row .expand-btn').removeClass('expanded');
if (appState.expandedParam) {
$(`.challenge-row-expand[data-expand="${appState.expandedParam}"]`).addClass('show');
$(this).find('.expand-btn').addClass('expanded');
}
});
$(document).on('click', '.mobile-param-row', function() {
const paramName = $(this).data('param');
if (appState.expandedParam === paramName) {
appState.expandedParam = null;
} else {
appState.expandedParam = paramName;
}
$('.mobile-param-description').removeClass('show');
$('.mobile-param-row .expand-btn').removeClass('expanded');
if (appState.expandedParam) {
$(`.mobile-param-description[data-desc="${appState.expandedParam}"]`).addClass('show');
$(this).find('.expand-btn').addClass('expanded');
}
});
$(document).on('click', '.start-challenge-btn', function() {
const size = $(this).data('size');
console.log('Start Challenge:', {
programType: appState.programType,
instantMode: appState.instantMode,
accountSize: size
});
});
$(document).on('click', '.mobile-start-btn', function() {
console.log('Start Challenge:', {
programType: appState.programType,
instantMode: appState.instantMode,
accountSize: appState.accountSize
});
});
// ============================================
// 初始化
// ============================================
$(document).ready(function() {
renderDesktopTable();
renderMobileAccountSizes();
renderMobileParams();
updateMobileAmount();
});
})(jQuery);
/* ===========================================================
Reusable Carousel Class
=========================================================== */
class Carousel {
constructor(opts) {
this.container = document.querySelector(opts.container);
this.track = this.container.querySelector(".carousel-track");
this.items = [...this.track.children];
this.prevBtn = document.getElementById(opts.prev);
this.nextBtn = document.getElementById(opts.next);
this.dotsContainer = document.querySelector(opts.dots);
this.index = 0;
this.pageWidth = this.items[0].offsetWidth + 40; // including margin
this.setupDots();
this.update();
this.bindEvents();
}
setupDots() {
this.dotsContainer.innerHTML = "";
this.items.forEach((_, i) => {
const dot = document.createElement("div");
dot.className = "dot";
dot.addEventListener("click", () => this.goTo(i));
this.dotsContainer.appendChild(dot);
});
}
bindEvents() {
this.prevBtn.addEventListener("click", () => this.prev());
this.nextBtn.addEventListener("click", () => this.next());
window.addEventListener("resize", () => {
this.pageWidth = this.items[0].offsetWidth + 40;
this.update();
});
}
prev() {
this.index = Math.max(0, this.index - 1);
this.update();
}
next() {
this.index = Math.min(this.items.length - 1, this.index + 1);
this.update();
}
goTo(i) {
this.index = i;
this.update();
}
update() {
const centerOffset = (window.innerWidth / 2) - (this.pageWidth / 2);
let x = this.index * this.pageWidth * -1 + centerOffset;
if (this.index === 0) x = 0;
if (this.index === this.items.length - 1) {
x = (this.items.length - 1) * this.pageWidth * -1 + (window.innerWidth - this.pageWidth);
}
this.track.style.transform = `translateX(${x}px)`;
[...this.dotsContainer.children].forEach((dot, i) => {
dot.classList.toggle("active", i === this.index);
});
}
}
function showInfoAlert(){
alert("New features are about to be launched");
}