TMGeneralTranslation

通过翻译服务器对游戏内球员名字进行汉化

  1. // ==UserScript==
  2. // @name TMGeneralTranslation
  3. // @namespace https://trophymanager.com/
  4. // @version 1.0.20
  5. // @author 提瓦特元素反应(https://trophymanager.com/club/4731723/)
  6. // @description 通过翻译服务器对游戏内球员名字进行汉化
  7. // @license MIT
  8. // @match https://trophymanager.com/*
  9. // @grant GM_xmlhttpRequest
  10. // @run-at document-end
  11. // ==/UserScript==
  12.  
  13. var VERSION = "1.0.20"
  14. var DEBUG_MODE = true;
  15. var BACK_END = "https://trans.tm.kit.ga";
  16.  
  17. //let LOCAL = []; //空表示全部翻译
  18. let WISH_LOCAL = ['cn', 'hk', 'jp', 'tw'];
  19. let LOCAL = JSON.parse(localStorage.getItem('LOCAL')) || WISH_LOCAL;
  20. var DEFAULT_COUNTRY = getCountry(document.querySelector('.top_user_info .clubs_block img').getAttribute('src'));
  21.  
  22. function measureAjaxLoadTime(url, callback) {
  23. const startTime = performance.now(); // 请求开始的时间
  24.  
  25. GM_xmlhttpRequest({
  26. method: 'GET',
  27. url: BACK_END,
  28. onload: function(response) {
  29. const endTime = performance.now(); // 请求完成的时间
  30. let roundedLoadTime = endTime - startTime; // 计算加载时间
  31. roundedLoadTime = Math.round(roundedLoadTime * 100) / 100;
  32. callback(roundedLoadTime);
  33. },
  34. onerror: function(error) {
  35. console.error('请求出错:', error);
  36. }
  37. });
  38. }
  39.  
  40. (function() {
  41. 'use strict';
  42. const newParagraph = document.createElement('a');
  43. const switchButton = document.createElement('a');
  44. const bodyEndElement = document.querySelector('.body_end');
  45. if (bodyEndElement && bodyEndElement.firstChild) {
  46. measureAjaxLoadTime(BACK_END, function(roundedLoadTime){
  47. newParagraph.textContent = 'TMGT已启动,有翻译错误/不满意/BUG/请联系作者 翻译服务器连接耗时:'+ roundedLoadTime + "ms";
  48. switchButton.textContent = `调整翻译国/地区籍 (Current: ${LOCAL.length === 0 ? "ALL" : LOCAL})`;
  49. switchButton.href = '#';
  50. switchButton.addEventListener('click', function(event) {
  51. event.preventDefault(); // 阻止默认链接行为
  52.  
  53. // 切换 LOCAL 的值
  54. if (LOCAL.length === 0) {
  55. LOCAL = WISH_LOCAL;
  56. } else {
  57. LOCAL = [];
  58. }
  59.  
  60. // 将新值保存到 localStorage
  61. localStorage.setItem('LOCAL', JSON.stringify(LOCAL));
  62.  
  63. // 重新加载
  64. location.reload();
  65. });
  66. newParagraph.href = 'https://qm.qq.com/q/QIo7VakoYW'
  67. document.querySelector('.body_end').insertBefore(newParagraph, document.querySelector('.body_end').firstChild);
  68. document.querySelector('.body_end').insertBefore(document.createElement('br'), document.querySelector('.body_end').firstChild);
  69. document.querySelector('.body_end').insertBefore(switchButton, document.querySelector('.body_end').firstChild);
  70. })
  71. }
  72. })();
  73.  
  74. function getCountry(str){
  75. if(str.includes('flag of') && str.split(' ').length >= 3){
  76. return str.split(' ')[2];
  77. }
  78. if(str.includes('/pics/flags/gradient/') && str.split('/').length >= 5){
  79. return str.split('/')[4].split('.')[0];
  80. }
  81. if(str.includes('/national-teams/') && str.split('/').length >= 4){
  82. return str.split('/')[2];
  83. }
  84. }
  85.  
  86. // 请求翻译
  87. function translateNumbers(players, callback, update) {
  88. players = players.filter(item => item.ID !== null && item.English !== null && item.English !== undefined && !item.English.includes('@'));
  89.  
  90. let copyPlayers = [];
  91.  
  92. for(let i = 0; i < players.length; i++){
  93. let player = {}
  94. if(filterCosName(players[i].English) !== ''){
  95. player.English = filterName(players[i].English);
  96. } else {
  97. player.English = players[i].English;
  98. }
  99. player.ID = players[i].ID
  100. player.Country = players[i].Country
  101. copyPlayers.push(player);
  102. }
  103. // 将输入的整数数组转换为JSON字符串
  104.  
  105. const jsonData = JSON.stringify({"version": VERSION, "data": copyPlayers, "update":update, "local": LOCAL});
  106. console.log(jsonData);
  107.  
  108. // console.log(jsonData);
  109. // 发送 POST 请求
  110. GM_xmlhttpRequest({
  111. method: "POST",
  112. url: BACK_END + "/translate",
  113. data: jsonData,
  114. headers: {
  115. "Content-Type": "application/json"
  116. },
  117. onload: function(response) {
  118. // 解析返回的 JSON 数据
  119. const responseData = JSON.parse(response.responseText);
  120. console.log(responseData);
  121.  
  122. // 获取翻译结果数组
  123. callback(responseData);
  124. }
  125. });
  126. }
  127.  
  128. // 请求上传
  129. function updateTranslation(player){
  130. let jsonData = JSON.stringify(player);
  131. // console.log(jsonData);
  132. GM_xmlhttpRequest({
  133. method: "POST",
  134. url: BACK_END + "/update-player",
  135. data: jsonData,
  136. headers: {
  137. "Content-Type": "application/json"
  138. },
  139. onload: function(response) {
  140. console.log("Response received: ", response.responseText);
  141. alert("更新完成:" + response.responseText);
  142. location.reload();
  143. },
  144. onerror: function(error) {
  145. console.error("Error occurred: ", error);
  146. }
  147. });
  148. }
  149.  
  150. function filterName(name){
  151. if(name.split('\'').length === 3){
  152. return name.split('\'')[0] + name.split('\'')[2];
  153. }
  154. return name;
  155. }
  156.  
  157. function filterCosName(name){
  158. if(name.split('\'').length === 3){
  159. return name.split('\'')[1];
  160. }
  161. return '';
  162. }
  163.  
  164. // 球员详情
  165. (function() {
  166. 'use strict';
  167.  
  168. if (window.location.pathname === '/players' || window.location.pathname === '/players/' || !window.location.pathname.includes('/players/')) {
  169. return;
  170. }
  171. var player = {};
  172.  
  173. // 检查国籍
  174. player.Country = getCountry(document.querySelector('.box_sub_header .country_link ib').getAttribute('alt'));
  175.  
  176. // 查找包含 id 的元素
  177. const element = document.querySelector('#change_player_link');
  178.  
  179. // 遍历每个元素并提取 id
  180. if(element.getAttribute('onclick').split(',').length === 3 && element.getAttribute('onclick').split(',')[0].split('(').length === 2){
  181. player.ID = parseInt(element.getAttribute('onclick').split(',')[0].split('(')[1]);
  182. player.English = document.querySelector("div.large").querySelector("strong").textContent.split(". ")[1];
  183. } else {
  184. return;
  185. }
  186.  
  187. // 找到具有 'large' 类的 <div>,然后选择第一个 <strong> 元素
  188. var largeDiv = document.querySelector("div.large");
  189. if (largeDiv) {
  190. var strongElement = largeDiv.querySelector("strong");
  191.  
  192. if (strongElement) {
  193. // 修改 <strong> 的文本内容
  194. // 调用函数并传入整数数组作为参数
  195. translateNumbers([player], function(translations) {
  196. if(filterCosName(player.English) !== ''){
  197. strongElement.textContent = '\'' + filterCosName(player.English) + '\' '+ translations[0].Chinese;
  198. } else {
  199. strongElement.textContent = document.querySelector("div.large").querySelector("strong").textContent.split(". ")[0] + ". " + translations[0].Chinese;
  200. }
  201. }, true);
  202. }
  203. }
  204. })();
  205.  
  206. // 战术-列表
  207. (function() {
  208. 'use strict';
  209.  
  210. if (!(window.location.pathname.includes('/tactics'))) {
  211. return;
  212. }
  213. let translationsResult = '';
  214.  
  215. function modifyNamesAndExtractIdsForNames() {
  216. var players = [];
  217. document.querySelectorAll("span.player_name").forEach(element => {
  218. var player = {};
  219.  
  220. // 检查国籍
  221. let parent = element.parentElement;
  222. if(parent.querySelectorAll('ib').length !== 0){
  223. player.Country = getCountry(parent.querySelector('ib').getAttribute('alt'));
  224. } else {
  225. player.Country = DEFAULT_COUNTRY;
  226. }
  227.  
  228. player.English = element.textContent;
  229. player.ID = parseInt(element.getAttribute("player_id"));
  230. players.push(player);
  231. });
  232.  
  233. document.querySelectorAll('.field_player_name').forEach(element => {
  234. var player = {};
  235. if (element.firstChild && element.firstChild.nodeType === Node.TEXT_NODE) {
  236. player.English = element.firstChild.nodeValue;
  237. }
  238.  
  239. // 检查国籍
  240. let parent = element.parentElement;
  241. if(parent.querySelectorAll('ib').length !== 0){
  242. player.Country = getCountry(parent.querySelector('ib').getAttribute('alt'));
  243. } else {
  244. player.Country = DEFAULT_COUNTRY;
  245. }
  246.  
  247. const parentDiv = element.closest('.field_player');
  248. if (parentDiv) {
  249. player.ID = parseInt(parentDiv.getAttribute('player_id'));
  250. }
  251. players.push(player);
  252. });
  253.  
  254. document.querySelectorAll('.bench_player_name').forEach(element => {
  255. var player = {};
  256. if (element.firstChild && element.firstChild.nodeType === Node.TEXT_NODE) {
  257. player.English = element.firstChild.nodeValue;
  258. }
  259.  
  260. // 检查国籍
  261. let parent = element.parentElement;
  262. if(parent.querySelectorAll('ib').length !== 0){
  263. player.Country = getCountry(parent.querySelector('ib').getAttribute('alt'));
  264. } else {
  265. player.Country = DEFAULT_COUNTRY;
  266. }
  267.  
  268. const parentLi = element.closest('li.bench_player');
  269. if (parentLi) {
  270. player.ID = parseInt(parentLi.getAttribute('player_id'));
  271. }
  272. players.push(player);
  273. });
  274.  
  275. function startTrans(translations){
  276. document.querySelectorAll("span.player_name").forEach(element => {
  277. translations.forEach(function(translation) {
  278. if (translation.ID === parseInt(element.getAttribute("player_id"))) {
  279. element.textContent = translation.Chinese;
  280. }
  281. })
  282. });
  283. document.querySelectorAll('.field_player_name').forEach(element => {
  284. const parentDiv = element.closest('.field_player');
  285. if (parentDiv) {
  286. translations.forEach(function(translation) {
  287. if (translation.ID === parseInt(parentDiv.getAttribute('player_id'))) {
  288. if (element.firstChild && element.firstChild.nodeType === Node.TEXT_NODE) {
  289. element.firstChild.nodeValue = translation.Chinese;
  290. }
  291. }
  292. })
  293. }
  294. });
  295. document.querySelectorAll('.bench_player_name').forEach(element => {
  296. const parentLi = element.closest('li.bench_player');
  297. if (parentLi) {
  298. translations.forEach(function(translation) {
  299. if (translation.ID === parseInt(parentLi.getAttribute('player_id'))) {
  300. if (element.firstChild && element.firstChild.nodeType === Node.TEXT_NODE) {
  301. element.firstChild.nodeValue = translation.Chinese;
  302. }
  303. }
  304. })
  305. }
  306. });
  307. document.querySelectorAll('.cond_order div[player_link]').forEach(element => {
  308. translations.forEach(function(translation) {
  309. if (translation.ID === parseInt(element.getAttribute('player_link'))) {
  310. element.textContent = translation.Chinese;
  311. }
  312. })
  313. });
  314. }
  315.  
  316. if(translationsResult !== ''){
  317. startTrans(translationsResult);
  318. } else {
  319. translateNumbers(players, function(translations) {
  320. translationsResult = translations;
  321. startTrans(translationsResult);
  322. }, true);
  323. }
  324. }
  325.  
  326. function modifyNamesForSubstitute() {
  327. let translations = translationsResult;
  328. document.querySelectorAll('.parm_select.player_select').forEach(element => {
  329. translations.forEach(function(translation) {
  330. if (translation.ID === parseInt(element.getAttribute('player_link'))) {
  331. element.firstChild.nodeValue = translation.Chinese; // 修改第一个文本节点内容
  332. }
  333. })
  334. });
  335. }
  336.  
  337. // 监控特定元素并应用回调函数
  338. function bindObserver(selector, callback) {
  339. const elements = document.querySelectorAll(selector);
  340. elements.forEach(element => {
  341. // 创建一个 MutationObserver 实例并应用给定的回调
  342. const observer = new MutationObserver(callback);
  343. observer.observe(element, {
  344. childList: true
  345. });
  346. });
  347. }
  348.  
  349. // 处理 DOM 变化的函数
  350. const handleMutations = mutations => {
  351. mutations.forEach(mutation => {
  352. // 检查新增的节点
  353. mutation.addedNodes.forEach(node => {
  354. if (node.id === 'popup_action' || node.matches && node.matches('#popup_action')) {
  355. modifyNamesForSubstitute();
  356. }
  357. });
  358. });
  359. };
  360.  
  361. bindObserver('#tactics_list_list', modifyNamesAndExtractIdsForNames);
  362. bindObserver('#cond_orders_list', modifyNamesAndExtractIdsForNames);
  363. // 创建全局观察者来监控元素的删除和重建
  364. const globalObserver = new MutationObserver(handleMutations);
  365. globalObserver.observe(document.body, {
  366. childList: true,
  367. subtree: true
  368. });
  369. })();
  370.  
  371.  
  372. // 其他球队 && 工资 && 联赛最佳/统计
  373. (function() {
  374. 'use strict';
  375.  
  376. if (!(
  377. window.location.pathname === '/finances/wages' || window.location.pathname === '/finances/wages/' ||
  378. window.location.pathname.includes('/league/team-of-the-round') ||
  379. window.location.pathname.includes('/statistics/league') ||
  380. window.location.pathname.includes('/club/') ||
  381. window.location.pathname.includes('/national-teams/')
  382. )) {
  383. return;
  384. }
  385. var players = [];
  386. var links = [];
  387.  
  388. let canUpdate = (!window.location.pathname.includes('/club/') && !window.location.pathname.includes('/league/team-of-the-round') && !window.location.pathname.includes('/national-teams/')) || (window.location.pathname.includes('/club/') && (window.location.pathname.includes('squad/') || window.location.pathname.includes('statistics/')));
  389. let defaultCountry = (window.location.pathname.includes('/club/')) ? getCountry(document.querySelector(".box_sub_header a.country_link").getAttribute("href")) : DEFAULT_COUNTRY;
  390. defaultCountry = window.location.pathname.includes('/national-teams/') ? getCountry(document.querySelector(".box_sub_header ib").getAttribute("alt")) : defaultCountry;
  391.  
  392. document.querySelectorAll("a[player_link]").forEach(function(element) {
  393. var link = element;
  394. var player = {};
  395.  
  396. // 检查国籍
  397. let parent = element.parentElement;
  398. player.Country = defaultCountry;
  399. parent.querySelectorAll(`img[src]`).forEach(function(img){
  400. if(img.getAttribute("src").includes('/pics/flags/gradient/')){
  401. player.Country = getCountry(img.getAttribute("src"));
  402. }
  403. if(img.getAttribute("src").includes('/pics/flags/gradient/')){
  404. player.Country = getCountry(img.getAttribute("src"));
  405. }
  406. })
  407. parent.querySelectorAll(`a[href]`).forEach(function(a){
  408. if(a.getAttribute("href").includes('/national-teams/')){
  409. player.Country = getCountry(a.getAttribute("href"));
  410. }
  411. if(a.getAttribute("href").includes('/national-teams/')){
  412. player.Country = getCountry(a.getAttribute("href"));
  413. }
  414. })
  415. if (link) {
  416. player.English = link.textContent;
  417. player.ID = parseInt(link.getAttribute("player_link"));
  418. players.push(player)
  419. links.push(link);
  420. }
  421. });
  422.  
  423. translateNumbers(players, function(translations) {
  424. links.forEach(function(link) {
  425. translations.forEach(function(translation) {
  426. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  427. if(filterCosName(link.textContent) !== ''){
  428. link.textContent = '\'' + filterCosName(link.textContent) + '\' '+ translation.Chinese;
  429. } else {
  430. link.textContent = translation.Chinese;
  431. }
  432. }
  433. })
  434. });
  435. }, canUpdate);
  436. })();
  437.  
  438. function createTooltip(message) {
  439. const tooltip = document.createElement('div');
  440. tooltip.className = 'custom-tooltip';
  441. tooltip.textContent = message;
  442. tooltip.style.position = 'absolute';
  443. tooltip.style.backgroundColor = '#333';
  444. tooltip.style.color = '#fff';
  445. tooltip.style.padding = '5px';
  446. tooltip.style.borderRadius = '3px';
  447. tooltip.style.zIndex = '1000';
  448. tooltip.style.visibility = 'hidden';
  449. document.body.appendChild(tooltip);
  450. return tooltip;
  451. }
  452.  
  453. function addHoverTooltip(element, message) {
  454. const tooltip = createTooltip(message);
  455.  
  456. element.onmouseover = function(event) {
  457. tooltip.style.left = event.pageX + 'px';
  458. tooltip.style.top = event.pageY + 'px';
  459. tooltip.style.visibility = 'visible';
  460. };
  461.  
  462. element.onmouseout = function() {
  463. tooltip.style.visibility = 'hidden';
  464. };
  465. }
  466.  
  467. function createPopup(player_id) {
  468. const popup = document.createElement('div');
  469. popup.style.border = '2px solid #000000';
  470. popup.setAttribute('tabindex', '-1');
  471. popup.style.position = 'absolute';
  472. popup.style.display = 'none';
  473. popup.style.backgroundColor = '#41631F';
  474.  
  475. const inputTitle = document.createElement('a');
  476. inputTitle.textContent = '修改翻译:';
  477. inputTitle.style.textDecoration = 'none'; // 禁用默认下划线
  478. inputTitle.onmouseover = () => {inputTitle.style.textDecoration = 'none'};
  479. inputTitle.onmouseleave = () => {inputTitle.style.textDecoration = 'none'};
  480.  
  481. addHoverTooltip(inputTitle, '请确保你的翻译没有违背球员姓名的意思,禁止绰号/别名');
  482.  
  483. const input = document.createElement('input');
  484. input.type = 'text';
  485. input.style.borderBottom = '3px solid black';
  486. input.style.backgroundColor = '#41631F';
  487. input.style.padding = '3px';
  488. input.style.color = 'white';
  489. input.style.fontFamily = ' Tahoma, Verdana, sans-serif';
  490. input.style.fontWeight = 'normal';
  491. input.style.border = 'none';
  492.  
  493. const confirmBtn = document.createElement('a');
  494. confirmBtn.style = 'padding: 3px;';
  495. confirmBtn.textContent = '确认';
  496. confirmBtn.onclick = () => {
  497. let player = {}
  498. player.ID = player_id;
  499. player.Chinese = input.value;
  500. updateTranslation(player);
  501. };
  502.  
  503. const closeBtn = document.createElement('a');
  504. closeBtn.style = 'padding: 3px;';
  505. closeBtn.textContent = '取消';
  506. closeBtn.onclick = () => {popup.style.display = 'none'};
  507.  
  508. popup.appendChild(inputTitle);
  509. popup.appendChild(input);
  510. popup.appendChild(confirmBtn);
  511. popup.appendChild(closeBtn);
  512.  
  513.  
  514. return popup;
  515. }
  516.  
  517. function addEditButton(element, player_id){
  518. element.parentNode.style.position = 'relative';
  519. const img = document.createElement('img');
  520. img.src = 'data:image/png;base64,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';
  521. img.width="20";
  522. img.style.position = 'absolute';
  523. img.style.top = '3px';
  524. img.style.cursor = 'pointer';
  525. img.onclick = (event) => {
  526. const popup = element.parentNode.querySelector('.notification_content') || createPopup(player_id);
  527.  
  528. // 获取按钮的位置
  529. const rect = img.getBoundingClientRect();
  530. popup.style.top = rect.top + window.scrollY - 3 + 'px';
  531. popup.style.left = rect.right + window.scrollX + 'px';
  532.  
  533. popup.style.display = 'block';
  534. document.body.appendChild(popup);
  535. };
  536.  
  537.  
  538. element.parentNode.appendChild(img);
  539. }
  540.  
  541. // 球员列表
  542. (function() {
  543. 'use strict';
  544.  
  545. if (!(
  546. window.location.pathname === '/players' || window.location.pathname === '/players/'
  547. )) {
  548. return;
  549. }
  550. var players = [];
  551. var links = [];
  552.  
  553. let canUpdate = (!window.location.pathname.includes('/club/') && !window.location.pathname.includes('/league/team-of-the-round')) || (window.location.pathname.includes('/club/') && (window.location.pathname.includes('squad/') || window.location.pathname.includes('statistics/')));
  554. let defaultCountry = window.location.pathname.includes('/club/') ? getCountry(document.querySelector(".box_sub_header a.country_link").getAttribute("href")) : DEFAULT_COUNTRY;
  555.  
  556. let translationsResult = "";
  557. let firstLoad = true;
  558.  
  559. if(document.querySelector("#toggle_b_team") != undefined){
  560. document.querySelector("#toggle_b_team").addEventListener('click', function(event) {
  561. if(document.querySelector("#toggle_b_team img").getAttribute("src") == "/pics/sort_btn_gray_on.gif" && firstLoad){
  562. location.reload();
  563. }
  564. firstLoad = false;
  565. });
  566. }
  567.  
  568. function startTranslate(){
  569. document.querySelectorAll("a[player_link]").forEach(function(element) {
  570. var link = element;
  571. var player = {};
  572. // 检查国籍
  573.  
  574. let parent = element.parentElement;
  575. player.Country = defaultCountry;
  576. parent.querySelectorAll(`img[src]`).forEach(function(img){
  577. if(img.getAttribute("src").includes('/pics/flags/gradient/')){
  578. player.Country = getCountry(img.getAttribute("src"));
  579. }
  580. if(img.getAttribute("src").includes('/pics/flags/gradient/')){
  581. player.Country = getCountry(img.getAttribute("src"));
  582. }
  583. })
  584. parent.querySelectorAll(`a[href]`).forEach(function(a){
  585. if(a.getAttribute("href").includes('/national-teams/')){
  586. player.Country = getCountry(a.getAttribute("href"));
  587. }
  588. if(a.getAttribute("href").includes('/national-teams/')){
  589. player.Country = getCountry(a.getAttribute("href"));
  590. }
  591. })
  592. if (link) {
  593. player.English = link.textContent;
  594. player.ID = parseInt(link.getAttribute("player_link"));
  595. players.push(player)
  596. links.push(link);
  597. }
  598. });
  599.  
  600. if(translationsResult == ""){
  601. translateNumbers(players, function(translations) {
  602. translationsResult = translations;
  603. links.forEach(function(link) {
  604. translations.forEach(function(translation) {
  605. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  606. link.textContent = translation.Chinese;
  607. addEditButton(link, parseInt(link.getAttribute("player_link")));
  608. }
  609. })
  610. });
  611. }, canUpdate);
  612. } else {
  613. let translations = translationsResult;
  614. links.forEach(function(link) {
  615. translations.forEach(function(translation) {
  616. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  617. link.textContent = translation.Chinese;
  618. }
  619. })
  620. });
  621. }}
  622.  
  623. // Mutation Observer 实例化
  624. //const mutationObserver = new MutationObserver((mutations) => {
  625. // startTranslate();
  626. //});
  627.  
  628. // 监视整个文档的变化
  629. //mutationObserver.observe(document.querySelector('#sq'), {
  630. // childList: true,
  631. // subtree: true,
  632. //});
  633. startTranslate();
  634. })();
  635.  
  636. // 转会
  637. (function() {
  638. 'use strict';
  639.  
  640. if (!(
  641. window.location.pathname.includes('/transfer')
  642. )) {
  643. return;
  644. }
  645.  
  646. var players = [];
  647. var links = [];
  648.  
  649.  
  650. // Mutation Observer 实例化
  651. const mutationObserver = new MutationObserver((mutations) => {
  652. mutations.forEach(mutation => {
  653. mutation.addedNodes.forEach(node => {
  654. if (node.matches && node.matches('table')) {
  655. document.querySelectorAll("#transfer_list a[player_link]").forEach(function(element) {
  656. var link = element;
  657.  
  658. // 检查国籍
  659. var player = {};
  660. if(true){
  661. let parent = element.parentElement.parentElement.parentElement;
  662. player.Country = getCountry(parent.querySelector('ib').getAttribute("alt"));
  663. }
  664.  
  665. if (link) {
  666. player.English = link.textContent;
  667. player.ID = parseInt(link.getAttribute("player_link"));
  668. players.push(player)
  669. links.push(link);
  670. }
  671. });
  672.  
  673. translateNumbers(players, function(translations) {
  674. links.forEach(function(link) {
  675. translations.forEach(function(translation) {
  676. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  677. link.textContent = translation.Chinese;
  678. }
  679. })
  680. });
  681. }, true);
  682. }
  683. });
  684. });
  685. });
  686.  
  687. // 监视整个文档的变化
  688. mutationObserver.observe(document.querySelector('#transfer_list'), {
  689. childList: true,
  690. subtree: true,
  691. });
  692.  
  693. })();
  694.  
  695. // 训练
  696. (function() {
  697. 'use strict';
  698.  
  699. if (!(
  700. window.location.pathname === '/training/' || window.location.pathname === '/training'
  701. )) {
  702. return;
  703. }
  704.  
  705. let translationResults = "";
  706.  
  707. function transTrain(){
  708. let players = [];
  709. let links = [];
  710. document.querySelectorAll(".team .player").forEach(function(element) {
  711. var link = element;
  712. if (link) {
  713. var player = {};
  714. player.English = link.querySelector(".player_name").textContent;
  715. player.ID = parseInt(link.getAttribute("player_link"));
  716. players.push(player)
  717. links.push(link);
  718. }
  719. });
  720.  
  721. if(translationResults !== ""){
  722. let translations = translationResults;
  723. links.forEach(function(link) {
  724. translations.forEach(function(translation) {
  725. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  726. link.querySelector(".player_name").textContent = translation.Chinese;
  727. }
  728. })
  729. })
  730. } else {
  731. translateNumbers(players, function(translations) {
  732. links.forEach(function(link) {
  733. translations.forEach(function(translation) {
  734. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  735. link.querySelector(".player_name").textContent = translation.Chinese;
  736. }
  737. })
  738. });
  739. }, false);
  740. }
  741. }
  742.  
  743. // 监控特定元素并应用回调函数
  744. function bindObserver(selector, callback) {
  745. const elements = document.querySelectorAll(selector);
  746. elements.forEach(element => {
  747. // 创建一个 MutationObserver 实例并应用给定的回调
  748. const observer = new MutationObserver(callback);
  749. observer.observe(element, {
  750. childList: true
  751. });
  752. });
  753. }
  754.  
  755. // Mutation Observer 实例化
  756. const mutationObserver = new MutationObserver((mutations) => {
  757. mutations.forEach(mutation => {
  758. mutation.addedNodes.forEach(node => {
  759. if (node.matches && node.matches('#team1, #team2, #team3, #team4, #team5, #team6, #team7')) {
  760. if(document.querySelectorAll("#team1, #team2, #team3, #team4, #team5, #team6, #team7").length === 7){
  761. bindObserver("#team1, #team2, #team3, #team4, #team5, #team6, #team7", transTrain);
  762. setTimeout(function(){
  763. transTrain();
  764. }, 1000);
  765. }
  766. }
  767. });
  768. });
  769. });
  770.  
  771. // 监视整个文档的变化
  772. mutationObserver.observe(document.querySelector(".training"), {
  773. childList: true,
  774. subtree: true,
  775. });
  776. })();
  777.  
  778.  
  779.  
  780. // 主页 && 俱乐部 && 联赛
  781. (function() {
  782. 'use strict';
  783.  
  784. if (!(window.location.pathname === '/home/'||
  785. window.location.pathname === '/home'||
  786. window.location.pathname.includes('/club')||
  787. window.location.pathname === '/league' ||
  788. window.location.pathname === '/league/'
  789. )) {
  790. return;
  791. }
  792.  
  793. function transBox(){
  794. var players = [];
  795. var links = [];
  796.  
  797. document.querySelectorAll(".box_body span[onclick]").forEach(function(element) {
  798. var link = element;
  799. if (link && link.getAttribute("onclick").split('/').length === 5) {
  800. var player = {};
  801. player.English = link.textContent;
  802. player.ID = parseInt(link.getAttribute("onclick").split('/')[4]);
  803. players.push(player)
  804. links.push(link);
  805. }
  806. });
  807. translateNumbers(players, function(translations) {
  808. links.forEach(function(link) {
  809. translations.forEach(function(translation) {
  810. if (translation.ID === parseInt(link.getAttribute("onclick").split('/')[4])) {
  811. link.textContent = translation.Chinese;
  812. }
  813. })
  814. });
  815. }, false);
  816. }
  817.  
  818. // Mutation Observer 实例化
  819. const mutationObserver = new MutationObserver((mutations) => {
  820. mutations.forEach(mutation => {
  821. mutation.addedNodes.forEach(node => {
  822. if (node.matches && node.matches('#feed .feed_content')) {
  823. setTimeout(function(){
  824. let players = [];
  825. let links = [];
  826.  
  827. document.querySelectorAll("a[player_link]").forEach(function(element) {
  828. let link = element;
  829. if (link) {
  830. let player = {};
  831. player.English = link.textContent;
  832. player.ID = parseInt(link.getAttribute("player_link"));
  833. players.push(player)
  834. links.push(link);
  835. }
  836. });
  837.  
  838. translateNumbers(players, function(translations) {
  839. links.forEach(function(link) {
  840. translations.forEach(function(translation) {
  841. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  842. link.textContent = translation.Chinese;
  843. }
  844. })
  845. });
  846. }, false);
  847. }, 800);
  848. }
  849. if (node.matches && node.matches('.column3_a .box:nth-of-type(2)')) {
  850. setTimeout(function(){
  851. transBox();
  852. }, 800);
  853. }
  854. });
  855. });
  856. });
  857.  
  858. // 监视整个文档的变化
  859. mutationObserver.observe(document.body, {
  860. childList: true,
  861. subtree: true,
  862. });
  863. })();
  864.  
  865. // 关注名单
  866. (function() {
  867. 'use strict';
  868.  
  869. if (!(
  870. window.location.pathname.includes('/shortlist')
  871. )) {
  872. return;
  873. }
  874. let players = [];
  875. let links = [];
  876. document.querySelectorAll("a[player_link]").forEach(function(link) {
  877. let parent = link.parentElement.parentElement.parentElement;
  878. // 检查国籍
  879. let player = {};
  880. parent.querySelectorAll("img[src]").forEach(function(img) {
  881. if(img.getAttribute('src').includes("/pics/flags/gradient")){
  882. player.Country = getCountry(img.getAttribute('src'));
  883. }
  884. })
  885. player.English = link.textContent;
  886. player.ID = parseInt(link.getAttribute("player_link"));
  887. players.push(player)
  888. links.push(link);
  889. });
  890.  
  891. translateNumbers(players, function(translations) {
  892. links.forEach(function(link) {
  893. translations.forEach(function(translation) {
  894. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  895. link.textContent = translation.Chinese;
  896. }
  897. })
  898. });
  899. }, true);
  900.  
  901. })();
  902.  
  903. // 当前竞价
  904. (function() {
  905. 'use strict';
  906.  
  907. if (!(
  908. window.location.pathname.includes('/bids')
  909. )) {
  910. return;
  911. }
  912.  
  913. // Mutation Observer 实例化
  914. const mutationObserver = new MutationObserver((mutations) => {
  915. mutations.forEach(mutation => {
  916. mutation.addedNodes.forEach(node => {
  917. if (node.matches && node.matches('div')) {
  918. let players = [];
  919. let links = [];
  920. document.querySelectorAll("a[player_link]").forEach(function(link) {
  921. let parent = link.parentElement;
  922. let player = {};
  923. parent.querySelectorAll("a[href]").forEach(function(a) {
  924. if(a.getAttribute('href').includes('/national-teams/')){
  925. player.Country = getCountry(a.getAttribute('href'));
  926. }
  927. })
  928. player.English = link.textContent;
  929. player.ID = parseInt(link.getAttribute("player_link"));
  930. players.push(player)
  931. links.push(link);
  932. });
  933.  
  934. translateNumbers(players, function(translations) {
  935. links.forEach(function(link) {
  936. translations.forEach(function(translation) {
  937. if (translation.ID === parseInt(link.getAttribute("player_link"))) {
  938. link.textContent = translation.Chinese;
  939. }
  940. })
  941. });
  942. }, true);
  943. }
  944. });
  945. });
  946. });
  947.  
  948. // 监视整个文档的变化
  949. mutationObserver.observe(document.querySelector('.column2_c .box_body .transfer-box'), {
  950. childList: true,
  951. subtree: true,
  952. });
  953.  
  954. })();
  955.  
  956. // 比赛
  957. (function() {
  958. 'use strict';
  959.  
  960. if (!(window.location.pathname.includes('/matches/')
  961. )) {
  962. return;
  963. }
  964.  
  965. function translateBoard(){
  966. let players = [];
  967. let links = [];
  968.  
  969. document.querySelectorAll("a.normal.no_hover").forEach(function(element) {
  970. var link = element;
  971. if (link) {
  972. var player = {};
  973. player.English = link.querySelector('.name').textContent;
  974. player.ID = parseInt(link.getAttribute("href").split('/')[2]);
  975. players.push(player)
  976. links.push(link);
  977. }
  978. });
  979.  
  980. translateNumbers(players, function(translations) {
  981. links.forEach(function(link) {
  982. translations.forEach(function(translation) {
  983. if (translation.ID === parseInt(link.getAttribute("href").split('/')[2])) {
  984. if(link.querySelector('.name').textContent.split('.').length > 1 && '9' >= link.querySelector('.name').textContent.split('.')[0][0] && link.querySelector('.name').textContent.split('.')[0][0] >= '0'){
  985. link.querySelector('.name').textContent = link.querySelector('.name').textContent.split('.')[0] + ". " + translation.Chinese;
  986. } else {
  987. link.querySelector('.name').textContent = translation.Chinese;
  988. }
  989. }
  990. })
  991. });
  992. }, false);
  993. }
  994.  
  995. function translateQuestionable(){
  996. let players = [];
  997. let links = [];
  998.  
  999. document.querySelectorAll("a.white.normal").forEach(function(element) {
  1000. var link = element;
  1001. if (link) {
  1002. var player = {};
  1003. player.English = link.textContent;
  1004. player.ID = parseInt(link.getAttribute("href").split('/')[2]);
  1005. players.push(player)
  1006. links.push(link);
  1007. }
  1008. });
  1009.  
  1010. translateNumbers(players, function(translations) {
  1011. links.forEach(function(link) {
  1012. translations.forEach(function(translation) {
  1013. if (translation.ID === parseInt(link.getAttribute("href").split('/')[2])) {
  1014. link.textContent = translation.Chinese;
  1015. }
  1016. })
  1017. });
  1018. }, false);
  1019. }
  1020.  
  1021. function translateReport(){
  1022. let players = [];
  1023. let ids = new Set();
  1024. let links = [];
  1025.  
  1026. document.querySelectorAll(".report_list a[href], .event_list a[href]").forEach(function(element) {
  1027. var link = element;
  1028. if (link.getAttribute("href").split('/').length === 3) {
  1029. var player = {};
  1030. player.ID = parseInt(link.getAttribute("href").split('/')[2]);
  1031. player.English = link.textContent;
  1032. if(!ids.has(parseInt(link.getAttribute("href").split('/')[2]))){
  1033. ids.add(player.ID);
  1034. players.push(player)
  1035. }
  1036. links.push(link);
  1037. }
  1038. });
  1039.  
  1040. translateNumbers(players, function(translations) {
  1041. links.forEach(function(link) {
  1042. translations.forEach(function(translation) {
  1043. if (translation.ID === parseInt(link.getAttribute("href").split('/')[2])) {
  1044. link.textContent = translation.Chinese;
  1045. }
  1046. })
  1047. });
  1048. }, false);
  1049. }
  1050.  
  1051. // Mutation Observer 实例化
  1052. const mutationObserver = new MutationObserver((mutations) => {
  1053. mutations.forEach(mutation => {
  1054. mutation.addedNodes.forEach(node => {
  1055. // Mutation Observer 实例化
  1056. if (node.matches && (node.matches('div[tab="field"]'))) {
  1057. translateBoard();
  1058. translateQuestionable();
  1059. }
  1060. if (node.matches && node.matches('.post_report')) {
  1061. setTimeout(function(){
  1062. translateBoard();
  1063. translateQuestionable();
  1064. translateReport();
  1065. }, 800);
  1066. }
  1067. });
  1068. });
  1069. });
  1070.  
  1071. // 监视整个文档的变化
  1072. mutationObserver.observe(document.body, {
  1073. childList: true,
  1074. subtree: true,
  1075. });
  1076. })();