Save reviews on Bookmeter(読書メーター) in real time
// ==UserScript==
// @name BookmeterReviewSaver
// @namespace https://github.com/mosaicer
// @version 1.0.0
// @description Save reviews on Bookmeter(読書メーター) in real time
// @author mosaicer
// @match https://bookmeter.com/*
// run-at document-idle
// @grant GM_getValue
// @grant GM_setValue
// ==/UserScript==
(function() {
'use strict';
const readModal = document.getElementById('modals').children[2];
const reviewArea = readModal.getElementsByTagName('textarea')[0]
const hiddenTextArea = reviewArea.previousSibling;
let targetBookId = null;
let working = false;
const hiddenTextAreaObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
// テキストが変更されたとき、キーを本のID・値を変更後のテキストで保存する
if (mutation.addedNodes.length > 0 && targetBookId && working) {
const text = mutation.addedNodes[0].textContent.trim();
GM_setValue(targetBookId, text);
}
});
});
const hiddenTextAreaObserverConfig = { childList: true };
const readModalObserver = new MutationObserver((mutations) => {
mutations.forEach((mutation) => {
// ダイアログ(モーダル)がアクティブ(前面)になったときに、監視を始める
if (mutation.target.classList.contains('modal--active')) {
hiddenTextAreaObserver.observe(hiddenTextArea, hiddenTextAreaObserverConfig);
}
// ダイアログ(モーダル)が閉じたときに、値をリセットし、動作を停止する
else {
working = false;
targetBookId = null;
hiddenTextAreaObserver.disconnect();
}
});
});
const readModalObserverConfig = { attributes: true };
// 読んだ本として登録するダイアログ(モーダル)の監視を開始する
readModalObserver.observe(readModal, readModalObserverConfig);
// クリックイベントリスナのセットアップ
document.addEventListener('click', (e) => {
const targetNode = e.target;
// 下記クラスを持たないノードは無視
if (targetNode.className !== 'js-modal-button modal-button') {
return;
}
// 読んだ本として登録するアクション以外は無視
const actionText = targetNode.textContent;
if (actionText !== '読んだ本に登録' && actionText !== '再読本に登録') {
return;
}
const bookData = JSON.parse(targetNode.getAttribute('data-modal'));
// 本情報を持っていなければ無視
if (!bookData.hasOwnProperty('book')) {
return;
}
targetBookId = bookData.book.id;
const draft = GM_getValue(targetBookId);
if (draft) {
reviewArea.value = draft;
// フォーカス&ブラーで入力チェッカーのイベントを発火させる
reviewArea.focus();
reviewArea.blur();
}
// 登録ボタン押下を動作開始の契機とする
working = true;
}, false);
})();