// ==UserScript==
// @name История изменений
// @namespace http://tampermonkey.net/
// @version 1.1
// @description История изменений списка для конкретного тайтла.
// @author grin3671
// @license MIT
// @match https://shikimori.one/*
// @icon https://www.google.com/s2/favicons?sz=64&domain=shikimori.one
// @grant none
// ==/UserScript==
(function() {
"use strict";
function checkPage () {
let currentURL = location.pathname.substring(1).split("/");
switch (currentURL[0]) {
case "animes":
case "mangas":
case "ranobe":
setTimeout(() => {
if (getTitleID() && currentURL[2] === undefined && isTitleInUserList()) {
console.log("addTitleHistoryButton");
// remove old
if (document.getElementById("btn-title-history")) document.getElementById("btn-title-history").remove();
if (document.getElementById("modal-title-history")) document.getElementById("modal-title-history").remove();
addButton();
}
}, 500);
break;
}
}
/**
* Adds Button to TitlePage
*/
function addButton () {
let parent = document.querySelector(".b-db_entry .b-user_rate").parentElement;
let el = createElement("div", { "id": "btn-title-history", "class": "b-link_button dark mt-2 mb-2", "text": "История изменений" });
el.onclick = () => {
openModal();
};
parent.append(el);
}
/**
* Gets User's ID
* @return (string) User ID
* @return (boolean) false if User ID can't be found
*/
function getUserID () {
let data = JSON.parse(document.body.getAttribute("data-user"));
return data.id || false;
}
/**
* Gets Title's ID
* @return (string) Title ID
* @return (boolean) false if Title ID can't be found
*/
function getTitleID () {
let el = document.querySelector(".b-db_entry .b-user_rate");
if (!el) return false; // for checkPage function
// data-entry="{"id":3649}"
let data = JSON.parse(el.getAttribute("data-entry"));
return data.id || false;
}
/**
* Gets Title's type
* @return (string) Title type for API (Anime || Manga)
*/
function getTitleType () {
let currentURL = location.pathname.substring(1).split("/");
switch (currentURL[0]) {
case "animes":
return "Anime";
break;
case "mangas":
case "ranobe":
return "Manga";
break;
}
}
function isTitleInUserList () {
let e = document.querySelector(".b-db_entry .b-user_rate form[action*='/api/v2/user_rates']");
e = e ? e.getAttribute("data-method") : false;
return e == "PATCH" ? true : false;
}
/**
* Utility. Creates Element with custom properties
* @property type (string) element type
* @property data (object) custom properties (id, class, text, style)
* @property callback (function) callback with element itself as func prop
* @return element
*/
function createElement (type, data, callback) {
let e = document.createElement(type || "div");
if (data && data.id) e.id = data.id;
if (data && data.class) e.classList = data.class;
if (data && data.text) e.textContent = data.text;
if (data && data.style) e.setAttribute("style", data.style);
if (typeof callback === "function") callback(e);
return e;
}
/**
* Creates Modal for Title's History
* @return element
*/
function createModal () {
let modal = createElement("div", { "id": "modal-title-history", "class": "b-modal hidden" });
let modal_inner = createElement("div", { "class": "inner", "style": "top: 0" });
let modal_title = createElement("div", { "class": "subheadline m5", "text": "История изменений" });
let modal_block = createElement("div", { "id": "title-history-block", "class": "pt-2 pb-2 mb-4", "style": "min-height: 150px; max-height: 500px; overflow: hidden auto; resize: vertical;" });
let modal_close = createElement("div", { "class": "b-button", "text": "Закрыть" }, (e) => { e.onclick = () => { closeModal() } });
let modal_shade = createElement("div", { "class": "b-shade active" }, (e) => { e.onclick = () => { closeModal() } });
modal_inner.append(modal_title, modal_block, modal_close);
modal.append(modal_shade, modal_inner);
getHistory(modal_block);
return modal;
}
function openModal () {
let modal = document.getElementById("modal-title-history") || document.body.appendChild(createModal());
modal.classList.remove("hidden");
}
function closeModal () {
let modal = document.getElementById("modal-title-history") || document.body.appendChild(createModal());
modal.classList.add("hidden");
}
/**
* Gets API History and Fills block
* @property block (element) Node where the History will be filled
*/
function getHistory (block) {
let user_id = getUserID();
let title_id = getTitleID();
let title_type = getTitleType();
// reset data
block.innerHTML = "";
block.classList.add("b-ajax");
fetch("https://shikimori.one/api/users/" + user_id + "/history?target_id=" + title_id + "&target_type=" + title_type + "&limit=100")
.then((response) => response.json())
.then((data) => {
block.classList.remove("b-ajax");
console.log(JSON.stringify(data, undefined, 2));
let history = fillHistoryBlocks(data);
let historyTbody;
let historyTable = block.appendChild(createElement("table", { "class": "b-table b-editable_grid block2" }, e => {
historyTbody = e.appendChild(createElement("tbody"));
}));
history.forEach(entry => historyTbody.append(entry));
if (history.length == 0) block.append(createElement("div", { "class": "b-nothing_here d-flex justify-content-center p-4", "text": "История изменений тайтла отсутствует." }));
});
}
/**
* Fills Table Row from API History
* @property history (array) API formatted data
* @return historyBlocks (array) Array with Nodes
*/
function fillHistoryBlocks (history) {
let historyBlocks = [];
history.forEach((entry, index) => {
let row = createElement("tr", { "class": "b_history-row" }, (e) => {
e.append(
createElement("td", { "text": (index + 1).toString() }),
createElement("td", { "text": entry.description }),
createElement("td", { "text": new Intl.DateTimeFormat([], { dateStyle: "long", timeStyle: "medium"}).format(Date.parse(entry.created_at)) }),
createElement("td", { "text": "id: " + entry.id })
)
});
historyBlocks.push(row);
});
return historyBlocks;
}
checkPage();
document.addEventListener("page:load", checkPage);
document.addEventListener("turbolinks:load", checkPage);
})();