您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Internet Roadtrip Cruise Control.
当前为
// ==UserScript== // @name Internet Roadtrip Cruise Control // @namespace spideramn.github.io // @match https://neal.fun/internet-roadtrip/* // @version 0.0.2 // @author Spideramn // @description Internet Roadtrip Cruise Control. // @license MIT // @grant GM.addStyle // @grant GM.setValues // @grant GM.getValues // @icon https://neal.fun/favicons/internet-roadtrip.png // @require https://cdn.jsdelivr.net/npm/[email protected] // @require https://cdnjs.cloudflare.com/ajax/libs/gauge.js/1.3.9/gauge.min.js // ==/UserScript== // This works together with irf.d.ts to give us type hints /* globals IRF Gauge */ /** * Internet Roadtrip Framework * @typedef {typeof import('internet-roadtrip-framework')} IRF */ (async function() { if (!IRF?.isInternetRoadtrip) { return; } // Get map methods and various objects const odometer = await IRF.vdom.odometer; const container = await IRF.vdom.container; class CruiseControl { async setup() { container.state.changeStop = new Proxy(container.methods.changeStop, { apply: async (target, thisArg, args) => { const returnValue = Reflect.apply(target, thisArg, args); this._changeStop(args[5]); return returnValue; } }); }; _changeStop(stops) { if(settings.cruise_control_enabled) { // only 1 option to choose from? if(stops.length == 1) { setTimeout(async () => this._vote(), 1000); } else { console.log('Not voting, there are ' + stops.length + ' options.'); } } } async _vote() { // already voted? if(!container.data.voted) { await container.methods.vote(0); // vote } } }; // Speedometer class SpeedOmeterControl { _queue = []; _speedOmeterContainer = undefined; _speedOmeterElement = undefined; _gauge = undefined; addDistance(distanceInMiles) { // prevent duplicate distances if(this._queue.length > 0) { const lastDistance = this._queue[this._queue.length - 1].distance; if(distanceInMiles == lastDistance) { this.update(); return; } } this._queue.push({time: Date.now(), distance: distanceInMiles}); if (this._queue.length > 15) { this._queue.shift(); } this.update(); }; async setup() { GM.addStyle(` .speedOmeterContainer { width: 70%; height: 25%; position: absolute; top: 10%; left: 15%; } .speedOmeterContainer canvas{ position: absolute; width: 100%; height: 100%; } .speedOmeterContainer span { position: absolute; width: 100%; bottom: 0%; left: 0%; height: 50%; text-align: center; font-family: Roboto,sans-serif; color: #1F1F1F; font-size: 17px; } `); // add dom elements this._speedOmeterContainer = document.createElement('div'); this._speedOmeterContainer.className = "speedOmeterContainer"; const speedOmeterCanvas = document.createElement('canvas'); this._speedOmeterElement = document.createElement('span'); this._speedOmeterContainer.appendChild(speedOmeterCanvas); this._speedOmeterContainer.appendChild(this._speedOmeterElement); (await IRF.dom.wheel).prepend(this._speedOmeterContainer); var opts = { angle: 0, lineWidth: 1, radiusScale: 1, pointer: { length: 1, strokeWidth: 0.035, color: '#AA000099' }, limitMax: true, limitMin: true, generateGradient: false, highDpiSupport: true, colorStart: '#FFFFFF99', colorStop: '#FFFFFF99', strokeColor: '#FFFFFF99', // renderTicks is Optional renderTicks: { divisions: 6, divWidth: 2, divLength: 0.1, divColor: '#333333', subDivisions: 5, subLength: 0.05, subWidth: 1, subColor: '#666666' }, staticLabels: { font: "10px Roboto,sans-serif", labels: [0, 5, 10, 15, 20, 25, 30], color: "#000000", fractionDigits: 0 } }; this._gauge = new Gauge(speedOmeterCanvas).setOptions(opts); this._gauge.maxValue = 30; this._gauge.set(0); // set actual value // set hook odometer.state.updateDisplay = new Proxy(odometer.methods.updateDisplay, { apply: (target, thisArg, args) => { this.addDistance(odometer.props.miles); return Reflect.apply(target, thisArg, args);; }, }); } update() { if(settings.speedometer_enabled) { this._speedOmeterContainer.style.visibility = 'visible'; if(this._queue.length < 3) { this._gauge.set(0); this._speedOmeterElement.innerText = 'Calibrating...'; return; } const lastItem = this._queue[this._queue.length - 1]; const firstItem = this._queue[0]; const timeInHours = (lastItem.time - firstItem.time) / (1000 * 60 * 60); let speed = (lastItem.distance - firstItem.distance) / timeInHours; // in mph if(odometer.data.isKilometers) { speed *= odometer.data.conversionFactor; } this._gauge.set(speed); this._speedOmeterElement.innerText = (speed.toFixed(1) + (odometer.data.isKilometers? ' km/h' : ' mph')); } else { this._gauge.set(0); this._speedOmeterElement.innerText = ''; this._speedOmeterContainer.style.visibility = 'hidden'; } } }; // // Settings const settings = { "cruise_control_enabled": false, "speedometer_enabled": false, }; const storedSettings = await GM.getValues(Object.keys(settings)) Object.assign(settings, storedSettings); await GM.setValues(settings); // settings panel let gm_info = GM.info gm_info.script.name = "Cruise Control" const irf_settings = IRF.ui.panel.createTabFor(gm_info, { tabName: "Cruise Control" }); const info_el = document.createElement("div"); info_el.innerText = "When Cruise Control is enabled you will automatically vote if there is only 1 option available."; irf_settings.container.appendChild(info_el); irf_settings.container.appendChild(document.createElement('hr')); add_checkbox('Cruise control enabled', 'cruise_control_enabled'); add_checkbox('Display speedometer', 'speedometer_enabled', (enabled) => speedOmeter.update()); function add_checkbox(name, identifier, callback=undefined, settings_container=irf_settings.container) { let label = document.createElement("label"); let checkbox = document.createElement("input"); checkbox.type = "checkbox"; checkbox.checked = settings[identifier]; checkbox.className = IRF.ui.panel.styles.toggle; label.appendChild(checkbox); let text = document.createElement("span"); text.innerText = " " + name; label.appendChild(text); checkbox.addEventListener("change", () => { settings[identifier] = checkbox.checked; GM.setValues(settings); if (callback) callback(checkbox.checked); }); settings_container.appendChild(label); settings_container.appendChild(document.createElement("br")); settings_container.appendChild(document.createElement("br")); return checkbox }; const speedOmeter = new SpeedOmeterControl(); await speedOmeter.setup(); const cruiseControlInstance = new CruiseControl(); await cruiseControlInstance.setup(); })();