jpdb-clear-review-history

Adds a button to clear review history for a word during jpdb reviews, with 'd' as hotkey

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Greasemonkey lub Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana jest instalacje jednego z następujących rozszerzeń: Tampermonkey, Violentmonkey.

Aby zainstalować ten skrypt, wymagana będzie instalacja rozszerzenia Tampermonkey lub Userscripts.

You will need to install an extension such as Tampermonkey to install this script.

Aby zainstalować ten skrypt, musisz zainstalować rozszerzenie menedżera skryptów użytkownika.

(Mam już menedżera skryptów użytkownika, pozwól mi to zainstalować!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Będziesz musiał zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

Musisz zainstalować rozszerzenie menedżera stylów użytkownika, aby zainstalować ten styl.

(Mam już menedżera stylów użytkownika, pozwól mi to zainstalować!)

// ==UserScript==
// @name jpdb-clear-review-history
// @version 1
// @homepage https://gitlab.com/ioric/jpdb-clear-review-history
// @namespace https://gitlab.com/ioric/jpdb-clear-review-history
// @description Adds a button to clear review history for a word during jpdb reviews, with 'd' as hotkey
// @author ioric
// @license https://opensource.org/licenses/MIT
// @match https://jpdb.io/review*
// @connect self
// ==/UserScript==
'use strict';

function clearReviewHistory() {
  // origin url is sent to clear-review-history form, which is
  // based on the vocabulary url that the answer word links to
  let orig = document.querySelector('.answer-box a')?.href;
  // while the 'c' value is sent to the grade review form, the
  // 'v' and 's' values in it are sent to clear-review-history
  const c = document.querySelector('input[name="c"]')?.value;
  if (!(c && orig)) {
    console.error('Vocabulary variables not found');
    return;
  }
  const [_, v, s] = c.split(',');
  orig = orig.replace('#a', '').concat('/review-history');

  fetch('/clear-review-history', {
    method: 'POST',
    headers: {
        Accept: '*/*',
        'Content-Type': 'application/x-www-form-urlencoded',
    },
    body: `v=${v}&s=${s}&origin=${orig}`,
  }).then((response) => {
    if (response.status == 302 || response.status == 200) {
      // now submit the review form to move to the next word
      document.querySelector('#grade-clear').parentElement.submit();
    } else {
      console.error(`Unexpected response from clearing review history: ${response.status}`);
    }
  }).catch((e) => {
    console.error(e);
  });
}

function insertClearButton() {
  // Add a new 'Clear history' button between Blacklist and Never forget

  const blacklistButton = document.querySelector('#grade-blacklist');
  if (blacklistButton == null) {
    console.error('blacklist button not found');
    return;
  }
  const blacklistForm = blacklistButton.parentElement;
  // we want the whole form for "submitting the review" later
  // this will have no effect on the word as the history has been cleared
  // but will move forward to the next review properly
  const clearForm = blacklistForm.cloneNode(true);
  const clearButton = clearForm.querySelector('#grade-blacklist');
  clearForm.querySelector('input[name="g"]').value = '1';
  clearButton.id = 'grade-clear';
  clearButton.value = 'Clear history';
  clearButton.type = 'button'; // prevent submit, which will be manually triggered later
  clearButton.style = 'padding: 0'; // restore style that only applies to 'submit' type
  clearButton.onclick = clearReviewHistory;
  blacklistForm.insertAdjacentElement('afterend', clearForm);

  // Change "I'll never forget" label to save some space
  document.querySelector('#grade-permaknown').value = 'Never forget';

  document.addEventListener("keyup", (e) => {
      if (e.key == 'd') { // 'c' would be nice but it's already used by jpdb
          e.preventDefault();
          document.querySelector('#grade-clear').click();
      }
  });
}

(() => {
  // Find the div with the 'Show answer' button, add a new button
  // after it changes to show the review buttons on the card back
  const reviewButtonGroup = document.querySelector('.review-button-group');
  if (reviewButtonGroup == null) {
    console.error('review-button-group not found');
    return;
  }

  const callback = (mutationList, _) => {
    for (const mutation of mutationList) {
      if (mutation.type == 'childList'
          && mutation.addedNodes[0].tagName == 'DIV') {
        insertClearButton();
      }
    }
  }
  const observer = new MutationObserver(callback);
  observer.observe(reviewButtonGroup, {childList: true, subtree: true});
})();