您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
imports match data from compatible exports
当前为
// ==UserScript== // @name match data importer // @namespace http://tampermonkey.net/ // @version 1.2 // @description imports match data from compatible exports // @author Hendrik Steinmetz // @match https://homecourt.vcc.heimspiel.de/01_admin/spiele/* // @match https://homecourt.vcc.heimspiel.de/01_admin/spiele/spiel_aktionen.php // @match file:///C:/Users/hendr/Desktop/test.html // @icon https://www.google.com/s2/favicons?sz=64&domain=heimspiel.de // @grant GM_addStyle // @license GPLv3 // ==/UserScript== let btn = document.createElement("button"); btn.id = "floatingButton"; btn.textContent = "Import"; btn.onclick = () => { document.querySelector("#customPopup").style.display = "block"; }; function triggerImport() { document.querySelector("#loadingSpinner").style.display = "block"; const json = document.getElementById("popup-body").value; if (json) { const report = JSON.parse(json); if (!report) { document.querySelector("#loading-spinner").style.display = "none"; } const homeTable = document.querySelectorAll("table")[0]; const awayTable = document.querySelectorAll("table")[1]; fillTable( homeTable, report.players?.filter((p) => p.home) ?? [], report.events.substitutions?.filter((sub) => sub.home) ?? [], report.events.cards?.filter((card) => card.home) ?? [], ); fillTable( awayTable, report.players?.filter((p) => !p.home) ?? [], report.events.substitutions?.filter((sub) => !sub.home) ?? [], report.events.cards?.filter((card) => !card.home) ?? [], ); } document.querySelector("#loadingSpinner").style.display = "none"; } /** * @param table {HTMLTableElement} */ function fillTable(table, playerData, subData, cardData) { const rowArray = Array.from(table.rows); for (let i = 3; i < table.rows.length - 1; i++) { const currentRow = rowArray[i]; const cells = Array.from(currentRow.cells); const numberCell = cells[0]; const fullNameCell = cells[2]; const startingCell = cells[4]; const benchCell = cells[6]; const subOnCell = cells[8]; const subOnMinuteCell = cells[9]; const subOnAddedCell = cells[10]; const subOffCell = cells[12]; const subOffMinuteCell = cells[13]; const subOffAddedCell = cells[14]; const yellowCell = cells[16]; const yellowMinuteCell = cells[17]; const yellowAddedCell = cells[18]; const yellowRedCell = cells[20]; const yellowRedMinuteCell = cells[21]; const yellowRedAddedCell = cells[22]; const redCell = cells[24]; const redMinuteCell = cells[25]; const redAddedCell = cells[26]; const checkCell = (cell) => (cell.querySelector("input").checked = true); const uncheckCell = (cell) => (cell.querySelector("input").checked = false); const setCellValue = (cell, val) => (cell.querySelector("input").value = val); const numberInput = numberCell.querySelector("input"); if (!numberInput) { console.log("Number input null for", currentRow); continue; } const player = findPlayer( playerData, numberInput.value, fullNameCell.innerText ?? "", ); if (!player) { uncheckCell(startingCell); uncheckCell(benchCell); continue; } let playerId = player.id; if (!player.sub) { checkCell(startingCell); uncheckCell(benchCell); } else if (player.sub) { uncheckCell(startingCell); checkCell(benchCell); } if (playerWasSubbedOn(playerId, subData)) { const substitution = subData.find((sub) => sub.on === playerId); checkCell(subOnCell); setCellValue(subOnMinuteCell, substitution.minute); setCellValue(subOnAddedCell, substitution.added ?? ""); } if (playerWasSubbedOff(playerId, subData)) { const substitution = subData.find((sub) => sub.off === playerId); checkCell(subOffCell); setCellValue(subOffMinuteCell, substitution.minute); setCellValue(subOffAddedCell, substitution.added ?? ""); } const cardEvents = cardData.filter((p) => p.id === playerId); if (cardEvents.length > 0) { cardEvents.forEach((event) => { if (event.card === "yellow") { checkCell(yellowCell); setCellValue(yellowMinuteCell, event.minute); setCellValue(yellowAddedCell, event.added ?? ""); } else if (event.card === "yellow-red") { checkCell(yellowRedCell); setCellValue(yellowRedMinuteCell, event.minute); setCellValue(yellowRedAddedCell, event.added ?? ""); } else if (event.card === "red") { checkCell(redCell); setCellValue(redMinuteCell, event.minute); setCellValue(redAddedCell, event.added ?? ""); } }); } //playerData = playerData.filter((p) => p.id !== playerId); } } /** * Finds players based on number and name: * - if no player with number is found: name is used * - if multiple with same number: use most similar name */ // TODO clean up and make more efficient // ONLY ONE similarity computation per filter // -> add as key and sort by // // one list filtered by number and one by name // if only one in number list: return if sim > 0.5 // else if many in number list: sort by sim return first if sim > 0.5 // else if no number or none in number list: return first of name search if sim > 0.5 // return null function findPlayer(players, number, nameFull) { const playersByNumber = players.filter( (player) => parseInt(player.number) === parseInt(number), ); nameFull = nameFull.toLowerCase(); const firstName = nameFull.split(" ")[0]; const similiarityThreshold = 0.6; if (playersByNumber.length === 1 || !number) { if ( number && similarity( playersByNumber[0].name, //playersByNumber[0].name.split(" ")[0].toLowerCase(), nameFull, ) > similiarityThreshold ) { return playersByNumber[0]; } const playersByName = players.filter((p) => similarity(p.name.toLowerCase(), nameFull), ); if (playersByName.length === 0) return null; playersByName.sort( (a, b) => similarity(b.name.toLowerCase(), nameFull) - similarity(a.name.toLowerCase(), nameFull), ); const firstScore = similarity( playersByName[0].name.toLowerCase(), nameFull, ); if (firstScore > similiarityThreshold) { return playersByName[0]; } } if (playersByNumber.length > 1) { playersByNumber.sort( (a, b) => similarity(b.name.toLowerCase(), nameFull) - similarity(a.name.toLowerCase(), nameFull), ); const firstScore = similarity( playersByNumber[0].name.toLowerCase(), nameFull, ); if (firstScore > similiarityThreshold) { return playersByNumber[0]; } } if (playersByNumber.length === 0) { console.log(players, number, nameFull); const nameSearch = players.filter( (p) => similarity(p.name.toLowerCase(), nameFull) > similiarityThreshold, ); nameSearch.sort( (a, b) => similarity(b.name.toLowerCase(), nameFull) - similarity(a.name.toLowerCase(), nameFull), ); console.log("similarity search result", nameSearch); if (nameSearch.length === 0) { return null; } const firstScore = similarity(nameSearch[0].name.toLowerCase(), nameFull); return firstScore > similiarityThreshold ? nameSearch[0] : null; } return null; } function playerWasSubbedOn(playerId, subData) { return subData.filter((sub) => sub.on === playerId).length > 0; } function playerWasSubbedOff(playerId, subData) { return subData.filter((sub) => sub.off === playerId).length > 0; } let popup = document.createElement("div"); popup.id = "customPopup"; popup.innerHTML = ` <div class="popup-content"> <h2 id="popup-title">Import</h2> <textarea id="popup-body"></textarea> <div id="loadingSpinner" class="spinner"></div> <button id="closePopup">Close</button> <button id="importBtn">Start import</button> </div> `; document.addEventListener("click", function (event) { if (event.target.id === "closePopup") { document.getElementById("customPopup").style.display = "none"; } if (event.target.id === "importBtn") { triggerImport(); } }); GM_addStyle(` #floatingButton { position: fixed; bottom: 20px; right: 20px; padding: 10px 20px; background-color: #007BFF; color: white; font-size: 16px; font-weight: bold; border: none; border-radius: 10px; box-shadow: 2px 2px 10px rgba(0, 0, 0, 0.2); cursor: pointer; z-index: 9999; transition: background-color 0.3s ease, transform 0.2s ease; } #floatingButton:hover { background-color: #0056b3; transform: scale(1.1); } #customPopup { display: none; position: fixed; top: 50%; left: 50%; transform: translate(-50%, -50%); background: white; padding: 20px; width: 50%; box-shadow: 0 4px 8px rgba(0, 0, 0, 0.2); border-radius: 10px; z-index: 10000; } #popup-title { text-align: center; } #popup-body { border: 1px solid #ccc; width: 100%; height: 120px; padding: 5px; font-size: 14px; border: none; resize: none; background: #f8f8f8; outline: none; } #closePopup { margin-top: 10px; padding: 8px 16px; background-color: #dc3545; color: white; border: none; border-radius: 5px; cursor: pointer; } #closePopup:hover { background-color: #c82333; } #importBtn { margin-top: 10px; margin-left: 10px; padding: 8px 16px; background-color: #007BFF; color: white; border: none; border-radius: 5px; cursor: pointer; } #importBtn:hover { background-color: #0056b3; } .spinner { display: none; margin: 10px auto; width: 40px; height: 40px; border: 4px solid rgba(0, 0, 0, 0.2); border-top: 4px solid #007BFF; border-radius: 50%; animation: spin 1s linear infinite; } @keyframes spin { 0% { transform: rotate(0deg); } 100% { transform: rotate(360deg); } } `); document.body.appendChild(btn); document.body.appendChild(popup); function similarity(s1, s2) { var longer = s1; var shorter = s2; if (s1.length < s2.length) { longer = s2; shorter = s1; } var longerLength = longer.length; if (longerLength == 0) { return 1.0; } return ( (longerLength - editDistance(longer, shorter)) / parseFloat(longerLength) ); } function editDistance(s1, s2) { s1 = s1.toLowerCase(); s2 = s2.toLowerCase(); var costs = new Array(); for (var i = 0; i <= s1.length; i++) { var lastValue = i; for (var j = 0; j <= s2.length; j++) { if (i == 0) costs[j] = j; else { if (j > 0) { var newValue = costs[j - 1]; if (s1.charAt(i - 1) != s2.charAt(j - 1)) newValue = Math.min(Math.min(newValue, lastValue), costs[j]) + 1; costs[j - 1] = lastValue; lastValue = newValue; } } } if (i > 0) costs[s2.length] = lastValue; } return costs[s2.length]; }
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址