Lichess show mistakes on the board

If your move is a blunder or inaccuracy, a red circle will signal this on the board.

您需要先安装一个扩展,例如 篡改猴Greasemonkey暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴暴力猴,之后才能安装此脚本。

您需要先安装一个扩展,例如 篡改猴Userscripts ,之后才能安装此脚本。

您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。

您需要先安装用户脚本管理器扩展后才能安装此脚本。

(我已经安装了用户脚本管理器,让我安装!)

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展,比如 Stylus,才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

您需要先安装一款用户样式管理器扩展后才能安装此样式。

(我已经安装了用户样式管理器,让我安装!)

// ==UserScript==
// @name     Lichess show mistakes on the board
// @description If your move is a blunder or inaccuracy, a red circle will signal this on the board.
// @require https://ajax.googleapis.com/ajax/libs/jquery/3.5.1/jquery.min.js
// @grant    none
// @include     http://*.lichess.org/*
// @include  https://lichess.org/*
// @version     1.6
// @namespace https://greasyfork.org/users/729712
// ==/UserScript==

let $ = jQuery.noConflict();

$(document).ready(() => {
  setTimeout(() => {
    init();
  }, 500);
});

let clickedElement;

const COLORS = {
  '?': '#E69F00',
  '?!': '#56B4E9',
  '??': '#ff3d00',
};

const MOVE_TYPES = {
  Blunder: '??',
  Mistake: '?!',
  Inaccuracy: '?',
};

/**
 * Highlight the move type on the board (OTB)
 */
const highlightMoveTypeOTB = () => {
  clearBoardIndicators();
  const activeMove = getActiveMove();

  if (activeMove) {
    let moveType;

    moveType = getMoveType(activeMove.innerText);

    const lm = $('.last-move')[0];
    if (moveType) {
      const color = COLORS[moveType];
      lm.innerHTML = `<div>
   <div style="width: 20px;height: 20px;background: ${color};position: absolute;right: 0;border-radius: 10px;"></div>
   <div style="position: absolute;right: 0px;top: 0px;font-weight: bold;width: 20px;height: 20px;/*! align-content: space-evenly; */text-align: center;line-height: 20px;color: black;">${moveType}</div>
 	</div>`;
    } else {
      lm.innerHTML = '';
    }
  } else {
    console.log('no active move found');
  }
};

/**
 * Returns the move type
 */
const getMoveType = (text) => {
  if (text.indexOf(MOVE_TYPES.Blunder) !== -1) return MOVE_TYPES.Blunder;
  if (text.indexOf(MOVE_TYPES.Mistake) !== -1) return MOVE_TYPES.Mistake;
  if (text.indexOf(MOVE_TYPES.Inaccuracy) !== -1) return MOVE_TYPES.Inaccuracy; // check this last!
  return null;
};

/**
 * Clears the board indicators. in case we move away from the main line via explorer
 */
const clearBoardIndicators = () => {
  $('.last-move')[0].innerHTML = '';
  $('.last-move')[1].innerHTML = '';
};

/**
 * determines if a button is a review navigation button
 */
const isReviewNavButton = (but) => {
  const goodActions = ['first', 'prev', 'next', 'last'];
  return goodActions.indexOf(but.dataset.act) !== -1;
};

/**
 * returns all the navigation buttons << < > >>
 */
const getNavButtons = () => {
  const navButtons = [];
  const allButtons = document.getElementsByTagName('button');

  for (let i = 0; i < allButtons.length; i++) {
    if (isReviewNavButton(allButtons[i])) {
      navButtons.push(allButtons[i]);
    }
  }
  return navButtons;
};

/**
 * Returns the active move
 */
const getActiveMove = () => {
  const activeElements = document.getElementsByClassName('active');
  for (let i = 0; i < activeElements.length; i++) {
    if (activeElements[i].tagName === 'MOVE') {
      return activeElements[i];
    }
  }
  return null;
};

/**
 * Highlight the move types in the moves list
 */
const highlightMoveTypeITML = () => {
  const moves = document.getElementsByTagName('move');
  for (let i = 0; i < moves.length; i++) {
    const moveType = getMoveType(moves[i].innerText);
    if (moveType) {
      moves[i].style.color = COLORS[moveType];
    }
  }
};

const init = () => {
  // initial highlight of the moves
  highlightMoveTypeITML();

  // set the listeners for the action buttons
  const navButtons = getNavButtons();
  navButtons.forEach((button) => {
    button.onclick = () => {
      highlightMoveTypeOTB();
      highlightMoveTypeITML();
    };
  });
//   $(document).mousedown((ev) => {
//     clickedElement = ev.target;
//   });

  // tricky one. a user can also click on the bad move and we need to show the move, or on the
  // engine line and we need to clear the mistakes. this will double trigger the render when
  // you click on the arrows
  $(window).mouseup((ev) => {
//     const isMove = clickedElement.tagName === "MOVE" || clickedElement.parentElement.tagName === "MOVE";
//     const isChoiceButton = ev.target.parentElement.classList == "choices";

    setTimeout(()=> {
      clearBoardIndicators();
      highlightMoveTypeOTB();
      highlightMoveTypeITML();
    }, 50);

  });

  // set the listeners for the key down / up
  document.onkeyup = function (e) {
    switch (e.which) {
      case 32: // spacebar
      case 37: // left
      case 38: // up
      case 39: // right
      case 40: // down
        clearBoardIndicators();
        highlightMoveTypeOTB();
        highlightMoveTypeITML();
        break;
      default: // exit this handler for other keys
    }
  };
};