🏆 [#1 Chess Cheat] A.C.A.S(高级国际象棋辅助系统)

利用尖端实时走法分析和策略辅助系统,提升您的国际象棋水平

目前为 2023-11-09 提交的版本。查看 最新版本

  1. // ==UserScript==
  2. // @name 🏆 [#1 Chess Cheat] A.C.A.S (Advanced Chess Assistance System)
  3. // @name:en 🏆 [#1 Chess Cheat] A.C.A.S (Advanced Chess Assistance System)
  4. // @name:fi 🏆 [#1 Chess Cheat] A.C.A.S (Edistynyt shakkiavustusjärjestelmä)
  5. // @name:sw 🏆 [#1 Chess Cheat] A.C.A.S (Advanserad Schack Assitant System)
  6. // @name:zh-CN 🏆 [#1 Chess Cheat] A.C.A.S(高级国际象棋辅助系统)
  7. // @name:es 🏆 [#1 Chess Cheat] A.C.A.S (Sistema Avanzado de Asistencia al Ajedrez)
  8. // @name:hi 🏆 [#1 Chess Cheat] A.C.A.S (उन्नत शतरंज सहायता प्रणाली)
  9. // @name:ar 🏆 [#1 Chess Cheat] A.C.A.S (نظام المساعدة المتقدم في الشطرنج)
  10. // @name:pt 🏆 [#1 Chess Cheat] A.C.A.S (Sistema Avançado de Assistência ao Xadrez)
  11. // @name:ja 🏆 [#1 Chess Cheat] A.C.A.S(先進的なチェス支援システム)
  12. // @name:de 🏆 [#1 Chess Cheat] A.C.A.S (Fortgeschrittenes Schach-Hilfesystem)
  13. // @name:fr 🏆 [#1 Chess Cheat] A.C.A.S (Système Avancé d'Assistance aux Échecs)
  14. // @name:it 🏆 [#1 Chess Cheat] A.C.A.S (Sistema Avanzato di Assistenza agli Scacchi)
  15. // @name:ko 🏆 [#1 Chess Cheat] A.C.A.S (고급 체스 보조 시스템)
  16. // @name:nl 🏆 [#1 Chess Cheat] A.C.A.S (Geavanceerd Schaakondersteuningssysteem)
  17. // @name:pl 🏆 [#1 Chess Cheat] A.C.A.S (Zaawansowany System Pomocy Szachowej)
  18. // @name:tr 🏆 [#1 Chess Cheat] A.C.A.S (Gelişmiş Satranç Yardım Sistemi)
  19. // @name:vi 🏆 [#1 Chess Cheat] A.C.A.S (Hệ Thống Hỗ Trợ Cờ Vua Nâng Cao)
  20. // @name:uk 🏆 [#1 Chess Cheat] A.C.A.S (Система передової допомоги в шахах)
  21. // @name:ru 🏆 [#1 Chess Cheat] A.C.A.S (Система расширенной помощи в шахматах)
  22. // @description Enhance your chess performance with a cutting-edge real-time move analysis and strategy assistance system
  23. // @description:en Enhance your chess performance with a cutting-edge real-time move analysis and strategy assistance system
  24. // @description:fi Paranna shakkipelisi suorituskykyä huippuluokan reaaliaikaisen siirtoanalyysin ja strategisen avustusjärjestelmän avulla
  25. // @description:sw Förbättra dina schackprestationer med ett banbrytande rörelseanalys i realtid och strategiassistans
  26. // @description:zh-CN 利用尖端实时走法分析和策略辅助系统,提升您的国际象棋水平
  27. // @description:es Mejora tu rendimiento en ajedrez con un sistema de análisis de movimientos en tiempo real y asistencia estratégica de vanguardia
  28. // @description:hi अपने शतरंज प्रदर्शन को उन्नत करें, एक कटिंग-एज रियल-टाइम मूव विश्लेषण और रणनीति सहायता प्रणाली के साथ
  29. // @description:ar قم بتحسين أداءك في الشطرنج مع تحليل حركات اللعب في الوقت الحقيقي ونظام مساعدة استراتيجية حديث
  30. // @description:pt Melhore seu desempenho no xadrez com uma análise de movimentos em tempo real e um sistema avançado de assistência estratégica
  31. // @description:ja 最新のリアルタイムのムーブ分析と戦略支援システムでチェスのパフォーマンスを向上させましょう
  32. // @description:de Verbessern Sie Ihre Schachleistung mit einer hochmodernen Echtzeitzug-Analyse- und Strategiehilfe-System
  33. // @description:fr Améliorez vos performances aux échecs avec une analyse de mouvement en temps réel de pointe et un système d'assistance stratégique
  34. // @description:it Migliora le tue prestazioni agli scacchi con un sistema all'avanguardia di analisi dei movimenti in tempo reale e assistenza strategica
  35. // @description:ko 최첨단 실시간 움직임 분석 및 전략 지원 시스템으로 체스 성과 향상
  36. // @description:nl Verbeter je schaakprestaties met een geavanceerd systeem voor realtime zetanalyse en strategische ondersteuning
  37. // @description:pl Popraw swoje osiągnięcia w szachach dzięki zaawansowanemu systemowi analizy ruchów w czasie rzeczywistym i wsparciu strategicznemu
  38. // @description:tr Keskinleşmiş gerçek zamanlı hareket analizi ve strateji yardım sistemiyle satranç performansınızı artırın
  39. // @description:vi Nâng cao hiệu suất cờ vua của bạn với hệ thống phân tích nước đi và hỗ trợ chiến thuật hiện đại
  40. // @description:uk Покращуйте свою шахову гру з використанням передової системи аналізу ходів в режимі реального часу та стратегічної підтримки
  41. // @description:ru Слава Украине
  42. // @homepageURL https://psyyke.github.io/A.C.A.S
  43. // @supportURL https://github.com/Psyyke/A.C.A.S/tree/main#why-doesnt-it-work
  44. // @match https://psyyke.github.io/A.C.A.S/*
  45. // @match http://localhost/*
  46. // @match https://www.chess.com/*
  47. // @match https://lichess.org/*
  48. // @match https://playstrategy.org/*
  49. // @match https://www.pychess.org/*
  50. // @match https://chess.org/*
  51. // @match https://papergames.io/*
  52. // @match https://vole.wtf/kilobytes-gambit/
  53. // @match https://chess.coolmath-games.com/*
  54. // @match https://www.coolmathgames.com/0-chess/*
  55. // @match https://immortal.game/*
  56. // @match https://chessarena.com/*
  57. // @match http://chess.net/*
  58. // @match https://www.freechess.club/*
  59. // @match https://*chessclub.com/*
  60. // @match https://gameknot.com/*
  61. // @match https://chesstempo.com/*
  62. // @match https://www.redhotpawn.com/*
  63. // @match https://www.chessanytime.com/*
  64. // @match https://chessworld.net/*
  65. // @grant GM_getValue
  66. // @grant GM_setValue
  67. // @grant GM_deleteValue
  68. // @grant GM_listValues
  69. // @grant GM_registerMenuCommand
  70. // @grant GM_openInTab
  71. // @grant GM_addStyle
  72. // @grant GM_setClipboard
  73. // @grant GM_notification
  74. // @grant unsafeWindow
  75. // @run-at document-start
  76. // @require https://gf.qytechs.cn/scripts/470418-commlink-js/code/CommLinkjs.js
  77. // @require https://gf.qytechs.cn/scripts/470417-universalboarddrawer-js/code/UniversalBoardDrawerjs.js
  78. // @icon https://raw.githubusercontent.com/Psyyke/A.C.A.S/main/assets/images/grey-logo.png
  79. // @version 2.1.5
  80. // @namespace HKR
  81. // @author HKR
  82. // @license GPL-3.0
  83. // ==/UserScript==
  84.  
  85. /*
  86. e e88~-_ e ,d88~~\
  87. d8b d888 \ d8b 8888
  88. /Y88b 8888 /Y88b `Y88b
  89. / Y88b 8888 / Y88b `Y88b,
  90. /____Y88b d88b Y888 / d88b /____Y88b d88b 8888
  91. / Y88b Y88P "88_-~ Y88P / Y88b Y88P \__88P'
  92.  
  93. Advanced Chess Assistance System (A.C.A.S) v2 | Q3 2023
  94.  
  95. [WARNING]
  96. - Please be advised that the use of A.C.A.S may violate the rules and lead to disqualification or banning from tournaments and online platforms.
  97. - The developers of A.C.A.S and related systems will NOT be held accountable for any consequences resulting from its use.
  98. - We strongly advise to use A.C.A.S only in a controlled environment ethically.
  99.  
  100. [ADDITIONAL]
  101. - Big fonts created with: https://www.patorjk.com/software/taag/ (Cyberlarge)
  102.  
  103. JOIN THE DISCUSSION ABOUT USERSCRIPTS IN GENERAL @ https://hakorr.github.io/Userscripts/community/invite ("Userscript Hub")
  104.  
  105. DANGER ZONE - DO NOT PROCEED IF YOU DON'T KNOW WHAT YOU'RE DOING*\
  106. \\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\
  107. //////////////////////////////////////////////////////////////////
  108. DANGER ZONE - DO NOT PROCEED IF YOU DON'T KNOW WHAT YOU'RE DOING*/
  109.  
  110.  
  111.  
  112.  
  113.  
  114.  
  115. /*
  116. ______ _____ ______ _______
  117. | ____ | | | |_____] |_____| |
  118. |_____| |_____ |_____| |_____] | | |_____
  119.  
  120.  
  121. Code below this point runs on any site, including the GUI.
  122. */
  123.  
  124. // KEEP THESE AS FALSE ON PRODUCTION
  125. const debugModeActivated = false;
  126. const onlyUseDevelopmentBackend = false;
  127.  
  128. const domain = window.location.hostname.replace('www.', '');
  129. const greasyforkURL = 'https://gf.qytechs.cn/en/scripts/459137';
  130.  
  131. const backendConfig = {
  132. 'hosts': { 'prod': 'psyyke.github.io', 'dev': 'localhost' },
  133. 'path': '/A.C.A.S/'
  134. };
  135.  
  136. const currentBackendUrlKey = 'currentBackendURL';
  137.  
  138. const isBackendUrlUpToDate = Object.values(backendConfig.hosts).some(x => GM_getValue(currentBackendUrlKey)?.includes(x));
  139.  
  140. function constructBackendURL(host) {
  141. const protocol = window.location.protocol + '//';
  142. const hosts = backendConfig.hosts;
  143.  
  144. return protocol + (host || (hosts?.prod || hosts?.path)) + backendConfig.path;
  145. }
  146.  
  147. function isRunningOnBackend() {
  148. const hostsArr = Object.values(backendConfig.hosts);
  149.  
  150. const foundHost = hostsArr.find(host => host === window?.location?.host);
  151. const isCorrectPath = window?.location?.pathname?.includes(backendConfig.path);
  152.  
  153. const isBackend = typeof foundHost === 'string' && isCorrectPath;
  154.  
  155. if(isBackend) {
  156. GM_setValue(currentBackendUrlKey, constructBackendURL(foundHost));
  157.  
  158. return true;
  159. }
  160.  
  161. return false;
  162. }
  163.  
  164. function prependProtocolWhenNeeded(url) {
  165. if(!url.startsWith('http://') && !url.startsWith('https://')) {
  166. return 'http://' + url;
  167. }
  168.  
  169. return url;
  170. }
  171.  
  172. function getCurrentBackendURL(skipGmStorage) {
  173. if(onlyUseDevelopmentBackend) {
  174. return constructBackendURL(backendConfig.hosts?.dev);
  175. }
  176.  
  177. const gmStorageUrl = GM_getValue(currentBackendUrlKey);
  178.  
  179. if(skipGmStorage || !gmStorageUrl) {
  180. return constructBackendURL();
  181. }
  182.  
  183. return prependProtocolWhenNeeded(gmStorageUrl);
  184. }
  185.  
  186. if(!isBackendUrlUpToDate) {
  187. GM_setValue(currentBackendUrlKey, getCurrentBackendURL(true));
  188. }
  189.  
  190. function createInstanceVariable(dbValue) {
  191. return {
  192. set: (instanceID, value) => GM_setValue(dbValues[dbValue](instanceID), { value, 'date': Date.now() }),
  193. get: instanceID => {
  194. const data = GM_getValue(dbValues[dbValue](instanceID));
  195.  
  196. if(data?.date) {
  197. data.date = Date.now();
  198.  
  199. GM_setValue(dbValues[dbValue](instanceID), data);
  200. }
  201.  
  202. return data?.value;
  203. }
  204. }
  205. }
  206.  
  207. const tempValueIndicator = '-temp-value-';
  208. const dbValues = {
  209. AcasConfig: 'AcasConfig',
  210. playerColor: instanceID => 'playerColor' + tempValueIndicator + instanceID,
  211. turn: instanceID => 'turn' + tempValueIndicator + instanceID,
  212. fen: instanceID => 'fen' + tempValueIndicator + instanceID
  213. };
  214.  
  215. const instanceVars = {
  216. playerColor: createInstanceVariable('playerColor'),
  217. turn: createInstanceVariable('turn'),
  218. fen: createInstanceVariable('fen')
  219. };
  220.  
  221. if(isRunningOnBackend()) {
  222. // expose variables and functions
  223. unsafeWindow.USERSCRIPT = {
  224. 'GM_info': GM_info,
  225. 'GM_getValue': val => GM_getValue(val),
  226. 'GM_setValue': (val, data) => GM_setValue(val, data),
  227. 'GM_deleteValue': val => GM_deleteValue(val),
  228. 'GM_listValues': val => GM_listValues(val),
  229. 'tempValueIndicator': tempValueIndicator,
  230. 'dbValues': dbValues,
  231. 'instanceVars': instanceVars,
  232. 'CommLinkHandler': CommLinkHandler,
  233. };
  234.  
  235. return;
  236. }
  237.  
  238.  
  239.  
  240.  
  241.  
  242.  
  243. /*
  244. _______ _ _ _______ _______ _______ _______ _____ _______ _______ _______
  245. | |_____| |______ |______ |______ |______ | | |______ |______
  246. |_____ | | |______ ______| ______| ______| __|__ | |______ ______|
  247.  
  248.  
  249. Code below this point only runs on chess sites, not on the GUI itself.
  250. */
  251.  
  252. function getUniqueID() {
  253. return ([1e7]+-1e3+4e3+-8e3+-1e11).replace(/[018]/g, c =>
  254. (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)
  255. )
  256. }
  257.  
  258. const commLinkInstanceID = getUniqueID();
  259.  
  260. const blacklistedURLs = [
  261. constructBackendURL(backendConfig?.hosts?.prod),
  262. constructBackendURL(backendConfig?.hosts?.dev),
  263. 'https://www.chess.com/play',
  264. 'https://lichess.org/',
  265. 'https://chess.org/',
  266. 'https://papergames.io/en/chess',
  267. 'https://playstrategy.org/',
  268. 'https://www.pychess.org/',
  269. 'https://www.coolmathgames.com/0-chess',
  270. 'https://chess.net/'
  271. ];
  272.  
  273. const configKeys = {
  274. 'engineElo': 'engineElo',
  275. 'moveSuggestionAmount': 'moveSuggestionAmount',
  276. 'arrowOpacity': 'arrowOpacity',
  277. 'displayMovesOnExternalSite': 'displayMovesOnExternalSite',
  278. 'showMoveGhost': 'showMoveGhost',
  279. 'showOpponentMoveGuess': 'showOpponentMoveGuess',
  280. 'onlyShowTopMoves': 'onlyShowTopMoves',
  281. 'maxMovetime': 'maxMovetime',
  282. 'chessVariant': 'chessVariant',
  283. 'chessFont': 'chessFont',
  284. 'useChess960': 'useChess960',
  285. 'onlyCalculateOwnTurn': 'onlyCalculateOwnTurn'
  286. };
  287.  
  288. const config = {};
  289.  
  290. Object.values(configKeys).forEach(key => {
  291. config[key] = {
  292. get: () => getGmConfigValue(key, commLinkInstanceID),
  293. set: null
  294. };
  295. });
  296.  
  297. let BoardDrawer = null;
  298. let chessBoardElem = null;
  299. let chesscomVariantBoardCoordsTable = null;
  300. let chesscomVariantPlayerColorsTable = null;
  301. let activeSiteMoveHighlights = [];
  302. let inactiveGuiMoveMarkings = [];
  303.  
  304. let lastBoardRanks = null;
  305. let lastBoardFiles = null;
  306.  
  307. let lastBoardSize = null;
  308. let lastPieceSize = null;
  309.  
  310. let lastBoardOrientation = null;
  311.  
  312. let isUserMouseDown = false;
  313.  
  314. const supportedSites = {};
  315.  
  316. const pieceNameToFen = {
  317. 'pawn': 'p',
  318. 'knight': 'n',
  319. 'bishop': 'b',
  320. 'rook': 'r',
  321. 'queen': 'q',
  322. 'king': 'k'
  323. };
  324.  
  325. const arrowStyles = {
  326. 'best': `
  327. fill: limegreen;
  328. opacity: ${getConfigValue(configKeys.arrowOpacity)/100 || '0.9'};
  329. stroke: rgb(0 0 0 / 50%);
  330. stroke-width: 2px;
  331. stroke-linejoin: round;
  332. `,
  333. 'secondary': `
  334. fill: dodgerblue;
  335. opacity: ${getConfigValue(configKeys.arrowOpacity)/100 || '0.7'};
  336. stroke: rgb(0 0 0 / 50%);
  337. stroke-width: 2px;
  338. stroke-linejoin: round;
  339. `,
  340. 'opponent': `
  341. fill: crimson;
  342. stroke: rgb(0 0 0 / 25%);
  343. stroke-width: 2px;
  344. stroke-linejoin: round;
  345. display: none;
  346. opacity: ${getConfigValue(configKeys.arrowOpacity)/100 || '0.3'};
  347. `
  348. };
  349.  
  350. const CommLink = new CommLinkHandler(`frontend_${commLinkInstanceID}`, {
  351. 'singlePacketResponseWaitTime': 1500,
  352. 'maxSendAttempts': 3,
  353. 'statusCheckInterval': 1,
  354. 'silentMode': true
  355. });
  356.  
  357. // manually register a command so that the variables are dynamic
  358. CommLink.commands['createInstance'] = async () => {
  359. return await CommLink.send('mum', 'createInstance', {
  360. 'domain': domain,
  361. 'instanceID': commLinkInstanceID,
  362. 'chessVariant': getChessVariant(),
  363. 'playerColor': getPlayerColorVariable()
  364. });
  365. }
  366.  
  367. CommLink.registerSendCommand('ping', { commlinkID: 'mum', data: 'ping' });
  368. CommLink.registerSendCommand('pingInstance', { data: 'ping' });
  369. CommLink.registerSendCommand('log');
  370. CommLink.registerSendCommand('updateBoardOrientation');
  371. CommLink.registerSendCommand('updateBoardFen');
  372. CommLink.registerSendCommand('calculateBestMoves');
  373.  
  374. CommLink.registerListener(`backend_${commLinkInstanceID}`, packet => {
  375. try {
  376. switch(packet.command) {
  377. case 'ping':
  378. return `pong (took ${Date.now() - packet.date}ms)`;
  379. case 'getFen':
  380. return getFen();
  381. case 'removeSiteMoveMarkings':
  382. boardUtils.removeBestMarkings();
  383. return true;
  384. case 'markMoveToSite':
  385. boardUtils.markMove(packet.data);
  386. return true;
  387. }
  388. } catch(e) {
  389. return null;
  390. }
  391. });
  392.  
  393. const boardUtils = {
  394. markMove: moveObj => {
  395. if(!getConfigValue(configKeys.displayMovesOnExternalSite)) return;
  396.  
  397. const [from, to] = moveObj.player;
  398. const [opponentFrom, opponentTo] = moveObj.opponent;
  399. const ranking = moveObj.ranking;
  400.  
  401. const existingExactSameMoveObj = activeSiteMoveHighlights.find(obj => {
  402. const [activeFrom, activeTo] = obj.player;
  403. const [activeOpponentFrom, activeOpponentTo] = obj.opponent;
  404.  
  405. return from == activeFrom
  406. && to == activeTo
  407. && opponentFrom == activeOpponentFrom
  408. && opponentTo == activeOpponentTo;
  409. });
  410.  
  411. activeSiteMoveHighlights.map(obj => {
  412. const [activeFrom, activeTo] = obj.player;
  413.  
  414. const existingSameMoveObj = from == activeFrom && to == activeTo;
  415.  
  416. if(existingSameMoveObj) {
  417. obj.promotedRanking = 1;
  418. }
  419.  
  420. return obj;
  421. });
  422.  
  423. const exactSameMoveDoesNotExist = typeof existingExactSameMoveObj !== 'object';
  424.  
  425. if(exactSameMoveDoesNotExist) {
  426.  
  427. const showOpponentMoveGuess = getConfigValue(configKeys.showOpponentMoveGuess);
  428.  
  429. const opponentMoveGuessExists = typeof opponentFrom == 'string';
  430.  
  431. const arrowStyle = ranking == 1 ? arrowStyles.best : arrowStyles.secondary;
  432.  
  433. let opponentArrowElem = null;
  434.  
  435. // create player move arrow element
  436. const arrowElem = BoardDrawer.createShape('arrow', [from, to],
  437. { style: arrowStyle }
  438. );
  439.  
  440. // create opponent move arrow element
  441. if(opponentMoveGuessExists && showOpponentMoveGuess) {
  442. opponentArrowElem = BoardDrawer.createShape('arrow', [opponentFrom, opponentTo],
  443. { style: arrowStyles.opponent }
  444. );
  445.  
  446. const squareListener = BoardDrawer.addSquareListener(from, type => {
  447. if(!opponentArrowElem) {
  448. squareListener.remove();
  449. }
  450.  
  451. switch(type) {
  452. case 'enter':
  453. opponentArrowElem.style.display = 'inherit';
  454. break;
  455. case 'leave':
  456. opponentArrowElem.style.display = 'none';
  457. break;
  458. }
  459. });
  460. }
  461.  
  462. activeSiteMoveHighlights.push({
  463. ...moveObj,
  464. 'opponentArrowElem': opponentArrowElem,
  465. 'playerArrowElem': arrowElem
  466. });
  467. }
  468.  
  469. boardUtils.removeOldMarkings();
  470. boardUtils.paintMarkings();
  471. },
  472. removeOldMarkings: () => {
  473. const markingLimit = getConfigValue(configKeys.moveSuggestionAmount);
  474. const showGhost = getConfigValue(configKeys.showMoveGhost);
  475.  
  476. const exceededMarkingLimit = activeSiteMoveHighlights.length > markingLimit;
  477.  
  478. if(exceededMarkingLimit) {
  479. const amountToRemove = activeSiteMoveHighlights.length - markingLimit;
  480.  
  481. for(let i = 0; i < amountToRemove; i++) {
  482. const oldestMarkingObj = activeSiteMoveHighlights[0];
  483.  
  484. activeSiteMoveHighlights = activeSiteMoveHighlights.slice(1);
  485.  
  486. if(oldestMarkingObj?.playerArrowElem?.style) {
  487. oldestMarkingObj.playerArrowElem.style.fill = 'grey';
  488. oldestMarkingObj.playerArrowElem.style.opacity = '0';
  489. oldestMarkingObj.playerArrowElem.style.transition = 'opacity 2.5s ease-in-out';
  490. }
  491.  
  492. if(oldestMarkingObj?.opponentArrowElem?.style) {
  493. oldestMarkingObj.opponentArrowElem.style.fill = 'grey';
  494. oldestMarkingObj.opponentArrowElem.style.opacity = '0';
  495. oldestMarkingObj.opponentArrowElem.style.transition = 'opacity 2.5s ease-in-out';
  496. }
  497.  
  498. if(showGhost) {
  499. inactiveGuiMoveMarkings.push(oldestMarkingObj);
  500. } else {
  501. oldestMarkingObj.playerArrowElem?.remove();
  502. oldestMarkingObj.opponentArrowElem?.remove();
  503. }
  504. }
  505. }
  506.  
  507. if(showGhost) {
  508. inactiveGuiMoveMarkings.forEach(markingObj => {
  509. const activeDuplicateArrow = activeSiteMoveHighlights.find(x => {
  510. const samePlayerArrow = x.player?.toString() == markingObj.player?.toString();
  511. const sameOpponentArrow = x.opponent?.toString() == markingObj.opponent?.toString();
  512.  
  513. return samePlayerArrow && sameOpponentArrow;
  514. });
  515.  
  516. const duplicateExists = activeDuplicateArrow ? true : false;
  517.  
  518. const removeArrows = () => {
  519. inactiveGuiMoveMarkings = inactiveGuiMoveMarkings.filter(x => x.playerArrowElem != markingObj.playerArrowElem);
  520.  
  521. markingObj.playerArrowElem?.remove();
  522. markingObj.opponentArrowElem?.remove();
  523. }
  524.  
  525. if(duplicateExists) {
  526. removeArrows();
  527. } else {
  528. setTimeout(removeArrows, 2500);
  529. }
  530. });
  531. }
  532. },
  533. paintMarkings: () => {
  534. const newestBestMarkingIndex = activeSiteMoveHighlights.findLastIndex(obj => obj.ranking == 1);
  535. const newestPromotedBestMarkingIndex = activeSiteMoveHighlights.findLastIndex(obj => obj?.promotedRanking == 1);
  536. const lastMarkingIndex = activeSiteMoveHighlights.length - 1;
  537.  
  538. const isLastMarkingBest = newestBestMarkingIndex == -1 && newestPromotedBestMarkingIndex == -1;
  539. const bestIndex = isLastMarkingBest ? lastMarkingIndex : Math.max(...[newestBestMarkingIndex, newestPromotedBestMarkingIndex]);
  540.  
  541. let bestMoveMarked = false;
  542.  
  543. activeSiteMoveHighlights.forEach((markingObj, idx) => {
  544. const isBestMarking = idx == bestIndex;
  545.  
  546. if(isBestMarking) {
  547. markingObj.playerArrowElem.style.cssText = arrowStyles.best;
  548.  
  549. const playerArrowElem = markingObj.playerArrowElem
  550. const opponentArrowElem = markingObj.opponentArrowElem;
  551.  
  552. // move best arrow element on top (multiple same moves can hide the best move)
  553. const parentElem = markingObj.playerArrowElem.parentElement;
  554.  
  555. parentElem.appendChild(playerArrowElem);
  556.  
  557. if(opponentArrowElem) {
  558. parentElem.appendChild(opponentArrowElem);
  559. }
  560.  
  561. bestMoveMarked = true;
  562. } else {
  563. markingObj.playerArrowElem.style.cssText = arrowStyles.secondary;
  564. }
  565. });
  566. },
  567. removeBestMarkings: () => {
  568. activeSiteMoveHighlights.forEach(markingObj => {
  569. markingObj.opponentArrowElem?.remove();
  570. markingObj.playerArrowElem?.remove();
  571. });
  572.  
  573. activeSiteMoveHighlights = [];
  574. },
  575. setBoardOrientation: orientation => {
  576. if(BoardDrawer) {
  577. if(debugModeActivated) console.warn('setBoardOrientation', orientation);
  578.  
  579. BoardDrawer.setOrientation(orientation);
  580. }
  581. },
  582. setBoardDimensions: dimensionArr => {
  583. if(BoardDrawer) {
  584. if(debugModeActivated) console.warn('setBoardDimensions', dimensionArr);
  585.  
  586. BoardDrawer.setBoardDimensions(dimensionArr);
  587. }
  588. }
  589. };
  590.  
  591. function displayImportantNotification(title, text) {
  592. if(typeof GM_notification === 'function') {
  593. GM_notification({ title: title, text: text });
  594. } else {
  595. alert(`[${title}]` + '\n\n' + text);
  596. }
  597. }
  598.  
  599. function filterInvisibleElems(elementArr, inverse) {
  600. return [...elementArr].filter(elem => {
  601. const style = getComputedStyle(elem);
  602. const bounds = elem.getBoundingClientRect();
  603.  
  604. const isHidden =
  605. style.visibility === 'hidden' ||
  606. style.display === 'none' ||
  607. style.opacity === '0' ||
  608. bounds.width == 0 ||
  609. bounds.height == 0;
  610.  
  611. return inverse ? isHidden : !isHidden;
  612. });
  613. }
  614.  
  615. function getElementSize(elem) {
  616. const rect = elem.getBoundingClientRect();
  617.  
  618. if(rect.width !== 0 && rect.height !== 0) {
  619. return { width: rect.width, height: rect.height };
  620. }
  621.  
  622. const computedStyle = window.getComputedStyle(elem);
  623. const width = parseFloat(computedStyle.width);
  624. const height = parseFloat(computedStyle.height);
  625.  
  626. return { width, height };
  627. }
  628.  
  629. function extractElemTransformData(elem) {
  630. const computedStyle = window.getComputedStyle(elem);
  631. const transformMatrix = new DOMMatrix(computedStyle.transform);
  632.  
  633. const x = transformMatrix.e;
  634. const y = transformMatrix.f;
  635.  
  636. return [x, y];
  637. }
  638.  
  639. function getElemCoordinatesFromTransform(elem, config) {
  640. const onlyFlipX = config?.onlyFlipX;
  641. const onlyFlipY = config?.onlyFlipY;
  642.  
  643. lastBoardSize = getElementSize(chessBoardElem);
  644.  
  645. const [files, ranks] = getBoardDimensions();
  646.  
  647. lastBoardRanks = ranks;
  648. lastBoardFiles = files;
  649.  
  650. const boardOrientation = getPlayerColorVariable();
  651.  
  652. let [x, y] = extractElemTransformData(elem);
  653.  
  654. const boardDimensions = lastBoardSize;
  655. let squareDimensions = boardDimensions.width / lastBoardRanks;
  656.  
  657. const normalizedX = Math.round(x / squareDimensions);
  658. const normalizedY = Math.round(y / squareDimensions);
  659.  
  660. if(onlyFlipY || boardOrientation === 'w') {
  661. const flippedY = lastBoardFiles - normalizedY - 1;
  662.  
  663. return [normalizedX, flippedY];
  664. } else {
  665. const flippedX = lastBoardRanks - normalizedX - 1;
  666.  
  667. return [flippedX, normalizedY];
  668. }
  669. }
  670.  
  671. function getElemCoordinatesFromLeftBottomPercentages(elem) {
  672. if(!lastBoardRanks || !lastBoardFiles) {
  673. const [files, ranks] = getBoardDimensions();
  674.  
  675. lastBoardRanks = ranks;
  676. lastBoardFiles = files;
  677. }
  678.  
  679. const boardOrientation = getPlayerColorVariable();
  680.  
  681. const leftPercentage = parseFloat(elem.style.left?.replace('%', ''));
  682. const bottomPercentage = parseFloat(elem.style.bottom?.replace('%', ''));
  683.  
  684. const x = Math.max(Math.round(leftPercentage / (100 / lastBoardRanks)), 0);
  685. const y = Math.max(Math.round(bottomPercentage / (100 / lastBoardFiles)), 0);
  686.  
  687. if (boardOrientation === 'w') {
  688. return [x, y];
  689. } else {
  690. const flippedX = lastBoardRanks - (x + 1);
  691. const flippedY = lastBoardFiles - (y + 1);
  692.  
  693. return [flippedX, flippedY];
  694. }
  695. }
  696.  
  697. function getElemCoordinatesFromLeftTopPixels(elem) {
  698. const pieceSize = getElementSize(elem);
  699.  
  700. const leftPixels = parseFloat(elem.style.left?.replace('px', ''));
  701. const topPixels = parseFloat(elem.style.top?.replace('px', ''));
  702.  
  703. const x = Math.max(Math.round(leftPixels / pieceSize.width), 0);
  704. const y = Math.max(Math.round(topPixels / pieceSize.width), 0);
  705.  
  706. const boardOrientation = getPlayerColorVariable();
  707.  
  708. if (boardOrientation === 'w') {
  709. const flippedY = lastBoardFiles - (y + 1);
  710.  
  711. return [x, flippedY];
  712. } else {
  713. const flippedX = lastBoardRanks - (x + 1);
  714.  
  715. return [flippedX, y];
  716. }
  717. }
  718.  
  719. function updateChesscomVariantBoardCoordsTable() {
  720. chesscomVariantBoardCoordsTable = {};
  721.  
  722. const boardElem = getBoardElem();
  723. const [boardWidth, boardHeight] = getBoardDimensions();
  724. const boardOrientation = getBoardOrientation();
  725.  
  726. const squareElems = getSquareElems(boardElem);
  727.  
  728. let squareIndex = 0;
  729.  
  730. for(let x = 0; x < boardWidth; x++) {
  731. for(let y = boardHeight; y > 0; y--) {
  732. const squareElem = squareElems[squareIndex];
  733. const id = squareElem?.dataset?.theme;
  734.  
  735. if(id) {
  736. if(boardOrientation === 'b') {
  737. chesscomVariantBoardCoordsTable[id] = [boardWidth - (x + 1), boardHeight - y];
  738. } else {
  739. chesscomVariantBoardCoordsTable[id] = [x, y - 1];
  740. }
  741. }
  742.  
  743. squareIndex++;
  744. }
  745. }
  746. }
  747.  
  748. function updateChesscomVariantPlayerColorsTable() {
  749. let colors = [];
  750.  
  751. document.querySelectorAll('*[data-color]').forEach(pieceElem => {
  752. const colorCode = Number(pieceElem?.dataset?.color);
  753.  
  754. if(!colors?.includes(colorCode)) {
  755. colors.push(colorCode);
  756. }
  757. });
  758.  
  759. if(colors?.length > 1) {
  760. colors = colors.sort((a, b) => a - b);
  761.  
  762. chesscomVariantPlayerColorsTable = { [colors[0]]: 'w', [colors[1]]: 'b' };
  763. }
  764. }
  765.  
  766. function getBoardDimensionsFromSize() {
  767. const boardDimensions = getElementSize(chessBoardElem);
  768.  
  769. lastBoardSize = getElementSize(chessBoardElem);
  770.  
  771. const boardWidth = boardDimensions?.width;
  772. const boardHeight = boardDimensions.height;
  773.  
  774. const boardPiece = getPieceElem();
  775.  
  776. if(boardPiece) {
  777. const pieceDimensions = getElementSize(boardPiece);
  778.  
  779. lastPieceSize = getElementSize(boardPiece);
  780.  
  781. const boardPieceWidth = pieceDimensions?.width;
  782. const boardPieceHeight = pieceDimensions?.height;
  783.  
  784. const boardRanks = Math.floor(boardWidth / boardPieceWidth);
  785. const boardFiles = Math.floor(boardHeight / boardPieceHeight);
  786.  
  787. const ranksInAllowedRange = 0 < boardRanks && boardRanks <= 69;
  788. const filesInAllowedRange = 0 < boardFiles && boardFiles <= 69;
  789.  
  790. if(ranksInAllowedRange && filesInAllowedRange) {
  791. return [boardRanks, boardFiles];
  792. }
  793. }
  794. }
  795.  
  796. function defaultTurnFromMutation(mutationArr) {
  797. const allChessPieceElems = getPieceElem(true);
  798.  
  799. const attributeMutationArr = mutationArr.filter(m => allChessPieceElems.includes(m.target));
  800. const movedChessPieceElem = attributeMutationArr?.[0]?.target;
  801.  
  802. if(movedChessPieceElem) {
  803. const pieceFen = getPieceElemFen(movedChessPieceElem);
  804.  
  805. if(pieceFen) {
  806. const newTurn = getFenPieceOppositeColor(pieceFen);
  807.  
  808. if(newTurn?.length === 1) {
  809. instanceVars.turn.set(commLinkInstanceID, newTurn);
  810.  
  811. return newTurn;
  812. }
  813. }
  814. }
  815. }
  816.  
  817. function chessCoordinatesToIndex(coord) {
  818. const x = coord.charCodeAt(0) - 97;
  819. let y = null;
  820.  
  821. const lastHalf = coord.slice(1);
  822.  
  823. if(lastHalf === ':') {
  824. y = 9;
  825. } else {
  826. y = Number(coord.slice(1)) - 1;
  827. }
  828.  
  829. return [x, y];
  830. }
  831.  
  832. function getGmConfigValue(key, instanceID) {
  833. const config = GM_getValue(dbValues.AcasConfig);
  834.  
  835. const instanceValue = config?.instance?.[instanceID]?.[key];
  836. const globalValue = config?.global?.[key];
  837.  
  838. if(instanceValue !== undefined) {
  839. return instanceValue;
  840. }
  841.  
  842. if(globalValue !== undefined) {
  843. return globalValue;
  844. }
  845.  
  846. return null;
  847. }
  848.  
  849. function getConfigValue(key) {
  850. return config[key]?.get();
  851. }
  852.  
  853. function setConfigValue(key, val) {
  854. return config[key]?.set(val);
  855. }
  856.  
  857. function squeezeEmptySquares(fenStr) {
  858. return fenStr.replace(/1+/g, match => match.length);
  859. }
  860.  
  861. function getPlayerColorVariable() {
  862. return instanceVars.playerColor.get(commLinkInstanceID);
  863. }
  864.  
  865. function getFenPieceColor(pieceFenStr) {
  866. return pieceFenStr == pieceFenStr.toUpperCase() ? 'w' : 'b';
  867. }
  868.  
  869. function getFenPieceOppositeColor(pieceFenStr) {
  870. return getFenPieceColor(pieceFenStr) == 'w' ? 'b' : 'w';
  871. }
  872.  
  873. function convertPieceStrToFen(str) {
  874. if(!str || str.length !== 2) {
  875. return null;
  876. }
  877.  
  878. const firstChar = str[0].toLowerCase();
  879. const secondChar = str[1];
  880.  
  881. if(firstChar === 'w') {
  882. return secondChar.toUpperCase();
  883. } else if (firstChar === 'b') {
  884. return secondChar.toLowerCase();
  885. }
  886.  
  887. return null;
  888. }
  889.  
  890. function getCanvasPixelColor(canvas, [xPercentage, yPercentage], debug) {
  891. const ctx = canvas.getContext('2d');
  892.  
  893. const x = xPercentage * canvas.width;
  894. const y = yPercentage * canvas.height;
  895.  
  896. const imageData = ctx.getImageData(x, y, 1, 1);
  897. const pixel = imageData.data;
  898. const brightness = (pixel[0] + pixel[1] + pixel[2]) / 3;
  899.  
  900. if(debug) {
  901. const clonedCanvas = document.createElement('canvas');
  902. clonedCanvas.width = canvas.width;
  903. clonedCanvas.height = canvas.height;
  904.  
  905. const clonedCtx = clonedCanvas.getContext('2d');
  906. clonedCtx.drawImage(canvas, 0, 0);
  907.  
  908. clonedCtx.fillStyle = 'red';
  909. clonedCtx.beginPath();
  910. clonedCtx.arc(x, y, 1, 0, Math.PI * 2);
  911. clonedCtx.fill();
  912.  
  913. const dataURL = clonedCanvas.toDataURL();
  914.  
  915. console.log(canvas, pixel, dataURL);
  916. }
  917.  
  918. return brightness < 128 ? 'b' : 'w';
  919. }
  920.  
  921. function canvasHasPixelAt(canvas, [xPercentage, yPercentage], debug) {
  922. xPercentage = Math.min(Math.max(xPercentage, 0), 100);
  923. yPercentage = Math.min(Math.max(yPercentage, 0), 100);
  924.  
  925. const ctx = canvas.getContext('2d');
  926. const x = xPercentage * canvas.width;
  927. const y = yPercentage * canvas.height;
  928.  
  929. const imageData = ctx.getImageData(x, y, 1, 1);
  930. const pixel = imageData.data;
  931.  
  932. if(debug) {
  933. const clonedCanvas = document.createElement('canvas');
  934. clonedCanvas.width = canvas.width;
  935. clonedCanvas.height = canvas.height;
  936.  
  937. const clonedCtx = clonedCanvas.getContext('2d');
  938. clonedCtx.drawImage(canvas, 0, 0);
  939.  
  940. clonedCtx.fillStyle = 'red';
  941. clonedCtx.beginPath();
  942. clonedCtx.arc(x, y, 1, 0, Math.PI * 2);
  943. clonedCtx.fill();
  944.  
  945. const dataURL = clonedCanvas.toDataURL();
  946.  
  947. console.log(canvas, pixel, dataURL);
  948. }
  949.  
  950. return pixel[3] !== 0;
  951. }
  952.  
  953. function getSiteData(dataType, obj) {
  954. const pathname = window.location.pathname;
  955.  
  956. let dataObj = { pathname };
  957.  
  958. if(obj && typeof obj === 'object') {
  959. dataObj = { ...dataObj, ...obj };
  960. }
  961.  
  962. const dataHandlerFunction = supportedSites[domain]?.[dataType];
  963.  
  964. if(typeof dataHandlerFunction !== 'function') {
  965. return null;
  966. }
  967.  
  968. const result = dataHandlerFunction(dataObj);
  969.  
  970. //if(debugModeActivated) console.warn('GET_SITE_DATA', '| DATA_TYPE:', dataType, '| INPUT_OBJ:', obj, '| DATA_OBJ:', dataObj, '| RESULT:', result);
  971.  
  972. return result;
  973. }
  974.  
  975. function addSupportedChessSite(domain, typeHandlerObj) {
  976. supportedSites[domain] = typeHandlerObj;
  977. }
  978.  
  979. function getBoardElem() {
  980. const boardElem = getSiteData('boardElem');
  981.  
  982. return boardElem || null;
  983. }
  984.  
  985. function getPieceElem(getAll) {
  986. const boardElem = getBoardElem();
  987.  
  988. const boardQuerySelector = (getAll ? query => [...boardElem?.querySelectorAll(query)] : boardElem?.querySelector?.bind(boardElem));
  989.  
  990. if(typeof boardQuerySelector !== 'function')
  991. return null;
  992.  
  993. const pieceElem = getSiteData('pieceElem', { boardQuerySelector, getAll });
  994.  
  995. return pieceElem || null;
  996. }
  997.  
  998. function getSquareElems(element) {
  999. const squareElems = getSiteData('squareElems', { element });
  1000.  
  1001. return squareElems || null;
  1002. }
  1003.  
  1004. function getChessVariant() {
  1005. const chessVariant = getSiteData('chessVariant');
  1006.  
  1007. return chessVariant || null;
  1008. }
  1009.  
  1010. function getBoardOrientation() {
  1011. const boardOrientation = getSiteData('boardOrientation');
  1012.  
  1013. return boardOrientation || null;
  1014. }
  1015.  
  1016. function getPieceElemFen(pieceElem) {
  1017. const pieceFen = getSiteData('pieceElemFen', { pieceElem });
  1018.  
  1019. return pieceFen || null;
  1020. }
  1021.  
  1022. // this function gets called a lot, needs to be optimized
  1023. function getPieceElemCoords(pieceElem) {
  1024. const pieceCoords = getSiteData('pieceElemCoords', { pieceElem });
  1025.  
  1026. return pieceCoords || null;
  1027. }
  1028.  
  1029. function getBoardDimensions() {
  1030. const boardDimensionArr = getSiteData('boardDimensions');
  1031.  
  1032. if(boardDimensionArr) {
  1033. lastBoardRanks = boardDimensionArr[0];
  1034. lastBoardFiles = boardDimensionArr[1];
  1035.  
  1036. return boardDimensionArr;
  1037. } else {
  1038. lastBoardRanks = 8;
  1039. lastBoardFiles = 8;
  1040.  
  1041. return [8, 8];
  1042. }
  1043. }
  1044.  
  1045. function isMutationNewMove(mutationArr) {
  1046. const isNewMove = getSiteData('isMutationNewMove', { mutationArr });
  1047.  
  1048. return isNewMove || false;
  1049. }
  1050.  
  1051. function getMutationTurn(mutationArr) {
  1052. const turn = getSiteData('turnFromMutation', { mutationArr });
  1053.  
  1054. return turn || getPlayerColorVariable();
  1055. }
  1056.  
  1057. function getFen(onlyBasic) {
  1058. const [boardRanks, boardFiles] = getBoardDimensions();
  1059.  
  1060. if(debugModeActivated) console.warn('getFen()', 'onlyBasic:', onlyBasic, 'Ranks:', boardRanks, 'Files:', boardFiles);
  1061.  
  1062. const board = Array.from({ length: boardFiles }, () => Array(boardRanks).fill(1));
  1063.  
  1064. function getBasicFen() {
  1065. const pieceElems = getPieceElem(true);
  1066. const isValidPieceElemsArray = Array.isArray(pieceElems) || pieceElems instanceof NodeList;
  1067.  
  1068. if(isValidPieceElemsArray) {
  1069. pieceElems.forEach(pieceElem => {
  1070. const pieceFenCode = getPieceElemFen(pieceElem);
  1071. const pieceCoordsArr = getPieceElemCoords(pieceElem);
  1072.  
  1073. if(debugModeActivated) console.warn('pieceElem', pieceElem, 'pieceFenCode', pieceFenCode, 'pieceCoordsArr', pieceCoordsArr);
  1074.  
  1075. try {
  1076. const [xIdx, yIdx] = pieceCoordsArr;
  1077.  
  1078. board[boardFiles - (yIdx + 1)][xIdx] = pieceFenCode;
  1079. } catch(e) {
  1080. if(debugModeActivated) console.error(e);
  1081. }
  1082. });
  1083. }
  1084.  
  1085. return squeezeEmptySquares(board.map(x => x.join('')).join('/'));
  1086. }
  1087.  
  1088. function getBoardPiece(fenCoord) {
  1089. const indexArr = chessCoordinatesToIndex(fenCoord);
  1090.  
  1091. return board?.[boardFiles - (indexArr[1] + 1)]?.[indexArr[0]];
  1092. }
  1093.  
  1094. // Works on 8x8 boards only
  1095. function getRights() {
  1096. let rights = '';
  1097.  
  1098. // check for white
  1099. const e1 = getBoardPiece('e1'),
  1100. h1 = getBoardPiece('h1'),
  1101. a1 = getBoardPiece('a1');
  1102.  
  1103. if(e1 == 'K' && h1 == 'R') rights += 'K';
  1104. if(e1 == 'K' && a1 == 'R') rights += 'Q';
  1105.  
  1106. //check for black
  1107. const e8 = getBoardPiece('e8'),
  1108. h8 = getBoardPiece('h8'),
  1109. a8 = getBoardPiece('a8');
  1110.  
  1111. if(e8 == 'k' && h8 == 'r') rights += 'k';
  1112. if(e8 == 'k' && a8 == 'r') rights += 'q';
  1113.  
  1114. return rights ? rights : '-';
  1115. }
  1116.  
  1117. const basicFen = getBasicFen();
  1118.  
  1119. if(debugModeActivated) console.warn('basicFen', basicFen);
  1120.  
  1121. if(onlyBasic) {
  1122. return basicFen;
  1123. }
  1124.  
  1125. // FEN structure: [fen] [player color] [castling rights] [en passant targets] [halfmove clock] [fullmove clock]
  1126. const fullFen = `${basicFen} ${getPlayerColorVariable()} ${getRights()} - - -`;
  1127.  
  1128. return fullFen;
  1129. }
  1130.  
  1131. function resetCachedValues() {
  1132. chesscomVariantBoardCoordsTable = null;
  1133. chesscomVariantPlayerColorsTable = null;
  1134. }
  1135.  
  1136. function onNewMove(mutationArr, bypassFenChangeDetection) {
  1137. const currentFullFen = getFen();
  1138. const lastFullFen = instanceVars.fen.get(commLinkInstanceID);
  1139.  
  1140. const fenChanged = currentFullFen !== lastFullFen;
  1141.  
  1142. setTimeout(() => {
  1143. if(getFen() !== instanceVars.fen.get(commLinkInstanceID)) {
  1144. onNewMove(null, true);
  1145. }
  1146. }, 500);
  1147.  
  1148. if(fenChanged || bypassFenChangeDetection) {
  1149. if(debugModeActivated) console.warn('NEW MOVE DETECTED!');
  1150.  
  1151. resetCachedValues();
  1152.  
  1153. boardUtils.setBoardDimensions(getBoardDimensions());
  1154.  
  1155. const lastPlayerColor = getPlayerColorVariable();
  1156.  
  1157. updatePlayerColor();
  1158.  
  1159. const playerColor = getPlayerColorVariable();
  1160. const orientationChanged = playerColor != lastPlayerColor;
  1161.  
  1162. if(orientationChanged) {
  1163. CommLink.commands.log(`Player color (e.g. board orientation) changed from ${lastPlayerColor} to ${playerColor}!`);
  1164.  
  1165. resetCachedValues();
  1166.  
  1167. instanceVars.turn.set(commLinkInstanceID, playerColor);
  1168.  
  1169. CommLink.commands.log(`Turn updated to ${playerColor}!`);
  1170. }
  1171.  
  1172. boardUtils.removeBestMarkings();
  1173.  
  1174. CommLink.commands.updateBoardFen(currentFullFen);
  1175.  
  1176. const turn = mutationArr ? getMutationTurn(mutationArr) : playerColor;
  1177. const onlyCalculateOwnTurn = getConfigValue(configKeys.onlyCalculateOwnTurn);
  1178.  
  1179. if(debugModeActivated) console.warn('TURN:', turn, '| PLAYERCOLOR:', playerColor, '| ORIENTATION_CHANGED:', orientationChanged, '| ONLY_CALC_OWN_TURN:', onlyCalculateOwnTurn);
  1180.  
  1181. if(orientationChanged || !onlyCalculateOwnTurn || turn === playerColor) {
  1182. CommLink.commands.calculateBestMoves(currentFullFen);
  1183. }
  1184. }
  1185. }
  1186.  
  1187. function observeNewMoves() {
  1188. let lastProcessedFen = null;
  1189.  
  1190. const boardObserver = new MutationObserver(mutationArr => {
  1191. if(debugModeActivated) console.log(mutationArr);
  1192.  
  1193. if(isMutationNewMove(mutationArr))
  1194. {
  1195. if(debugModeActivated) console.warn('Mutation is a new move:', mutationArr);
  1196.  
  1197. if(domain === 'chess.org')
  1198. {
  1199. setTimeout(() => onNewMove(mutationArr), 250);
  1200. }
  1201. else
  1202. {
  1203. onNewMove(mutationArr);
  1204. }
  1205. }
  1206. });
  1207.  
  1208. boardObserver.observe(chessBoardElem, { childList: true, subtree: true, attributes: true });
  1209. }
  1210.  
  1211. async function updatePlayerColor() {
  1212. const boardOrientation = getBoardOrientation();
  1213.  
  1214. const boardOrientationChanged = lastBoardOrientation !== boardOrientation;
  1215. const boardOrientationDiffers = BoardDrawer && BoardDrawer?.orientation !== boardOrientation;
  1216.  
  1217. if(boardOrientationChanged || boardOrientationDiffers) {
  1218. lastBoardOrientation = boardOrientation;
  1219.  
  1220. instanceVars.playerColor.set(commLinkInstanceID, boardOrientation);
  1221. instanceVars.turn.set(commLinkInstanceID, boardOrientation);
  1222.  
  1223. boardUtils.setBoardOrientation(boardOrientation);
  1224.  
  1225. await CommLink.commands.updateBoardOrientation(boardOrientation);
  1226. }
  1227. }
  1228.  
  1229.  
  1230.  
  1231.  
  1232.  
  1233.  
  1234. /*
  1235. _______ _____ _______ _______ _______ _____ _______ _______ _____ _______ _____ _______
  1236. |______ | | |______ |______ |_____] |______ | | |______ | |
  1237. ______| __|__ | |______ ______| | |______ |_____ __|__ | __|__ |_____
  1238.  
  1239. Code below this point handles chess site specific things. (e.g. which element is the board or the pieces)
  1240. */
  1241.  
  1242. addSupportedChessSite('chess.com', {
  1243. 'boardElem': obj => {
  1244. const pathname = obj.pathname;
  1245.  
  1246. if(pathname?.includes('/variants')) {
  1247. return document.querySelector('.TheBoard-layers');
  1248. }
  1249.  
  1250. return document.querySelector('#board-layout-chessboard > .board');
  1251. },
  1252.  
  1253. 'pieceElem': obj => {
  1254. const pathname = obj.pathname;
  1255. const getAll = obj.getAll;
  1256.  
  1257. if(pathname?.includes('/variants')) {
  1258. const filteredPieceElems = filterInvisibleElems(document.querySelectorAll('.TheBoard-layers *[data-piece]'))
  1259. .filter(elem => elem?.dataset?.piece?.toLowerCase() !== 'x');
  1260.  
  1261. return getAll ? filteredPieceElems : filteredPieceElems[0];
  1262. }
  1263.  
  1264. return obj.boardQuerySelector('.piece');
  1265. },
  1266.  
  1267. 'squareElems': obj => {
  1268. const pathname = obj.pathname;
  1269. const element = obj.element;
  1270.  
  1271. if(pathname?.includes('/variants')) {
  1272. return [...element.querySelectorAll('.square')];
  1273. }
  1274. },
  1275.  
  1276. 'chessVariant': obj => {
  1277. const pathname = obj.pathname;
  1278.  
  1279. if(pathname?.includes('/variants')) {
  1280. const variant = pathname.match(/variants\/([^\/]*)/)?.[1]
  1281. .replaceAll('-chess', '')
  1282. .replaceAll('-', '');
  1283.  
  1284. const replacementTable = {
  1285. 'doubles-bughouse': 'bughouse',
  1286. 'paradigm-chess30': 'paradigm'
  1287. };
  1288.  
  1289. return replacementTable[variant] || variant;
  1290. }
  1291. },
  1292.  
  1293. 'boardOrientation': obj => {
  1294. const pathname = obj.pathname;
  1295.  
  1296. if(pathname?.includes('/variants')) {
  1297. const playerNumberStr = document.querySelector('.playerbox-bottom [data-player]')?.dataset?.player;
  1298.  
  1299. if(!playerNumberStr)
  1300. return 'w';
  1301.  
  1302. return playerNumberStr === '0' ? 'w' : 'b';
  1303. }
  1304.  
  1305. const boardElem = getBoardElem();
  1306.  
  1307. return boardElem?.classList.contains('flipped') ? 'b' : 'w';
  1308. },
  1309.  
  1310. 'pieceElemFen': obj => {
  1311. const pathname = obj.pathname;
  1312. const pieceElem = obj.pieceElem;
  1313.  
  1314. let pieceColor = null;
  1315. let pieceName = null;
  1316.  
  1317. if(pathname?.includes('/variants')) {
  1318. if(!chesscomVariantPlayerColorsTable) {
  1319. updateChesscomVariantPlayerColorsTable();
  1320. }
  1321.  
  1322. const pieceFenStr = pieceElem?.dataset?.piece;
  1323.  
  1324. pieceColor = chesscomVariantPlayerColorsTable[pieceElem?.dataset?.color];
  1325. pieceName = pieceElem?.dataset?.piece;
  1326. } else {
  1327. const pieceStr = [...pieceElem.classList].find(x => x.match(/^(b|w)[prnbqk]{1}$/));
  1328.  
  1329. [pieceColor, pieceName] = pieceStr.split('');
  1330. }
  1331.  
  1332. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1333. },
  1334.  
  1335. 'pieceElemCoords': obj => {
  1336. const pathname = obj.pathname;
  1337. const pieceElem = obj.pieceElem;
  1338.  
  1339. if(pathname?.includes('/variants')) {
  1340. if(!chesscomVariantBoardCoordsTable) {
  1341. updateChesscomVariantBoardCoordsTable();
  1342. }
  1343.  
  1344. const pieceBoundary = pieceElem.getBoundingClientRect();
  1345. const elementsBehindPieceElem = document.elementsFromPoint(pieceBoundary.x, pieceBoundary.y);
  1346.  
  1347. const squareElem = elementsBehindPieceElem?.find(x => x?.classList?.contains('square'));
  1348.  
  1349. //console.log(squareElem?.dataset?.theme, chesscomVariantBoardCoordsTable[squareElem?.dataset?.theme]);
  1350.  
  1351. const coords = chesscomVariantBoardCoordsTable[squareElem?.dataset?.theme];
  1352.  
  1353. return coords;
  1354. }
  1355.  
  1356. return pieceElem.classList.toString()
  1357. ?.match(/square-(\d)(\d)/)
  1358. ?.slice(1)
  1359. ?.map(x => Number(x) - 1);
  1360. },
  1361.  
  1362. 'boardDimensions': obj => {
  1363. const pathname = obj.pathname;
  1364.  
  1365. if(pathname?.includes('/variants')) {
  1366. const squaresContainerElem = document.querySelector('.TheBoard-squares');
  1367.  
  1368. let ranks = 0;
  1369. let files = 0;
  1370.  
  1371. [...squaresContainerElem.childNodes].forEach((x, i) => {
  1372. const visibleChildElems = filterInvisibleElems([...x.childNodes]);
  1373.  
  1374. if(visibleChildElems?.length > 0) {
  1375. ranks = ranks + 1;
  1376.  
  1377. if(visibleChildElems.length > files) {
  1378. files = visibleChildElems.length;
  1379. }
  1380. }
  1381. });
  1382.  
  1383. //console.log([ranks, files]);
  1384.  
  1385. return [ranks, files];
  1386. } else {
  1387. return [8, 8];
  1388. }
  1389. },
  1390.  
  1391. 'isMutationNewMove': obj => {
  1392. const pathname = obj.pathname;
  1393. const mutationArr = obj.mutationArr;
  1394.  
  1395. if(pathname?.includes('/variants')) {
  1396. return mutationArr.find(m => m.type === 'childList') ? true : false;
  1397. }
  1398.  
  1399. if(mutationArr.length == 1)
  1400. return false;
  1401.  
  1402. const modifiedHoverSquare = mutationArr.find(m => m?.target?.classList?.contains('hover-square')) ? true : false;
  1403. const modifiedHighlight = mutationArr.find(m => m?.target?.classList?.contains('highlight')) ? true : false;
  1404. const modifiedElemPool = mutationArr.find(m => m?.target?.classList?.contains('element-pool')) ? true : false;
  1405.  
  1406. return (mutationArr.length >= 4 && !modifiedHoverSquare)
  1407. || mutationArr.length >= 7
  1408. || modifiedHighlight
  1409. || modifiedElemPool;
  1410. },
  1411.  
  1412. 'turnFromMutation': obj => {
  1413. const pathname = obj.pathname;
  1414. const mutationArr = obj.mutationArr;
  1415.  
  1416. return defaultTurnFromMutation(mutationArr);
  1417. }
  1418. });
  1419.  
  1420. addSupportedChessSite('lichess.org', {
  1421. 'boardElem': obj => {
  1422. return document.querySelector('cg-board');
  1423. },
  1424.  
  1425. 'pieceElem': obj => {
  1426. return obj.boardQuerySelector('piece:not(.ghost)');
  1427. },
  1428.  
  1429. 'chessVariant': obj => {
  1430. const variantLinkElem = document.querySelector('.variant-link');
  1431.  
  1432. if(variantLinkElem) {
  1433. let variant = variantLinkElem?.innerText?.toLowerCase()?.replaceAll(' ', '-');
  1434.  
  1435. const replacementTable = {
  1436. 'correspondence': 'chess',
  1437. 'koth': 'kingofthehill',
  1438. 'three-check': '3check'
  1439. };
  1440.  
  1441. return replacementTable[variant] || variant;
  1442. }
  1443. },
  1444.  
  1445. 'boardOrientation': obj => {
  1446. const filesElem = document.querySelector('coords.files');
  1447.  
  1448. return filesElem?.classList?.contains('black') ? 'b' : 'w';
  1449. },
  1450.  
  1451. 'pieceElemFen': obj => {
  1452. const pieceElem = obj.pieceElem;
  1453.  
  1454. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  1455. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  1456.  
  1457. if(pieceColor && elemPieceName) {
  1458. const pieceName = pieceNameToFen[elemPieceName];
  1459.  
  1460. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1461. }
  1462. },
  1463.  
  1464. 'pieceElemCoords': obj => {
  1465. const pieceElem = obj.pieceElem;
  1466.  
  1467. const key = pieceElem?.cgKey;
  1468.  
  1469. if(key) {
  1470. return chessCoordinatesToIndex(key);
  1471. }
  1472. },
  1473.  
  1474. 'boardDimensions': obj => {
  1475. return [8, 8];
  1476. },
  1477.  
  1478. 'isMutationNewMove': obj => {
  1479. const mutationArr = obj.mutationArr;
  1480.  
  1481. return mutationArr.length >= 4
  1482. || mutationArr.find(m => m.type === 'childList') ? true : false
  1483. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  1484. },
  1485.  
  1486. 'turnFromMutation': obj => {
  1487. const pathname = obj.pathname;
  1488. const mutationArr = obj.mutationArr;
  1489.  
  1490. return defaultTurnFromMutation(mutationArr);
  1491. }
  1492. });
  1493.  
  1494. addSupportedChessSite('playstrategy.org', {
  1495. 'boardElem': obj => {
  1496. return document.querySelector('cg-board');
  1497. },
  1498.  
  1499. 'pieceElem': obj => {
  1500. return obj.boardQuerySelector('piece[class*="-piece"]:not(.ghost)');
  1501. },
  1502.  
  1503. 'chessVariant': obj => {
  1504. const variantLinkElem = document.querySelector('.variant-link');
  1505.  
  1506. if(variantLinkElem) {
  1507. let variant = variantLinkElem?.innerText
  1508. ?.toLowerCase()
  1509. ?.replaceAll(' ', '-');
  1510.  
  1511. const replacementTable = {
  1512. 'correspondence': 'chess',
  1513. 'koth': 'kingofthehill',
  1514. 'three-check': '3check',
  1515. 'five-check': '5check',
  1516. 'no-castling': 'nocastle'
  1517. };
  1518.  
  1519. return replacementTable[variant] || variant;
  1520. }
  1521. },
  1522.  
  1523. 'boardOrientation': obj => {
  1524. const cgWrapElem = document.querySelector('.cg-wrap');
  1525.  
  1526. return cgWrapElem.classList?.contains('orientation-p1') ? 'w' : 'b';
  1527. },
  1528.  
  1529. 'pieceElemFen': obj => {
  1530. const pieceElem = obj.pieceElem;
  1531.  
  1532. const playerColor = getPlayerColorVariable();
  1533. const pieceColor = pieceElem?.classList?.contains('ally') ? playerColor : (playerColor == 'w' ? 'b' : 'w');
  1534.  
  1535. let pieceName = null;
  1536.  
  1537. [...pieceElem?.classList]?.forEach(className => {
  1538. if(className?.includes('-piece')) {
  1539. const elemPieceName = className?.split('-piece')?.[0];
  1540.  
  1541. if(elemPieceName && elemPieceName?.length === 1) {
  1542. pieceName = elemPieceName;
  1543. }
  1544. }
  1545. });
  1546.  
  1547. if(pieceColor && pieceName) {
  1548. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1549. }
  1550. },
  1551.  
  1552. 'pieceElemCoords': obj => {
  1553. const pieceElem = obj.pieceElem;
  1554.  
  1555. const key = pieceElem?.cgKey;
  1556.  
  1557. if(key) {
  1558. return chessCoordinatesToIndex(key);
  1559. }
  1560. },
  1561.  
  1562. 'boardDimensions': obj => {
  1563. return getBoardDimensionsFromSize();
  1564. },
  1565.  
  1566. 'isMutationNewMove': obj => {
  1567. const mutationArr = obj.mutationArr;
  1568.  
  1569. return mutationArr.length >= 4
  1570. || mutationArr.find(m => m.type === 'childList') ? true : false
  1571. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  1572. },
  1573.  
  1574. 'turnFromMutation': obj => {
  1575. const pathname = obj.pathname;
  1576. const mutationArr = obj.mutationArr;
  1577.  
  1578. return defaultTurnFromMutation(mutationArr);
  1579. }
  1580. });
  1581.  
  1582. addSupportedChessSite('pychess.org', {
  1583. 'boardElem': obj => {
  1584. return document.querySelector('cg-board');
  1585. },
  1586.  
  1587. 'pieceElem': obj => {
  1588. return obj.boardQuerySelector('piece[class*="-piece"]:not(.ghost)');
  1589. },
  1590.  
  1591. 'chessVariant': obj => {
  1592. const variantLinkElem = document.querySelector('#main-wrap .tc .user-link');
  1593.  
  1594. if(variantLinkElem) {
  1595. let variant = variantLinkElem?.innerText
  1596. ?.toLowerCase()
  1597. ?.replaceAll(' ', '')
  1598. ?.replaceAll('-', '');
  1599.  
  1600. const replacementTable = {
  1601. 'correspondence': 'chess',
  1602. 'koth': 'kingofthehill',
  1603. 'nocastling': 'nocastle',
  1604. 'gorogoro+': 'gorogoro',
  1605. 'oukchaktrang': 'cambodian'
  1606. };
  1607.  
  1608. return replacementTable[variant] || variant;
  1609. }
  1610. },
  1611.  
  1612. 'boardOrientation': obj => {
  1613. const cgWrapElem = document.querySelector('.cg-wrap');
  1614.  
  1615. return cgWrapElem.classList?.contains('orientation-black') ? 'b' : 'w';
  1616. },
  1617.  
  1618. 'pieceElemFen': obj => {
  1619. const pieceElem = obj.pieceElem;
  1620.  
  1621. const playerColor = getPlayerColorVariable();
  1622. const pieceColor = pieceElem?.classList?.contains('ally') ? playerColor : (playerColor == 'w' ? 'b' : 'w');
  1623.  
  1624. let pieceName = null;
  1625.  
  1626. [...pieceElem?.classList]?.forEach(className => {
  1627. if(className?.includes('-piece')) {
  1628. const elemPieceName = className?.split('-piece')?.[0];
  1629.  
  1630. if(elemPieceName && elemPieceName?.length === 1) {
  1631. pieceName = elemPieceName;
  1632. }
  1633. }
  1634. });
  1635.  
  1636. if(pieceColor && pieceName) {
  1637. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1638. }
  1639. },
  1640.  
  1641. 'pieceElemCoords': obj => {
  1642. const pieceElem = obj.pieceElem;
  1643.  
  1644. const key = pieceElem?.cgKey;
  1645.  
  1646. if(key) {
  1647. return chessCoordinatesToIndex(key);
  1648. }
  1649. },
  1650.  
  1651. 'boardDimensions': obj => {
  1652. return getBoardDimensionsFromSize();
  1653. },
  1654.  
  1655. 'isMutationNewMove': obj => {
  1656. const mutationArr = obj.mutationArr;
  1657.  
  1658. return mutationArr.length >= 4
  1659. || mutationArr.find(m => m.type === 'childList') ? true : false
  1660. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  1661. },
  1662.  
  1663. 'turnFromMutation': obj => {
  1664. const pathname = obj.pathname;
  1665. const mutationArr = obj.mutationArr;
  1666.  
  1667. return defaultTurnFromMutation(mutationArr);
  1668. }
  1669. });
  1670.  
  1671. addSupportedChessSite('chess.org', {
  1672. 'boardElem': obj => {
  1673. return document.querySelector('.cg-board');
  1674. },
  1675.  
  1676. 'pieceElem': obj => {
  1677. return obj.boardQuerySelector('piece:not(.ghost)');
  1678. },
  1679.  
  1680. 'chessVariant': obj => {
  1681. const variantNum = unsafeWindow?.GameConfig?.instance?.variant;
  1682. const variant = GameConfig?.VARIANT_NAMES?.[variantNum]?.toLowerCase();
  1683.  
  1684. if(variant) {
  1685. const replacementTable = {
  1686. 'standard': 'chess'
  1687. };
  1688.  
  1689. return replacementTable[variant] || variant;
  1690. }
  1691. },
  1692.  
  1693. 'boardOrientation': obj => {
  1694. const filesElem = document.querySelector('coords.files');
  1695.  
  1696. return filesElem?.classList?.contains('black') ? 'b' : 'w';
  1697. },
  1698.  
  1699. 'pieceElemFen': obj => {
  1700. const pieceElem = obj.pieceElem;
  1701.  
  1702. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  1703. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  1704.  
  1705. if(pieceColor && elemPieceName) {
  1706. const pieceName = pieceNameToFen[elemPieceName];
  1707.  
  1708. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1709. }
  1710. },
  1711.  
  1712. 'pieceElemCoords': obj => {
  1713. const pieceElem = obj.pieceElem;
  1714.  
  1715. return getElemCoordinatesFromTransform(pieceElem);
  1716. },
  1717.  
  1718. 'boardDimensions': obj => {
  1719. return [8, 8];
  1720. },
  1721.  
  1722. 'isMutationNewMove': obj => {
  1723. const mutationArr = obj.mutationArr;
  1724.  
  1725. if(isUserMouseDown) {
  1726. return false;
  1727. }
  1728.  
  1729. return mutationArr.length >= 4
  1730. || mutationArr.find(m => m.type === 'childList') ? true : false
  1731. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  1732. },
  1733.  
  1734. 'turnFromMutation': obj => {
  1735. const pathname = obj.pathname;
  1736. const mutationArr = obj.mutationArr;
  1737.  
  1738. return defaultTurnFromMutation(mutationArr);
  1739. }
  1740. });
  1741.  
  1742. addSupportedChessSite('chess.coolmath-games.com', {
  1743. 'boardElem': obj => {
  1744. return document.querySelector('.cg-board');
  1745. },
  1746.  
  1747. 'pieceElem': obj => {
  1748. return obj.boardQuerySelector('piece:not(.ghost)');
  1749. },
  1750.  
  1751. 'chessVariant': obj => {
  1752. return 'chess';
  1753. },
  1754.  
  1755. 'boardOrientation': obj => {
  1756. const boardElem = getBoardElem();
  1757.  
  1758. return boardElem.classList?.contains('orientation-black') ? 'b' : 'w';
  1759. },
  1760.  
  1761. 'pieceElemFen': obj => {
  1762. const pieceElem = obj.pieceElem;
  1763.  
  1764. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  1765. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  1766.  
  1767. if(pieceColor && elemPieceName) {
  1768. const pieceName = pieceNameToFen[elemPieceName];
  1769.  
  1770. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1771. }
  1772. },
  1773.  
  1774. 'pieceElemCoords': obj => {
  1775. const pieceElem = obj.pieceElem;
  1776.  
  1777. return getElemCoordinatesFromLeftBottomPercentages(pieceElem?.parentElement);
  1778. },
  1779.  
  1780. 'boardDimensions': obj => {
  1781. return [8, 8];
  1782. },
  1783.  
  1784. 'isMutationNewMove': obj => {
  1785. const mutationArr = obj.mutationArr;
  1786.  
  1787. if(isUserMouseDown) {
  1788. return false;
  1789. }
  1790.  
  1791. return mutationArr.length >= 4
  1792. || mutationArr.find(m => m.type === 'childList') ? true : false
  1793. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  1794. },
  1795.  
  1796. 'turnFromMutation': obj => {
  1797. const pathname = obj.pathname;
  1798. const mutationArr = obj.mutationArr;
  1799.  
  1800. return defaultTurnFromMutation(mutationArr);
  1801. }
  1802. });
  1803.  
  1804. addSupportedChessSite('papergames.io', {
  1805. 'boardElem': obj => {
  1806. return document.querySelector('#chessboard');
  1807. },
  1808.  
  1809. 'pieceElem': obj => {
  1810. return obj.boardQuerySelector('*[data-piece][data-square]');
  1811. },
  1812.  
  1813. 'chessVariant': obj => {
  1814. return 'chess';
  1815. },
  1816.  
  1817. 'boardOrientation': obj => {
  1818. const boardElem = getBoardElem();
  1819.  
  1820. if(boardElem) {
  1821. const firstRankText = [...boardElem.querySelector('.coordinates').childNodes]?.[0].textContent;
  1822.  
  1823. return firstRankText == 'h' ? 'b' : 'w';
  1824. }
  1825. },
  1826.  
  1827. 'pieceElemFen': obj => {
  1828. const pieceElem = obj.pieceElem;
  1829.  
  1830. return convertPieceStrToFen(pieceElem?.dataset?.piece);
  1831. },
  1832.  
  1833. 'pieceElemCoords': obj => {
  1834. const pieceElem = obj.pieceElem;
  1835.  
  1836. const key = pieceElem?.dataset?.square;
  1837.  
  1838. if(key) {
  1839. return chessCoordinatesToIndex(key);
  1840. }
  1841. },
  1842.  
  1843. 'boardDimensions': obj => {
  1844. return [8, 8];
  1845. },
  1846.  
  1847. 'isMutationNewMove': obj => {
  1848. const mutationArr = obj.mutationArr;
  1849.  
  1850. return mutationArr.length >= 12;
  1851. },
  1852.  
  1853. 'turnFromMutation': obj => {
  1854. const pathname = obj.pathname;
  1855. const mutationArr = obj.mutationArr;
  1856.  
  1857. return defaultTurnFromMutation(mutationArr);
  1858. }
  1859. });
  1860.  
  1861. addSupportedChessSite('vole.wtf', {
  1862. 'boardElem': obj => {
  1863. return document.querySelector('#board');
  1864. },
  1865.  
  1866. 'pieceElem': obj => {
  1867. return obj.boardQuerySelector('*[data-t][data-l][data-p]:not([data-p="0"]');
  1868. },
  1869.  
  1870. 'chessVariant': obj => {
  1871. return 'chess';
  1872. },
  1873.  
  1874. 'boardOrientation': obj => {
  1875. return 'w';
  1876. },
  1877.  
  1878. 'pieceElemFen': obj => {
  1879. const pieceElem = obj.pieceElem;
  1880.  
  1881. const pieceNum = Number(pieceElem?.dataset?.p);
  1882. const pieceFenStr = 'pknbrq';
  1883.  
  1884. if(pieceNum > 8) {
  1885. return pieceFenStr[pieceNum - 9].toUpperCase();
  1886. } else {
  1887. return pieceFenStr[pieceNum - 1];
  1888. }
  1889. },
  1890.  
  1891. 'pieceElemCoords': obj => {
  1892. const pieceElem = obj.pieceElem;
  1893.  
  1894. return [Number(pieceElem?.dataset?.l), 7 - Number(pieceElem?.dataset?.t)];
  1895. },
  1896.  
  1897. 'boardDimensions': obj => {
  1898. return [8, 8];
  1899. },
  1900.  
  1901. 'isMutationNewMove': obj => {
  1902. const mutationArr = obj.mutationArr;
  1903.  
  1904. return mutationArr.length >= 12;
  1905. },
  1906.  
  1907. 'turnFromMutation': obj => {
  1908. const pathname = obj.pathname;
  1909. const mutationArr = obj.mutationArr;
  1910.  
  1911. return defaultTurnFromMutation(mutationArr);
  1912. }
  1913. });
  1914.  
  1915. addSupportedChessSite('immortal.game', {
  1916. 'boardElem': obj => {
  1917. return document.querySelector('div.pawn.relative, div.knight.relative, div.bishop.relative, div.rook.relative, div.queen.relative, div.king.relative')?.parentElement?.parentElement;
  1918. },
  1919.  
  1920. 'pieceElem': obj => {
  1921. return obj.boardQuerySelector('div.pawn.relative, div.knight.relative, div.bishop.relative, div.rook.relative, div.queen.relative, div.king.relative');
  1922. },
  1923.  
  1924. 'chessVariant': obj => {
  1925. return 'chess';
  1926. },
  1927.  
  1928. 'boardOrientation': obj => {
  1929. const coordA = [...document.querySelectorAll('svg text[x]')]
  1930. .find(elem => elem?.textContent == 'a');
  1931.  
  1932. const coordAX = Number(coordA?.getAttribute('x')) || 10;
  1933.  
  1934. return coordAX < 15 ? 'w' : 'b';
  1935. },
  1936.  
  1937. 'pieceElemFen': obj => {
  1938. const pieceElem = obj.pieceElem;
  1939.  
  1940. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  1941. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  1942.  
  1943. if(pieceColor && elemPieceName) {
  1944. const pieceName = pieceNameToFen[elemPieceName];
  1945.  
  1946. return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  1947. }
  1948. },
  1949.  
  1950. 'pieceElemCoords': obj => {
  1951. const pieceElem = obj.pieceElem;
  1952.  
  1953. return getElemCoordinatesFromTransform(pieceElem?.parentElement);
  1954. },
  1955.  
  1956. 'boardDimensions': obj => {
  1957. return [8, 8];
  1958. },
  1959.  
  1960. 'isMutationNewMove': obj => {
  1961. const mutationArr = obj.mutationArr;
  1962.  
  1963. if(isUserMouseDown) {
  1964. return false;
  1965. }
  1966.  
  1967. return mutationArr.length >= 5;
  1968. },
  1969.  
  1970. 'turnFromMutation': obj => {
  1971. const pathname = obj.pathname;
  1972. const mutationArr = obj.mutationArr;
  1973.  
  1974. return defaultTurnFromMutation(mutationArr);
  1975. }
  1976. });
  1977.  
  1978. addSupportedChessSite('chessarena.com', {
  1979. 'boardElem': obj => {
  1980. return document.querySelector('cg-board');
  1981. },
  1982.  
  1983. 'pieceElem': obj => {
  1984. return obj.boardQuerySelector('cg-piece:not(*[style*="visibility: hidden;"])');
  1985. },
  1986.  
  1987. 'chessVariant': obj => {
  1988. return 'chess';
  1989. },
  1990.  
  1991. 'boardOrientation': obj => {
  1992. const titlesElem = document.querySelector('cg-titles');
  1993.  
  1994. return titlesElem?.classList?.contains('rotated') ? 'b' : 'w';
  1995. },
  1996.  
  1997. 'pieceElemFen': obj => {
  1998. const pieceElem = obj.pieceElem;
  1999.  
  2000. const pieceColor = pieceElem?.className?.[0];
  2001. const elemPieceName = pieceElem?.className?.[1];
  2002.  
  2003. if(pieceColor && elemPieceName) {
  2004. const pieceName = elemPieceName; // pieceNameToFen[elemPieceName]
  2005.  
  2006. return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2007. }
  2008. },
  2009.  
  2010. 'pieceElemCoords': obj => {
  2011. const pieceElem = obj.pieceElem;
  2012.  
  2013. return getElemCoordinatesFromTransform(pieceElem, { 'onlyFlipY': true });
  2014. },
  2015.  
  2016. 'boardDimensions': obj => {
  2017. return [8, 8];
  2018. },
  2019.  
  2020. 'isMutationNewMove': obj => {
  2021. const mutationArr = obj.mutationArr;
  2022.  
  2023. if(isUserMouseDown) {
  2024. return false;
  2025. }
  2026.  
  2027. return mutationArr.find(m => m?.attributeName === 'style') ? true : false;
  2028. },
  2029.  
  2030. 'turnFromMutation': obj => {
  2031. const pathname = obj.pathname;
  2032. const mutationArr = obj.mutationArr;
  2033.  
  2034. return defaultTurnFromMutation(mutationArr);
  2035. }
  2036. });
  2037.  
  2038.  
  2039. addSupportedChessSite('chess.net', {
  2040. 'boardElem': obj => {
  2041. return document.querySelector('cg-board');
  2042. },
  2043.  
  2044. 'pieceElem': obj => {
  2045. return obj.boardQuerySelector('piece:not(.ghost)');
  2046. },
  2047.  
  2048. 'chessVariant': obj => {
  2049. const variantLinkElem = document.querySelector('.variant-link');
  2050.  
  2051. if(variantLinkElem) {
  2052. let variant = variantLinkElem?.innerText?.toLowerCase()?.replaceAll(' ', '-');
  2053.  
  2054. const replacementTable = {
  2055. 'correspondence': 'chess',
  2056. 'koth': 'kingofthehill',
  2057. 'three-check': '3check'
  2058. };
  2059.  
  2060. return replacementTable[variant] || variant;
  2061. }
  2062. },
  2063.  
  2064. 'boardOrientation': obj => {
  2065. const filesElem = document.querySelector('coords.files');
  2066.  
  2067. return filesElem?.classList?.contains('black') ? 'b' : 'w';
  2068. },
  2069.  
  2070. 'pieceElemFen': obj => {
  2071. const pieceElem = obj.pieceElem;
  2072.  
  2073. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  2074. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  2075.  
  2076. if(pieceColor && elemPieceName) {
  2077. const pieceName = pieceNameToFen[elemPieceName];
  2078.  
  2079. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2080. }
  2081. },
  2082.  
  2083. 'pieceElemCoords': obj => {
  2084. const pieceElem = obj.pieceElem;
  2085.  
  2086. const key = pieceElem?.cgKey;
  2087.  
  2088. if(key) {
  2089. return chessCoordinatesToIndex(key);
  2090. }
  2091. },
  2092.  
  2093. 'boardDimensions': obj => {
  2094. return [8, 8];
  2095. },
  2096.  
  2097. 'isMutationNewMove': obj => {
  2098. const mutationArr = obj.mutationArr;
  2099.  
  2100. return mutationArr.length >= 4
  2101. || mutationArr.find(m => m.type === 'childList') ? true : false
  2102. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  2103. },
  2104.  
  2105. 'turnFromMutation': obj => {
  2106. const pathname = obj.pathname;
  2107. const mutationArr = obj.mutationArr;
  2108.  
  2109. return defaultTurnFromMutation(mutationArr);
  2110. }
  2111. });
  2112.  
  2113. addSupportedChessSite('freechess.club', {
  2114. 'boardElem': obj => {
  2115. return document.querySelector('cg-board');
  2116. },
  2117.  
  2118. 'pieceElem': obj => {
  2119. return obj.boardQuerySelector('piece:not(.ghost)');
  2120. },
  2121.  
  2122. 'chessVariant': obj => {
  2123. return 'chess';
  2124. },
  2125.  
  2126. 'boardOrientation': obj => {
  2127. const filesElem = document.querySelector('coords.files');
  2128.  
  2129. return filesElem?.classList?.contains('black') ? 'b' : 'w';
  2130. },
  2131.  
  2132. 'pieceElemFen': obj => {
  2133. const pieceElem = obj.pieceElem;
  2134.  
  2135. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  2136. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  2137.  
  2138. if(pieceColor && elemPieceName) {
  2139. const pieceName = pieceNameToFen[elemPieceName];
  2140.  
  2141. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2142. }
  2143. },
  2144.  
  2145. 'pieceElemCoords': obj => {
  2146. const pieceElem = obj.pieceElem;
  2147.  
  2148. const key = pieceElem?.cgKey;
  2149.  
  2150. if(key) {
  2151. return chessCoordinatesToIndex(key);
  2152. }
  2153. },
  2154.  
  2155. 'boardDimensions': obj => {
  2156. return [8, 8];
  2157. },
  2158.  
  2159. 'isMutationNewMove': obj => {
  2160. const mutationArr = obj.mutationArr;
  2161.  
  2162. return mutationArr.length >= 4
  2163. || mutationArr.find(m => m.type === 'childList') ? true : false
  2164. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  2165. },
  2166.  
  2167. 'turnFromMutation': obj => {
  2168. const pathname = obj.pathname;
  2169. const mutationArr = obj.mutationArr;
  2170.  
  2171. return defaultTurnFromMutation(mutationArr);
  2172. }
  2173. });
  2174.  
  2175. addSupportedChessSite('play.chessclub.com', {
  2176. 'boardElem': obj => {
  2177. return document.querySelector('cg-board');
  2178. },
  2179.  
  2180. 'pieceElem': obj => {
  2181. return obj.boardQuerySelector('piece:not(.ghost)');
  2182. },
  2183.  
  2184. 'chessVariant': obj => {
  2185. return 'chess';
  2186. },
  2187.  
  2188. 'boardOrientation': obj => {
  2189. const filesElem = document.querySelector('coords.files');
  2190.  
  2191. return filesElem?.classList?.contains('black') ? 'b' : 'w';
  2192. },
  2193.  
  2194. 'pieceElemFen': obj => {
  2195. const pieceElem = obj.pieceElem;
  2196.  
  2197. const pieceColor = pieceElem?.classList?.contains('white') ? 'w' : 'b';
  2198. const elemPieceName = [...pieceElem?.classList]?.find(className => Object.keys(pieceNameToFen).includes(className));
  2199.  
  2200. if(pieceColor && elemPieceName) {
  2201. const pieceName = pieceNameToFen[elemPieceName];
  2202.  
  2203. return pieceColor == 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2204. }
  2205. },
  2206.  
  2207. 'pieceElemCoords': obj => {
  2208. const pieceElem = obj.pieceElem;
  2209.  
  2210. const key = pieceElem?.cgKey;
  2211.  
  2212. if(key) {
  2213. return chessCoordinatesToIndex(key);
  2214. }
  2215. },
  2216.  
  2217. 'boardDimensions': obj => {
  2218. return [8, 8];
  2219. },
  2220.  
  2221. 'isMutationNewMove': obj => {
  2222. const mutationArr = obj.mutationArr;
  2223.  
  2224. return mutationArr.length >= 4
  2225. || mutationArr.find(m => m.type === 'childList') ? true : false
  2226. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  2227. },
  2228.  
  2229. 'turnFromMutation': obj => {
  2230. const pathname = obj.pathname;
  2231. const mutationArr = obj.mutationArr;
  2232.  
  2233. return defaultTurnFromMutation(mutationArr);
  2234. }
  2235. });
  2236.  
  2237. addSupportedChessSite('gameknot.com', {
  2238. 'boardElem': obj => {
  2239. return document.querySelector('#chess-board-acboard');
  2240. },
  2241.  
  2242. 'pieceElem': obj => {
  2243. return obj.boardQuerySelector('*[class*="square_"] > img[src*="chess36."][style*="visible"]');
  2244. },
  2245.  
  2246. 'chessVariant': obj => {
  2247. return 'chess';
  2248. },
  2249.  
  2250. 'boardOrientation': obj => {
  2251. return document.querySelector('#chess-board-my-side-color .player_white') ? 'w' : 'b';
  2252. },
  2253.  
  2254. 'pieceElemFen': obj => {
  2255. const pieceElem = obj.pieceElem;
  2256.  
  2257. const left = Number(pieceElem.style.left.replace('px', ''));
  2258. const top = Number(pieceElem.style.top.replace('px', ''));
  2259.  
  2260. const pieceColor = left >= 0 ? 'w' : 'b';
  2261. const pieceName = 'kqrnbp'[(top * -1) / 60];
  2262.  
  2263. return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2264. },
  2265.  
  2266. 'pieceElemCoords': obj => {
  2267. const pieceElem = obj.pieceElem;
  2268.  
  2269. return getElemCoordinatesFromLeftTopPixels(pieceElem.parentElement);
  2270. },
  2271.  
  2272. 'boardDimensions': obj => {
  2273. return [8, 8];
  2274. },
  2275.  
  2276. 'isMutationNewMove': obj => {
  2277. const mutationArr = obj.mutationArr;
  2278.  
  2279. if(isUserMouseDown) {
  2280. return false;
  2281. }
  2282.  
  2283. return mutationArr.length >= 4
  2284. || mutationArr.find(m => m.type === 'childList') ? true : false
  2285. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  2286. },
  2287.  
  2288. 'turnFromMutation': obj => {
  2289. const pathname = obj.pathname;
  2290. const mutationArr = obj.mutationArr;
  2291.  
  2292. return defaultTurnFromMutation(mutationArr);
  2293. }
  2294. });
  2295.  
  2296. addSupportedChessSite('chesstempo.com', {
  2297. 'boardElem': obj => {
  2298. return document.querySelector('.ct-board-squares');
  2299. },
  2300.  
  2301. 'pieceElem': obj => {
  2302. return obj.boardQuerySelector('*[class*="ct-pieceClass"][class*="ct-piece-"]');
  2303. },
  2304.  
  2305. 'chessVariant': obj => {
  2306. return 'chess';
  2307. },
  2308.  
  2309. 'boardOrientation': obj => {
  2310. return document.querySelector('.ct-coord-column').innerText === 'a' ? 'w' : 'b';
  2311. },
  2312.  
  2313. 'pieceElemFen': obj => {
  2314. const pieceElem = obj.pieceElem;
  2315.  
  2316. const pieceNameClass = [...pieceElem.classList].find(x => x?.includes('ct-piece-'));
  2317. const colorNameCombo = pieceNameClass?.split('ct-piece-')?.pop();
  2318.  
  2319. const elemPieceColor = colorNameCombo.startsWith('white') ? 'w' : 'b';
  2320. const elemPieceName = colorNameCombo.substring(5);
  2321.  
  2322. const pieceName = pieceNameToFen[elemPieceName];
  2323.  
  2324. return elemPieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2325. },
  2326.  
  2327. 'pieceElemCoords': obj => {
  2328. const pieceElem = obj.pieceElem;
  2329.  
  2330. return [pieceElem?.ct?.piece?.piece?.column, pieceElem?.ct?.piece?.piece?.row];
  2331. },
  2332.  
  2333. 'boardDimensions': obj => {
  2334. return [8, 8];
  2335. },
  2336.  
  2337. 'isMutationNewMove': obj => {
  2338. const mutationArr = obj.mutationArr;
  2339.  
  2340. return mutationArr.length >= 4;
  2341. },
  2342.  
  2343. 'turnFromMutation': obj => {
  2344. const pathname = obj.pathname;
  2345. const mutationArr = obj.mutationArr;
  2346.  
  2347. return defaultTurnFromMutation(mutationArr);
  2348. }
  2349. });
  2350.  
  2351. addSupportedChessSite('redhotpawn.com', {
  2352. 'boardElem': obj => {
  2353. return document.querySelector('#board-0_1');
  2354. },
  2355.  
  2356. 'pieceElem': obj => {
  2357. return obj.boardQuerySelector('li.piece[id*="-pc-"]');
  2358. },
  2359.  
  2360. 'chessVariant': obj => {
  2361. return 'chess';
  2362. },
  2363.  
  2364. 'boardOrientation': obj => {
  2365. const aCoordLeftStyleNum = Number([...document.querySelectorAll('.boardCoordinate')]
  2366. .find(elem => elem?.innerText === 'a')
  2367. ?.style?.left?.replace('px', ''));
  2368.  
  2369. return aCoordLeftStyleNum < 200 ? 'w' : 'b';
  2370. },
  2371.  
  2372. 'pieceElemFen': obj => {
  2373. const pieceElem = obj.pieceElem;
  2374.  
  2375. return (pieceElem?.id?.match(/-pc-(.*?)-/) || [])[1];
  2376. },
  2377.  
  2378. 'pieceElemCoords': obj => {
  2379. const pieceElem = obj.pieceElem;
  2380.  
  2381. return getElemCoordinatesFromLeftTopPixels(pieceElem);
  2382. },
  2383.  
  2384. 'boardDimensions': obj => {
  2385. return [8, 8];
  2386. },
  2387.  
  2388. 'isMutationNewMove': obj => {
  2389. const mutationArr = obj.mutationArr;
  2390.  
  2391. if(isUserMouseDown) {
  2392. return false;
  2393. }
  2394.  
  2395. return mutationArr.length >= 4;
  2396. },
  2397.  
  2398. 'turnFromMutation': obj => {
  2399. const pathname = obj.pathname;
  2400. const mutationArr = obj.mutationArr;
  2401.  
  2402. return defaultTurnFromMutation(mutationArr);
  2403. }
  2404. });
  2405.  
  2406. addSupportedChessSite('chessanytime.com', {
  2407. 'boardElem': obj => {
  2408. return document.querySelector('#play');
  2409. },
  2410.  
  2411. 'pieceElem': obj => {
  2412. const getAll = obj.getAll;
  2413.  
  2414. const pieceElems = [...document.querySelectorAll('canvas.canvas_piece')].filter(elem => canvasHasPixelAt(elem, [0.5, 0.5]));
  2415.  
  2416. return getAll ? pieceElems : pieceElems[0];
  2417. },
  2418.  
  2419. 'chessVariant': obj => {
  2420. return 'chess';
  2421. },
  2422.  
  2423. 'boardOrientation': obj => {
  2424. return document.querySelector('#play_coordy0')?.innerText === '8' ? 'w' : 'b';
  2425. },
  2426.  
  2427. 'pieceElemFen': obj => {
  2428. const pieceElem = obj.pieceElem;
  2429.  
  2430. const pieceTypeCoordPercentages = [
  2431. { 'name' : 'k', 'coords': [52/60, 26/60] },
  2432. { 'name' : 'q', 'coords': [8/60, 16/60] },
  2433. { 'name' : 'n', 'coords': [51/60, 42/60] },
  2434. { 'name' : 'b', 'coords': [9/60, 50/60] },
  2435. { 'name' : 'r', 'coords': [45/60, 15/60] },
  2436. { 'name' : 'p', 'coords': [0.5, 0.5] }
  2437. ];
  2438.  
  2439. const pieceColorCoordPercentages = {
  2440. 'k': [42/60, 27/60],
  2441. 'q': [30/60, 50/60],
  2442. 'n': [38/60, 41/60],
  2443. 'b': [30/60, 20/60]
  2444. };
  2445.  
  2446. let pieceName = null;
  2447.  
  2448. for(obj of pieceTypeCoordPercentages) {
  2449. const isThisPiece = canvasHasPixelAt(pieceElem, obj.coords);
  2450.  
  2451. if(isThisPiece) {
  2452. pieceName = obj.name;
  2453.  
  2454. break;
  2455. }
  2456. }
  2457.  
  2458. if(pieceName) {
  2459. const colorCoords = pieceColorCoordPercentages[pieceName] || [0.5, 0.5];
  2460.  
  2461. const pieceColor = getCanvasPixelColor(pieceElem, colorCoords);
  2462.  
  2463. return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2464. }
  2465. },
  2466.  
  2467. 'pieceElemCoords': obj => {
  2468. const pieceElem = obj.pieceElem;
  2469.  
  2470. return getElemCoordinatesFromLeftTopPixels(pieceElem);
  2471. },
  2472.  
  2473. 'boardDimensions': obj => {
  2474. return [8, 8];
  2475. },
  2476.  
  2477. 'isMutationNewMove': obj => {
  2478. const mutationArr = obj.mutationArr;
  2479.  
  2480. if(isUserMouseDown) {
  2481. return false;
  2482. }
  2483.  
  2484. return mutationArr.length >= 7;
  2485. },
  2486.  
  2487. 'turnFromMutation': obj => {
  2488. const pathname = obj.pathname;
  2489. const mutationArr = obj.mutationArr;
  2490.  
  2491. return defaultTurnFromMutation(mutationArr);
  2492. }
  2493. });
  2494.  
  2495. addSupportedChessSite('chessworld.net', {
  2496. 'boardElem': obj => {
  2497. return document.querySelector('#ChessWorldChessBoard');
  2498. },
  2499.  
  2500. 'pieceElem': obj => {
  2501. return obj.boardQuerySelector('img[src*="merida"');
  2502. },
  2503.  
  2504. 'chessVariant': obj => {
  2505. return 'chess';
  2506. },
  2507.  
  2508. 'boardOrientation': obj => {
  2509. return document.querySelector('div[style*="boardb.jpg"]') ? 'w' : 'b';
  2510. },
  2511.  
  2512. 'pieceElemFen': obj => {
  2513. const pieceElem = obj.pieceElem;
  2514.  
  2515. const [elemPieceColor, elemPieceName] = pieceElem
  2516. ?.src
  2517. ?.split('/')
  2518. ?.pop()
  2519. ?.replace('.png', '')
  2520. ?.split('_');
  2521.  
  2522. const pieceColor = elemPieceColor === 'white' ? 'w' : 'b';
  2523. const pieceName = pieceNameToFen[elemPieceName];
  2524.  
  2525. return pieceColor === 'w' ? pieceName.toUpperCase() : pieceName.toLowerCase();
  2526. },
  2527.  
  2528. 'pieceElemCoords': obj => {
  2529. const pieceElem = obj.pieceElem;
  2530.  
  2531. return chessCoordinatesToIndex(pieceElem?.id);
  2532. },
  2533.  
  2534. 'boardDimensions': obj => {
  2535. return [8, 8];
  2536. },
  2537.  
  2538. 'isMutationNewMove': obj => {
  2539. const mutationArr = obj.mutationArr;
  2540.  
  2541. return mutationArr.length >= 2;
  2542. },
  2543.  
  2544. 'turnFromMutation': obj => {
  2545. const pathname = obj.pathname;
  2546. const mutationArr = obj.mutationArr;
  2547.  
  2548. return defaultTurnFromMutation(mutationArr);
  2549. }
  2550. });
  2551.  
  2552. /* The site is very shitty I don't feel like finishing this
  2553. addSupportedChessSite('chessfriends.com', {
  2554. 'boardElem': obj => {
  2555. return document.querySelector('div[id*="id_board_"');
  2556. },
  2557.  
  2558. 'pieceElem': obj => {
  2559. return obj.boardQuerySelector('img[name][id*="id_piece_"][src*="/pieces/"]');
  2560. },
  2561.  
  2562. 'chessVariant': obj => {
  2563. return 'chess';
  2564. },
  2565.  
  2566. 'boardOrientation': obj => {
  2567. const firstSquareTop = document.querySelector('*[id*="id_square_00_"]')?.style?.top;
  2568.  
  2569. return firstSquareTop === '0px' ? 'w' : 'b';
  2570. },
  2571.  
  2572. 'pieceElemFen': obj => {
  2573. const pieceElem = obj.pieceElem;
  2574.  
  2575. const dataStr = pieceElem.getAttribute('name');
  2576.  
  2577. if(dataStr?.length === 2) {
  2578. const pieceColor = dataStr[0];
  2579. const elemPieceName = dataStr[1];
  2580.  
  2581. return pieceColor == 'w' ? elemPieceName.toUpperCase() : elemPieceName.toLowerCase();
  2582. }
  2583. },
  2584.  
  2585. 'pieceElemCoords': obj => {
  2586. const pieceElem = obj.pieceElem;
  2587.  
  2588. //console.log(getElemCoordinatesFromLeftTopPixels(pieceElem));
  2589.  
  2590. return getElemCoordinatesFromLeftTopPixels(pieceElem);
  2591. },
  2592.  
  2593. 'boardDimensions': obj => {
  2594. return [8, 8];
  2595. },
  2596.  
  2597. 'isMutationNewMove': obj => {
  2598. const mutationArr = obj.mutationArr;
  2599.  
  2600. if(isUserMouseDown) {
  2601. return false;
  2602. }
  2603.  
  2604. return mutationArr.length >= 4
  2605. || mutationArr.find(m => m.type === 'childList') ? true : false
  2606. || mutationArr.find(m => m?.target?.classList?.contains('last-move')) ? true : false;
  2607. },
  2608.  
  2609. 'turnFromMutation': obj => {
  2610. const pathname = obj.pathname;
  2611. const mutationArr = obj.mutationArr;
  2612.  
  2613. return defaultTurnFromMutation(mutationArr);
  2614. }
  2615. });*/
  2616.  
  2617.  
  2618.  
  2619.  
  2620. /*
  2621. _____ __ _ _____ _______ _____ _______ _____ ______ _______ _______ _____ _____ __ _
  2622. | | \ | | | | |_____| | | ____/ |_____| | | | | | \ |
  2623. __|__ | \_| __|__ | __|__ | | |_____ __|__ /_____ | | | __|__ |_____| | \_|
  2624.  
  2625. Code below this point is related to initialization. (e.g. wait for chess board and create the instance)
  2626. */
  2627.  
  2628. async function isAcasBackendReady() {
  2629. const res = await CommLink.commands.ping();
  2630.  
  2631. return res ? true : false;
  2632. }
  2633.  
  2634. async function start() {
  2635. await CommLink.commands.createInstance(commLinkInstanceID);
  2636.  
  2637. const pathname = window.location.pathname;
  2638. const adjustSizeByDimensions = domain === 'chess.com' && pathname?.includes('/variants');
  2639.  
  2640. const boardOrientation = getBoardOrientation();
  2641.  
  2642. instanceVars.playerColor.set(commLinkInstanceID, boardOrientation);
  2643. instanceVars.turn.set(commLinkInstanceID, boardOrientation);
  2644. instanceVars.fen.set(commLinkInstanceID, getFen());
  2645.  
  2646. if(getConfigValue(configKeys.displayMovesOnExternalSite)) {
  2647. BoardDrawer = new UniversalBoardDrawer(chessBoardElem, {
  2648. 'window': window,
  2649. 'boardDimensions': getBoardDimensions(),
  2650. 'playerColor': getPlayerColorVariable(),
  2651. 'zIndex': domain === 'chessarena.com' ? 9999 : 500,
  2652. 'prepend': true,
  2653. 'debugMode': debugModeActivated,
  2654. 'adjustSizeByDimensions': adjustSizeByDimensions ? true : false,
  2655. 'adjustSizeConfig': {
  2656. 'noLeftAdjustment': true
  2657. }
  2658. });
  2659. }
  2660.  
  2661. await updatePlayerColor();
  2662.  
  2663. observeNewMoves();
  2664.  
  2665. CommLink.setIntervalAsync(async () => {
  2666. await CommLink.commands.createInstance(commLinkInstanceID);
  2667. }, 1000);
  2668. }
  2669.  
  2670. function startWhenBackendReady() {
  2671. let timesUrlForceOpened = 0;
  2672.  
  2673. const interval = CommLink.setIntervalAsync(async () => {
  2674. if(await isAcasBackendReady()) {
  2675. start();
  2676.  
  2677. interval.stop();
  2678. } else if(timesUrlForceOpened < 1) {
  2679. timesUrlForceOpened++;
  2680.  
  2681. GM_openInTab(getCurrentBackendURL(), true);
  2682. }
  2683. }, 1000);
  2684. }
  2685.  
  2686. function initializeIfSiteReady() {
  2687. const boardElem = getBoardElem();
  2688. const firstPieceElem = getPieceElem();
  2689.  
  2690. const bothElemsExist = boardElem && firstPieceElem;
  2691. const boardElemChanged = chessBoardElem != boardElem;
  2692.  
  2693. if(bothElemsExist && boardElemChanged) {
  2694. chessBoardElem = boardElem;
  2695.  
  2696. chessBoardElem.addEventListener('mousedown', () => { isUserMouseDown = true; });
  2697. chessBoardElem.addEventListener('mouseup', () => { isUserMouseDown = false; });
  2698.  
  2699. if(!blacklistedURLs.includes(window.location.href)) {
  2700. startWhenBackendReady();
  2701. }
  2702. }
  2703. }
  2704.  
  2705. if(typeof GM_registerMenuCommand === 'function') {
  2706. GM_registerMenuCommand('[u] Open GreasyFork Page', e => {
  2707. GM_openInTab(greasyforkURL, true);
  2708. }, 'u');
  2709.  
  2710. GM_registerMenuCommand('[o] Open GUI Manually', e => {
  2711. GM_openInTab(getCurrentBackendURL(), true);
  2712. }, 'o');
  2713.  
  2714. GM_registerMenuCommand('[s] Start Manually', e => {
  2715. if(chessBoardElem) {
  2716. start();
  2717. } else {
  2718. displayImportantNotification('Failed to start manually', 'No chessboard element found!');
  2719. }
  2720. }, 's');
  2721.  
  2722. GM_registerMenuCommand('[g] Get Moves Manually', e => {
  2723. if(chessBoardElem) {
  2724. onNewMove(null, true);
  2725. } else {
  2726. displayImportantNotification('Failed to get moves', 'No chessboard element found!');
  2727. }
  2728. }, 'g');
  2729.  
  2730. GM_registerMenuCommand('[r] Render BoardDrawer Manually', e => {
  2731. if(typeof BoardDrawer?.updateDimensions === 'function') {
  2732. BoardDrawer.updateDimensions();
  2733. } else {
  2734. displayImportantNotification('Failed to render BoardDrawer', 'BoardDrawer not initialized or something else went wrong!');
  2735. }
  2736. }, 'r');
  2737.  
  2738. if(typeof GM_setClipboard === 'function') {
  2739. GM_registerMenuCommand('[c] Copy FEN to Clipboard', e => {
  2740. if(chessBoardElem) {
  2741. GM_setClipboard(getFen());
  2742. } else {
  2743. displayImportantNotification('Failed to get FEN', 'No chessboard element found!');
  2744. }
  2745. }, 'c');
  2746. }
  2747. }
  2748.  
  2749. setInterval(initializeIfSiteReady, 1000);

QingJ © 2025

镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址