This commit is contained in:
Liwanyi 2022-09-08 18:13:24 +08:00
parent e8bff038dd
commit d1c5d22f29
18 changed files with 59 additions and 90 deletions

7
global.d.ts vendored
View File

@ -1,7 +0,0 @@
declare interface String {
contains(keywords: RegExp): boolean
contains(keywords: String): boolean
}

3
src/GlobalVars.ts Normal file
View File

@ -0,0 +1,3 @@
export default interface Global {
startTimestamp: number;
}

View File

@ -1,4 +1,4 @@
const titleDict = { export const titleDict = {
'Home': '主页', 'Home': '主页',
'Estate Agents': '地产中介', 'Estate Agents': '地产中介',
'Newspaper': '报纸', 'Newspaper': '报纸',
@ -34,7 +34,7 @@ const titleDict = {
"Christmas Town": '圣诞小镇', "Christmas Town": '圣诞小镇',
"My Maps": '我的地图', "My Maps": '我的地图',
} }
const titleLinksDict = { export const titleLinksDict = {
'Personal stats': '个人统计信息', 'Personal stats': '个人统计信息',
'Log': '日志', 'Log': '日志',
'Tell your story': '说出你的故事', 'Tell your story': '说出你的故事',
@ -71,7 +71,7 @@ const titleLinksDict = {
"My Maps": '我的地图', "My Maps": '我的地图',
"Mods": '模组', "Mods": '模组',
} }
const sidebarDict = { // todo 从json加载 export const sidebarDict = { // todo 从json加载
'Money': '现金', 'Money': '现金',
'Level': '等级', 'Level': '等级',
'Points': 'PT', 'Points': 'PT',
@ -132,7 +132,7 @@ const sidebarDict = { // todo 从json加载
// 'You are not in Torn': '你不在城里', // 'You are not in Torn': '你不在城里',
// 'Rank': '阶级', // 'Rank': '阶级',
// }; // };
const statusDict = { export const statusDict = {
'Traveling to Mexico': '正在飞往墨西哥', 'Traveling to Mexico': '正在飞往墨西哥',
'Traveling to Cayman Islands': '正在飞往开曼群岛', 'Traveling to Cayman Islands': '正在飞往开曼群岛',
'Traveling to Canada': '正在飞往加拿大', 'Traveling to Canada': '正在飞往加拿大',
@ -168,7 +168,7 @@ const statusDict = {
'In South Africa': '在南非', 'In South Africa': '在南非',
} }
// const miniProfileDict = {} // const miniProfileDict = {}
const homeDict = { export const homeDict = {
'General Information': '基本信息', 'General Information': '基本信息',
'Property Information': '房产信息', 'Property Information': '房产信息',
'Battle Stats': '战斗能力 (BS)', 'Battle Stats': '战斗能力 (BS)',
@ -226,7 +226,7 @@ const homeDict = {
'Intelligence': '智力 INT', 'Intelligence': '智力 INT',
'Endurance': '耐心 END', 'Endurance': '耐心 END',
} }
const attackDict = { export const attackDict = {
"mugged": "打劫了 ", "mugged": "打劫了 ",
"attached": "攻击了 ", "attached": "攻击了 ",
"arrested": "逮捕了 ", "arrested": "逮捕了 ",
@ -235,7 +235,7 @@ const attackDict = {
"Someone": "有人", "Someone": "有人",
"lost": "失败了" "lost": "失败了"
} }
const newspaperDict = { export const newspaperDict = {
'front page': '头版', 'front page': '头版',
'archive': '归档', 'archive': '归档',
'job listing': '工作', 'job listing': '工作',
@ -303,7 +303,7 @@ const newspaperDict = {
'None': '无', 'None': '无',
'A total of {$} listings were found.': '总共发现了 {$} 个悬赏', 'A total of {$} listings were found.': '总共发现了 {$} 个悬赏',
} }
const propertyDict = { export const propertyDict = {
'Shack': '棚屋', 'Shack': '棚屋',
'Trailer': '拖车', 'Trailer': '拖车',
'Apartment': '公寓', 'Apartment': '公寓',
@ -356,7 +356,7 @@ const propertyDict = {
'Pay bills': '支付账单', 'Pay bills': '支付账单',
'Recent upkeep payments': '最近的维护费用', 'Recent upkeep payments': '最近的维护费用',
} }
const travelingDict = { export const travelingDict = {
'Remaining Flight Time -': '剩余时间 - ', 'Remaining Flight Time -': '剩余时间 - ',
'Torn to London.': '正在飞往英国伦敦 (London)。', 'Torn to London.': '正在飞往英国伦敦 (London)。',
'London to Torn.': '正从英国伦敦 (London) 回城。', 'London to Torn.': '正从英国伦敦 (London) 回城。',
@ -367,14 +367,14 @@ const travelingDict = {
'Torn to Buenos Aires.': '正在飞往阿根廷布宜诺斯艾利斯 (Buenos Aires)。', 'Torn to Buenos Aires.': '正在飞往阿根廷布宜诺斯艾利斯 (Buenos Aires)。',
'Buenos Aires to Torn.': '正从阿根廷布宜诺斯艾利斯 (Buenos Aires) 回城。', 'Buenos Aires to Torn.': '正从阿根廷布宜诺斯艾利斯 (Buenos Aires) 回城。',
} }
const tipsDict = { export const tipsDict = {
'According to the Economist Intelligence Unit\'s Quality of Life Index, Switzerland is the second best place in the world to be born, after Denmark. Torn ranked 224th, despite there being only 194 recognised nations in the world.': "根据经济学人信息部提供的生活质量指数瑞士是世界上第二个最适合出生的地方仅次于丹麦。尽管世界上只有194个公认的国家但托恩却排名第224位。", 'According to the Economist Intelligence Unit\'s Quality of Life Index, Switzerland is the second best place in the world to be born, after Denmark. Torn ranked 224th, despite there being only 194 recognised nations in the world.': "根据经济学人信息部提供的生活质量指数瑞士是世界上第二个最适合出生的地方仅次于丹麦。尽管世界上只有194个公认的国家但托恩却排名第224位。",
'The opportunities for employment in Torn are wide and varied, from jobs in the zoo and the meat warehouse through to fantastic openings at the strip club - a description rarely used to refer to those who work there.': "托恩的就业机会广泛而多样,从动物园和肉类仓库的工作,到脱衣舞俱乐部的奇妙开场--这是一个很少用来形容在那里工作的人的描述。", 'The opportunities for employment in Torn are wide and varied, from jobs in the zoo and the meat warehouse through to fantastic openings at the strip club - a description rarely used to refer to those who work there.': "托恩的就业机会广泛而多样,从动物园和肉类仓库的工作,到脱衣舞俱乐部的奇妙开场--这是一个很少用来形容在那里工作的人的描述。",
'One of the main requirements for a wedding to take place in Torn is the procurement of a ring. You may either purchase one from the Jewellery Store, or you can choose the more romantic option of stealing one, which requires far more effort if you think about it.': "在托恩举行婚礼的主要要求之一就是购买一枚戒指。你可以从珠宝店购买,也可以选择在偷窃戒指中选择一种很浪漫的方式,但是你冷静下来想想的话,这也是需要付出更大代价的。", 'One of the main requirements for a wedding to take place in Torn is the procurement of a ring. You may either purchase one from the Jewellery Store, or you can choose the more romantic option of stealing one, which requires far more effort if you think about it.': "在托恩举行婚礼的主要要求之一就是购买一枚戒指。你可以从珠宝店购买,也可以选择在偷窃戒指中选择一种很浪漫的方式,但是你冷静下来想想的话,这也是需要付出更大代价的。",
"Despite having no discernible court system Torn still employs several thousand people within its Judicial Services department. Nobody knows what the hell these people do all day, but if we had to guess, we'd say Solitaire.": "尽管没有明显的法院系统,托恩仍然在其司法服务部门雇用了几千人。没有人知道这些人整天都在做什么,但如果我们必须猜测,我们会说是接龙。", "Despite having no discernible court system Torn still employs several thousand people within its Judicial Services department. Nobody knows what the hell these people do all day, but if we had to guess, we'd say Solitaire.": "尽管没有明显的法院系统,托恩仍然在其司法服务部门雇用了几千人。没有人知道这些人整天都在做什么,但如果我们必须猜测,我们会说是接龙。",
"At one point a Dual Wield Melee course was available at Torn City College for a fee of $50,000,000, but this was discontinued when Torn's citizens realized they were effectively paying to learn how to hold two things at once.": "曾经托恩城市学院开设了双持近战课程,收费$50,000,000。当市民意识到他们其实是在花钱学习怎么同时拿着两个东西的时侯这个课程正好结课了。", "At one point a Dual Wield Melee course was available at Torn City College for a fee of $50,000,000, but this was discontinued when Torn's citizens realized they were effectively paying to learn how to hold two things at once.": "曾经托恩城市学院开设了双持近战课程,收费$50,000,000。当市民意识到他们其实是在花钱学习怎么同时拿着两个东西的时侯这个课程正好结课了。",
} }
const cityDict = { export const cityDict = {
'Map': '地图', 'Map': '地图',
'Quick Links': '快速链接', 'Quick Links': '快速链接',
'Key of Symbols': '标志建筑', 'Key of Symbols': '标志建筑',
@ -443,7 +443,7 @@ const cityDict = {
"Bits 'n' Bobs": '胖鲍勃的杂货店', "Bits 'n' Bobs": '胖鲍勃的杂货店',
// '':'', // '':'',
} }
const gymDict = { export const gymDict = {
'Strength': '力量 STR', 'Strength': '力量 STR',
'Defense': '防御 DEF', 'Defense': '防御 DEF',
'Speed': '速度 SPD', 'Speed': '速度 SPD',
@ -545,7 +545,7 @@ const gymDict = {
'Weighted Chin-ups': '负重引体向上', 'Weighted Chin-ups': '负重引体向上',
} }
const gymList = { export const gymList = {
'Premier Fitness': '首席健身', 'Premier Fitness': '首席健身',
'Average Joes': '平衡乔伊', 'Average Joes': '平衡乔伊',
"Woody's Workout Club": '无敌健身', "Woody's Workout Club": '无敌健身',
@ -583,7 +583,7 @@ const gymList = {
'The Sports Science Lab': '运动科学实验室', 'The Sports Science Lab': '运动科学实验室',
'The Jail Gym': '监狱健身房', 'The Jail Gym': '监狱健身房',
} }
const eduDict = { export const eduDict = {
'Biology': '生物学', 'Biology': '生物学',
'Business': '商学', 'Business': '商学',
'Combat Training': '战斗训练', 'Combat Training': '战斗训练',
@ -662,7 +662,7 @@ const eduDict = {
'Computer Security and Defense': '计算机安全与防御', 'Computer Security and Defense': '计算机安全与防御',
'Gain a 10% increase in hacking crime success rate': '提高黑客犯罪10%的成功率', 'Gain a 10% increase in hacking crime success rate': '提高黑客犯罪10%的成功率',
} }
const headerDict = { export const headerDict = {
"User's Name / ID": '用户名或ID', "User's Name / ID": '用户名或ID',
"Name": '用户名', "Name": '用户名',
'Faction': '帮派', 'Faction': '帮派',
@ -709,7 +709,7 @@ const headerDict = {
'Logout': '登出', 'Logout': '登出',
'Gender': '性别', 'Gender': '性别',
} }
const eventsDict = { export const eventsDict = {
// "ALL EVENTS": "所有通知", // "ALL EVENTS": "所有通知",
// "RECEIVED": "收到的通知", // "RECEIVED": "收到的通知",
// "SAVED": "已保存通知", // "SAVED": "已保存通知",
@ -770,7 +770,7 @@ const eventsDict = {
'You took some Xanax and downed a glass of water. A headache was followed by nausea and vomiting. You overdosed.': 'You took some Xanax and downed a glass of water. A headache was followed by nausea and vomiting. You overdosed.':
'你吃了一些 Xanax喝了一杯水。头痛之后是恶心和呕吐。你用药过量了(OD)。', '你吃了一些 Xanax喝了一杯水。头痛之后是恶心和呕吐。你用药过量了(OD)。',
} }
const chatDict = { export const chatDict = {
'Global': '世界', 'Global': '世界',
'Faction': '帮派', 'Faction': '帮派',
'People': '联系人', 'People': '联系人',
@ -822,7 +822,7 @@ const chatDict = {
'Enter a name or ID to add to this list': '输入用户名或ID来添加到这个列表', 'Enter a name or ID to add to this list': '输入用户名或ID来添加到这个列表',
'This list is empty.': '这个列表是空的。', 'This list is empty.': '这个列表是空的。',
} }
const hosDict = { export const hosDict = {
"You take a trip down to the hospital to see who's been unlucky today.": "你来了一趟医院,看看今天是谁那么倒霉", "You take a trip down to the hospital to see who's been unlucky today.": "你来了一趟医院,看看今天是谁那么倒霉",
"Nobody can revive you.": "现在没有玩家可以复活你", "Nobody can revive you.": "现在没有玩家可以复活你",
"Anyone can now revive you.": "现在任何玩家都能复活你", "Anyone can now revive you.": "现在任何玩家都能复活你",
@ -864,7 +864,7 @@ const hosDict = {
"Lost to": "输给了 ", "Lost to": "输给了 ",
"Suspect of a presidential assassination": "刺杀总统的嫌疑犯", "Suspect of a presidential assassination": "刺杀总统的嫌疑犯",
} }
const awDict = { export const awDict = {
'Honors (': '荣誉 (', 'Honors (': '荣誉 (',
'Medals (': '勋章 (', 'Medals (': '勋章 (',
'Merits (': '天赋加点 (', 'Merits (': '天赋加点 (',
@ -1091,7 +1091,7 @@ const awDict = {
// 'Trader': '', // 'Trader': '',
// 'Tycoon': '', // 'Tycoon': '',
// }; // };
const ocList = { export const ocList = {
'blackmail someone': '敲诈某人', 'blackmail someone': '敲诈某人',
'blackmailed someone': '敲诈某人', 'blackmailed someone': '敲诈某人',
'kidnap someone': '绑架某人', 'kidnap someone': '绑架某人',
@ -1104,7 +1104,7 @@ const ocList = {
// 'hijack a plane':'劫持飞机', // 'hijack a plane':'劫持飞机',
// 'political assassination':'政治暗杀 (PA)', // 'political assassination':'政治暗杀 (PA)',
} }
const profileDict = { export const profileDict = {
'User Information': '用户信息', 'User Information': '用户信息',
'Actions': '动作', 'Actions': '动作',
'Status': '状态', 'Status': '状态',
@ -1183,7 +1183,7 @@ const profileDict = {
'There is no active competition': '现在没有活动', 'There is no active competition': '现在没有活动',
"doesn't wish to share": '不希望公开', "doesn't wish to share": '不希望公开',
} }
const sendCashDict = { export const sendCashDict = {
'Some players may be out to part you from your money, using any means necessary. Use the secure trade feature if you want to protect an exchange and check the contents very carefully.': 'Some players may be out to part you from your money, using any means necessary. Use the secure trade feature if you want to protect an exchange and check the contents very carefully.':
'有些玩家可能会利用任何必要的手段来骗走你的钱。如果你想保护交易,请使用更安全的“交易”功能,并仔细检查交易的内容。', '有些玩家可能会利用任何必要的手段来骗走你的钱。如果你想保护交易,请使用更安全的“交易”功能,并仔细检查交易的内容。',
'Do not fall for scams!': '切勿上当受骗!', 'Do not fall for scams!': '切勿上当受骗!',
@ -1194,7 +1194,7 @@ const sendCashDict = {
'Click here to add the maximum amount, or use shortcuts like <br /> 5k, 1.5m, max, half, quarter, 1/2, 1/3, 1/4, 25%': 'Click here to add the maximum amount, or use shortcuts like <br /> 5k, 1.5m, max, half, quarter, 1/2, 1/3, 1/4, 25%':
'点此添加最大额度,或输入快捷选项如 <br /> 5k, 1.5m, max, half, quarter, 1/2, 1/3, 1/4, 25%', '点此添加最大额度,或输入快捷选项如 <br /> 5k, 1.5m, max, half, quarter, 1/2, 1/3, 1/4, 25%',
}; };
const stockDict = { export const stockDict = {
'Stock': '股票', 'Stock': '股票',
'Name': '名称', 'Name': '名称',
'Price': '单价', 'Price': '单价',
@ -1324,7 +1324,7 @@ const stockDict = {
'high:': '最高:', 'high:': '最高:',
'low:': '最低:', 'low:': '最低:',
} }
const itemPageDict = { export const itemPageDict = {
'Primary': '主手', 'Primary': '主手',
'Secondary': '副手', 'Secondary': '副手',
'Melee': '近战', 'Melee': '近战',
@ -1400,7 +1400,7 @@ const itemPageDict = {
'Rate of Fire:': '开火率:', 'Rate of Fire:': '开火率:',
'Read:': '已读:', 'Read:': '已读:',
} }
const itemNameDict = { export const itemNameDict = {
"Ammunition Pack": '弹药包', "Ammunition Pack": '弹药包',
"Clothing Cache": '服饰箱', "Clothing Cache": '服饰箱',
"First Aid Kit": '急救包', "First Aid Kit": '急救包',
@ -2242,7 +2242,7 @@ const itemNameDict = {
// "Temporarily passively increases all statistics while attacking or defending by 25%.": // "Temporarily passively increases all statistics while attacking or defending by 25%.":
// '在攻击或防守时暂时地获得25%战斗属性增益。', // '在攻击或防守时暂时地获得25%战斗属性增益。',
// }; // };
const itemEffectDict = { export const itemEffectDict = {
// 彩票 // 彩票
'Effect: Provides 100 Lucky Shot Lotto tickets when used.': '', 'Effect: Provides 100 Lucky Shot Lotto tickets when used.': '',
// enhancer // enhancer
@ -2278,7 +2278,7 @@ const itemEffectDict = {
"Effect: Increases happiness by 5-20 if under 20% of maximum.": '', "Effect: Increases happiness by 5-20 if under 20% of maximum.": '',
"Effect: Can be used with a Fruitcake and Cake Frosting to create a Special Fruitcake.": '', "Effect: Can be used with a Fruitcake and Cake Frosting to create a Special Fruitcake.": '',
} }
const itemTypeDict = { export const itemTypeDict = {
'is a Defensive Armor.': '是一种防御性盔甲。', 'is a Defensive Armor.': '是一种防御性盔甲。',
'are a Defensive Armor.': '是一种防御性盔甲。', 'are a Defensive Armor.': '是一种防御性盔甲。',
'is a Machine Gun Weapon.': '是一种机枪。', 'is a Machine Gun Weapon.': '是一种机枪。',
@ -2316,7 +2316,7 @@ const itemTypeDict = {
'is a Special Item.': '是一种特殊物品。', 'is a Special Item.': '是一种特殊物品。',
} }
// const itemReqDict = {}; // const itemReqDict = {};
const tornSettingsDict = { export const tornSettingsDict = {
'API Keys': 'API 密钥', 'API Keys': 'API 密钥',
'General settings': '通用设置', 'General settings': '通用设置',
'Attack settings': '攻击设置', 'Attack settings': '攻击设置',
@ -2329,7 +2329,7 @@ const tornSettingsDict = {
'Here you can share your account\'s information with': 'Here you can share your account\'s information with':
'在这里你可以通过向脚本、扩展和应用程序等软件提供API密钥与它们分享你的账户信息。一旦你创建了一个具有相关权限的API密钥你可以在任何时候通过删除该密钥来取消授权。', '在这里你可以通过向脚本、扩展和应用程序等软件提供API密钥与它们分享你的账户信息。一旦你创建了一个具有相关权限的API密钥你可以在任何时候通过删除该密钥来取消授权。',
} }
const missionDict = { export const missionDict = {
'_taskHint': { '_taskHint': {
a_good_day_to_get_hard: { a_good_day_to_get_hard: {
task: "实现3-10的连杀", task: "实现3-10的连杀",
@ -2647,10 +2647,10 @@ const missionDict = {
"with a brick.": '使用砖头 (brick)', "with a brick.": '使用砖头 (brick)',
"Put": '', "Put": '',
} }
const pcDict = { export const pcDict = {
'Virus Programming': '病毒编程', 'Virus Programming': '病毒编程',
} }
const npcShopDict = { export const npcShopDict = {
"Buy Items": '购买物品', "Buy Items": '购买物品',
"Buy": '买', "Buy": '买',
"in stock)": '存货)', "in stock)": '存货)',
@ -2680,7 +2680,7 @@ const npcShopDict = {
"Are you sure you would like to buy": '你是否想买', "Are you sure you would like to buy": '你是否想买',
"Sell your points": '出售你的PT', "Sell your points": '出售你的PT',
} }
const calDict = { export const calDict = {
'January': '一月', 'January': '一月',
'February': '二月', 'February': '二月',
"March": "三月", "March": "三月",
@ -2695,7 +2695,7 @@ const calDict = {
"December": "十二月", "December": "十二月",
} }
// 中文字符集正则 // 中文字符集正则
const CC_set = /[\u4e00-\u9fa5]/ export const CC_set = /[\u4e00-\u9fa5]/
// const transDict = {}; // const transDict = {};
// transDict.titleDict = titleDict; // transDict.titleDict = titleDict;
// transDict.titleLinksDict = titleLinksDict; // transDict.titleLinksDict = titleLinksDict;
@ -2736,43 +2736,4 @@ const CC_set = /[\u4e00-\u9fa5]/
// transDict.calDict = calDict; // transDict.calDict = calDict;
// if (!localStorage.getItem('wh_trans_transDict')) localStorage.setItem('wh_trans_transDict', JSON.stringify(transDict)) // if (!localStorage.getItem('wh_trans_transDict')) localStorage.setItem('wh_trans_transDict', JSON.stringify(transDict))
export { export * from './translation'
titleDict,
titleLinksDict,
sidebarDict,
// tooltipDict,
statusDict,
// miniProfileDict,
homeDict,
attackDict,
newspaperDict,
propertyDict,
travelingDict,
tipsDict,
cityDict,
gymDict,
gymList,
eduDict,
headerDict,
eventsDict,
chatDict,
hosDict,
awDict,
// playerTitleList,
ocList,
profileDict,
sendCashDict,
stockDict,
itemPageDict,
itemNameDict,
// itemDescDict,
itemEffectDict,
itemTypeDict,
// itemReqDict,
tornSettingsDict,
missionDict,
pcDict,
npcShopDict,
calDict,
CC_set,
}

View File

@ -1,7 +1,7 @@
import './global' import './global'
import log from "./function/utils/log"; import log from "./func/utils/log";
import getWhSettingObj from "./function/utils/getWhSettingObj"; import getWhSettingObj from "./func/utils/getWhSettingObj";
import miniprofTrans from "./function/translate/miniprofTrans"; import miniprofTrans from "./func/translate/miniprofTrans";
export default function init() { export default function init() {
const UWCopy: Window & typeof globalThis = window["unsafeWindow"]; const UWCopy: Window & typeof globalThis = window["unsafeWindow"];

5
src/main.d.ts vendored Normal file
View File

@ -0,0 +1,5 @@
declare interface String {
contains(keywords: RegExp|String): boolean
}

View File

@ -1,2 +1,7 @@
import userscript from "./userscript"; import userscript from "./userscript";
userscript(); import Global from "./global";
const glob: Global = {
startTimestamp: -1,
};
userscript(glob);

View File

@ -39,13 +39,15 @@ import {
calDict, calDict,
CC_set, CC_set,
} from './dictionary/translation'; } from './dictionary/translation';
import * as DICTION from './dictionary/translation'
import Device from "./enum/Device"; import Device from "./enum/Device";
import UserScriptEngine from "./enum/UserScriptEngine"; import UserScriptEngine from "./enum/UserScriptEngine";
import getPlayerInfo from "./function/utils/getPlayerInfo"; import getPlayerInfo from "./func/utils/getPlayerInfo";
import autoFetchJSON from "./function/utils/autoFetchJSON"; import autoFetchJSON from "./func/utils/autoFetchJSON";
import Global from "./global";
export default function userscript(): void { export default function userscript(glob: Global): void {
const start_timestamp: number = Date.now(); glob.startTimestamp = Date.now();
if (document.title.toLowerCase().includes('just a moment')) return; if (document.title.toLowerCase().includes('just a moment')) return;
let {version, isIframe, $, PDA_APIKey, isPDA} = init(); let {version, isIframe, $, PDA_APIKey, isPDA} = init();

View File

@ -12,8 +12,8 @@ async function main() {
} }
} }
// 防止脚本重复运行 // 防止脚本重复运行
if (window.WHTRANS) return; if (window.hasOwnProperty('WHTRANS')) return;
window.WHTRANS = true; else window.WHTRANS = true;
const version = '$$WUHU_DEV_VERSION$$'; const version = '$$WUHU_DEV_VERSION$$';
const isIframe = self !== top; const isIframe = self !== top;
const $ = window['jQuery']; const $ = window['jQuery'];