您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Guess how many busts you can do without getting jailed
当前为
/** @format */ // ==UserScript== // @name BUSTR: Busting Reminder + PDA // @namespace http://torn.city.com.dot.com.com // @version 1.0.1 // @description Guess how many busts you can do without getting jailed // @author Adobi & Ironhydedragon // @match https://www.torn.com/* // @license MIT // ==/UserScript== console.log('😎 BUSTR-SCRIPT ON!!!!'); // TEST //////// GLOBAL VARIABLES //// State let GLOBAL_BUSTR_STATE = { userSettings: { reminderLimits: { redLimit: 0, greenLimit: 3, }, statsRefreshRate: 30, refetchRate: 600, customPenaltyThreshold: 0, // quickBust: true, // quickBail: false, showHardnessScore: true, }, penaltyScore: 0, penaltyThreshold: 0, availableBusts: 0, timestampsArray: [], lastFetchTimestampMs: 0, renderedView: undefined, }; const PDA_API_KEY = '###PDA-APIKEY###'; function isPDA() { const PDATestRegex = !/^(###).+(###)$/.test(PDA_API_KEY); return PDATestRegex; } //// Colors const red = '#E54C19'; const redLight = 'rgb(255, 168, 168)'; // const orange = '#B25900'; // const orange = '#d98c00'; const orange = '#d08000'; const orangeLight = '#FFBF00'; const green = ''; // link color const greenLight = '#85b200'; // Icon Color const white = 'rgb(51, 51, 51)'; //// SVG Gradients const greenSvgGradient = 'url(#sidebar_svg_gradient_regular_green_mobile'; const orangeSvgGradient = 'url(#svg_status_idle'; //// Utils Functions function createTimestampsArray(data) { let timestamps = []; for (const entry in data.log) { timestamps.push(data.log[entry].timestamp); } return timestamps; } function calcTenHours(hours) { return hours / 7.2; } function calcPenaltyScore(timestampsArray) { const currentTime = Date.now() / 1000; let score = 0; let localScore = 0; for (const ts of timestampsArray) { const hours = (currentTime - ts) / 60 / 60; const tenHours = calcTenHours(hours); if (hours <= 72) { localScore = 128 / Math.pow(2, tenHours); score += localScore; } } return Math.floor(score); } function calcPenaltyThreshold(timestampsArray) { if (getUserSettings().customPenaltyThreshold && typeof getUserSettings().customPenaltyThreshold === 'number') return getUserSettings().customPenaltyThreshold; const period = 24 * 60 * 60 * 3; let longestSequence = 0; let currentSequence = 1; let currentMin = timestampsArray[0]; let currentMax = timestampsArray[0]; let firstTimestamp; for (let i = 1; i < timestampsArray.length; i++) { const TS = timestampsArray[i]; if (currentMin - TS <= period && currentMax - TS <= period) { currentSequence++; currentMin = Math.min(currentMin, TS); currentMax = Math.max(currentMax, TS); } else { if (longestSequence < currentSequence) { firstTimestamp = currentMin; } longestSequence = Math.max(longestSequence, currentSequence); currentSequence = 1; currentMin = TS; currentMax = TS; } } let currentMaxScore = 0; for (let i = 0; i < timestampsArray.length - longestSequence; i++) { let score = 0; let localScore = 0; const initial_timestamp = timestampsArray[i]; for (let j = 0; j < longestSequence; j++) { const hours = (initial_timestamp - timestampsArray[i + j]) / 60 / 60; const tenHours = calcTenHours(hours); localScore = 128 / Math.pow(2, tenHours); score += localScore; } currentMaxScore = Math.max(currentMaxScore, score); } return Math.floor(currentMaxScore); } function calcAvailableBusts(penaltyScore, penaltyThreshold) { return Math.floor((penaltyThreshold - penaltyScore) / 128); } async function fetchBustsData(apiKey) { try { const url = `https://api.torn.com/user/?selections=log&log=5360&key=${apiKey}`; const response = await fetch(url); const data = await response.json(); setLastFecthTimestampMs(); if (data.error) { if (data.error.error === 'Incorrect key' || data.error.error === 'Access level of this key is not high enough') { throw new Error(`Error: ${data.error.error}`); } throw new Error('Something went wrong'); } return data; } catch (err) { console.error(err); } } function calcBustrStats(timestampsArray) { const penaltyScore = calcPenaltyScore(timestampsArray); const penaltyThreshold = calcPenaltyThreshold(timestampsArray); const availableBusts = calcAvailableBusts(penaltyScore, penaltyThreshold); return { penaltyScore, penaltyThreshold, availableBusts }; } function getLevelJailDurationInfo(playerEl) { const levelEl = playerEl.querySelector('.level'); const durationEl = playerEl.querySelector('.time'); const level = +levelEl.innerText.match(/\d+/)[0]; const hours = +durationEl.innerText.match(/\d+(?=h)/) || 0; const mins = +durationEl.innerText.match(/\d+(?=m)/) || 0; const durationInHours = hours + mins / 60; return [level, +durationInHours]; } function calcHardnessScore(level, durationInHours) { return Math.floor(level * (durationInHours + 3)); } function renderHardnessScore(playerEl, hardnessScore) { playerEl.querySelector('.bustr-hardness-score').textContent = hardnessScore; } function sortByHardnessScore(playerEl, hardnessScore) { playerEl.style.order = hardnessScore; } //// Callback functions function submitFormCallback() { const inputEl = document.querySelector('#bustr-form__input'); const submitBtnEl = document.querySelector('#bustr-form__submit'); const apiKey = inputEl.value; if (apiKey.length !== 16) { inputEl.style.border = `2px solid ${red}`; submitBtnEl.disabled = true; return; } setApiKey(apiKey); dismountBustrForm(); window.location.reload(); } function inputValidatorCallback(event) { const inputEl = document.querySelector('#bustr-form__input'); const submitBtnEl = document.querySelector('#bustr-form__submit'); if (event.target.value.length === 16) { submitBtnEl.disabled = false; inputEl.style.border = '1px solid #444'; } if (event.target.value.length !== 16) { submitBtnEl.disabled = true; } } async function successfulBustMutationCallback(mutationList, observer) { try { for (const mutation of mutationList) { if (!mutation.target.innerText) return; if (mutation.target.innerText.match(/^(You busted ).+/) && mutation.removedNodes.length > 0) { console.log('👀 You busted...', mutation); // TEST observer.disconnect(); addOneTimestampsArray(Math.floor(Date.now() / 1000)); await loadController(); successfulBustUpdateController(); } } } catch (err) { console.error(err); } } function mountJailPlayerCallback(mutationList, observer) { for (const mutation of mutationList) { if (mutation.target.classList.contains('user-info-list-wrap') && mutation.addedNodes.length > 1) { hardnessScoreController(); observer.disconnect(); } } } //// Observers function createJailMutationObserver() { const jailObserver = new MutationObserver(successfulBustMutationCallback); jailObserver.observe(document, { attributes: false, childList: true, subtree: true, }); } function createMountJailPlayerOberserver() { const mountJailPlayerObserver = new MutationObserver(mountJailPlayerCallback); mountJailPlayerObserver.observe(document, { attributes: false, childList: true, subtree: true, }); } //////// MODEL //////// //// Getters and Setters function setGlobalBustrState(newState) { GLOBAL_BUSTR_STATE = { ...GLOBAL_BUSTR_STATE, ...newState }; localStorage.setItem('globalBustrState', JSON.stringify(GLOBAL_BUSTR_STATE)); saveGlobalBustrState(); } function getGlobalBustrState() { return GLOBAL_BUSTR_STATE; } function loadGlobalBustrState() { if (!localStorage.getItem('globalBustrState')) return; const loadedState = JSON.parse(localStorage.getItem('globalBustrState')); GLOBAL_BUSTR_STATE = { ...GLOBAL_BUSTR_STATE, ...loadedState }; return localStorage.getItem('globalBustrState'); } function saveGlobalBustrState() { localStorage.setItem('globalBustrState', JSON.stringify(getGlobalBustrState())); } function deleteGlobalBustrState() { GLOBAL_BUSTR_STATE = { userSettings: { reminderLimits: { redLimit: 0, greenLimit: 3, }, }, penaltyScore: 0, penaltyThreshold: 0, availableBusts: 0, timestampsArray: [], }; localStorage.removeItem('bustrGlobalState'); } function getMyViewportWidthType() { let width = visualViewport.width; if (width > 1000) return 'Desktop'; if (width < 1000 || width) return 'Mobile'; throw new Error('Visual viewport not loaded'); } function setApiKey(apiKey) { localStorage.setItem('bustrApiKey', JSON.stringify(apiKey)); } function getApiKey() { if (isPDA()) return PDA_API_KEY; if (!localStorage.getItem('bustrApiKey')) return; return JSON.parse(localStorage.getItem('bustrApiKey')); } function deleteApiKey() { localStorage.removeItem('bustrApiKey'); } function setUserSettings(newUserSettings, currentState) { currentState = currentState || getGlobalBustrState(); const newState = { ...currentState, userSettings: newUserSettings }; setGlobalBustrState(newState); } function getUserSettings() { return getGlobalBustrState().userSettings; } function setRenderedView(newRenderedView, currentState) { currentState = currentState || getGlobalBustrState(); const newState = { ...currentState, renderedView: newRenderedView }; setGlobalBustrState(newState); } function getRenderedView(newRenderedView, currentState) { return getGlobalBustrState().renderedView; } function setTimestampsArray(newTimestampsArr, currentState) { currentState = currentState || getGlobalBustrState(); return setGlobalBustrState({ ...currentState, timestampsArray: newTimestampsArr, }); } function addOneTimestampsArray(timestamp, currentState) { currentState = currentState || getGlobalBustrState(); const newTimestampsArr = [timestamp, ...currentState.timestampsArray]; setTimestampsArray(newTimestampsArr); } function getTimestampsArray() { return getGlobalBustrState().timestampsArray; } function setLastFecthTimestampMs(currentState) { currentState = currentState || getGlobalBustrState(); const currentTimestampMs = Date.now(); setGlobalBustrState({ ...currentState, lastFetchTimestampMs: currentTimestampMs, }); } function getLastFecthTimestampMs() { return getGlobalBustrState().lastFetchTimestampMs; } function setPenaltyThreshold(newPenaltyThreshold, currentState) { currentState = currentState || getGlobalBustrState(); const newState = { ...currentState, penaltyThreshold: newPenaltyThreshold }; setGlobalBustrState(newState); } function getPenaltyThreshold() { return getGlobalBustrState().penaltyThreshold; } function setPenaltyScore(newPenaltyScore, currentState) { currentState = currentState || getGlobalBustrState(); const newState = { ...currentState, penaltyScore: newPenaltyScore }; setGlobalBustrState(newState); } function getPenaltyScore() { return getGlobalBustrState().penaltyScore; } function setAvailableBusts(newAvailableBusts, currentState) { currentState = currentState || getGlobalBustrState(); const newState = { ...currentState, availableBusts: newAvailableBusts }; setGlobalBustrState(newState); } function getAvailableBusts() { return getGlobalBustrState().availableBusts; } //////// VIEW //////// //// Stylesheet const bustrStylesheetHTML = `<style> .bustr--green { --color: ${green} } .bustr--orange { --color: ${orange} } .bustr--red { --color: ${red} } .dark-mode.bustr--green, .bustr--green .swiper-slide { --color: ${greenLight} } .dark-mode.bustr--orange, .bustr--orange .swiper-slide { --color: ${orangeLight} } .dark-mode.bustr--red, .bustr--red .swiper-slide { --color: ${redLight} } .bg-gradient--green{ --gradient-green: linear-gradient(to bottom, rgba(143, 113, 113, 1) 0, rgba(92, 62, 62, 1) 100%); } #bustr-form.header-wrapper-top { display: flex; } #bustr-form.header-wrapper-top .container { display: flex; justify-content: start; align-items: center; padding-left: 20px; } #bustr-form.header-wrapper-top h2 { display: block; text-align: center; margin: 0; width: 172px; } #bustr-form.header-wrapper-top input { background: linear-gradient(0deg,#111,#000); border-radius: 5px; box-shadow: 0 1px 0 hsla(0,0%,100%,.102); box-sizing: border-box; color: #9f9f9f; display: inline; font-weight: 400; height: 24px; width: clamp(170px, 50%, 250px); margin: 0 0 0 21px; outline: none; padding: 0 10px 0 10px; font-size: 12px; font-style: italic; vertical-align: middle; border: 0; text-shadow: none; z-index: 100; } #bustr-form.header-wrapper-top a { margin: 0 8px; } #nav-jail .bustr-stats, #bustr-context .bustr-stats { color: var(--color, inherit); } #nav-jail .bustr-stats span { margin-left: unset; } #bustr-context.contextMenu___bjhoL { display: none; left: unset; right: -92px; padding: 0 8px; } .contextMenuActive___e6i_B #bustr-context.contextMenu___bjhoL { display: flex; } #bustr-context.contextMenu___bjhoL .arrow___tKP13 { right: unset; left: -6px; border-width: 8px 6px 8px 0; border-color: transparent #444 transparent transparent; } #bustr-context.contextMenu___bjhoL .arrow___tKP13:before { border-color: transparent #373636 transparent transparent; border-width: 6px 5px 6px 0; content: ""; left: unset; right: -6px; top: -6px; } #prefs-tab-menu #bustr-settings { display: none; } #prefs-tab-menu #bustr-settings.active { display: block; } #bustr-settings input[type="number"] { height: 24px; width: 48px; padding: 1px 5px; text-align: center; } #bustr-settings-dropdown:hover { background: #fff; } .dark-mode #prefs-tab-menu #bustr-settings-dropdown:hover { background: #444; } #prefs-tab-menu #bustr-settings-sidetab.active { background: #fff; color: #999 } .dark-mode #prefs-tab-menu #bustr-settings-sidetab.active { background: #444; color: #999 } #body .users-list-title { display: flex; justify-content: start; align-items: center; } #body .users-list-title .title{ width: 269px; } #body .users-list-title .time{ width: 50px; } #body .users-list-title .level{ width: 53px; } #body .users-list-title .reason{ width: 205px; } #body .users-list-title .hardness{ display: block; width: 79px; text-align: center; } #body .user-info-list-wrap > li .info-wrap .hardness { display: block; text-align: center; } #body .user-info-list-wrap > li .info-wrap .hardness span.title { display: none; } #body .user-info-list-wrap { display: flex; flex-direction: column; justify-content: start; align-items: center; } #body .user-info-list-wrap > li { display: flex; flex-wrap: wrap; justify-content: start; align-items: center; } #body .user-info-list-wrap > li .info-wrap { display: flex; flex-wrap: wrap; justify-content: start; align-items: center; } #body .user-info-list-wrap > li .info-wrap .time { width: 54px; } #body .user-info-list-wrap > li .info-wrap .level { width: 57px; } #body .user-info-list-wrap > li .info-wrap .reason { width: 193px; } #body .user-info-list-wrap > li .info-wrap .hardness { width: 50px; } @media screen and (max-width:1000px) { #bustr-form.header-wrapper-top h2 { width: 148px; } #bustr-form.header-wrapper-top input { margin-left: 10px; } } @media screen and (max-width:784px) { #bustr-form.header-wrapper-top h2 { font-size: 16px; width: 80px; } #body .users-list-title .hardness{ display: none; } #body .user-info-list-wrap > li .info-wrap .hardness span.title{ display: block; } #body .user-info-list-wrap > li .info-wrap .reason { width: 164px; border-right: 1px solid rgb(34, 34, 34); } #body .user-info-list-wrap > li .info-wrap .hardness { width: 64px; } } @media screen and (max-width:386px) { #body .user-info-list-wrap > li .info-wrap .time { width: 98px; height: 37px; } #body .user-info-list-wrap > li .info-wrap .level { width: 91px; height: 37px; } #body .user-info-list-wrap > li .info-wrap .reason { width: 171px; height: 24px; border-right: 1px solid rgb(34, 34, 34); } #body .user-info-list-wrap > li .info-wrap .hardness { width: 107px; } } } </style>`; function renderBustrStylesheet() { const headEl = document.querySelector('head'); headEl.insertAdjacentHTML('beforeend', bustrStylesheetHTML); } function renderBustrColorClass(availableBusts) { const navJailEl = document.querySelector('#nav-jail'); const redLimit = typeof getUserSettings().reminderLimits.redLimit === 'number' ? getUserSettings().reminderLimits.redLimit : 0; const greenLimit = typeof getUserSettings().reminderLimits.greenLimit === 'number' ? getUserSettings().reminderLimits.greenLimit : 3; if (+availableBusts <= redLimit) { document.body.classList.add('bustr--red'); return; } if (+availableBusts >= greenLimit) { document.body.classList.add('available___ZS04X', 'bustr--green'); return; } if (availableBusts > redLimit && availableBusts < greenLimit) { document.body.classList.add('bustr--orange'); } } //// Init form view function renderBustrForm() { const topHeaderBannerEl = document.querySelector('#topHeaderBanner'); const bustrFormHTML = ` <div id="bustr-form" class="header-wrapper-top"> <div class="container clear-fix"> <h2>Bustr API</h2> <input id="bustr-form__input" type="text" placeholder="Enter a full-acces API key..." /> <a href="#" id="bustr-form__submit" type="btn" disabled><span class="link-text">Submit</span</button> </div> </div>`; topHeaderBannerEl.insertAdjacentHTML('afterbegin', bustrFormHTML); } function dismountBustrForm() { document.querySelector('#bustr-form').remove(); } function renderBustrSettingsTabs() { const sideMenuTabsElArr = [...document.querySelectorAll('#prefs-tab-menu .headers li')]; const dropdownCategoriesBtn = document.querySelector('#categories-button'); const dropdownMenuListEl = document.querySelector('ul.ui-selectmenu-menu-dropdown'); const prefsTitle = document.querySelector('.prefs-tab-title'); const prefsContentArr = [...document.querySelectorAll('.prefs-cont')]; const bustrSettingsSideTabHTML = ` <li class="delimiter"></li> <li id="bustr-settings-sidetab" class="c-pointer ui-state-default ui-corner-top ui-tabs-active" data-title-name="Change your general settings" role="tab" tabindex="0" aria-controls="settings" aria-labelledby="ui-id-2" aria-selected="true"> <a class="t-gray-6 bold h settings ui-tabs-anchor" href="#settings" role="presentation" tabindex="-1" id="bustr-settings-sidetab__link" i-data="i_192_87_149_34">Bustr settings</a> </li> `; const bustrSettingsDropdownTabHTML = ` <li role="presentation" id='bustr-settings-dropdown'> <a href="#nogo" tabindex="-1" role="option" aria-selected="false" id="bustr-dropdown__select">Bustr settings</a> </li>`; sideMenuTabsElArr[6].insertAdjacentHTML('afterend', bustrSettingsSideTabHTML); dropdownMenuListEl.insertAdjacentHTML('afterbegin', bustrSettingsDropdownTabHTML); dropdownCategoriesBtn.addEventListener('click', () => { const busterDropdownIsChild = [...dropdownMenuListEl.children].filter((child) => child.id === 'bustr-settings-dropdown'); if (!busterDropdownIsChild) { dropdownMenuListEl.insertAdjacentHTML('afterbegin', bustrSettingsDropdownTabHTML); } }); const dropdownMenuItemsArr = [...dropdownMenuListEl.querySelectorAll('li')]; // listeners for dropdown li click dropdownMenuItemsArr.forEach((li, i, arr) => { li.addEventListener('click', (e) => { if (li.id === 'bustr-settings-dropdown') { prefsContentArr.forEach((el) => { if (el.id !== 'bustr-settings') el.style.display = 'none'; }); document.querySelector('#bustr-settings').style.display = 'block'; arr.forEach((li) => li.classList.remove('')); dropdownCategoriesBtn.textContent = 'Bustr settings'; prefsTitle.textContent = 'Change your bust reminder settings'; } if (li.id !== 'bustr-settings-dropdown') { document.querySelector('#bustr-settings').classList.remove('active'); document.querySelector('#bustr-settings').style.display = 'none'; } }); sideMenuTabsElArr.forEach((li, i, arr) => { li.addEventListener('click', (e) => { if (li.id === 'bustr-settings-sidetab') { prefsContentArr.forEach((el) => { if (el.id !== 'bustr-settings') el.style.display = 'none'; }); document.querySelector('#bustr-settings').style.display = 'block'; arr.forEach((li) => li.classList.remove('')); dropdownCategoriesBtn.textContent = 'Bustr settings'; prefsTitle.textContent = 'Change your bust reminder settings'; } if (li.id !== 'bustr-settings-sidetab') { document.querySelector('#bustr-settings').classList.remove('active'); document.querySelector('#bustr-settings').style.display = 'none'; } }); }); }); // if, #bustr-settings-dropdown // then, set other forms to display none, add active class to bustr form, set categores button innerText to bustr settings // if, not #bustr-settings-dropdown // then, remove active class from form } function renderBustrSettingsForm() { const prefsTabTitleEl = document.querySelector('.prefs-tab-title'); prefsTabTitleEl.textContent = 'Change your bust reminder settings'; const sideMenuTabsListEl = document.querySelector('#prefs-tab-menu .headers'); // show tabs class : ui-tabs-active // active tab class : ui-state-active bustrSettingsFormHTML = ` <div id="bustr-settings" class="prefs-cont left ui-tabs-panel ui-widget-content ui-corner-bottom" aria-labelledby="ui-id-3" role="tabpanel" aria-expanded="true" aria-hidden="false"> <div class="inner-block b-border-c t-border-f"> <ul class="prefs-list small-select-menu-wrap"> <li> <p class="m-bottom5">Custom Penalty Threshold:</p> <input type="number" name="customThreshold" id="bustr-custom-threshold" size="5" value="0" min="0" /> </li> </ul> </div> <div class="inner-block b-border-c t-border-f"> <ul class="prefs-list small-select-menu-wrap"> <li> <p class="m-bottom5">Custom Penalty Threshold:</p> <input type="number" name="customThreshold" id="bustr-custom-threshold" size="5" value="0" min="0" /> </li> </ul> </div> <div class="inner-block b-border-c t-border-f"> <ul class="prefs-list attack-pref-block small-select-menu-wrap"> <li role="radiogroup"> <div class="title left">Quick Bust</div> <div class="choice-container left-position"> <input id="quick-bust-on" class="radio-css" type="radio" name="quick-bust" value="true" checked="checked" /> <label for="quick-bust-on" class="marker-css">On</label> </div> <div class="choice-container right-position"> <input id="quick-bust-off" class="radio-css" type="radio" name="quick-bust" value="false" /> <label for="quick-bust-off" class="marker-css">Off</label> </div> <div class="clear"></div> </li> <li role="radiogroup"> <div class="title left">Quick Bail</div> <div class="choice-container left-position"> <input id="quick-bail-on" class="radio-css" type="radio" name="quick-bail" value="true" checked="checked" /> <label for="quick-bail-on" class="marker-css">On</label> </div> <div class="choice-container right-position"> <input id="quick-bail-off" class="radio-css" type="radio" name="quick-bail" value="false" /> <label for="quick-bail-off" class="marker-css">Off</label> </div> <div class="clear"></div> </li> </ul> </div> <div class="inner-block b-border-c t-border-f"> <ul class="prefs-list small-select-menu-wrap"> <li> <p class="m-bottom5">Red Limit:</p> <input type="number" name="redLimit" id="bustr-red-input" size="5" value="0" min="0" /> </li> <li> <p class="m-top10 m-bottom5">Green Limit:</p> <input id="bustr-green-input" type="number" name="Red Limit" value="3" size="5" /> </li> </ul> </div> <div class="inner-block b-border-c t-border-f"> <ul class="prefs-list small-select-menu-wrap"> <li> <p class="m-top10 m-bottom5">Stats Refresh Rate (seconds):</p> <input type="number" name="city" id="bustr-refresh-input" size="5" value="30" /> </li> <li> <p class="m-top10 m-bottom5">API Refresh Rate (minutes):</p> <input type="number" name="API Refetch Rate" id="bustr-refetch-input" value="10" /> </li> </ul> </div> <div class="inner-block t-border-f"> <div class="btn-wrap silver"> <div class="btn"> <input class="torn-btn update" type="submit" value="SAVE" id="bustr-save-btn" /> </div> </div> </div> </div>`; sideMenuTabsListEl.insertAdjacentHTML('afterend', bustrSettingsFormHTML); } // function renderJailIcon() { // console.log('REPLACE JAIL ICON'); // TEST // const fill = 'url(#sidebar_svg_gradient_regular_green_mobile'; // const jailIconEl = document.querySelector('#nav-jail svg'); // const jailIconContainerEl = // document.querySelector('#nav-jail svg').parentElement; // const greenJailHTML = ` // <svg xmlns="http://www.w3.org/2000/svg" class="default___XXAGt " filter fill="${fill}" stroke="transparent" stroke-width="0" width="17" height="17" viewBox="0 1 17 17"> // <path d="M11.56,1V18h2V1Zm-5,12.56h4v-2h-4ZM0,13.56H2.56v-2H0Zm14.56,0h2.5v-2h-2.5Zm-8-6h4v-2h-4ZM0,7.56H2.56v-2H0Zm14.56,0h2.5v-2h-2.5ZM3.56,1V18h2V1Z" filter="url(#svg_sidebar_mobile)"></path> // <path d="M11.56,1V18h2V1Zm-5,12.56h4v-2h-4ZM0,13.56H2.56v-2H0Zm14.56,0h2.5v-2h-2.5Zm-8-6h4v-2h-4ZM0,7.56H2.56v-2H0Zm14.56,0h2.5v-2h-2.5ZM3.56,1V18h2V1Z"></path> // </svg>`; // jailIconEl.remove(); // jailIconContainerEl.insertAdjacentHTML('afterbegin', greenJailHTML); // } function renderBustrStats(statsObj) { for (const [key, value] of Object.entries(statsObj)) { const statsElArr = [...document.querySelectorAll(`.bustr-stats__${key}`)]; statsElArr.forEach((el) => (el.textContent = value)); } } //// Desktop view function renderBustrDesktopView() { const jailLinkEl = document.querySelector('#nav-jail a'); const statsHTML = ` <span class="amount___p8QZX bustr-stats"> <span class="bustr-stats__penaltyScore">#</span> / <span class="bustr-stats__penaltyThreshold">#</span> : <span class="bustr-stats__availableBusts">#</span> </span>`; jailLinkEl.insertAdjacentHTML('beforeend', statsHTML); } //// Mobile view function renderMobileBustrNotification() { const navJailLinkEl = document.querySelector('#nav-jail a'); const notificationHTML = ` <div class="mobileAmount___ua3ye bustr-stats"><span class="bustr-stats__availableBusts">#</span></div>`; navJailLinkEl.insertAdjacentHTML('beforebegin', notificationHTML); } function renderBustrMobileView() { renderMobileBustrNotification(); const bustrContextMenuHTML = ` <div id="bustr-context" class='contextMenu___bjhoL bustr-context-menu'> <span class='linkName___FoKha bustr-stats'> <span class="bustr-stats__penaltyScore">#</span> / <span class="bustr-stats__penaltyThreshold">#</span> : <span class="bustr-stats__availableBusts">#</span> </span> <span class='arrow___tKP13 bustr-arrow'></span> </div>`; const navJailEl = document.querySelector('#nav-jail'); navJailEl.insertAdjacentHTML('afterend', bustrContextMenuHTML); } function renderHardnessJailView() { const headingsContainerEl = document.querySelector('.users-list-title'); const hardnessTitleHTML = ` <span class="hardness title-divider divider-spiky">Hardness</span>`; if (!headingsContainerEl.querySelector('span.hardness')) { headingsContainerEl.children[3].insertAdjacentHTML('afterend', hardnessTitleHTML); } const playerRowsArr = [...document.querySelectorAll('.user-info-list-wrap > li')]; playerRowsArr.forEach((el) => { const playerInfoContainerEl = el.querySelector('.info-wrap'); const hardnessScoreHTML = ` <span class="hardness reason"> <span class="title bold">HARDNESS</span> <span class="bustr-hardness-score">#####</span> </span>`; playerInfoContainerEl.children[2].insertAdjacentHTML('afterend', hardnessScoreHTML); }); } //////// CONTROLLERS //////// async function initController() { try { // render stylesheet renderBustrStylesheet(); // check if apiKey is saved // if saved exit function if (isPDA() && !getApiKey()) { setApiKey(PDA_API_KEY); } if (!visualViewport.width) { await new Promise((res) => { document.addEventListener('load', () => res()); }); } if (getMyViewportWidthType() === 'Desktop') { renderBustrDesktopView(); setRenderedView('Desktop'); } if (getMyViewportWidthType() === 'Mobile') { renderBustrMobileView(); setRenderedView('Mobile'); } if (getApiKey()) return; // if not saved render bustr form renderBustrForm(); // set event liseners //// Event listeners document.querySelector('#bustr-form__submit').addEventListener('click', submitFormCallback); document.querySelector('#bustr-form__input').addEventListener('input', inputValidatorCallback); document.querySelector('#bustr-form__input').addEventListener('keyup', (event) => { if (event.key === 'Enter' || event.keyCode === 13) { submitFormCallback(); } }); } catch (err) { console.error(err); } } //// load async function loadController() { try { // guard clause if no api key if (!getApiKey()) return; if (loadGlobalBustrState()) { loadGlobalBustrState(); } // fetch data const refetchRate = typeof getUserSettings().refetchRate === 'number' && getUserSettings().refetchRate > 0 ? getUserSettings().refetchRate : 600; if (getTimestampsArray().length === 0 || Date.now() - getLastFecthTimestampMs() > 1000 * refetchRate || !getLastFecthTimestampMs()) { const data = await fetchBustsData(getApiKey()); setTimestampsArray(createTimestampsArray(data)); } const statsObj = calcBustrStats(getTimestampsArray()); setPenaltyScore(statsObj.penaltyScore); setPenaltyThreshold(statsObj.penaltyThreshold); setAvailableBusts(statsObj.availableBusts); // render color class renderBustrColorClass(getAvailableBusts()); // render stats renderBustrStats(statsObj); } catch (err) { // deleteApiKey(); console.error(err); } } function successfulBustUpdateController() { // update after a successful bust const origin = window.location.origin; const pathname = window.location.pathname; createJailMutationObserver(); } function refreshStatsController() { const statsRefreshRate = typeof getUserSettings().statsRefreshRate === 'number' && getUserSettings().statsRefreshRate > 0 ? getUserSettings().statsRefreshRate : 30; setInterval(async () => { await loadController(); }, statsRefreshRate * 1000 || 30000); } function viewportResizeController() { visualViewport.addEventListener('resize', async (e) => { if (!getRenderedView()) return; const viewportWidthType = getMyViewportWidthType(); if (viewportWidthType !== getRenderedView()) { initController(); await loadController(); } }); } function userSettingsController() { if (window.location.pathname !== '/preferences.php') return; renderBustrSettingsTabs(); renderBustrSettingsForm(); } function hardnessScoreController() { if (window.location.pathname !== '/jailview.php') return; createMountJailPlayerOberserver(); renderHardnessJailView(); const playersArr = [...document.querySelectorAll('ul.user-info-list-wrap > li')]; for (const playerEl of playersArr) { const [level, durationInHours] = getLevelJailDurationInfo(playerEl); const hardnessScore = calcHardnessScore(level, durationInHours); renderHardnessScore(playerEl, hardnessScore); sortByHardnessScore(playerEl, hardnessScore); } } //// Promise race conditions // necessary as PDA scripts are inject after window.onload const PDAPromise = new Promise((res, rej) => { if (document.readyState === 'complete') res(); }); const browserPromise = new Promise((res, rej) => { window.addEventListener('load', () => res()); }); (async function () { try { await Promise.race([PDAPromise, browserPromise]); initController(); await loadController(); // userSettingsController(); if (getUserSettings().showHardnessScore) { hardnessScoreController(); } successfulBustUpdateController(); refreshStatsController(); viewportResizeController(); } catch (err) { console.error(err); } })();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址