diff --git a/torn-trans-zhcn.user.js b/torn-trans-zhcn.user.js index 0d75a9f..d9cab81 100644 --- a/torn-trans-zhcn.user.js +++ b/torn-trans-zhcn.user.js @@ -1,8 +1,8 @@ // ==UserScript== -// @lastmodified 202202222055 +// @lastmodified 202202231723 // @name 芜湖助手 // @namespace WOOH -// @version 0.3.19 +// @version 0.3.20 // @description 托恩,起飞! // @author Woohoo[2687093] Sabrina_Devil[2696209] // @match https://www.torn.com/* @@ -23,13 +23,18 @@ if (window.WHTRANS) return; window.WHTRANS = true; // 版本 - const version = '0.3.19'; + const version = '0.3.20'; // 修改历史 const changelist = [ { todo: true, cont: `翻译:baza npc商店、imarket、imarket搜索结果`, }, + { + ver: '0.3.20', + date: '20220223', + cont: `性能优化,修复一些翻译错误,添加XAN价格监视`, + }, { ver: '0.3.19', date: '20220222', @@ -349,17 +354,16 @@ const PDA_APIKey = '###PDA-APIKEY###'; // isPDA const isPDA = PDA_APIKey.slice(-1) !== '#'; + // uuid + window.whuuid = uuidv4(); // 请求通知权限 - try { + if (window.Notification) { Notification.requestPermission().then(status => { // 这将使我们能在 Chrome/Safari 中使用 Notification.permission if (Notification.permission !== status) { Notification.permission = status; } }) - } catch (e) { - window.Notification = undefined; - log(e) } const titleDict = { @@ -3119,7 +3123,7 @@ const player_info = getPlayerInfo(); // 海外库存对象 const fstock = autoFetchJSON('https://yata.yt/api/v1/travel/export/'); - // 价格监视 + // 价格监视实例对象 const priceWatcher = isIframe ? null : priceWatcherHandle(); // 返回一个加载中gif图形HTML const loading_gif_html = () => { @@ -3127,6 +3131,9 @@ return `lgif`; } + // 对当前窗口记录唯一id + const isWindowActive = getWindowActiveState(); + // 对新值应用默认设置 [ // 开启翻译 @@ -3167,11 +3174,11 @@ // 危险行为⚠️ {key: 'dangerZone', val: false}, ].forEach(_default => { - if (typeof getWhSetting()[_default.key] !== typeof _default.val) setWhSetting(_default.key, _default.val); + if (typeof getWhSettingObj()[_default.key] !== typeof _default.val) setWhSetting(_default.key, _default.val); }); // 是否开启翻译 - const isTransEnabled = getWhSetting()['transEnable']; + const isTransEnabled = getWhSettingObj()['transEnable']; // 菜单配置列表 const settingsArr = []; @@ -3454,7 +3461,7 @@ dictName: 'quickFinishAtt', }); // 危险行为⚠️ - if (getWhSetting()['dangerZone'] === true) { + if (getWhSettingObj()['dangerZone'] === true) { // 攻击界面自刷新 settingsArr.push({ domType: 'select', @@ -3512,11 +3519,7 @@ popupMsg(insert, '飞花库存'); } else { const popup = popupMsg(`请稍后${loading_gif_html()}`, '飞花库存'); - let table = ` -`; + let table = `
目的地 - 更新时间库存
`; const dest = [ { name: 'mex', show: '墨西哥', @@ -3617,22 +3620,24 @@ #wh-popup-cont label span{font-weight:bold;}

-

NNB, Natural Nerve Bar, 意思是:扣除所有加成后,玩家本身的犯罪条上限,可用于衡量大佬隐藏的犯罪技能等级

-

一般来说,左侧红色的犯罪条(称为Nerve Bar/NB, 与NNB不同)的上限都是包含加成的,如来自帮派、天赋的加成等。额外的加成并不会影响玩家的犯罪技能

-

查看NNB的方法很简单,在Torn主页面的最下方有一栏Perks,NB上限扣除加成的犯罪条上限后就是NNB

+

NNBNatural Nerve Bar)意思是:扣除所有加成后,玩家本身的犯罪条上限,可用于衡量大佬隐藏的犯罪技能等级

+

一般来说,左侧红色的犯罪条(Nerve Bar/NB)的上限都是包含加成的,如来自帮派、天赋的加成等。额外的加成不会影响玩家的犯罪技能

+

查看NNB的方法很简单,在Torn主页面的最下方有一栏Perks,NB-Perks=NNB

-

不想算?

+

以下是两种计算NNB的方法:

- + `; const popup = popupMsg(insert, '查看NNB'); const select = popup.querySelector('input'); @@ -3774,30 +3779,26 @@ height:30px; domId: 'wh-price-watcher-btn', domText: '价格监视', clickFunc: function () { - const watcher_conf = getWhSetting()['priceWatcher']; + const watcher_conf = getWhSettingObj()['priceWatcher']; const html = `

输入需要监视的价格,低于该价格发出通知,-1为关闭

-

注:需要APIKey,将从冰蛙选择

+

注:需要APIKey,当前可用APIKey为
+(来自冰蛙)
+(来自PDA) +

PT

XAN

`; - const popup = popupMsg(html, '价格监视设置', () => save_settings()); - const save_settings = () => { + const popup = popupMsg(html, '价格监视设置', () => { const pre_str = JSON.stringify(watcher_conf); - const [pt_node, xan_node] = popup.querySelectorAll('input'); + const [pt_node, xan_node] = popup.querySelectorAll('input[type="number"]'); watcher_conf.pt = pt_node.value | 0; watcher_conf.xan = xan_node.value | 0; if (JSON.stringify(watcher_conf) !== pre_str) setWhSetting('priceWatcher', watcher_conf) - } + }); } }); // 危险行为开关⚠️ @@ -3808,7 +3809,7 @@ height:30px; clickFunc: function (e) { e.target.blur(); const insert = `

即将打开危险功能,使用这些功能可能会造成账号封禁。请自行考虑是否使用。

-

+

`; const popup = popupMsg(insert, '⚠️警告'); const warning_check = popup.querySelector('input'); @@ -3816,6 +3817,7 @@ height:30px; warning_check.onchange = () => ok_btn.disabled = false; ok_btn.onclick = () => { setWhSetting('dangerZone', warning_check.checked); + popup.close(); window.location.reload(); }; }, @@ -3888,20 +3890,19 @@ height:30px; // 节日 $zhongNode.querySelectorAll('#wh-trans-fest-date button').forEach((el, i) => i === 0 ? el.addEventListener('click', () => { - let html = ''; - settingsArr.fest_date_list.sort().forEach(date => html += `${1 + (date.slice(0, 2) | 0)}月${date.slice(2)}日 ${settingsArr.fest_date_dict[date].name} - ${settingsArr.fest_date_dict[date].eff}
`); - popupMsg(html, '节日'); + let html = '
目的地 - 更新时间库存
'; + settingsArr.fest_date_list.sort().forEach(date => html += ``); + popupMsg(html += '
${1 + (date.slice(0, 2) | 0)}月${date.slice(2)}日${settingsArr.fest_date_dict[date].name}${settingsArr.fest_date_dict[date].eff}
', '节日'); }) : el.addEventListener('click', ev => popupMsg(ev.target.attributes['title'].nodeValue)) ); // 活动 $zhongNode.querySelectorAll('#wh-trans-event-cont button').forEach((el, i) => i === 0 ? el.addEventListener('click', () => { - let html = ''; + let html = ''; settingsArr.events.forEach(el => - html += `

${el.start[0] + 1}月${el.start[1]}日${el.start[2]}:00~${el.end[0] + 1}月${el.end[1]}日${el.end[2]}:00【${el.name}】
${el.eff}

`); - html += '

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

' - popupMsg(html, '活动'); + html += ``); + popupMsg(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', ev => popupMsg(ev.target.attributes['title'].nodeValue)) ); @@ -4045,7 +4046,7 @@ color:black; popupMsg(insert, '开发者详情'); }; // 啤酒提醒 - if (getWhSetting()['_15Alarm']) { + if (getWhSettingObj()['_15Alarm']) { // 今日不再提醒 const ign = () => { const date = new Date(); @@ -4063,14 +4064,15 @@ color:black; // 循环函数 const loop = () => { const now = [new Date().getUTCFullYear(), new Date().getUTCMonth(), new Date().getUTCDate()]; - const ignore_date = getWhSetting()['_15_alarm_ignore'] || '{}'; + const ignore_date = getWhSettingObj()['_15_alarm_ignore'] || '{}'; if (JSON.stringify(now) === JSON.stringify(ignore_date)) return; const notify = WHNotify( `啤酒小助手
提醒您:还有不到 50 秒 NPC 的商品就要刷新了,啤酒血包要抢的可以准备咯。
【啤酒店】 【血包店】`, - 30, - null, - true); + { + timeout: 30, + sysNotify: true, + }); notify.querySelector('.wh-notify-msg button').addEventListener('click', ign); window.setTimeout(audioPlay, 800); window.setTimeout(audioPlay, 800 * 2); @@ -4177,6 +4179,16 @@ div#wh-popup::after { color: black; border-radius: 3px; } +#wh-popup-cont input{ + padding: 2px; + text-align: center; + border: 1px solid #fff0; + border-radius: 5px; + margin:1px 2px; +} +#wh-popup-cont input:focus{border-color:blue;} +#wh-popup-cont table{width:100%;border-collapse:collapse;border:1px solid;} +#wh-popup-cont td, #wh-popup-cont th{border-collapse:collapse;padding:4px;border:1px solid;} `); /** @@ -4549,7 +4561,7 @@ div#wh-popup::after { travelOB.observe(document.querySelector('div.content-wrapper'), {childList: true, subtree: true}); } // 飞行闹钟 - if (device === Device.PC && getWhSetting().trvAlarm) elementReady('#countrTravel.hasCountdown').then(node => { + if (device === Device.PC && getWhSettingObj().trvAlarm) elementReady('#countrTravel.hasCountdown').then(node => { const logo_node = document.querySelector('#tcLogo[title]'); // const DEST_STR = { // 'Mexico': '墨西哥', 'Canada': '加拿大', 'Cayman Islands': '开曼', @@ -4841,7 +4853,7 @@ display:none; */ if (window.location.href.contains(/travelagency\.php/)) { const $$ = $('.content-wrapper'); - if (getWhSetting().energyAlert) { + if (getWhSettingObj().energyAlert) { const OB = new MutationObserver(() => { OB.disconnect(); titleTrans(); @@ -4894,9 +4906,9 @@ display:none; if (window.location.href.contains(/loader\.php\?sid=attack/)) { let stop_reload = false; // 光速拔刀 - if (getWhSetting().quickAttIndex !== 6) { + if (getWhSettingObj().quickAttIndex !== 6) { // const selectedId = ['weapon_main', 'weapon_second', 'weapon_melee', 'weapon_temp', 'weapon_fists', 'weapon_boots'] - // [getWhSetting().quickAttIndex]; + // [getWhSettingObj().quickAttIndex]; elementReady('div[class^="modal___"] button').then(btn => { if (!(btn.innerText.toLowerCase().includes('start fight') || btn.innerText.toLowerCase().includes('join'))) return; // 判断是否存在脚踢 @@ -4912,7 +4924,7 @@ display:none; modal.style.display = 'none'; // 根据选择的武器调整css let css_top = '0'; - switch (getWhSetting().quickAttIndex) { + switch (getWhSettingObj().quickAttIndex) { case 1: { // weapon_second css_top = '97px'; break; @@ -4942,7 +4954,7 @@ display:none; document.body.classList.toggle('wh-move-btn'); // 绑定点击事件 btn.onclick = () => { - if (getWhSetting().quickFinishAtt !== 3) { + if (getWhSettingObj().quickFinishAtt !== 3) { btn.remove(); // 停止自动刷新 stop_reload = true; @@ -4960,7 +4972,7 @@ display:none; // 判断有没有脚踢 if (hasKick) { // 根据选择的武器调整 - switch (getWhSetting().quickAttIndex) { + switch (getWhSettingObj().quickAttIndex) { case 1: { // weapon_second css_top = '76px'; break; @@ -4987,7 +4999,7 @@ display:none; const height = slot.offsetHeight + 1; slot_height = height; // 根据选择的武器调整 - switch (getWhSetting().quickAttIndex) { + switch (getWhSettingObj().quickAttIndex) { case 1: { // weapon_second css_top = `${height}px`; break; @@ -5020,7 +5032,7 @@ display:none; addStyle(css_rule); document.body.classList.toggle('wh-move-btn'); btn.onclick = () => { - if (getWhSetting().quickFinishAtt !== 3) { + if (getWhSettingObj().quickFinishAtt !== 3) { btn.remove(); // 停止自动刷新 stop_reload = true; @@ -5035,7 +5047,7 @@ display:none; } } // 自动开打 - if (getWhSetting().autoStartFinish === true) { + if (getWhSettingObj().autoStartFinish === true) { if (btn.innerText.includes('(')) { let interval_id = window.setInterval(() => { if (!btn) { @@ -5069,8 +5081,8 @@ display:none; } } // 光速跑路 - if (getWhSetting().quickFinishAtt !== 3) { - const user_btn_select = ['leave', 'mug', 'hosp'][getWhSetting().quickFinishAtt]; + if (getWhSettingObj().quickFinishAtt !== 3) { + const user_btn_select = ['leave', 'mug', 'hosp'][getWhSettingObj().quickFinishAtt]; const wrap = document.querySelector('#react-root'); log('光速跑路选项选中:', user_btn_select); new MutationObserver(() => { @@ -5080,7 +5092,7 @@ display:none; log('按钮内容:', btn.innerText, ',是否包含选中:', flag); if (!flag) btn.style.display = 'none'; // 自动结束 - else if (getWhSetting().autoStartFinish === true) { + else if (getWhSettingObj().autoStartFinish === true) { try { btn.click(); } catch { @@ -5091,7 +5103,7 @@ display:none; } // 自刷新 let audio_played_flag; - if (getWhSetting().attReload !== 6 && stop_reload !== true) { + if (getWhSettingObj().attReload !== 6 && stop_reload !== true) { const selector_device_map = { 'pc': '#defender div[class^="modal___"]', 'mobile': '#attacker div[class^="modal___"]', @@ -5100,11 +5112,11 @@ display:none; const selector = selector_device_map[device]; elementReady(selector).then(elem => { if (!elem.querySelector('button')) { - if (getWhSetting().attReload === 0 && stop_reload !== true) { + if (getWhSettingObj().attReload === 0 && stop_reload !== true) { window.location.reload(); } else { let reload_flag; - const timeout = getWhSetting().attReload * 1000 + getRandomInt(-500, 500); + const timeout = getWhSettingObj().attReload * 1000 + getRandomInt(-500, 500); log(`[WH] ${timeout / 1000}s 后自动刷新`); window.setInterval(() => { if (reload_flag === undefined) { @@ -5131,7 +5143,7 @@ display:none; } // 错误的攻击页面 - if (getWhSetting()['attRelocate'] && window.location.href.includes('loader2.php')) { + if (getWhSettingObj()['attRelocate'] && window.location.href.includes('loader2.php')) { const spl = window.location.href.trim().split('='); const uid = spl[spl.length - 1]; if (!/^[0-9]+$/.test(uid)) return; @@ -5222,7 +5234,7 @@ display:none; cityOB.observe(document.querySelector('div.content-wrapper'), {childList: true, subtree: true}); } // 捡垃圾 - if (getWhSetting().cityFinder) { + if (getWhSettingObj().cityFinder) { addStyle(` .wh-city-finds .leaflet-marker-pane img[src*="torn.com/images/items/"]{ display: block !important; @@ -5474,7 +5486,7 @@ display:inline-block; gymTrans(); gymOB.observe($('div.content-wrapper')[0], {childList: true, subtree: true, attributes: true}); } - if (getWhSetting().SEProtect) { + if (getWhSettingObj().SEProtect) { elementReady('#gymroot').then(node => { addStyle(`.wh-display-none{ display:none !important; @@ -5557,7 +5569,7 @@ display:none !important; const is_captcha = $$.querySelector('div#tab-menu.captcha') !== null; const $title = $('div.content-title'); const $info = $('.info-msg-cont'); - if (!is_wh_translate && !is_captcha && getWhSetting().quickCrime) { + if (!is_wh_translate && !is_captcha && getWhSettingObj().quickCrime) { if ($title.length > 0) $title.before(dom); else if ($info.length > 0) $info.before(dom); } @@ -5822,7 +5834,7 @@ display:none !important; */ if (window.location.href.contains(/loader\.php\?sid=missions/)) { const $$ = $('.content-wrapper'); - if (getWhSetting()['missionHint']) { + if (getWhSettingObj()['missionHint']) { const OB = new MutationObserver(() => { OB.disconnect(); titleTrans(); @@ -7198,7 +7210,7 @@ display:none !important; }); } // 解密攻略 - if (getWhSetting().xmasTownWT) { + if (getWhSettingObj().xmasTownWT) { const insert_html = `
水晶球解密地图攻略
`; document.body.append(popup); - const clickFunc = e => { + popup.addEventListener('click', e => { e.stopImmediatePropagation(); if (e.target === popup) { - popup.removeEventListener('click', clickFunc); popup.remove(); callback(); } + }); + const rt = popup.querySelector('#wh-popup-cont') + rt.close = () => { + popup.remove(); + callback(); }; - popup.addEventListener('click', clickFunc); - return popup.querySelector('#wh-popup-cont'); + return rt; } // 弹出窗口是否存在 @@ -8975,16 +8989,14 @@ margin: 0 0 3px; if (isDev()) console.log('[WH]', ...o) } - /** - * 通知 - * - * @param {String?} msg 通知上显示的内容,默认为空 - * @param {Number?} timeout 停留的时间,默认3秒 - * @param {Function?} callback 通知结束后执行的函数 - * @param {Boolean?} sysNotify 是否显示系统通知 - * @returns {HTMLElement} 通知的node - */ - function WHNotify(msg = '', timeout = 3, callback = null, sysNotify = false) { + function WHNotify(msg = '', { + timeout = 3, + callback = doNothing, + sysNotify = false, + sysNotifyTag = '芜湖助手', + sysNotifyClick = () => window.focus(), + } = {}) { + if (!isWindowActive() || isIframe) return null; const date = new Date(); // 通知的唯一id const uid = `${date.getHours()}${date.getSeconds()}${date.getMilliseconds()}${getRandomInt(1000, 9999)}`; @@ -9028,7 +9040,7 @@ margin: 0 0 3px; const removeNode = () => { clearInterval(intervalID); new_node.remove(); - if (callback !== null) callback(); + callback(); }; new_node.del = removeNode; new_node.querySelector('.wh-notify-close button').addEventListener('click', removeNode); @@ -9083,21 +9095,15 @@ text-decoration:none; } const notify_obj = add_notify(); // 浏览器通知 - try { - if (Notification.permission === 'granted' && sysNotify) { - const last_notify = getWhSetting('lastNotify'); - const last_notify_date = new Date(last_notify); - const date_local_string = `[${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}]\r` - if (!!last_notify_date || date - last_notify_date > 3000) { - new Notification('芜湖助手', { - body: date_local_string + notify_contain.msgInnerText, - requireInteraction: false - }); - setWhSetting('lastNotify', date.getTime(), false) - } - } - } catch (e) { - log(e) + if (window.Notification && Notification.permission === 'granted' && sysNotify) { + const date_local_string = `[${date.getHours()}:${date.getMinutes()}:${date.getSeconds()}]\r`; + const sys_notify = new Notification('芜湖助手', { + body: date_local_string + notify_contain.msgInnerText, + requireInteraction: true, + renotify: true, + tag: sysNotifyTag, + }); + sys_notify.onclick = sysNotifyClick; } return notify_obj; } @@ -9162,7 +9168,7 @@ z-index:100001; _window.GM_setValue("gsp_x", 10); _window.GM_setValue("gsp_y", 10); notify.del(); - notify = WHNotify('飞贼小助手已加载', 1); + notify = WHNotify('飞贼小助手已加载', {timeout: 1}); const gsp = _docu.querySelector('#gsp'); const init = () => { ifr.style.height = `${gsp.offsetHeight + 10}px`; @@ -9216,9 +9222,8 @@ z-index:100001; /** * 播放音频 - * - * @param url:String 播放的音频URL - * @returns void + * @param {string} url 播放的音频URL + * @returns {undefined} */ function audioPlay(url = 'https://www.torn.com/js/chat/sounds/Warble_1.mp3') { const audio = new Audio(url); @@ -9252,18 +9257,18 @@ z-index:100001; } // 插件的配置 getter - function getWhSetting() { + function getWhSettingObj() { return JSON.parse(localStorage.getItem('wh_trans_settings')) || {} } // 插件的配置 setter function setWhSetting(key, value, notify = true) { - const obj = getWhSetting() + const obj = getWhSettingObj() obj[key] = value localStorage.setItem('wh_trans_settings', JSON.stringify(obj)) // 通知 - if (notify) WHNotify('已保存设置', 3, null, false) + if (notify) WHNotify('已保存设置') } // 循环获取json对象 @@ -9271,6 +9276,7 @@ z-index:100001; let obj; const res = COFetch(dest); setInterval(async () => { + if (!isWindowActive()) return; const res = await COFetch(dest); obj = JSON.parse(res); }, time * 1000); @@ -9288,10 +9294,14 @@ z-index:100001; // 价格监视handle function priceWatcherHandle() { setInterval(() => { - const price_conf = getWhSetting()['priceWatcher']; + const price_conf = getWhSettingObj()['priceWatcher']; const apikey = isPDA ? PDA_APIKey : localStorage.getItem('APIKey'); + if (!apikey) { + log('无法获取APIKey') + return; + } if (price_conf['pt'] !== -1) priceWatcherPt(apikey, price_conf['pt']).then(); - if (price_conf['xan'] !== -1) priceWatcherXan(apikey).then(); + if (price_conf['xan'] !== -1) priceWatcherXan(apikey, price_conf['xan']).then(); }, 10000) return {status: true}; } @@ -9315,7 +9325,11 @@ z-index:100001; // 将id与之前存在的比较,不相同时发送通知 if (JSON.stringify(priceWatcher['watch-pt-lower-id']) !== JSON.stringify(lower_arr)) { priceWatcher['watch-pt-lower-id'] = lower_arr; - WHNotify(`PT新低价:$${low}(<${lower_price}) - 点击转跳`, 3, null, true); + WHNotify(`PT新低价:$${toThousands(low)}( < $${toThousands(lower_price)}) - 点击转跳`, { + timeout: 6, + sysNotify: true, + sysNotifyClick: () => window.open('https://www.torn.com/pmarket.php'), + }); } } else { // 查询出错了 @@ -9324,10 +9338,59 @@ z-index:100001; } // xan价格监视 - async function priceWatcherXan(apikey) { + async function priceWatcherXan(apikey, lower_price) { + // 初始化记录上一个条目的id,避免重复发送通知 + if (!priceWatcher['watch-xan-lower-id']) priceWatcher['watch-xan-lower-id'] = ''; + const res = await fetch('https://api.torn.com/market/206?selections=bazaar&key=' + apikey); + const obj = await res.json(); + if (obj['bazaar']) { + const lowest_item = obj['bazaar'][0] + if (lowest_item['cost'] <= lower_price) { + if (priceWatcher['watch-xan-lower-id'] !== lowest_item['ID']) { + priceWatcher['watch-xan-lower-id'] = lowest_item['ID']; + WHNotify(`XAN新低价:$${toThousands(lowest_item['cost'])}( < $${toThousands(lower_price)}) - 点击转跳`,{ + timeout:6, + sysNotify:true, + sysNotifyClick:()=>window.open('https://www.torn.com/imarket.php#/p=shop&step=shop&type=&searchname=Xanax') + }); + } + } + } else { + // 查询出错了 + log('xan查询出错了') + } } // 空函数 function doNothing() { } + + // 返回UUID + function uuidv4() { + if (crypto.randomUUID) return crypto.randomUUID(); + return ([1e7] + -1e3 + -4e3 + -8e3 + -1e11).replace(/[018]/g, c => + (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16) + ); + } + + // 返回一个可用查询当前窗口是否激活的函数 + function getWindowActiveState() { + if (isIframe) return false; + const uuid = uuidv4(); + let isFocus = false; + localStorage.setItem('whuuid', uuid); + document.addEventListener('visibilitychange', () => + (document.visibilityState !== 'hidden') && (localStorage.setItem('whuuid', uuid)) + ); + addEventListener('focus', () => isFocus = true) + addEventListener('blur', () => isFocus = false) + return function () { + // 当前窗口获得了焦点 优先级最高 + if (isFocus) return true; + // 可视性 + if (!document.hidden) return true; + // 全部在后台,使用唯一id判断 + return uuid === localStorage.getItem('whuuid') + }; + } }());