您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Highlights cars and upgrades based on templates
// ==UserScript== // @name Racing: Car Templates // @namespace heartflower.torn.com // @version 1.3.3 // @description Highlights cars and upgrades based on templates // @author Heartflower [2626587] // @match https://www.torn.com/page.php?sid=racing* // @match https://www.torn.com/loader.php?sid=racing* // @icon https://www.google.com/s2/favicons?sz=64&domain=torn.com // ==/UserScript== (function() { 'use strict'; let settings = 'old'; let showAdaptRevert = true; // Set to 'false' if you don't want the adapt/revert link to show let savedSettings = localStorage.getItem('hf-car-settings'); if (savedSettings) { settings = savedSettings; } else { localStorage.setItem('hf-car-settings', settings); } // When set to true, it will show you what the script entails let showExplanation = 'true'; let storedShowExplanation = localStorage.getItem('hf-car-templates-explanation'); // If it's the first time loading the script, always show the explanation if (!storedShowExplanation) { showExplanation = 'true'; localStorage.setItem('hf-car-templates-explanation', 'true'); } else { showExplanation = storedShowExplanation; } // Show background colours, by default set to true let redColor = 'true'; let storedRedColor = localStorage.getItem('hf-car-templates-red-color'); if (storedRedColor) { redColor = storedRedColor; } let blueColor = 'true'; let storedBlueColor = localStorage.getItem('hf-car-templates-blue-color'); if (storedBlueColor) { blueColor = storedBlueColor; } let greenColor = 'true'; let storedGreenColor = localStorage.getItem('hf-car-templates-green-color'); if (storedGreenColor) { greenColor = storedGreenColor; } // Declare some variables to use later let chosenCar = ''; let selectedCar = ''; let selectedTrack = ''; let carType = ''; let carRatio = ''; let carTurbo = ''; let possibleTemplates = []; let chosenTrack = ''; // Set added car notes to false unless they've been added let addedCarNotes = false; // If the person chose a template already during this session, remember it let selectedTemplate = ''; let storedTemplate = sessionStorage.getItem('selectedTemplate'); if (storedTemplate) { selectedTemplate = storedTemplate; } // Create a set to store already logged title elements let loggedTitleElements = new Set(); // Default Class A Car Templates as chosen by most racing guides let oldCarTemplates = [ { name: 'Mudpit: Sierra Cosworth DS3', noteRecogniser: 'DS3' }, { name: 'Two Islands: Honda NSX DL2', noteRecogniser: 'DL2' }, { name: 'Parkland: Honda NSX DS3', noteRecogniser: 'DS3' }, { name: 'Hammerhead: Honda NSX DS2', noteRecogniser: 'DS2' }, { name: 'Stone Park: Audi R8 DS3', noteRecogniser: 'DS3' }, { name: 'Withdrawal: Lexus LFA TL3', noteRecogniser: 'TL3' }, { name: 'Speedway: Lexus LFA TL3', noteRecogniser: 'TL3' }, { name: 'Uptown: Lexus LFA TL3', noteRecogniser: 'TL3' }, { name: 'Underdog: Honda NSX TS2', noteRecogniser: 'TS2' }, { name: 'Commerce: Honda NSX TS2', noteRecogniser: 'TS2' }, { name: 'Sewage: Honda NSX TS2', noteRecogniser: 'TS2' }, { name: 'Industrial: Honda NSX TS3', noteRecogniser: 'TS3' }, { name: 'Vector: Honda NSX TS3', noteRecogniser: 'TS3' }, { name: 'Meltdown: Honda NSX TS3', noteRecogniser: 'TS3' }, { name: 'Docks: Ford GT40 TS3', noteRecogniser: 'TS3' }, { name: 'Convict: Mercedes SLR TL3', noteRecogniser: 'TL3' }, ]; let newCarTemplates = [ { name: 'Mudpit: Colina Tanprice DS3', noteRecogniser: 'DS3' }, { name: 'Two Islands: Edomondo NSX DL2', noteRecogniser: 'DL2' }, { name: 'Parkland: Edomondo NSX DS3', noteRecogniser: 'DS3' }, { name: 'Hammerhead: Edomondo NSX DS2', noteRecogniser: 'DS2' }, { name: 'Stone Park: Echo R8 DS3', noteRecogniser: 'DS3' }, { name: 'Withdrawal: Veloria LFA TL3', noteRecogniser: 'TL3' }, { name: 'Speedway: Veloria LFA TL3', noteRecogniser: 'TL3' }, { name: 'Uptown: Veloria LFA TL3', noteRecogniser: 'TL3' }, { name: 'Underdog: Edomondo NSX TS2', noteRecogniser: 'TS2' }, { name: 'Commerce: Edomondo NSX TS2', noteRecogniser: 'TS2' }, { name: 'Sewage: Edomondo NSX TS2', noteRecogniser: 'TS2' }, { name: 'Industrial: Edomondo NSX TS3', noteRecogniser: 'TS3' }, { name: 'Vector: Edomondo NSX TS3', noteRecogniser: 'TS3' }, { name: 'Meltdown: Edomondo NSX TS3', noteRecogniser: 'TS3' }, { name: 'Docks: Volt GT TS3', noteRecogniser: 'TS3' }, { name: 'Convict: Mercia SLR TL3', noteRecogniser: 'TL3' }, ]; let carTemplates = newCarTemplates; if (settings === 'old') { carTemplates = oldCarTemplates; } // If the user already has their own templates, set those instead let storedCarTemplates = JSON.parse(localStorage.getItem('carTemplates')); if (storedCarTemplates) { carTemplates = storedCarTemplates; } else { carTemplates.sort(); // Order alphabetically for easiness localStorage.setItem('carTemplates', JSON.stringify(carTemplates)); // Set the default templates in storage } // Recommended upgrades to always have on the car const overallUpgrades = ['Air Forced Engine Cooling','Air Cooling Ducts for Brakes','Adjustable Rear Spoiler','Front Diffuser','Rear Diffuser','Fast Road Brake Fluid','Braided Brake Hoses', 'Grooved and Drilled Brake Discs','Competition Racing Brake Pads','Brake Balance Bias Control','6 Pot Uprated Brakes','Ported and Polished Head','Competition Racing Fuel Pump', 'Competition Polished Throttle Body','Bored Out Engine + Forged Pistons','Front Mounted Intercooler','Stage Three Remap','Competition Racing Camshaft','Full Exhaust System', 'Stainless Steel 4-1 Manifold','Custom Forced Induction Kit','Super Octane Fuel Plus Nitrous','Polyurethane Bushings Front','Polyurethane Bushings Rear','Upper Front Strut Brace', 'Lower Front Strut Brace','Rear Strut Brace','Front Adjustable Tie Rods','Adjustable Rear Control arms','Quick Shift','4 Pin Differential','Competition Racing Clutch','Ultra-Light Flywheel', 'Strip Out','Racing Steering Wheel','Lightweight Flocked Dash','Polycarbonate Windows','Carbon Fiber Roof','Carbon Fiber Trunk','Carbon Fiber Hood','Ultra-Lightweight Alloys']; // Recommended dirt upgrades const dirtLongRatioUpgrades = ['Group N Rally Suspension','Rally Gearbox (Long Ratio)','Rally Tires']; const dirtShortRatioUpgrades = ['Group N Rally Suspension','Rally Gearbox (Short Ratio)','Rally Tires']; // Recommended tarmac upgrades const tarmacLongRatioUpgrades = ['Adjustable Coilover Suspension','Paddle Shift Gearbox (Long Ratio)','Track Tires']; const tarmacShortRatioUpgrades = ['Adjustable Coilover Suspension','Paddle Shift Gearbox (Short Ratio)','Track Tires']; // Recommended turbo upgrades const turbo2Upgrades = ['Stage Two Turbo kit']; const turbo3Upgrades = ['Stage Three Turbo Kit']; // Set needed car upgrades by default to overall upgrades let carUpgrades = [...overallUpgrades]; // All available tracks on Torn let trackNames = [ 'Commerce', 'Convict', 'Docks', 'Hammerhead', 'Industrial', 'Meltdown', 'Mudpit', 'Parkland', 'Sewage', 'Speedway', 'Stone Park', 'Two Islands', 'Underdog', 'Uptown', 'Vector', 'Withdrawal', ]; // All available cars on Torn let oldCarNames = [ 'Alfa Romeo 156', 'Aston Martin One-77', 'Audi R8', 'Audi S3', 'Audi S4', 'Audi TT Quattro', 'BMW M5', 'BMW X5', 'BMW Z8', 'Bugatti Veyron', 'Chevrolet Cavalier', 'Chevrolet Corvette Z06', 'Citroen Saxo', 'Classic Mini', 'Dodge Charger', 'Ferrari 458', 'Fiat Punto', 'Ford Focus RS', 'Ford GT40', 'Ford Mustang', 'Holden SS', 'Honda Accord', 'Honda Civic', 'Honda Integra R', 'Honda NSX', 'Honda S2000', 'Hummer H3', 'Lamborghini Gallardo', 'Lexus LFA', 'Lotus Exige', 'Mercedes SLR', 'Mini Cooper S', 'Mitsubishi Evo X', 'Nissan GT-R', 'Nissan Micra', 'Peugeot 106', 'Pontiac Firebird', 'Porsche 911 GT3', 'Reliant Robin', 'Renault Clio', 'Seat Leon Cupra', 'Sierra Cosworth', 'Subaru Impreza STI', 'Toyota MR2', 'TVR Sagaris', 'Vauxhall Astra GSI', 'Vauxhall Corsa', 'Volkswagen Beetle', 'Volkswagen Golf GTI', 'Volvo 850' ]; // Car names after the change let newCarNames = [ 'Mercia SLR', 'Veloria LFA', 'Weston Marlin 177', 'Lambrini Torobravo', 'Volt GT', 'Edomondo NSX', 'Zaibatsu GT-R', 'Lolo 458', 'Echo R8', 'Volt MNG', 'Bavaria M5', 'Dart Rampager', 'Echo S4', 'Wington GGU ', 'Tsubasa Impressor', 'Yotsuhada EVX', 'Colina Tanprice', 'Cosmos EX', 'Chevalier CZ06', 'Oceania SS', 'Knight Firebrand', 'Sturmfahrt 111', 'Edomondo S2', 'Volt RS', 'Bavaria Z8', 'Edomondo IR', 'Bedford Nova', 'Echo S3', 'Nano Cavalier', 'Echo Quadrato', 'Tabata RM2', 'Chevalier CVR', 'Edomondo ACD', 'Bavaria X5', 'Alpha Milano 156', 'Invader H3', 'Coche Basurero', 'Edomondo Localé', 'Verpestung Insecta', 'Papani Colé', 'Stålhög 860', 'Verpestung Sport', 'Zaibatsu Macro', 'Çagoutte 10-6', 'Nano Pioneer', 'Trident', 'Vita Bravo', 'Limoen Saxon', 'Bedford Racer', ]; let carNames = newCarNames; if (settings === 'old') { carNames = oldCarNames; } // If the message element is there, add the necessary things to it function observeMessageElement() { const messageWrap = document.querySelector('.info-msg-cont'); const categoriesWrap = document.querySelector('.pm-categories-wrap'); if (categoriesWrap) { messageWrap.style.background = 'rgba(20, 113, 151, 0.5)'; const messageElement = messageWrap.querySelector('.right-round'); // Find the element containing the racing points text let racingPointsElement = document.getElementById('racing-points'); if (racingPointsElement) { // Get the current racing points value let racingPoints = racingPointsElement.innerText; // Find the element containing the car name let carNameElement = document.querySelector('.msg.right-round b'); if (carNameElement && addedCarNotes == false) { // Get the car name let carName = carNameElement.innerText; selectedCar = carName; // Update the text let newText = carName + " (" + chosenCar + ")"; // Replace only the specific part of the text let textNode = racingPointsElement.parentElement.childNodes[1]; // Assuming the second child node textNode.textContent = newText; racingPointsElement.parentElement.innerHTML; addedCarNotes = true; } } // Only show the explanation if wanted and not already there let existingTextDiv = document.getElementById('carScriptTextDiv'); if (!existingTextDiv && showExplanation == 'true') { let textDiv = document.createElement('div'); textDiv.id = 'carScriptTextDiv'; textDiv.style.paddingTop = '16px'; textDiv.innerHTML = `The <b style="color:red; font-style:italic">Racing: Car Templates</b> script shows you what upgrades you have and/or are missing based on car templates. There are already a few car templates added based on some popular guides, but you're able to remove or add them as you please. Clicking the ✓ sets a new template, whilst clicking the X removes an existing template.<br><br> Upon setting a new template, you will be asked to enter a <span style='font-style:italic'>note recogniser</span>. This recogniser will be used to highlight the suggested car for a specific race you are joining or creating. You're also able to edit this note recogniser at any time by simply clicking the note button.<br><br> You can choose to show or not show background colours by simply clicking their colour.<br> A <b style="color:rgb(116, 184, 22)">green</b> background means that the upgrade is suggested and fitted. A <b style="color:rgb(20, 113, 151)">blue</b> background means that the upgrade is suggested, but not fitted. An <b style="color:rgb(247, 103, 7)">orange</b> background means that the upgrade is not suggested, but fitted. <br><br> <a href="#" id="removeExplanationLink">Click here to remove the explanation forever.</a>`; messageElement.appendChild(textDiv); let removeLink = document.getElementById('removeExplanationLink'); if (removeLink) { removeLink.addEventListener('click', function(event) { event.preventDefault(); // Prevent the default behavior of the link removeExplanation(); // Call the removeExplanation function }); }; } // Create the radio buttons createRadioButtons(messageElement); // Create the datalist container let datalistContainer = document.querySelector('#racingScriptDatalistContainer'); if (!datalistContainer) { datalistContainer = document.createElement('div'); datalistContainer.id = 'racingScriptDatalistContainer'; datalistContainer.style.display = 'flex'; datalistContainer.style.flexWrap = 'wrap'; datalistContainer.style.paddingTop = '15px'; datalistContainer.style.paddingBottom = '10px'; messageElement.appendChild(datalistContainer); } // Create other containers createCarNamesContainer(datalistContainer); createTrackNamesContainer(datalistContainer); createCarTemplatesContainer(datalistContainer); // Create the button container (template buttons) if not already there let buttonContainer = ''; let existingButtonContainer = document.getElementById('racingButtonContainer'); if (!existingButtonContainer) { buttonContainer = document.createElement('div'); buttonContainer.id = 'racingButtonContainer'; buttonContainer.style.display = 'flex'; buttonContainer.style.paddingTop = '8px'; datalistContainer.appendChild(buttonContainer); // Add button for creating new template let createTemplateButtonDiv = document.createElement('div'); createTemplateButtonDiv.style.paddingRight = '8px'; buttonContainer.appendChild(createTemplateButtonDiv); let createTemplateButton = document.createElement('button'); createTemplateButton.textContent = '✓'; createTemplateButton.style.color = 'white'; createTemplateButton.onclick = createNewTemplate; createTemplateButton.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; createTemplateButton.style.cursor = 'pointer'; createTemplateButton.style.borderRadius = '5px'; createTemplateButton.style.transition = 'background-color 0.3s ease'; // Add event listener to change background color on hover createTemplateButton.addEventListener('mouseenter', function(event) { createTemplateButton.style.background = 'rgba(0, 191, 255, 0.5)'; createTemplateButton.style.color = 'var(--default-color)'; }); // Restore original background color when mouse leaves createTemplateButton.addEventListener('mouseleave', function() { createTemplateButton.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; createTemplateButton.style.color = 'white'; }); createTemplateButtonDiv.appendChild(createTemplateButton); // Add button for removing selected template let removeTemplateButtonDiv = document.createElement('div'); removeTemplateButtonDiv.style.paddingRight = '8px'; buttonContainer.appendChild(removeTemplateButtonDiv); let removeTemplateButton = document.createElement('button'); removeTemplateButton.textContent = 'X'; removeTemplateButton.style.color = 'white'; removeTemplateButton.onclick = removeSelectedTemplate; removeTemplateButton.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; removeTemplateButton.style.cursor = 'pointer'; removeTemplateButton.style.borderRadius = '5px'; removeTemplateButton.style.transition = 'background-color 0.3s ease'; // Add event listener to change background color on hover removeTemplateButtonDiv.addEventListener('mouseenter', function(event) { removeTemplateButton.style.background = 'rgba(0, 191, 255, 0.5)'; removeTemplateButton.style.color = 'var(--default-color)'; }); // Restore original background color when mouse leaves removeTemplateButtonDiv.addEventListener('mouseleave', function() { removeTemplateButton.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; removeTemplateButton.style.color = 'white'; }); removeTemplateButtonDiv.appendChild(removeTemplateButton); // Add button for editing noteRecogniser let changeNoteRecogniserButtonDiv = document.createElement('div'); changeNoteRecogniserButtonDiv.style.width = '25px'; changeNoteRecogniserButtonDiv.style.height = '21px'; changeNoteRecogniserButtonDiv.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; changeNoteRecogniserButtonDiv.style.borderRadius = '5px'; buttonContainer.appendChild(changeNoteRecogniserButtonDiv); let changeNoteRecogniserButton = document.createElement('button'); changeNoteRecogniserButton.textContent = ' '; changeNoteRecogniserButton.style.color = 'white'; changeNoteRecogniserButton.onclick = function() { // Get the input element let input = document.querySelector('#carTemplatesContainer input'); // Get the selected template name let selectedTemplateName = input.value; changeTemplateNoteRecogniser(selectedTemplateName); }; changeNoteRecogniserButton.style.background = 'url(/images/v2/racing/edit_car_name.svg) no-repeat'; changeNoteRecogniserButton.style.backgroundPositionX = '2px'; changeNoteRecogniserButton.style.backgroundPositionY = '1px'; changeNoteRecogniserButton.style.width = '25px'; changeNoteRecogniserButton.style.height = '18px'; changeNoteRecogniserButton.style.cursor = 'pointer'; changeNoteRecogniserButton.style.transition = 'background-color 0.3s ease'; // Add event listener to change background color on hover changeNoteRecogniserButton.addEventListener('mouseenter', function(event) { changeNoteRecogniserButton.style.backgroundPositionY = '-17px'; changeNoteRecogniserButtonDiv.style.background = 'rgba(0, 191, 255, 0.5)'; }); // Restore original background color when mouse leaves changeNoteRecogniserButton.addEventListener('mouseleave', function() { changeNoteRecogniserButton.style.backgroundPositionY = '1px'; changeNoteRecogniserButton.style.color = 'white'; changeNoteRecogniserButtonDiv.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; }); changeNoteRecogniserButtonDiv.appendChild(changeNoteRecogniserButton); } else { buttonContainer = existingButtonContainer; } } else { // If the message container is not there yet, keep trying to find it setTimeout(() => { observeMessageElement(); }, 100); } } // If the explanation is no longer wanted, remove it function removeExplanation() { showExplanation = false; localStorage.setItem('hf-car-templates-explanation', false); let existingTextDiv = document.getElementById('carScriptTextDiv'); if (existingTextDiv) { existingTextDiv.remove(); } } // Create the container and datalist for track names function createTrackNamesContainer(messageElement) { let existingDataListContainer = document.getElementById('trackNamesContainer'); if (existingDataListContainer) { return; } let dataListContainer = document.createElement('div'); dataListContainer.id = 'trackNamesContainer'; dataListContainer.style.paddingRight = '16px'; dataListContainer.style.display = 'flex'; dataListContainer.style.alignItems = 'center'; dataListContainer.style.paddingTop = '8px'; let textContainer = document.createElement('p'); textContainer.textContent = 'Track:'; textContainer.style.fontWeight = 'bold'; textContainer.style.paddingRight = '4px'; dataListContainer.appendChild(textContainer); let input = document.createElement('input'); input.style.border = '1px solid black'; input.style.borderRadius = '5px'; input.style.padding = '2px 4px'; input.style.width = '100px'; input.setAttribute('list', 'trackNames'); // Add an input event listener input.addEventListener('input', function() { selectedTrack = input.value; observeTitleElements(); }); dataListContainer.appendChild(input); let dataList = document.createElement('datalist'); dataList.id = 'trackNames'; trackNames.forEach((track) => { let option = document.createElement('option'); option.value = track; dataList.appendChild(option); }); dataListContainer.appendChild(dataList); messageElement.appendChild(dataListContainer); } // Create the container and datalist for car names function createCarNamesContainer(messageElement) { let existingDataListContainer = document.getElementById('carNamesContainer'); if (existingDataListContainer) { return; } let dataListContainer = document.createElement('div'); dataListContainer.id = 'carNamesContainer'; dataListContainer.style.paddingRight = '16px'; dataListContainer.style.paddingTop = '8px'; dataListContainer.style.display = 'flex'; dataListContainer.style.alignItems = 'center'; let textContainer = document.createElement('p'); textContainer.textContent = 'Car:'; textContainer.style.fontWeight = 'bold'; textContainer.style.paddingRight = '4px'; dataListContainer.appendChild(textContainer); let input = document.createElement('input'); input.style.border = '1px solid black'; input.style.borderRadius = '5px'; input.style.padding = '2px 4px'; input.setAttribute('list', 'carNames'); // Add an input event listener input.addEventListener('input', function() { // Get the selected value from the input let selectedCar = input.value; observeTitleElements(); }); dataListContainer.appendChild(input); let dataList = document.createElement('datalist'); dataList.id = 'carNames'; carNames.forEach((car) => { let option = document.createElement('option'); option.value = car; dataList.appendChild(option); }); dataListContainer.appendChild(dataList); messageElement.appendChild(dataListContainer); } // Create the container and datalist for car templates function createCarTemplatesContainer(messageElement) { let existingDataListContainer = document.getElementById('carTemplatesContainer'); if (existingDataListContainer) { return; } let dataListContainer = document.createElement('div'); dataListContainer.id = 'carTemplatesContainer'; dataListContainer.style.paddingRight = '8px'; dataListContainer.style.paddingTop = '8px'; dataListContainer.style.display = 'flex'; dataListContainer.style.alignItems = 'center'; let textContainer = document.createElement('p'); textContainer.textContent = 'Template:'; textContainer.style.fontWeight = 'bold'; textContainer.style.paddingRight = '4px'; dataListContainer.appendChild(textContainer); let input = document.createElement('input'); input.style.border = '1px solid black'; input.style.borderRadius = '5px'; input.style.padding = '2px 4px'; input.style.width = '189px'; input.setAttribute('list', 'carTemplates'); // Add an input event listener input.addEventListener('input', function() { selectedTemplate = input.value; changeUpgrades(selectedTemplate); observeTitleElements(); }); dataListContainer.appendChild(input); let dataList = document.createElement('datalist'); dataList.id = 'carTemplates'; carTemplates.forEach((template) => { let option = document.createElement('option'); option.value = template.name; dataList.appendChild(option); }); dataListContainer.appendChild(dataList); messageElement.appendChild(dataListContainer); changeCarNamesInput(selectedCar); writeCarInTemplates(selectedCar); } // Create a new template function createNewTemplate() { let type = ''; let ratio = ''; let turbo = ''; if (carType == 'dirt') { type = 'D'; } else if (carType == 'tarmac') { type = 'T'; } if (carRatio == 'longRatio') { ratio = 'L'; } else if (carRatio == 'shortRatio') { ratio = 'S'; } if (carTurbo == 'turbo2') { turbo = '2'; } else if (carTurbo == 'turbo3') { turbo = '3'; } // Construct the new template name let newTemplateName = selectedTrack + ': ' + selectedCar + ' ' + type + ratio + turbo; // Check if the template already exists if (carTemplates.some(template => template.name === newTemplateName)) { alert('The template ' + newTemplateName + ' already exists, not adding again.'); return; // Exit the function if the template already exists } if (selectedTrack == '') { alert('Please make sure to fill in a preferred track!'); return; } if (selectedCar == '') { alert('Please make sure to fill in a preferred car!'); return; } if (type == '') { alert('Please choose between dirt and tarmac!'); return; } if (ratio == '') { alert('Please choose between long and short ratio!'); return; } if (turbo == '') { alert('Please choose between turbo 2 and turbo 3!'); return; } // Prompt the user to enter a note recogniser let noteRecogniser = prompt("Enter a note recogniser for the new template " + newTemplateName + ":"); // Create an object representing the new template let newTemplate = { name: newTemplateName, noteRecogniser: noteRecogniser }; carTemplates.push(newTemplate); carTemplates.sort(); // Save updated carTemplates to localStorage localStorage.setItem('carTemplates', JSON.stringify(carTemplates)); // Update the datalist options let dataList = document.getElementById('carTemplates'); let option = document.createElement('option'); option.value = newTemplateName; dataList.appendChild(option); writeCarInTemplates(newTemplateName); alert("Successfully added new template: " + newTemplateName + " with note recogniser " + noteRecogniser); } // Write the wanted car in templates to make it easier function writeCarInTemplates(selectedCar) { let input = document.querySelector('#carTemplatesContainer input'); input.value = selectedCar; } // Remove an existing template function removeSelectedTemplate() { let selectedTemplateName = document.querySelector('#carTemplatesContainer input').value; // Find the index of the selected template in carTemplates let index = -1; for (let i = 0; i < carTemplates.length; i++) { if (carTemplates[i].name === selectedTemplateName) { index = i; break; } } // If the selected template is found, remove it if (index !== -1) { carTemplates.splice(index, 1); carTemplates.sort(); // Save updated carTemplates to localStorage localStorage.setItem('carTemplates', JSON.stringify(carTemplates)); // Update the datalist options let dataList = document.getElementById('carTemplates'); let optionToRemove = dataList.querySelector(`option[value='${selectedTemplateName}']`); if (optionToRemove) { dataList.removeChild(optionToRemove); alert("Successfully removed template: " + selectedTemplateName); writeCarInTemplates(selectedCar); } } } // Change the upgrades based on a template function changeUpgrades(template) { let lastWord = selectedTemplate.split(' ').pop(); let type = ''; let ratio = ''; let turbo = ''; carUpgrades = [...overallUpgrades]; if (lastWord.includes('D')) { type = 'dirt'; } else if (lastWord.includes('T')) { type = 'tarmac'; } if (lastWord.includes('S')) { ratio = 'shortRatio'; } else if (lastWord.includes('L')) { ratio = 'longRatio'; } if (lastWord.includes('2')) { turbo = 'turbo2'; } else if (lastWord.includes('3')) { turbo = 'turbo3'; } if (type == 'dirt' && ratio == 'shortRatio') { carUpgrades.push(...dirtShortRatioUpgrades); } else if (type == 'dirt' && ratio == 'longRatio') { carUpgrades.push(...dirtLongRatioUpgrades); } else if (type == 'tarmac' && ratio == 'shortRatio') { carUpgrades.push(...tarmacShortRatioUpgrades); } else if (type == 'tarmac' && ratio == 'longRatio') { carUpgrades.push(...tarmacLongRatioUpgrades); } if (turbo == 'turbo2') { carUpgrades.push(...turbo2Upgrades); } else if (turbo == 'turbo3') { carUpgrades.push(...turbo3Upgrades); } clickRadioButtons(type, ratio, turbo); changeCarNamesInput(selectedTemplate); changeTrackNamesInput(selectedTemplate); observeTitleElements(); sessionStorage.setItem('selectedTemplate', selectedTemplate); } // Click radio buttons based on the wanted values function clickRadioButtons(typeValue, ratioValue, turboValue) { let typeRadioButton = document.querySelector(`input[value="${typeValue}"]`); let ratioRadioButton = document.querySelector(`input[value="${ratioValue}"]`); let turboRadioButton = document.querySelector(`input[value="${turboValue}"]`); if (typeRadioButton && ratioRadioButton && turboRadioButton) { typeRadioButton.checked = true; ratioRadioButton.checked = true; turboRadioButton.checked = true; // Trigger 'change' event on the radio buttons typeRadioButton.dispatchEvent(new Event('change')); ratioRadioButton.dispatchEvent(new Event('change')); turboRadioButton.dispatchEvent(new Event('change')); } } // Change car template input based on the wanted value function changeCarTemplatesInput(template) { let carTemplatesInput = document.querySelector('#carTemplatesContainer input'); let carTemplatesDatalist = document.querySelector('#carTemplates'); let matchedOption = null; // Loop through options in the datalist to find a match in the template for (let option of carTemplatesDatalist.options) { if (template.includes(option.value)) { matchedOption = option.value; break; } } if (matchedOption) { carTemplatesInput.value = matchedOption; } else { console.error('No matching option found in the template'); } } // Change car name input based on the wanted value function changeCarNamesInput(template) { let carNamesInput = document.querySelector('#carNamesContainer input'); let carNamesDatalist = document.querySelector('#carNames'); let matchedOption = null; // Loop through options in the datalist to find a match in the template for (let option of carNamesDatalist.options) { if (template.includes(option.value)) { matchedOption = option.value; break; } } if (matchedOption) { carNamesInput.value = matchedOption; selectedCar = matchedOption; } else { console.error('No matching option found in the template'); } } // Change track name input based on the wanted value function changeTrackNamesInput(template) { let trackNamesInput = document.querySelector('#trackNamesContainer input'); let trackNamesDatalist = document.querySelector('#trackNames'); let matchedOption = null; // Loop through options in the datalist to find a match in the template for (let option of trackNamesDatalist.options) { if (template.includes(option.value)) { matchedOption = option.value; break; } } if (matchedOption) { trackNamesInput.value = matchedOption; selectedTrack = matchedOption; } else { console.error('No matching option found in the template'); } } // Create the radio buttons (type, turbo, ratio) function createRadioButtons(messageElement) { let existingRadioContainer = document.getElementById('carUpgradesRadioContainer'); if (existingRadioContainer) { return; } let radioContainer = document.createElement('div'); radioContainer.id = 'carUpgradesRadioContainer'; radioContainer.style.display = 'flex'; radioContainer.style.flexWrap = 'wrap'; radioContainer.style.paddingTop = '10px'; // Create Long/Short Ratio radio buttons let ratioContainer = document.createElement('div'); ratioContainer.style.paddingRight = '16px'; ratioContainer.style.paddingTop = '16px'; ratioContainer.style.display = 'flex'; createRadioButton(ratioContainer, 'longRatio', 'Long Ratio', 'ratio', 'left'); createRadioButton(ratioContainer, 'shortRatio', 'Short Ratio', 'ratio', 'right'); radioContainer.appendChild(ratioContainer); // Create Turbo 2/Turbo 3 radio buttons let turboContainer = document.createElement('div'); turboContainer.style.paddingRight = '16px'; turboContainer.style.paddingTop = '16px'; turboContainer.style.display = 'flex'; createRadioButton(turboContainer, 'turbo2', 'Turbo 2', 'turbo', 'left'); createRadioButton(turboContainer, 'turbo3', 'Turbo 3', 'turbo', 'right'); radioContainer.appendChild(turboContainer) // Create Dirt/Tarmac radio buttons let typeContainer = document.createElement('div'); typeContainer.style.paddingRight = '16px'; typeContainer.style.paddingTop = '16px'; typeContainer.style.display = 'flex'; createRadioButton(typeContainer, 'dirt', 'Dirt', 'type', 'left'); createRadioButton(typeContainer, 'tarmac', 'Tarmac', 'type', 'right'); radioContainer.appendChild(typeContainer); createCheckboxes(radioContainer); messageElement.appendChild(radioContainer); // Add event listener to update additional text based on radio buttons let radioOptions = document.querySelectorAll('.carScriptRadioButton'); radioOptions.forEach(radio => { radio.addEventListener('change', function () { updateUpgradeBackgrounds(); readRadioButtons(); observeTitleElements(); }); }); } // Create a single radio button couple function createRadioButton(parentElement, value, labelText, name, side) { let radioDiv = document.createElement('div'); radioDiv.style.display = 'block'; let radioButton = document.createElement('input'); radioButton.type = 'radio'; radioButton.name = name; radioButton.value = value; radioButton.className = 'carScriptRadioButton'; radioButton.id = name; radioButton.style.display = 'none'; let labelDiv = document.createElement('div'); let label = document.createElement('label'); label.id = name + '-label'; label.textContent = labelText; label.style.color = 'white'; label.style.padding = '6px 10px'; label.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; label.style.cursor = 'pointer'; if (side == 'left') { label.style.borderBottomLeftRadius = '5px'; label.style.borderTopLeftRadius = '5px'; } else if (side == 'right') { label.style.borderBottomRightRadius = '5px'; label.style.borderTopRightRadius = '5px'; } // Add click event listener to the label label.addEventListener('click', function () { radioButton.click(); // Trigger the associated radio button's click event observeTitleElements(); }); radioDiv.appendChild(radioButton); radioDiv.appendChild(label); parentElement.appendChild(radioDiv); } // Change upgrades based on which radio buttons are checked function readRadioButtons() { let radioOptions = document.querySelectorAll('.carScriptRadioButton'); // Set car upgrades to default upgrades carUpgrades = [...overallUpgrades]; radioOptions.forEach(radio => { if (radio.checked) { switch (radio.name) { case 'type': carType = radio.value; break; case 'ratio': carRatio = radio.value; break; case 'turbo': carTurbo = radio.value; break; } } }); if (carType == 'dirt' && carRatio == 'shortRatio') { carUpgrades.push(...dirtShortRatioUpgrades); } else if (carType == 'dirt' && carRatio == 'longRatio') { carUpgrades.push(...dirtLongRatioUpgrades); } else if (carType == 'tarmac' && carRatio == 'shortRatio') { carUpgrades.push(...tarmacShortRatioUpgrades); } else if (carType == 'tarmac' && carRatio == 'longRatio') { carUpgrades.push(...tarmacLongRatioUpgrades); } if (carTurbo == 'turbo2') { carUpgrades.push(...turbo2Upgrades); } else if (carTurbo == 'turbo3') { carUpgrades.push(...turbo3Upgrades); } } // Create checkboxes to show or don't show certain background colours function createCheckboxes(messageElement) { // If already there, don't add again let existingCheckboxDiv = document.getElementById('checkboxDiv'); if (existingCheckboxDiv) { return; } let checkboxDiv = document.createElement('div'); checkboxDiv.id = 'checkboxDiv'; checkboxDiv.style.display = 'flex'; checkboxDiv.style.paddingTop = '14px'; checkboxDiv.style.paddingLeft = '16px'; let textDiv = document.createElement('div'); textDiv.textContent = 'Show background colors:'; textDiv.style.fontWeight = 'bold'; textDiv.style.alignSelf = 'center'; textDiv.style.paddingRight = '4px'; checkboxDiv.appendChild(textDiv); createCheckbox('green', checkboxDiv); createCheckbox('blue', checkboxDiv); createCheckbox('red', checkboxDiv); messageElement.appendChild(checkboxDiv); let checkboxOptions = document.querySelectorAll('.carScriptCheckbox'); checkboxOptions.forEach(checkbox => { checkbox.addEventListener('change', function () { checkLabels(); observeTitleElements(); }); }); } // Create a single checkbox function createCheckbox(colour, checkboxDiv) { const checkbox = document.createElement('input'); checkbox.type = 'checkbox'; checkbox.id = colour + '-checkbox'; checkbox.className = 'carScriptCheckbox'; checkbox.style.display = 'none'; let labelDiv = document.createElement('div'); labelDiv.style.paddingRight = '4px'; const label = document.createElement('label'); label.htmlFor = checkbox.id; label.style.width = '20px'; label.style.height = '20px'; label.style.display = 'inline-block'; label.style.cursor = 'pointer'; label.style.textAlign = 'center'; label.style.lineHeight = '20px'; label.style.fontSize = '14px'; label.style.verticalAlign = 'middle'; label.style.borderRadius = '5px'; if (colour == 'red') { label.style.background = 'rgba(247,103,7,0.5)'; if (redColor === true || redColor === 'true') { checkbox.checked = true; label.textContent = 'X'; } else { checkbox.checked = false; label.textContent = ''; } } else if (colour == 'blue') { label.style.background = 'rgba(20, 113, 151, 0.5)'; if (blueColor === true || blueColor === 'true') { checkbox.checked = true; label.textContent = 'X'; } else { checkbox.checked = false; label.textContent = ''; } } else if (colour == 'green') { label.style.background = 'rgba(116,184,22,0.5)'; if (greenColor === true || greenColor === 'true') { checkbox.checked = true; label.textContent = 'X'; } else { checkbox.checked = false; label.textContent = ''; } } // Add click event listener to the label label.addEventListener('click', function (event) { event.preventDefault(); // Prevent default label click behavior checkbox.checked = !checkbox.checked; // Toggle the 'checked' state of the associated checkbox checkbox.dispatchEvent(new Event('change')); // Dispatch a change event on the checkbox checkLabels(); // Update labels after toggling the checkbox observeTitleElements(); }); labelDiv.appendChild(checkbox); labelDiv.appendChild(label); checkboxDiv.appendChild(labelDiv); } // Keep checking the labels to see if any backgrounds need to be turned off function checkLabels() { let checkboxOptions = document.querySelectorAll('.carScriptCheckbox'); checkboxOptions.forEach(checkbox => { let label = checkbox.nextElementSibling; if (checkbox.checked) { label.textContent = 'X'; // Update color variables based on checkbox state if (checkbox.id === 'red-checkbox') { redColor = true; localStorage.setItem('hf-car-templates-red-color', 'true'); } else if (checkbox.id === 'blue-checkbox') { blueColor = true; localStorage.setItem('hf-car-templates-blue-color', 'true'); } else if (checkbox.id === 'green-checkbox') { greenColor = true; localStorage.setItem('hf-car-templates-green-color', 'true'); } } else { label.textContent = ''; // Update color variables based on checkbox state if (checkbox.id === 'red-checkbox') { redColor = false; localStorage.setItem('hf-car-templates-red-color', 'false'); } else if (checkbox.id === 'blue-checkbox') { blueColor = false; localStorage.setItem('hf-car-templates-blue-color', 'false'); } else if (checkbox.id === 'green-checkbox') { greenColor = false; localStorage.setItem('hf-car-templates-green-color', 'false'); } } }); } // Start observing the title elements until you found the racing wrap function observeTitleElements() { const pmItemsWraps = document.querySelectorAll('.pm-items-wrap'); if (pmItemsWraps) { pmItemsWraps.forEach(function(pmItemsWrap) { // Find all title elements (car upgrades) const titleElements = pmItemsWrap.querySelectorAll('.title.t-overflow'); titleElements.forEach(function(titleElement) { highlightTitleContents(titleElement, pmItemsWrap); }); }); } } // Highlight upgrade titles based on the car upgrades function highlightTitleContents(titleElement, pmItemsWrap) { const titleText = titleElement.textContent.trim(); // Check if not already included and if valid option if (carUpgrades.includes(titleText)) { loggedTitleElements.add(titleText); // Add to the set to avoid duplicate logging changeBackgroundColors(titleElement); } else { removeBackgroundColors(titleElement); } } // Remove previous background colours if upgrades are no longer wanted function removeBackgroundColors(titleElement) { const bgWrap = titleElement.closest('.bg-wrap'); if (bgWrap) { // Check if the "info" div contains the specific text const infoDiv = bgWrap.querySelector('.info'); if (infoDiv && infoDiv.textContent.trim() === 'Upgrade is already fitted to this car') { if (redColor === 'true' || redColor === true) { bgWrap.style.background = 'rgba(247,103,7,0.15)'; // Red colour } else { bgWrap.style.background = ''; } } else { bgWrap.style.background = ''; } } const boxWrap = titleElement.closest('.box-wrap'); if (boxWrap) { const parentElement = boxWrap.parentElement; // Get the parent of box-wrap // Check if the "info" div contains the specific text const infoDiv = boxWrap.querySelector('.info'); if (infoDiv && infoDiv.textContent.trim() === 'Upgrade is already fitted to this car') { if (redColor === 'true' || redColor === true) { parentElement.style.background = 'rgba(247,103,7,0.15)'; // Red colour } else { parentElement.style.background = ''; } } else { parentElement.style.background = ''; } } } // Change background colours for wanted upgrades function changeBackgroundColors(titleElement) { const bgWrap = titleElement.closest('.bg-wrap'); if (bgWrap) { // Check if the "info" div contains the specific text const infoDiv = bgWrap.querySelector('.info'); if (infoDiv && infoDiv.textContent.trim() === 'Upgrade is already fitted to this car') { if (greenColor === 'true' || greenColor === true) { bgWrap.style.background = 'rgba(116,184,22,0.15)'; // Green background } else { bgWrap.style.background = ''; } } else { if (blueColor === 'true' || blueColor === true) { bgWrap.style.background = 'rgba(20, 113, 151, 0.15'; // Blue background } else { bgWrap.style.background = ''; } } } const boxWrap = titleElement.closest('.box-wrap'); if (boxWrap) { const parentElement = boxWrap.parentElement; // Get the parent of box-wrap // Check if the "info" div contains the specific text const infoDiv = boxWrap.querySelector('.info'); if (infoDiv && infoDiv.textContent.trim() === 'Upgrade is already fitted to this car') { if (greenColor === 'true' || greenColor === true) { parentElement.style.background = 'rgba(116,184,22,0.15)'; // Green background } else { parentElement.style.background = ''; } } else { if (blueColor === 'true' || blueColor === true) { parentElement.style.background = 'rgba(20, 113, 151, 0.15)'; // Blue background } else { parentElement.style.background = ''; } } } } // Change background colours based on radio buttons function updateUpgradeBackgrounds() { let radioOptions = document.querySelectorAll('.carScriptRadioButton'); let selectedOptions = []; radioOptions.forEach(radio => { let container = radio.parentElement; if (radio.checked) { selectedOptions.push(radio.value); // Set the background color of all siblings to green Array.from(container.children).forEach(sibling => { if (sibling !== radio) { sibling.style.background = 'rgba(0, 191, 255, 0.5)'; sibling.style.color = 'var(--default-color)'; } }); } else { // Reset the background color of all siblings to black Array.from(container.children).forEach(sibling => { sibling.style.background = 'url(/images/v2/racing/header/stripy_bg.png) 0 0 repeat'; sibling.style.color = 'white'; }); } }); } // Check if the user clicks 'Official Race' or 'Custom Race' function checkButtons() { let headerWrap = document.querySelector('.header-wrap'); if (headerWrap) { document.querySelector('a[tab-value="race"]').addEventListener('click', function(event) { officialRace(); }); document.querySelector('a[tab-value="customrace"]').addEventListener('click', function(event) { customRace(); }); } } // When the user clicks 'Custom Race', check if they create or join a race function customRace() { const createRaceButtons = document.querySelectorAll('.btn-wrap.silver.c-pointer a.btn-action-tab'); const joinRaceButtons = document.querySelectorAll('.join-wrap a.link.btn-action-tab'); if (createRaceButtons && joinRaceButtons) { document.addEventListener('click', function(event) { const target = event.target; // Check if the clicked element is a "create race" button if (target.matches('.btn-wrap.silver.c-pointer a.btn-action-tab')) { createCustomRace(); } // Check if the clicked element is a "join race" button if (target.matches('.join-wrap a.link.btn-action-tab') || target.closest('.join-wrap a.link.btn-action-tab') || target.classList.contains('btn-action-form')) { // Find the closest .active-row ancestor of the clicked button const successRow = target.closest('.active-row'); if (successRow) { // Find the track name within the success row element const trackElement = successRow.querySelector('.track'); if (trackElement) { let trackText = trackElement.textContent.trim(); chosenTrack = trackText.split('(')[0].trim(); let raceName = trackElement.querySelector('.hf-race-name'); if (raceName) { let textContent = raceName.textContent; chosenTrack = chosenTrack.replace(textContent, '').trim(); } possibleTemplates = []; for (let template of carTemplates) { if (chosenTrack === template.name.split(':')[0].trim()) { possibleTemplates.push(template); } } setTimeout(function() { highlightSuggestedCar(possibleTemplates, chosenTrack); }, 100); } else { setTimeout(customRace, 100) } } else { console.error('[Racing: Car Templates] Something went wrong whilst trying to fetch the track name'); } } }); } else { setTimeout(customRace, 100); } } // If the user creates a custom race, remember the track they choose function createCustomRace() { // Find the button by its class name let createButton = document.querySelector('[name="createCustomRace"]'); if (createButton) { createButton.addEventListener('click', function() { let selectMenuStatus = document.querySelector('.ui-selectmenu'); let mobileSelectMenu = document.querySelector('#select-racing-track'); if (selectMenuStatus) { chosenTrack = selectMenuStatus.textContent.trim(); possibleTemplates = []; for (let template of carTemplates) { if (template.name.includes(chosenTrack)) { possibleTemplates.push(template); } } setTimeout(function() { highlightSuggestedCar(possibleTemplates, chosenTrack); }, 100); } else if (mobileSelectMenu) { let selectedOption = mobileSelectMenu.options[mobileSelectMenu.selectedIndex]; chosenTrack = selectedOption.textContent; possibleTemplates = []; for (let template of carTemplates) { if (template.name.includes(chosenTrack)) { possibleTemplates.push(template); } } setTimeout(function() { highlightSuggestedCar(possibleTemplates, chosenTrack); }, 300); } else { console.error('[Racing: Car Templates] Something went wrong whilst trying to fetch the track name'); } }); } else { setTimeout(createCustomRace, 100); } } // When the user clicks 'Official Race' function officialRace() { let joinButton = document.querySelector('a[href*="tab=race"][href*="section=changeRacingCar"][href*="step=getInRace"]'); if (joinButton) { joinButton.addEventListener('click', function() { findOfficialTrack(); }); } else { setTimeout(officialRace, 100); } } // When the user joins an official race, remember the track they choose function findOfficialTrack() { let enlistWrap = document.querySelector('.enlist-wrap'); if (enlistWrap) { let trackElement = document.querySelector('.enlisted-btn-wrap').textContent; if (trackElement) { chosenTrack = trackElement.split(' - Official race')[0].trim(); possibleTemplates = []; for (let template of carTemplates) { if (chosenTrack === template.name.split(':')[0].trim()) { possibleTemplates.push(template); } } setTimeout(function() { highlightSuggestedCar(possibleTemplates, chosenTrack); }, 100); } else { console.error('[Racing: Car Templates] Something went wrong whilst trying to fetch the track name'); } } else { setTimeout(findOfficialTrack, 100); } } // Display and highlight the suggested car based on the track and templates function highlightSuggestedCar(templates, chosenTrack) { if (chosenTrack == 'ENLIST ANOTHER CAR' || chosenTrack == '') { chosenTrack = 'unknown track'; return; } let racingInfoWrap = document.querySelector('.racing-info-wrap'); if (racingInfoWrap) { racingInfoWrap.style.paddingTop = '10px'; let existingContainer = document.getElementById('hf-suggested-car-templates'); if (existingContainer) { existingContainer.remove(); } let container = document.createElement('div'); container.id = 'hf-suggested-car-templates'; container.style.background = 'linear-gradient(180deg, rgba(20, 113, 151, 0.5), rgba(20, 113, 151, 0.15))'; container.style.display = 'flex'; container.style.flexWrap = 'wrap'; racingInfoWrap.appendChild(container); let textDivContainer = document.createElement('div'); textDivContainer.style.padding = '8px'; textDivContainer.style.borderRadius = '5px'; let textDiv = document.createElement('p'); textDiv.textContent = 'Suggested car template(s) for ' + chosenTrack + ':'; textDiv.style.fontWeight = 'bold'; textDiv.style.paddingBottom = '4px'; textDivContainer.appendChild(textDiv); container.appendChild(textDivContainer); if (templates !== '') { templates.forEach(template => { let templateName = template.name; let noteRecogniser = template.noteRecogniser; let index = templateName.indexOf(':'); if (index !== -1) { let suggestedCar = templateName.substring(index + 1).trim(); let modelWithoutClass = suggestedCar.split(' ').slice(0, -1).join(' '); // Remove last word let existingParagraph = document.getElementById('hf-racing-' + suggestedCar); if (existingParagraph) { existingParagraph.remove(); } let paragraphContainer = document.createElement('div'); paragraphContainer.id = 'hf-racing-' + suggestedCar; paragraphContainer.style.display = 'flex'; paragraphContainer.style.fontWeight = 'normal'; let paragraph = document.createElement('p'); paragraph.textContent = '\u2022 ' + suggestedCar + ' (' + noteRecogniser + ')'; paragraph.style.alignSelf = 'center'; paragraphContainer.appendChild(paragraph); // Add button for editing noteRecogniser let changeNoteRecogniserButton = document.createElement('button'); changeNoteRecogniserButton.textContent = ' '; changeNoteRecogniserButton.style.color = 'white'; changeNoteRecogniserButton.onclick = function() { changeTemplateNoteRecogniser(templateName, templates, chosenTrack); }; changeNoteRecogniserButton.style.background = 'url(/images/v2/racing/edit_car_name.svg) no-repeat'; changeNoteRecogniserButton.style.width = '18px'; changeNoteRecogniserButton.style.height = '18px'; changeNoteRecogniserButton.style.cursor = 'pointer'; changeNoteRecogniserButton.style.transition = 'background-color 0.3s ease'; changeNoteRecogniserButton.style.marginTop = '6px'; // Add event listener to change background color on hover changeNoteRecogniserButton.addEventListener('mouseenter', function(event) { changeNoteRecogniserButton.style.backgroundPositionY = '-18px'; }); // Restore original background color when mouse leaves changeNoteRecogniserButton.addEventListener('mouseleave', function() { changeNoteRecogniserButton.style.backgroundPositionY = '0px'; changeNoteRecogniserButton.style.color = 'white'; }); paragraphContainer.appendChild(changeNoteRecogniserButton); textDivContainer.appendChild(paragraphContainer); // Loop through all the li elements document.querySelectorAll('li').forEach(li => { // Check if the list item contains modelWithoutClass if (li.textContent.includes(modelWithoutClass)) { if (li.textContent.includes(noteRecogniser)) { li.style.background = 'rgba(20, 113, 151, 0.15)'; let enlistInfoWrap = li.querySelector('.enlist-info-wrap'); enlistInfoWrap.style.background = 'rgba(20, 113, 151, 0.15'; let enlistCar = li.querySelector('.enlist-car'); enlistCar.style.background = 'rgba(20, 113, 151, 0.15'; // Get the parent ul element let parentUl = li.parentNode; // Append li as first child parentUl.insertBefore(li, parentUl.firstChild); } } else { li.style.background = ''; let enlistInfoWrap = li.querySelector('.enlist-info-wrap'); if (enlistInfoWrap) { enlistInfoWrap.style.background = ''; } let enlistCar = li.querySelector('.enlist-car'); if (enlistCar) { enlistCar.style.background = ''; } } }); } }); } else { let paragraph = document.createElement('p'); paragraph.textContent = 'No template found for this track'; paragraph.style.fontWeight = 'normal'; paragraph.style.paddingTop = '4px'; } } else { setTimeout(function() { highlightSuggestedCar(templates, chosenTrack); }, 100); } } // Deprecated, but leaving this here in case I do need it later function manuallyAddTrackName() { let existingContainer = document.getElementById('hf-racing-track-error'); if (existingContainer) { return; } let enlistList = document.querySelector('.enlist-list'); if (!enlistList) { setTimeout(manuallyAddTrackName, 100); } let delimiterElement = document.querySelector('.delimiter-999.m-top10'); let container = document.createElement('div'); container.id = 'hf-racing-track-error'; container.style.paddingTop = '4px'; let infoMsgDiv = document.createElement('div'); infoMsgDiv.className = 'info-msg-cont border-round m-top10 factionMessageDiv'; infoMsgDiv.style.background = 'red'; // Create the inner div for the message content let innerDiv = document.createElement('div'); innerDiv.className = 'info-msg border-round'; // Create the icon element let iconElement = document.createElement('i'); iconElement.className = 'info-icon'; // Create the message container div let msgContainer = document.createElement('div'); msgContainer.className = 'delimiter'; // Create the message element let messageElement = document.createElement('div'); messageElement.className = 'msg right-round'; messageElement.setAttribute('role', 'alert'); messageElement.setAttribute('aria-live', 'assertive'); let textElement = document.createElement('div'); textElement.className = 'factionTextElement'; textElement.textContent = 'There was an error fetching the track name. Click here to manually insert track Name'; textElement.style.color = 'var(--default-red-color)'; textElement.style.textDecoration = 'underline'; textElement.addEventListener('click', function() { let manualTrack = prompt('Please manually enter the track name: '); if (manualTrack) { chosenTrack = manualTrack; container.remove(); possibleTemplates = []; for (let template of carTemplates) { if (chosenTrack.toLowerCase() === template.name.split(':')[0].trim().toLowerCase()) { possibleTemplates.push(template); } } setTimeout(function() { highlightSuggestedCar(possibleTemplates, chosenTrack); }, 100); } }); messageElement.appendChild(textElement); // Append elements to construct the message structure msgContainer.appendChild(messageElement); innerDiv.appendChild(iconElement); innerDiv.appendChild(msgContainer); infoMsgDiv.appendChild(innerDiv); container.appendChild(infoMsgDiv); delimiterElement.parentNode.insertBefore(container, delimiterElement); } // Function to handle the button click event function changeTemplateNoteRecogniser(selectedTemplateName, templates, chosenTrack) { // Find the corresponding template object let selectedTemplate = carTemplates.find(template => template.name === selectedTemplateName); // If template found, proceed if (selectedTemplate) { // Prompt with the current note recogniser pre-filled let newNoteRecogniser = prompt('Change the note recogniser for the template ' + selectedTemplate.name, selectedTemplate.noteRecogniser); // If user enters something and clicks OK if (newNoteRecogniser !== null) { // Update the note recogniser of the selected template selectedTemplate.noteRecogniser = newNoteRecogniser; // Save updated carTemplates to localStorage localStorage.setItem('carTemplates', JSON.stringify(carTemplates)); // Alert the user alert("Note recogniser updated for " + selectedTemplateName); } if (templates && chosenTrack) { setTimeout(function() { highlightSuggestedCar(templates, chosenTrack); }, 100); } } else { alert("No template selected."); } } // Initial script setup observeTitleElements(); observeMessageElement(); checkButtons(); // Attach click event listener document.body.addEventListener('click', handleButtonClick); // Reconnect observer when a button is clicked function handleButtonClick(event) { const clickedElement = event.target; const isAnchor = clickedElement.tagName === 'a' || clickedElement.closest('a') !== null; if (isAnchor) { observeTitleElements(); observeMessageElement(); checkButtons(); setTimeout(function() { if (clickedElement.classList.contains('page-number')) { highlightSuggestedCar(possibleTemplates, chosenTrack); } }, 200); // Find the closest parent <li> element of the clicked <a> element const listItem = event.target.closest('li'); // Check if listItem exists if (listItem) { const carNameSpan = listItem.querySelector('[class^="model-car-name-"]'); if (carNameSpan) { chosenCar = carNameSpan.textContent.trim(); addedCarNotes = false; } } } } let carChanges = { 'Mitsubishi Evo X': 'Yotsuhada EVX', 'Volvo 850': 'Stålhög 860', 'Alfa Romeo 156': 'Alpha Milano 156', 'BMW X5': 'Bavaria X5', 'Seat Leon Cupra': 'Coche Basurero', 'Vauxhall Astra GSI': 'Bedford Nova', 'Volkswagen Golf GTI': 'Verpestung Sport', 'Audi S3': 'Echo S3', 'Ford Focus RS': 'Volt RS', 'Honda s2': 'Edomondo S2', 'Mini Cooper S': 'Nano Cavalier', 'Sierra Cosworth': 'Colina Tanprice', 'Lotus Exige': 'Cosmos EX', 'Vauxhall Corsa': 'Bedford Racer', 'Porsche 911 GT3': 'Sturmfahrt 111', 'Subaru Impreza STI': 'Tsubasa Impressor', 'TVR Sagaris': 'Wington GGU', 'Aston Martin One-77': 'Weston Marlin 177', 'Audi R8': 'Echo R8', 'Bugatti Veyron': 'Stormatti Casteon', 'Ferrari 458': 'Lolo 458', 'Lamborghini Gallardo': 'Lambrini Torobravo', 'Lexus LFA': 'Veloria LFA', 'Mercedes SLR': 'Mercia SLR', 'Nissan GT-R': 'Zaibatsu GT-R', 'Honda Civic': 'Edomondo Localé', 'Honda NSX': 'Edomondo NSX', 'Audi TT Quattro': 'Echo Quadrato', 'BMW M5': 'Bavaria M5', 'BMW Z8': 'Bavaria Z8', 'Chevrolet Corvette Z06': 'Chevalier CZ06', 'Dodge Charger': 'Dart Rampager', 'Pontiac Firebird': 'Knight Firebrand', 'Ford GT40': 'Volt GT', 'Hummer H3': 'Invader H3', 'Audi S4': 'Echo S4', 'Honda Integra R': 'Edomondo IR', 'Honda Accord': 'Edomondo ACD', 'Toyota MR2': 'Tabata RM2', 'Volkswagen Beetle': 'Verpestung Insecta', 'Chevrolet Cavalier': 'Chevalier CVR', 'Ford Mustang': 'Volt MNG', 'Reliant Robin': 'Trident', 'Holden SS': 'Oceania SS', 'Citroen Saxo': 'Limoen Saxon', 'Classic Mini': 'Nano Pioneer', 'Fiat Punto': 'Vita Bravo', 'Nissan Micra': 'Zaibatsu Macro', 'Peugeot 106': 'Çagoutte 10-6', 'Renault Clio': 'Papani Colé', }; // Function to replace one set of car names with another set in the templates function updateCarNames(oldName, newName) { let matchingTemplates = carTemplates.filter(template => { let templateName = template.name.toLowerCase(); let searchTerm = oldName.toLowerCase(); return templateName.includes(searchTerm); }); matchingTemplates.forEach(template => { template.name = template.name.replace(oldName, newName); }); } function adaptToNewCars() { settings = 'new'; localStorage.setItem('hf-car-settings', settings); Object.entries(carChanges).forEach(([oldName, newName]) => { updateCarNames(oldName, newName); }); carTemplates.sort(); // Order alphabetically for easiness localStorage.setItem('carTemplates', JSON.stringify(carTemplates)); // Set the default templates in storage location.reload(); } function revertToOldCars() { settings = 'old'; localStorage.setItem('hf-car-settings', settings); Object.entries(carChanges).forEach(([oldName, newName]) => { if (oldName === 'Volt GT40') { oldName = 'Volt GT'; } updateCarNames(newName, oldName); }); carTemplates.sort(); // Order alphabetically for easiness localStorage.setItem('carTemplates', JSON.stringify(carTemplates)); // Set the default templates in storage location.reload(); } function addAdaptRevert() { let linksElement = document.getElementById('top-page-links-list'); if (!linksElement) { setTimeout(addAdaptRevert, 100); return; } linksElement.style.display = 'flex'; linksElement.style.justifyContent = 'flex-end'; linksElement.style.alignItems = 'center'; let span = document.createElement('span'); span.style.paddingRight = '8px'; span.style.cursor = 'pointer'; span.style.color = 'var(--default-blue-color)'; linksElement.insertBefore(span, linksElement.children[0]); if (settings === 'old') { span.textContent = 'Adapt to new cars'; span.onclick = adaptToNewCars; } else if (settings === 'new') { span.textContent = 'Revert to old cars'; span.onclick = revertToOldCars; } } if (showAdaptRevert === true) { addAdaptRevert(); } })();