import adHelper from "../func/module/adHelper"; import safeKeeper from "../func/module/safeKeeper"; import initMiniProf from "../func/utils/initMiniProf"; import CommonUtils from "./utils/CommonUtils"; import Alert from "./utils/Alert"; import * as EVENTS from "../../static/json/event.json"; import * as FEST from "../../static/json/fest.json"; import Popup from "./utils/Popup"; import TravelItem from "./action/TravelItem"; import ZHONG_MENU_HTML from "../../static/html/zhong/zhong_menu.html"; import ZHONG_UPDATE_HTML from "../../static/html/zhong/zhong_update.html"; import ZHONG_LOOT_HTML from "../../static/html/zhong/zhong_loot.html"; import Test from "../test/Test"; import Timer from "./utils/Timer"; import QuickFlyBtnHandler from "./handler/QuickFlyBtnHandler"; import NNB from "./handler/NNB"; import QuickLinksHandler from "./handler/QuickLinksHandler"; import ItemPriceWatcherHandler from "./handler/ItemPriceWatcherHandler"; import ChangeLogHandler from "./handler/ChangeLogHandler"; import ItemPriceHandler from "./handler/ItemPriceHandler"; import SettingsHandler from "./handler/SettingsHandler"; import { MENU_ITEM_TYPE } from "../interface/MenuItem"; import { Injectable } from "../container/Injectable"; import ClassName from "../container/ClassName"; import LocalConfigWrapper from "./LocalConfigWrapper"; import Logger from "./Logger"; import { Container } from "../container/Container"; import TornPDAUtils from "./utils/TornPDAUtils"; import InfoUtils from "./utils/InfoUtils"; import globVars from "../globVars"; @Injectable() @ClassName('ZhongIcon') export default class ZhongIcon { public static ZhongNode: MyHTMLElement = null; private menuItemList: MenuItemConfig[] = null; private cashView: HTMLElement = null; public constructor( private readonly commonUtils: CommonUtils, private readonly localConfigWrapper: LocalConfigWrapper, private readonly nnb: NNB, private readonly itemPriceWatcherHandler: ItemPriceWatcherHandler, private readonly logger: Logger, private readonly tornPDAUtils: TornPDAUtils, private readonly infoUtils: InfoUtils, ) { } public init() { this.constructMenuList() .insert2Dom() .dragHandler(); this.logger.info('设置图标结束'); } public updateCashView(content: string): void { if (!this.cashView || !ZhongIcon.ZhongNode.contains(this.cashView)) { this.cashView = document.createElement('div'); this.cashView.id = 'wh-cash-monitor'; ZhongIcon.ZhongNode.append(this.cashView); } this.cashView.innerText = content; } private static setPosition(x: number, y: number) { if (!(x && y)) return; if (x > 0 && x < document.documentElement.offsetWidth - 100) { ZhongIcon.ZhongNode.style.left = x + "px"; } if (y > 0 && y < document.documentElement.offsetHeight - 60) { ZhongIcon.ZhongNode.style.top = y + "px"; } } /** * 添加左侧图标 */ private insert2Dom(): ZhongIcon { let zhongNode: MyHTMLElement = document.querySelector('div#wh-trans-icon'); let version = globVars.version; if ((self !== top) || !!zhongNode) return null; zhongNode = document.createElement('div'); ZhongIcon.ZhongNode = zhongNode; zhongNode.id = 'wh-trans-icon'; zhongNode.classList.add('cont-gray'); zhongNode.innerHTML = ZHONG_MENU_HTML.replace('{{}}', version.slice(-1) === '$' ? 'DEV' : version); // 助手菜单 const menu_cont = zhongNode.querySelector('#wh-gSettings'); // 遍历菜单node设置、生成node、插入dom this.menuItemList.forEach(setting => this.commonUtils.elemGenerator(setting, menu_cont)); this.logger.info('生成元素插入完成'); // 计时node zhongNode.initTimer = zhongNode.querySelector('#wh-inittimer'); // 芜湖助手图标点击事件 (zhongNode.querySelector('#wh-trans-icon-btn')).onclick = () => { zhongNode.classList.toggle('wh-icon-expanded'); const click_func = e => { this.logger.info(e.target); if (e.target === zhongNode.querySelector('#wh-trans-icon-btn')) return; if (!zhongNode.contains(e.target)) { this.logger.info('移除事件监听器'); document.body.removeEventListener('click', click_func); zhongNode.classList.remove('wh-icon-expanded'); } }; if (zhongNode.classList.contains('wh-icon-expanded')) { this.logger.info('芜湖助手图标点击->添加监听'); document.body.addEventListener('click', click_func); } else { this.logger.info('芜湖助手图标->移除监听'); document.body.removeEventListener('click', click_func); } }; // 更新按钮点击事件 (zhongNode.querySelector('#wh-update-btn')).onclick = e => { (e.target).blur(); // 直接复制的按钮 new Popup(ZHONG_UPDATE_HTML, '如何更新') .element .querySelector('button').onclick = async (e) => { let target = e.target as HTMLButtonElement; target.innerHTML = '加载中'; const js_text = await CommonUtils.COFetch(`https://jjins.github.io/fyfuzhi/release.min.user.js?${ performance.now() }`); target.innerHTML = '点击复制到剪切板'; target.onclick = () => { const textarea_node = document.createElement('textarea'); textarea_node.innerHTML = js_text; target.parentElement.append(textarea_node); textarea_node.focus(); textarea_node.select(); document.execCommand('Copy'); textarea_node.remove(); target.innerHTML = '已复制'; target.onclick = null; new Alert('脚本已复制,请前往粘贴'); }; }; }; // 节日 zhongNode.querySelectorAll('#wh-trans-fest-date button').forEach((el, i) => i === 0 ? el.addEventListener('click', () => { let html = ''; // TODO 动态节日数据 Object.keys(FEST.val).sort().forEach(date => html += `` ); new Popup(html += '
${ 1 + ((date.slice(0, 2)) | 0) }月${ date.slice(2) }日${ FEST.val[date].name }${ FEST.val[date].eff }
', '节日'); }) : el.addEventListener('click', null)); // 活动 zhongNode.querySelectorAll('#wh-trans-event-cont button').forEach((el, i) => i === 0 ? el.addEventListener('click', () => { let html = ''; // TODO 动态节日数据 EVENTS.default.forEach(el => html += ``); new Popup(html += '
${ el.name }${ el.start[0] + 1 }月${ el.start[1] }日${ el.start[2] }:00~${ el.end[0] + 1 }月${ el.end[1] }日${ el.end[2] }:00
${ el.eff }

更多信息请关注群聊和公众号

', '活动'); }) : el.addEventListener('click', null)); // 调整图标至有记录的位置 if (this.localConfigWrapper.config.SaveIconPosition) { let iconPosition = this.localConfigWrapper.config.IconPosition; let documentSize = { x: document.documentElement.offsetWidth, y: document.documentElement.offsetHeight }; ZhongIcon.setPosition( iconPosition.x > documentSize.x ? documentSize.x * 0.9 | 0 : iconPosition.x, iconPosition.y > documentSize.y ? documentSize.y * 0.9 | 0 : iconPosition.y ); } document.body.append(zhongNode); // 引入torn自带浮动提示 this.logger.info('引入torn浮动提示'); (window.initializeTooltip) && (window.initializeTooltip('.wh-container', 'white-tooltip')); // 加载torn mini profile this.logger.info('加载torn mini profile'); let miniProfileInterval = { id: window.setInterval(() => { miniProfileInterval.counter++; if (window.$ || (window.unsafeWindow && window.unsafeWindow.$)) { initMiniProf('#wh-trans-icon'); window.clearInterval(miniProfileInterval.id); } if (miniProfileInterval.counter > 30) window.clearInterval(miniProfileInterval.id); }, 1000), counter: 0 }; this.logger.info('图标加入文档树'); return this; } private dragHandler(): ZhongIcon { let isMouseDown = false; let isMouseMoved = false; let offsetXY = { x: 0, y: 0 }; ZhongIcon.ZhongNode.addEventListener('mousedown', (e) => { if (e.button === 0) { e.preventDefault(); isMouseDown = true; let nodeXY = ZhongIcon.getPosition(); offsetXY.x = e.x - nodeXY.x; offsetXY.y = e.y - nodeXY.y; } }); document.addEventListener('mouseup', () => { isMouseDown = false; if (isMouseMoved) { isMouseMoved = false; if (this.localConfigWrapper.config.SaveIconPosition) { this.localConfigWrapper.config.IconPosition = ZhongIcon.getPosition(); } } }); document.addEventListener('mousemove', (e) => { if (isMouseDown) { ZhongIcon.setPosition(e.x - offsetXY.x, e.y - offsetXY.y); isMouseMoved = true; } }); return this; } private static getPosition(): { x: number, y: number } { return { x: ZhongIcon.ZhongNode.style.left ? parseInt(ZhongIcon.ZhongNode.style.left.slice(0, -2)) : ZhongIcon.ZhongNode.offsetLeft, y: ZhongIcon.ZhongNode.style.top ? parseInt(ZhongIcon.ZhongNode.style.top.slice(0, -2)) : ZhongIcon.ZhongNode.offsetTop } } // 菜单 private constructMenuList(): ZhongIcon { this.logger.info('构造菜单列表开始'); let timer = new Timer(); const date = new Date(); let userInfo = this.infoUtils.getPlayerInfo(); let list: MenuItemConfig[] = []; // 欢迎 显示玩家id if (userInfo.userID !== 0) { list.push({ domType: 'plain', domId: 'wh-trans-welcome', domHTML: `${ userInfo.playername }[${ userInfo.userID }]`, }); } // 节日 let fest_date_html = ': '; { // 节日字典 const dict = FEST.val; // list.fest_date_dict = dict; // list.fest_date_list = Object.keys(dict); const formatMMDD = (m, d) => { const MM = m < 10 ? `0${ m }` : m.toString(); const DD = d < 10 ? `0${ d }` : d.toString(); return MM + DD; } const fest_date_key = formatMMDD(date.getUTCMonth(), date.getUTCDate()); if (dict[fest_date_key]) fest_date_html += `今天 - ${ dict[fest_date_key]['name'] }()`; else { // 月日列表 let list = Object.keys(dict); list.push(fest_date_key); // 下个节日的位置 const index: number = list.sort().indexOf(fest_date_key) + 1; // 下个节日obj const next_fest_date = dict[list[index] || list[0]]; // 下个节日的时间 let next = new Date( index !== list.length ? date.getUTCFullYear() : date.getUTCFullYear() + 1, (list[index !== list.length ? index : 0] as any).slice(0, 2) | 0, (list[index !== list.length ? index : 0] as any).slice(2) | 0, 8 ).getTime(); // 剩余天数 const left = (next - date.getTime()) / 86400000 | 0; fest_date_html += `${ left }天后 - ${ next_fest_date.name }()`; } } list.push({ domType: 'plain', domId: 'wh-trans-fest-date', domHTML: fest_date_html, }); // 活动 let eventObj: EventWrapper = { onEv: false, daysLeft: Infinity, events: EVENTS.default, }; list.events = EVENTS.default; eventObj.events.forEach((obj, index) => { if (eventObj.onEv) return; // 当前年份 const nowYear = date.getFullYear(); // 当前遍历的活动开始时间 const start = new Date(nowYear, obj.start[0], obj.start[1], obj.start[2]); // 当前遍历的活动结束时间 const end = new Date(nowYear, obj.end[0], obj.end[1], obj.end[2]); // 当前处于活动中 if (start < date && date < end) { eventObj.onEv = true; eventObj.daysLeft = (end.getTime() - date.getTime()) / 86400000 | 0; eventObj.current = obj; } // 当前没有活动 else { // 当前遍历的活动如果已经经过了,那么下次活动就是遍历的下一个活动对象,否则为当前活动。 // 如果本年度活动都经过了,那么下次活动是列表的第一个活动对象 const next = end < date ? eventObj.events[index + 1] || eventObj.events[0] : obj; // 经过了最后一个活动所以下次活动开始时间是第二年 const start = new Date(next !== obj && index === eventObj.events.length - 1 ? nowYear + 1 : nowYear, next.start[0], next.start[1], next.start[2]); const daysLeft = (start.getTime() - date.getTime()) / 86400000 | 0; if (0 <= daysLeft && daysLeft < eventObj.daysLeft) { eventObj.daysLeft = daysLeft; eventObj.next = next; } } }); eventObj.html = ': '; eventObj.onEv ? eventObj.html += `${ eventObj.current.name }() - 剩余${ eventObj.daysLeft }天` : eventObj.html += `${ eventObj.daysLeft }天后 - ${ eventObj.next.name }()`; list.push({ domType: 'plain', domId: 'wh-trans-event-cont', domHTML: eventObj.html, }); // 一键起飞 list.push({ domType: 'button', domId: 'wh-quick-fly-btn', domText: '✈️ 一键起飞', clickFunc: () => Container.factory(QuickFlyBtnHandler).handle(), }); // 飞花库存 list.push({ domType: 'button', domId: 'wh-foreign-stock-btn', domText: '🌸 飞花库存', clickFunc: () => Container.factory(TravelItem).clickHandler().then(), }); // NPC LOOT list.push({ domType: 'button', domId: 'wh-npc-loot-btn', domText: '🔫 LOOT', clickFunc: () => { const insert = ZHONG_LOOT_HTML.replace('{{}}', performance.now().toString()); new Popup(insert, 'NPC LOOT'); }, tip: '显示5个可击杀NPC的开打时间', }); // 查看NNB list.push({ domType: 'button', domId: 'wh-nnb-info', domText: '👮‍ 查看NNB', clickFunc: () => this.nnb.handle(), }); // 常用链接 list.push({ domType: 'button', domId: 'wh-link-collection', domText: '🔗 常用链接', clickFunc: () => Container.factory(QuickLinksHandler).handle() }); // 飞贼 // list.push({ // domType: 'button', // domId: 'wh-gs-btn', // domText: '🐏 飞贼小助手', // clickFunc: () => loadGS(CommonUtils.getScriptEngine()), // tip: '加载从PC端移植的伞佬的油猴版飞贼小助手', // }); // 物品价格监视 list.push({ domType: 'button', domId: 'wh-price-watcher-btn', domText: '💊 价格监视', clickFunc: () => this.itemPriceWatcherHandler.handle() }); // 全屏 if (!this.tornPDAUtils.isPDA()) list.push({ domType: 'button', domId: '', domText: '🖥️ 进入全屏', clickFunc() { document.documentElement.requestFullscreen().then(); } }); // 传单助手 list.push({ domType: 'button', domId: '', domText: '📜️ 传单助手', clickFunc: adHelper }); // 守望者 list.push({ domType: 'button', domId: '', domText: '🛡️ 守望者', clickFunc: function () { safeKeeper(); }, }); // 寻找木桩 list.push({ domType: 'button', domId: '', domText: '🌲 寻找木桩', clickFunc() { window.location.replace('https://www.torn.com/item.php?temp=4#xunzhaomuzhuang') } }); // 物品查价 list.push(ItemPriceHandler); // 更新历史 list.push(ChangeLogHandler); // 助手设置 list.push(SettingsHandler); // 测试 if (this.logger.debug()) list.push(Test); this.menuItemList = list; this.logger.info('构造展开菜单列表结束' + timer.getTimeMs()); return this; } } export interface MenuItemConfig { domType: 'button' | 'plain' | 'checkbox' | 'select' | MENU_ITEM_TYPE; tagName?: string; domId?: string; domText?: string; clickFunc?: (ev?) => void; domHTML?: string; tip?: string; dictName?: string; changeEv?: (ev) => void; domSelectOpt?: { domVal: string, domText: string }[]; /** * 隐藏菜单已弃用 * @deprecated */ isHide?: boolean; isTornBtn?: boolean; } interface EventWrapper { onEv: boolean; daysLeft: number; events: Event[]; current?: Event; next?: Event; html?: string; } interface Event { start: number[]; end: number[]; name: string; eff: string; }