feat: add traffic intelligence toolkit

This commit is contained in:
王源
2026-10-19 19:50:06 +08:00
commit f34e0e085a
10 changed files with 609 additions and 0 deletions
+121
View File
@@ -0,0 +1,121 @@
(() => {
const data = window.CHANNEL_RULES;
if (!data) return;
const form = document.getElementById('recommendationForm');
const category = document.getElementById('category');
const country = document.getElementById('country');
const packageName = document.getElementById('packageName');
const message = document.getElementById('formMessage');
const empty = document.getElementById('resultEmpty');
const content = document.getElementById('resultContent');
const list = document.getElementById('resultList');
const summary = document.getElementById('querySummary');
const resetBtn = document.getElementById('resetBtn');
data.categories.forEach((item) => category.add(new Option(item.label, item.value)));
data.countries.forEach(([code, name]) => country.add(new Option(`${name} · ${code}`, code)));
const categoryLabel = (value) => data.categories.find((item) => item.value === value)?.label || value;
const countryLabel = (value) => {
const item = data.countries.find(([code]) => code === value);
return item ? `${item[1]} · ${item[0]}` : value;
};
function scoreChannel(channel, query) {
let score = channel.base;
const reasons = [`基础能力 ${channel.base} 分`];
const region = data.regionMap[query.country];
if (channel.categories.includes(query.category)) {
score += 35;
reasons.push(`适配「${categoryLabel(query.category)}」品类`);
} else if (channel.categories.includes('other')) {
score += 10;
reasons.push('支持通用品类测试');
}
if (channel.countries.includes(query.country)) {
score += 30;
reasons.push(`明确覆盖 ${query.country}`);
} else if (region && channel.regions.includes(region)) {
score += 24;
reasons.push(`覆盖 ${region} 区域`);
} else if (channel.countries.includes('GLOBAL')) {
score += 18;
reasons.push('具备全球流量覆盖');
}
if (query.os === 'ALL' || channel.os.includes(query.os)) {
score += 10;
reasons.push(query.os === 'ALL' ? '同时支持 Android / iOS' : `支持 ${query.os === 'AOS' ? 'Android' : 'iOS'}`);
}
const lowerPkg = query.pkg.toLowerCase();
const hint = channel.pkgHints.find((word) => lowerPkg.includes(word));
if (hint) {
score += 5;
reasons.push(`包名特征命中「${hint}」`);
}
return { ...channel, score: Math.min(score, 100), reasons };
}
function priority(score, index) {
if (index === 0 || score >= 80) return ['P1', '优先测试', 'priority-high'];
if (score >= 65) return ['P2', '建议测试', 'priority-mid'];
return ['P3', '补充候选', 'priority-low'];
}
function render(query) {
const results = data.channels.map((channel) => scoreChannel(channel, query)).sort((a, b) => b.score - a.score).slice(0, 6);
summary.innerHTML = `<div><span>PACKAGE</span><strong>${escapeHtml(query.pkg)}</strong></div><div><span>CATEGORY</span><strong>${escapeHtml(categoryLabel(query.category))}</strong></div><div><span>GEO</span><strong>${escapeHtml(countryLabel(query.country))}</strong></div>`;
list.innerHTML = results.map((item, index) => {
const [level, label, className] = priority(item.score, index);
return `<article class="result-card ${index === 0 ? 'top-result' : ''}">
<div class="rank">${String(index + 1).padStart(2, '0')}</div>
<div class="result-main"><div class="result-name"><h3>${escapeHtml(item.name)}</h3><span>${escapeHtml(item.type)}</span></div>
<div class="reason-tags">${item.reasons.slice(1, 4).map((reason) => `<span>${escapeHtml(reason)}</span>`).join('')}</div>
<p>${escapeHtml(item.strengths.join(' · '))}</p>
</div>
<div class="score-block"><div class="score-ring" style="--score:${item.score}"><strong>${item.score}</strong><small>/100</small></div><span class="priority ${className}">${level} · ${label}</span></div>
</article>`;
}).join('');
empty.classList.add('hidden');
content.classList.remove('hidden');
resetBtn.classList.remove('hidden');
}
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, (char) => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;' })[char]);
}
form.addEventListener('submit', (event) => {
event.preventDefault();
const pkg = packageName.value.trim();
if (!pkg || !category.value || !country.value) {
message.textContent = '请完整填写包名、产品品类和投放国家。';
return;
}
message.textContent = '';
const os = form.querySelector('input[name="os"]:checked').value;
render({ pkg, category: category.value, country: country.value, os });
});
document.getElementById('sampleBtn').addEventListener('click', () => {
packageName.value = 'com.example.finance';
category.value = 'finance';
country.value = 'ID';
form.querySelector('input[name="os"][value="AOS"]').checked = true;
message.textContent = '';
});
resetBtn.addEventListener('click', () => {
form.reset();
message.textContent = '';
content.classList.add('hidden');
resetBtn.classList.add('hidden');
empty.classList.remove('hidden');
packageName.focus();
});
})();
+12
View File
@@ -0,0 +1,12 @@
(() => {
document.querySelectorAll('[data-year]').forEach((node) => {
node.textContent = new Date().getFullYear();
});
document.querySelectorAll('a[href]').forEach((link) => {
if (link.hostname && link.hostname !== window.location.hostname) {
link.target = '_blank';
link.rel = 'noopener noreferrer';
}
});
})();
+36
View File
@@ -0,0 +1,36 @@
window.CHANNEL_RULES = {
version: '0.1',
categories: [
{ value: 'finance', label: '金融 / 借贷 / 交易' },
{ value: 'gaming', label: '游戏' },
{ value: 'ecommerce', label: '电商 / 购物' },
{ value: 'social', label: '社交 / 直播' },
{ value: 'entertainment', label: '娱乐 / 短剧 / 视频' },
{ value: 'utility', label: '工具 / 效率' },
{ value: 'travel', label: '旅游 / 出行' },
{ value: 'food', label: '餐饮 / 本地生活' },
{ value: 'other', label: '其他' }
],
countries: [
['US','美国'],['CA','加拿大'],['MX','墨西哥'],['BR','巴西'],['AR','阿根廷'],['CO','哥伦比亚'],
['IN','印度'],['ID','印度尼西亚'],['PH','菲律宾'],['TH','泰国'],['VN','越南'],['MY','马来西亚'],['SG','新加坡'],
['SA','沙特阿拉伯'],['AE','阿联酋'],['EG','埃及'],['TR','土耳其'],['ZA','南非'],['NG','尼日利亚'],
['RU','俄罗斯'],['GB','英国'],['DE','德国'],['FR','法国'],['ES','西班牙'],['IT','意大利'],['AU','澳大利亚'],['JP','日本'],['KR','韩国']
],
regionMap: {
US:'NA', CA:'NA', MX:'LATAM', BR:'LATAM', AR:'LATAM', CO:'LATAM',
IN:'SASIA', ID:'SEA', PH:'SEA', TH:'SEA', VN:'SEA', MY:'SEA', SG:'SEA',
SA:'MENA', AE:'MENA', EG:'MENA', TR:'MENA', ZA:'AFRICA', NG:'AFRICA',
RU:'EEA', GB:'EU', DE:'EU', FR:'EU', ES:'EU', IT:'EU', AU:'OCEANIA', JP:'NEA', KR:'NEA'
},
channels: [
{ name:'Mintegral', type:'自投媒体', base:20, categories:['gaming','utility','ecommerce','entertainment'], countries:['GLOBAL'], regions:['SEA','SASIA','LATAM','MENA','EU','NA'], os:['AOS','IOS'], strengths:['程序化流量覆盖广','App 场景适配度高'], pkgHints:['game','play','shop','video'] },
{ name:'Bigo Ads', type:'自投媒体', base:18, categories:['social','entertainment','gaming','ecommerce'], countries:['ID','PH','TH','VN','MY','BR','MX','SA','AE'], regions:['SEA','LATAM','MENA'], os:['AOS','IOS'], strengths:['社交娱乐流量场景','东南亚与新兴市场覆盖'], pkgHints:['live','chat','video','social'] },
{ name:'Kwai for Business', type:'自投媒体', base:18, categories:['ecommerce','entertainment','gaming','social','finance'], countries:['BR','MX','ID','PH','SA','AE'], regions:['LATAM','SEA','MENA'], os:['AOS','IOS'], strengths:['短视频内容场景','拉美区域覆盖'], pkgHints:['video','shop','live','game'] },
{ name:'MediaGO', type:'自投媒体', base:17, categories:['finance','ecommerce','utility','travel'], countries:['GLOBAL'], regions:['NA','EU','EEA','SEA','LATAM'], os:['AOS','IOS'], strengths:['原生广告场景','金融与电商候选流量'], pkgHints:['finance','loan','credit','bank','shop'] },
{ name:'NewsBreak', type:'自投媒体', base:16, categories:['finance','ecommerce','utility','travel','food'], countries:['US','CA'], regions:['NA'], os:['AOS','IOS'], strengths:['北美资讯内容场景','本地化受众触达'], pkgHints:['news','finance','food','local'] },
{ name:'Adsterra', type:'自投媒体', base:15, categories:['gaming','finance','utility','entertainment'], countries:['GLOBAL'], regions:['SEA','SASIA','LATAM','MENA','AFRICA','EEA'], os:['AOS','IOS'], strengths:['多广告形式覆盖','新兴市场可扩量'], pkgHints:['bet','casino','loan','vpn','game'] },
{ name:'SmartyAds', type:'自投媒体', base:14, categories:['gaming','ecommerce','utility','entertainment','other'], countries:['GLOBAL'], regions:['NA','EU','SEA','LATAM','MENA'], os:['AOS','IOS'], strengths:['程序化流量补充','支持多种媒体宏'], pkgHints:['app','mobile','game','shop'] },
{ name:'Runto', type:'代理渠道', base:13, categories:['finance','gaming','ecommerce','social','utility','travel','food','other'], countries:['GLOBAL'], regions:['SEA','SASIA','LATAM','MENA','AFRICA','EEA','EU'], os:['AOS','IOS'], strengths:['历史包名与国家池较丰富','适合作为代理渠道候选'], pkgHints:[] }
]
};
+57
View File
@@ -0,0 +1,57 @@
window.LINK_TEMPLATES = [
{
name:'Mintegral', platformCode:'M_API_Self_Mintegral', status:'已配置',
macros:[['{click_id}','媒体点击 ID'],['{offer_name}','Offer 名称'],['{ad_id}','素材 ID'],['{ad_name}','素材名称'],['{affiliate_id}','子渠道 ID'],['{idfa}','iOS 广告标识'],['{gaid}','Google 广告标识'],['{device_os}','设备系统'],['{country_code}','ISO 国家码'],['{ip}','设备 IP'],['{ua}','User Agent']],
links:{
click:{endpoint:'https://trident.bigpinggo.com/click', mappings:{clickid:'{click_id}',offer_name:'{offer_name}',creative_id:'{ad_id}',creative_name:'{ad_name}',affiliate_id:'{affiliate_id}',idfa:'{idfa}',gaid:'{gaid}',uagent:'{ua}',os_platform:'{device_os}',os_version:'{os_version}',device_model:'{device_model}',ip:'{ip}',country_code:'{country_code}',ext:'{uuid}',rta_token:'{rta_token}'}},
imp:{endpoint:'https://typhoon.bigpinggo.com/imp', mappings:{clickid:'{impression_id}',offer_name:'{offer_name}',creative_id:'{ad_id}',creative_name:'{ad_name}',affiliate_id:'{affiliate_id}',idfa:'{idfa}',gaid:'{gaid}',uagent:'{ua}',os_platform:'{device_os}',device_model:'{device_model}',ip:'{ip}',country_code:'{country_code}',ext:'{uuid}'}}
}
},
{
name:'Bigo', platformCode:'M_API_Self_Bigo', status:'已配置',
macros:[['__SID__','广告请求 / 点击 ID'],['__APPID__','推广 App ID'],['__CAMPAIGN_ID__','广告系列 ID'],['__CAMPAIGN_NAME__','广告系列名称'],['__AD_ID__','广告 ID'],['__AD_NAME__','广告名称'],['__IDFA__','iOS 广告标识'],['__GAID__','Google 广告标识'],['__IP__','设备 IP'],['__OS__','操作系统'],['__PUBLISHER_ID__','子渠道 ID']],
links:{
click:{endpoint:'https://trident.bigpinggo.com/click', mappings:{clickid:'__SID__',offer_name:'__CAMPAIGN_NAME__',creative_id:'__AD_ID__',creative_name:'__AD_NAME__',affiliate_id:'__PUBLISHER_ID__',idfa:'__IDFA__',gaid:'__GAID__',os_platform:'__OS__',device_model:'__DEVICE_MODEL__',ip:'__IP__',country_code:'',ext:'__AD_EXT__',rta_token:'{rta_token}'}},
imp:{endpoint:'https://typhoon.bigpinggo.com/imp', mappings:{clickid:'imp__SID__',offer_name:'__CAMPAIGN_NAME__',creative_id:'__AD_ID__',creative_name:'__AD_NAME__',affiliate_id:'__PUBLISHER_ID__',idfa:'__IDFA__',gaid:'__GAID__',os_platform:'__OS__',device_model:'__DEVICE_MODEL__',ip:'__IP__',country_code:'',ext:'__AD_EXT__'}}
}
},
{
name:'Kwai', platformCode:'M_API_Self_Kwai_NB', status:'已配置',
macros:[['__CALLBACK__','Click ID'],['__GAID1__','Google 广告标识'],['__IDFA__','iOS 广告标识'],['__IP__','设备 IP'],['__UA__','设备 User Agent'],['__CMPNID__','广告计划 ID'],['__ADSETID__','广告组 ID'],['__ADID__','广告 ID']],
links:{
click:{endpoint:'https://trident.bigpinggo.com/click', mappings:{clickid:'__CALLBACK__',offer_name:'__CMPNID__',creative_id:'__ADSETID__',creative_name:'__ADID__',affiliate_id:'__ADSETID__',idfa:'__IDFA__',gaid:'__GAID1__',uagent:'__UA__',ip:'__IP__',country_code:'',ext:'',rta_token:'{rta_token}'}},
imp:{endpoint:'https://typhoon.bigpinggo.com/imp', mappings:{clickid:'__CALLBACK__',offer_name:'__CMPNID__',creative_id:'__ADSETID__',creative_name:'__ADID__',affiliate_id:'__CMPNID__',idfa:'__IDFA__',gaid:'__GAID1__',uagent:'__UA__',ip:'__IP__',country_code:'',ext:''}}
}
},
{
name:'MediaGO', platformCode:'M_API_Self_Mediago', status:'已配置',
macros:[['{TRACKING_ID}','完整用户旅程 Tracking ID'],['{CLICK_ID}','点击 ID'],['{ADVERTISING_ID}','通用广告标识'],['{ANDROID_ID}','Android 设备 ID'],['{AD_SET_ID}','广告组 / Affiliate ID']],
links:{
click:{endpoint:'https://trident.bigpinggo.com/click', mappings:{clickid:'{TRACKING_ID}',offer_name:'',creative_id:'',creative_name:'{AD_SET_ID}',affiliate_id:'{AD_SET_ID}',idfa:'',gaid:'{ADVERTISING_ID}',android_id:'{ANDROID_ID}',country_code:'',ext:'',rta_token:''}},
imp:{endpoint:'https://typhoon.bigpinggo.com/imp', mappings:{clickid:'{TRACKING_ID}',affiliate_id:'{AD_SET_ID}',gaid:'{ADVERTISING_ID}',android_id:'{ANDROID_ID}',country_code:'',ext:''}}
}
},
{
name:'NewsBreak', platformCode:'A_API_SPDragon', status:'待业务复核',
macros:[['__CALLBACK_PARAM__','平台唯一回调参数'],['__CAMPAIGN_ID__','Campaign ID'],['__CAMPAIGN_NAME__','Campaign 名称'],['__FLIGHT_ID__','Ad Set ID'],['__FLIGHT_NAME__','Ad Set 名称'],['__CREATIVE_ID__','Creative ID'],['__CREATIVE_NAME__','Creative 名称'],['__ADID__','GAID'],['__IDFA__','IDFA'],['__OS__','操作系统']],
links:{
click:{endpoint:'https://trident.bigpinggo.com/l/click', mappings:{clickid:'__CALLBACK_PARAM__',offer_name:'__CAMPAIGN_NAME__',creative_id:'__CREATIVE_ID__',creative_name:'__CREATIVE_NAME__',affiliate_id:'__FLIGHT_ID_____FLIGHT_NAME__',idfa:'__IDFA__',gaid:'__ADID__',os_platform:'__OS__',country_code:'',ext:'',rta_token:'{rta_token}'}},
imp:{endpoint:'https://typhoon.bigpinggo.com/imp', mappings:{clickid:'__CALLBACK_PARAM__',offer_name:'__CAMPAIGN_NAME__',creative_id:'__CREATIVE_ID__',creative_name:'__CREATIVE_NAME__',affiliate_id:'__FLIGHT_ID_____FLIGHT_NAME__',idfa:'__IDFA__',gaid:'__ADID__',os_platform:'__OS__',country_code:'',ext:''}}
}
},
{
name:'Adsterra', platformCode:'P_API_Adsterra', status:'已配置',
macros:[['##SUB_ID_SHORT(action)##','Click ID'],['##PLACEMENT_ID##','广告位 / Affiliate ID'],['##BANNER_ID##','素材 ID'],['##USERAGENT##','User Agent'],['##USER_OS##','设备系统'],['##USER_OS_VER##','系统版本'],['##REMOTE_IP##','设备 IP'],['##COUNTRY_CODE##','国家码']],
links:{click:{endpoint:'https://trident.bigpinggo.com/l/click', mappings:{clickid:'##SUB_ID_SHORT(action)##',offer_name:'',creative_id:'##BANNER_ID##',creative_name:'',affiliate_id:'##PLACEMENT_ID##',uagent:'##USERAGENT##',os_platform:'##USER_OS##',os_version:'##USER_OS_VER##',ip:'##REMOTE_IP##',country_code:'##COUNTRY_CODE##',ext:''}}}
},
{
name:'SmartyAds', platformCode:'M_API_Self_Smartyads', status:'已配置',
macros:[['[CLICK_ID]','Click ID'],['[TIMESTAMP]','UNIX 时间戳'],['[CAMPAIGN_ID]','Campaign ID'],['[CREATIVE_ID]','素材 ID'],['[CREATIVE_NAME]','素材名称'],['[APP_BUNDLE]','媒体 App Bundle'],['[IFA]','设备广告标识'],['[USER_AGENT]','User Agent'],['[OS]','操作系统'],['[IP]','设备 IP'],['[COUNTRY]','国家码']],
links:{click:{endpoint:'https://trident.bigpinggo.com/l/click', mappings:{clickid:'[CLICK_ID]_[TIMESTAMP]',offer_name:'[CAMPAIGN_ID]',creative_id:'[CREATIVE_ID]',creative_name:'[CREATIVE_NAME]',affiliate_id:'[APP_BUNDLE]',gaid:'[IFA]',uagent:'[USER_AGENT]',os_platform:'[OS]',os_version:'[OSV]',device_model:'[DEVICE_MAKE]',ip:'[IP]',country_code:'[COUNTRY]',ext:'[CLICK_ID]',rta_token:''}}}
},
{
name:'Traffic Star', platformCode:'A_API_SPDragon', status:'待业务复核',
macros:[['{click_id}','S2S Click ID'],['{campaign_id}','Campaign ID'],['{creative_id}','素材 ID'],['{site_id}','站点 ID'],['{os}','操作系统'],['{device_type_id}','设备类型 ID'],['{ip}','设备 IP'],['{geo}','两位国家码'],['{format}','素材格式']],
links:{click:{endpoint:'https://trident.bigpinggo.com/l/click', mappings:{clickid:'{click_id}',offer_name:'{campaign_id}',creative_id:'{creative_id}',creative_name:'{format}',affiliate_id:'{site_id}',os_platform:'{os}',os_version:'{os_id}',device_model:'{device_type_id}',ip:'{ip}',country_code:'{geo}',ext:'',rta_token:''}}}
}
];
+142
View File
@@ -0,0 +1,142 @@
(() => {
const templates = window.LINK_TEMPLATES;
if (!templates?.length) return;
const $ = (id) => document.getElementById(id);
const platformSelect = $('platformSelect');
const linkTypeSelect = $('linkTypeSelect');
const mappingBody = $('mappingTableBody');
const macroBody = $('macroTableBody');
const output = $('urlOutput');
const businessIds = ['advid','campid','pkg','platformCode','sanCampid','adsetId','countryCode'];
let state = { platformIndex:0, type:'click', mappings:{} };
$('templateCount').textContent = `${templates.length} 个平台模板`;
templates.forEach((template, index) => platformSelect.add(new Option(template.name, index)));
const activeTemplate = () => templates[state.platformIndex];
const activeLink = () => activeTemplate().links[state.type];
function loadPlatform() {
const template = activeTemplate();
linkTypeSelect.innerHTML = '';
Object.keys(template.links).forEach((type) => linkTypeSelect.add(new Option(type === 'click' ? 'Click Link' : 'Impression Link', type)));
state.type = Object.keys(template.links)[0];
$('platformCode').value = template.platformCode;
$('platformStatus').innerHTML = `<span class="template-status ${template.status.includes('待') ? 'review' : ''}">${template.status}</span>`;
renderMacros();
loadLink();
}
function loadLink() {
state.mappings = { ...activeLink().mappings };
renderMappings();
buildUrl();
}
function renderMacros() {
const query = $('macroSearch').value.trim().toLowerCase();
const rows = activeTemplate().macros.filter(([macro, desc]) => `${macro} ${desc}`.toLowerCase().includes(query));
macroBody.innerHTML = rows.length
? rows.map(([macro, desc]) => `<tr><td><code>${escapeHtml(macro)}</code></td><td>${escapeHtml(desc)}</td></tr>`).join('')
: '<tr><td colspan="2" class="table-empty">没有匹配的媒体宏</td></tr>';
}
function renderMappings() {
mappingBody.innerHTML = Object.entries(state.mappings).map(([key, value]) => `<tr><td><code>${escapeHtml(key)}</code></td><td><input data-key="${escapeHtml(key)}" value="${escapeHtml(value)}" aria-label="${escapeHtml(key)} value"></td></tr>`).join('');
mappingBody.querySelectorAll('input').forEach((input) => input.addEventListener('input', () => {
state.mappings[input.dataset.key] = input.value;
buildUrl();
}));
}
function buildUrl() {
const link = activeLink();
const campid = $('campid').value.trim();
const pkg = $('pkg').value.trim();
const type = state.type;
const params = [];
const addBusiness = (key, value) => params.push(`${key}=${encodeURIComponent(value)}`);
const addRaw = (key, value) => params.push(`${key}=${value}`);
if (type === 'click') {
addBusiness('advid', $('advid').value.trim());
addBusiness('campid', campid);
addBusiness('platform', $('platformCode').value.trim());
addBusiness('pkg', pkg);
} else {
addBusiness('campid', campid);
addBusiness('pkg', pkg);
}
Object.entries(state.mappings).forEach(([key, value]) => {
const finalValue = key === 'country_code' && $('countryCode').value.trim()
? $('countryCode').value.trim().toUpperCase()
: value;
addRaw(key, finalValue);
});
if (type === 'click') {
if ($('sanCampid').value.trim()) addBusiness('san_campid', $('sanCampid').value.trim());
if ($('adsetId').value.trim()) addBusiness('adset_id', $('adsetId').value.trim());
addRaw('c_cto', '0');
} else {
addRaw('at_tag', '2');
}
const url = `${link.endpoint}?${params.join('&')}`;
output.textContent = url;
$('urlLength').textContent = `${url.length} chars`;
const missing = [];
if (!campid) missing.push('Campaign ID');
if (!pkg) missing.push('Package Name');
$('generatorMessage').textContent = missing.length ? `待填写:${missing.join('、')}` : '';
}
function resetGenerator() {
$('advid').value = '30';
$('campid').value = '';
$('pkg').value = '';
$('platformCode').value = activeTemplate().platformCode;
$('sanCampid').value = '';
$('adsetId').value = '-1';
$('countryCode').value = '';
loadLink();
}
async function copyUrl() {
if (!$('campid').value.trim() || !$('pkg').value.trim()) {
$('copyStatus').textContent = '请先补全必填参数';
return;
}
try {
await navigator.clipboard.writeText(output.textContent);
} catch (_) {
const range = document.createRange();
range.selectNodeContents(output);
const selection = window.getSelection();
selection.removeAllRanges();
selection.addRange(range);
document.execCommand('copy');
selection.removeAllRanges();
}
$('copyStatus').textContent = '已复制';
const toast = $('toast');
toast.textContent = '最终链接已复制到剪贴板';
toast.classList.add('show');
setTimeout(() => toast.classList.remove('show'), 1800);
}
function escapeHtml(value) {
return String(value).replace(/[&<>"']/g, (char) => ({ '&':'&amp;', '<':'&lt;', '>':'&gt;', '"':'&quot;', "'":'&#39;' })[char]);
}
platformSelect.addEventListener('change', () => { state.platformIndex = Number(platformSelect.value); loadPlatform(); });
linkTypeSelect.addEventListener('change', () => { state.type = linkTypeSelect.value; loadLink(); });
$('macroSearch').addEventListener('input', renderMacros);
businessIds.forEach((id) => $(id).addEventListener('input', buildUrl));
$('resetGenerator').addEventListener('click', resetGenerator);
$('copyLink').addEventListener('click', copyUrl);
loadPlatform();
})();
+11
View File
File diff suppressed because one or more lines are too long