您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Weist maximal mögliche Anzahl an Personal einem Fahrzeug zu. Originalskript von BOS-Ernie, angepasst und erweitert, zur Unterstützung der neusten Lehrgänge, durch leeSalami. Weitere Lehrgänge hinzugefügt von Manute1337.
// ==UserScript== // @name * Personalzuweiser // @namespace bos-ernie.leitstellenspiel.de // @version 2.2.3 // @license BSD-3-Clause // @author BOS-Ernie, leeSalami, Manute1337 // @description Weist maximal mögliche Anzahl an Personal einem Fahrzeug zu. Originalskript von BOS-Ernie, angepasst und erweitert, zur Unterstützung der neusten Lehrgänge, durch leeSalami. Weitere Lehrgänge hinzugefügt von Manute1337. // @match https://*.leitstellenspiel.de/vehicles/*/zuweisung // @icon https://www.google.com/s2/favicons?sz=64&domain=leitstellenspiel.de // @run-at document-idle // @grant none // ==/UserScript== (async function () { 'use strict'; updatePersonalCount(); const csrfToken = document.querySelector('meta[name=csrf-token]')?.content; if (!csrfToken) { return; } const loadingText = I18n.t('common.loading'); addButtonGroup(); async function assign() { const assignedPersonsElement = getAssignedPersonsElement(); const numberOfAssignedPersonnel = parseInt(assignedPersonsElement.innerText); const vehicleCapacity = parseInt(assignedPersonsElement.parentElement.firstElementChild.innerText); let numberOfPersonnelToAssign = vehicleCapacity - numberOfAssignedPersonnel; const vehicleTypeId = getVehicleTypeId(); if (numberOfPersonnelToAssign > 0 && vehicleTypeId !== null) { const vehicleTraining = getIdentifierByVehicleTypeId(vehicleTypeId); if (vehicleTraining === null) { return; } const trainingCount = Object.keys(vehicleTraining).length; if (trainingCount === 0) { return; } if (trainingCount !== 1 || !('no_training' in vehicleTraining)) { const rowsWithTraining = document.querySelectorAll('tbody tr:not([data-filterable-by="[]"]):has(a.btn-success):not(:has(span[data-education-key]))'); const sortedRowsWithTraining = sortRows(rowsWithTraining); for (let i = 0, n = sortedRowsWithTraining.length; i < n; i++) { let hasIdentifier = true; let currentIdentifier = null; for (const educationIdentifier in vehicleTraining) { if (educationIdentifier === 'no_training') { continue; } const rowHasTraining = sortedRowsWithTraining[i].dataset.filterableBy.includes('"' + educationIdentifier + '"'); if (vehicleTraining[educationIdentifier] === true && !rowHasTraining) { hasIdentifier = false; } else if (typeof vehicleTraining[educationIdentifier] === 'number') { if (!rowHasTraining || vehicleTraining[educationIdentifier] <= 0) { hasIdentifier = false; } else { hasIdentifier = true; currentIdentifier = educationIdentifier; break; } } } if (!hasIdentifier) { continue; } if (await changeAssignment(sortedRowsWithTraining[i].querySelector('a.btn-success'))) { numberOfPersonnelToAssign--; if (currentIdentifier) { vehicleTraining[currentIdentifier]--; } if (numberOfPersonnelToAssign === 0) { break; } await new Promise(r => setTimeout(r, 5)); } } } if (numberOfPersonnelToAssign === 0) { return; } if ('no_training' in vehicleTraining) { if (typeof vehicleTraining['no_training'] === 'number') { numberOfPersonnelToAssign = Math.min(numberOfPersonnelToAssign, vehicleTraining['no_training']); } await assignPersonsWithoutTraining(numberOfPersonnelToAssign); } } } async function assignPersonsWithoutTraining(amount) { const rowsWithoutTraining = document.querySelectorAll('tbody tr[data-filterable-by="[]"]:has(a.btn-success):not(:has(span[data-education-key]))'); const sortedRowsWithoutTraining = sortRows(rowsWithoutTraining); for (let i = 0, n = sortedRowsWithoutTraining.length; i < n; i++) { if (await changeAssignment(sortedRowsWithoutTraining[i].querySelector('a.btn-success'))) { amount--; if (amount === 0) { break; } await new Promise(r => setTimeout(r, 5)); } } } async function changeAssignment(button) { if (button) { const personalId = button.getAttribute('personal_id'); const personalElement = document.getElementById(`personal_${personalId}`); personalElement.innerHTML = `<td colspan="4">${loadingText}</td>`; try { const response = await fetch(button.href, { method: 'POST', headers: { 'content-type': 'application/x-www-form-urlencoded; charset=UTF-8', 'x-csrf-token': csrfToken, 'x-requested-with': 'XMLHttpRequest', }, }); if (!response.ok) { return false; } personalElement.innerHTML = await response.text(); updatePersonalCount(); return true; } catch (e) { return false; } } return false } function updatePersonalCount() { const counterElement = document.getElementById('count_personal'); if (!counterElement) { return; } const counter = document.querySelectorAll('.btn-assigned').length; const vehicleCapacity = parseInt(counterElement.parentElement.firstElementChild.innerText); counterElement.innerText = String(counter); if (counter !== vehicleCapacity) { counterElement.classList.remove('label-success'); counterElement.classList.add('label-warning'); } else { counterElement.classList.remove('label-warning'); counterElement.classList.add('label-success'); } } function sortRows(rows) { const vehicleId = getVehicleId(); return Array.from(rows) .sort((a, b) => { const aInVehicle = a.querySelector('td:nth-child(3) a')?.href?.endsWith('/' + vehicleId); const bInVehicle = b.querySelector('td:nth-child(3) a')?.href?.endsWith('/' + vehicleId); if ((aInVehicle === true && !bInVehicle) || (aInVehicle === undefined && bInVehicle === false)) { return -1; } else if (aInVehicle === bInVehicle) { return 0; } else { return 1; } }); } async function reset() { const selectButtons = document.getElementsByClassName('btn btn-default btn-assigned'); // Since the click event removes the button from the DOM, only every second item would be clicked. // To prevent this, the loop is executed backwards. for (let i = selectButtons.length - 1; i >= 0; i--) { await changeAssignment(selectButtons[i]); await new Promise(r => setTimeout(r, 5)); } } function assignClickEvent(event) { assign(); event.preventDefault(); } function resetClickEvent(event) { reset(); event.preventDefault(); } function getAssignedPersonsElement() { return document.getElementById("count_personal"); } function addButtonGroup() { const okIcon = document.createElement("span"); okIcon.className = "glyphicon glyphicon-ok"; const assignButton = document.createElement("button"); assignButton.type = "button"; assignButton.className = "btn btn-default"; assignButton.appendChild(okIcon); assignButton.addEventListener("click", assignClickEvent); const resetIcon = document.createElement("span"); resetIcon.className = "glyphicon glyphicon-trash"; const resetButton = document.createElement("button"); resetButton.type = "button"; resetButton.className = "btn btn-default"; resetButton.appendChild(resetIcon); resetButton.addEventListener("click", resetClickEvent); const buttonGroup = document.createElement("div"); buttonGroup.id = "vehicle-assigner-button-group"; buttonGroup.className = "btn-group"; buttonGroup.style.marginLeft = "5px"; buttonGroup.appendChild(assignButton); buttonGroup.appendChild(resetButton); // Append button group to element with class "vehicles-education-filter-box" document.getElementsByClassName("vehicles-education-filter-box")[0].appendChild(buttonGroup); } function getVehicleId() { return window.location.pathname.split("/")[2]; } /** * @return {number|null} */ function getVehicleTypeId() { const vehicleId = getVehicleId(); const request = new XMLHttpRequest(); request.open("GET", `/api/v2/vehicles/${vehicleId}`, false); request.send(); if (request.status === 200) { const vehicle = JSON.parse(request.responseText); return vehicle.result.vehicle_type; } return null; } /** * @return {{}|null} */ function getIdentifierByVehicleTypeId(vehicleTypeId) { switch (vehicleTypeId) { case 0: //LF 20 return {'no_training': true}; case 1: //LF 10 return {'no_training': true}; case 2: //DLK 23 return {'no_training': true}; case 3: //ELW 1 return {'no_training': true}; case 4: //RW return {'no_training': true}; case 5: //GW-A return {'no_training': true}; case 6: //LF 8/6 return {'no_training': true}; case 7: //LF 20/16 return {'no_training': true}; case 8: //LF 10/6 return {'no_training': true}; case 9: //LF 16-TS return {'no_training': true}; case 10: //GW-Öl return {'no_training': true}; case 11: //GW-L2-Wasser return {'no_training': true}; case 12: //GW-Messtechnik return {'gw_messtechnik': true}; case 13: //SW 1000 return {'no_training': true}; case 14: //SW 2000 return {'no_training': true}; case 15: //SW 2000-Tr return {'no_training': true}; case 16: //SW Kats return {'no_training': true}; case 17: //TLF 2000 return {'no_training': true}; case 18: //TLF 3000 return {'no_training': true}; case 19: //TLF 8/8 return {'no_training': true}; case 20: //TLF 8/18 return {'no_training': true}; case 21: //TLF 16/24-Tr return {'no_training': true}; case 22: //TLF 16/25 return {'no_training': true}; case 23: //TLF 16/45 return {'no_training': true}; case 24: //TLF 20/40 return {'no_training': true}; case 25: //TLF 20/40-SL return {'no_training': true}; case 26: //TLF 16 return {'no_training': true}; case 27: //GW-Gefahrgut return {'gw_gefahrgut': true}; case 28: //RTW return {'no_training': true}; case 29: //NEF return {'notarzt': true}; case 30: //HLF 20 return {'no_training': true}; case 31: //RTH return {'notarzt': true}; case 32: //FuStW return {'no_training': true}; case 33: //GW-Höhenrettung return {'gw_hoehenrettung': true}; case 34: //ELW 2 return {'elw2': true}; case 35: //leBefKw return {'police_einsatzleiter': true}; case 36: //MTW return {'no_training': true}; case 37: //TSF-W return {'no_training': true}; case 38: //KTW return {'no_training': true}; case 39: //GKW return {'no_training': true}; case 40: //MTW-TZ return {'thw_zugtrupp': true}; case 41: //MzKW return {'no_training': true}; case 42: //LKW K 9 return {'thw_raumen': true}; case 45: //MLW 5 return {'thw_raumen': true}; case 46: //WLF return {'wechsellader': true}; case 50: //GruKw return {'no_training': true}; case 51: //FüKw return {'police_fukw': true}; case 52: //GefKw return {'no_training': true}; case 53: //Dekon-P return {'dekon_p': true}; case 55: //KdoW-LNA return {'lna': true}; case 56: //KdoW-OrgL return {'orgl': true}; case 57: //FwK return {'fwk': true}; case 58: //KTW Typ B return {'no_training': true}; case 59: //ELW 1 (SEG) return {'seg_elw': true}; case 60: //GW-San return {'seg_gw_san': true}; case 61: //Polizeihubschrauber return {'polizeihubschrauber': true}; case 63: //GW-Taucher return {'gw_taucher': true}; case 64: //GW-Wasserrettung return {'gw_wasserrettung': true}; case 65: //LKW 7 Lkr 19 tm return {'no_training': true}; case 69: //Tauchkraftwagen return {'gw_taucher': true}; case 72: //WaWe 10 return {'police_wasserwerfer': true}; case 73: //GRTW return {'notarzt': 1, 'no_training': 5}; case 74: //NAW return {'notarzt': 1, 'no_training': 2}; case 75: //FLF return {'arff': true}; case 76: //Rettungstreppe return {'rettungstreppe': true}; case 79: //SEK - ZF return {'police_sek': true}; case 80: //SEK - MTF return {'police_sek': true}; case 81: //MEK - ZF return {'police_mek': true}; case 82: //MEK - MTF return {'police_mek': true}; case 83: //GW-Werkfeuerwehr return {'werkfeuerwehr': true}; case 84: //ULF mit Löscharm return {'werkfeuerwehr': true}; case 85: //TM 50 return {'werkfeuerwehr': true}; case 86: //Turbolöscher return {'werkfeuerwehr': true}; case 87: //TLF 4000 return {'no_training': true}; case 88: //KLF return {'no_training': true}; case 89: //MLF return {'no_training': true}; case 90: //HLF 10 return {'no_training': true}; case 91: //Rettungshundefahrzeug return {'seg_rescue_dogs': true}; case 93: //MTW-O return {'thw_rescue_dogs': true}; case 94: //DHuFüKw return {'k9': true}; case 95: //Polizeimotorrad return {'police_motorcycle': true}; case 97: //ITW return {'intensive_care': 2, 'notarzt': 1}; case 98: //Zivilstreifenwagen return {'criminal_investigation': true}; case 99: //LKW 7 Lbw return {'water_damage_pump': true}; case 100: //MLW 4 return {'water_damage_pump': true}; case 103: //FuStW (DGL) return {'police_service_group_leader': true}; case 104: //GW-L1 return {'no_training': true}; case 105: //GW-L2 return {'no_training': true}; case 106: //MTF-L return {'no_training': true}; case 107: //LF-L return {'no_training': true}; case 109: //MzGW SB return {'heavy_rescue': true}; case 114: //GW-Lüfter return {'no_training': true}; case 121: //GTLF return {'no_training': true}; case 122: //LKW 7 Lbw (FGr E) return {'thw_energy_supply': true}; case 123: //LKW 7 Lbw (FGr WP) return {'water_damage_pump': true}; case 124: //MTW-OV return {'no_training': true}; case 125: //MTW-Tr UL return {'thw_drone': true}; case 126: //MTF Drohne return {'fire_drone': true}; case 127: //GW UAS return {'seg_drone': true}; case 128: //ELW Drohne return {'fire_drone': true}; case 129: //ELW2 Drohne return {'fire_drone': true, 'elw2': true}; case 130: //GW-Bt return {'care_service': 1, 'care_service_equipment': 2}; case 131: //Bt-Kombi return {'care_service': true}; case 133: //Bt LKW return {'care_service': 1, 'care_service_equipment': 2}; case 134: //Pferdetransporter klein return {'police_horse': true}; case 135: //Pferdetransporter groß return {'police_horse': true}; case 137: //Zugfahrzeug Pferdetransport return {'police_horse': true}; case 140: //MTW-Verpflegung return {"fire_care_service": true}; case 144: //FüKw (THW) return {"thw_command": true}; case 145: //FüKomKW return {"thw_command": true}; case 147: //FmKW return {"thw_command": true}; case 148: //MTW Fgr K return {"thw_command": true}; default: return null; } } })();