// ==UserScript==
// @name Proxer Statistics
// @namespace https://greasyfork.org/de/users/83349-deimos
// @version 1.31
// @description Zählt die bereits geschauten/gelesenen Animes/Mangas und erlaubt es die Tabellen per Klick zusammenzuklappen bzw. sich mehr Details anzeigen zu lassen.
// @author Deimos
// @run-at document-start
// @include http://proxer.me/*
// @include https://proxer.me/*
// @include http://www.proxer.me/*
// @include https://www.proxer.me/*
// @grant unsafeWindow
// @history 1.31 Beheben von Cookie Problem, Beheben von nicht erkannten URLs
// @history 1.30 Entferne Userscript Anker, Ergänze Novel, Zähle Medienkategorien dynamisch
// @history 1.2 Script funktioniert auch im User-Control-Panel
// @history 1.1 Einbinden des Userscript Anker von Blue.Reaper
// @history 1.0 Zählen von Anime/Manga, anzeigen von Details, minimieren von Tabellen
// ==/UserScript==
//############## Initialisierung ####################
var page = 0;
var n_values = 12
//guckt ob Cookie vorhanden ist und korrekted Format hat, ansonsten wird neuer erstellt
var hv_values = checkCookie("hv_values", "v".repeat(n_values)); //v:= visible h:= hidden
var dn_values = checkCookie("dn_values", "n".repeat(n_values)); //d:= details n:= no details
//############################# Einbinden des Scripts #############################
//startet das Script beim Laden der Seite und
document.addEventListener('DOMContentLoaded', function(event) {
waitForKeyElements ("#pageMetaAjax", applyChange, false);
});
function applyChange(){
var path1 = window.location.pathname.split('/')[3]
var path2 = window.location.search
if(path1 == "anime" || path2.substring(0,8) === "?s=anime") ///User befindet sich auf Anime Verzeichnis
{
page = 0;
tableListener();
}
else if(path1 == "manga" || path2.substring(0,8) === "?s=manga") ///User befindet sich auf Manga Verzeichnis
{
page = 1;
tableListener();
}
else if(path1 == "novel" || path2.substring(0,8) === "?s=novel") ///User befindet sich auf Novel Verzeichnis
{
page = 2;
tableListener();
}
}
//############################# Hauptteil #############################
//Ermitteln der Tabellenlänge und setzen der EventListener
function tableListener()
{
var tables = document.getElementsByTagName("table");
for(var i = 0; i<tables.length;i++){
var tr = tables[i].rows;
var l = 0;
if(tr[2].getElementsByTagName("td")[0].innerHTML !== "Keine Einträge.")
l = tr.length - 2;
var message= ": " + l;
//Anzahl anzeigen
tr[0].getElementsByTagName("th")[0].innerHTML+= message;
tr[0].addEventListener("click",action);
tr[0].id ="tr"+(i+page*4);
//Tabellen einklappen
if(hv_values[i+page*4]=="h")
hide(tr);
//Details anzeigen
if(dn_values[i+page*4]=="d")
details(tr);
}
}
//Auswahl ob "hide" oder "details", setzen des neuen Cookies
function action(e)
{
var tr = this.parentElement.parentElement.rows;
var xPosition = e.clientX;
var rect = tr[0].getBoundingClientRect();
var width = tr[0].offsetWidth;
if(xPosition<width/2){
details(tr);
setCookie("dn_values",dn_values);
}
else{
hide(tr);
setCookie("hv_values",hv_values);
}
}
//Anzeigen der genaueren Details der ausgewählten Tabelle
function details(tr)
{
//Checke ob Einträge vorhanden sind
if(!tr[2].getElementsByTagName("td")[2]){
return false
}
var id = parseInt(tr[0].id.substring(2));
var text = tr[0].getElementsByTagName("th")[0].innerHTML;
//Details werden minimiert
if(text.includes("<br>")){
text = text.slice(0,text.indexOf("<br>"));
tr[0].getElementsByTagName("th")[0].innerHTML = text;
//Update des Status
dn_values = dn_values.substring(0,id) + "n" + dn_values.substring(id+1, dn_values.length); //erstellen eines neuen Cookies
return true;
}
var l = tr.length;
var content = {}
//Zähle verschieden Medientypen
for(var e = 2; e<l; e++){
var type = tr[e].getElementsByTagName("td")[2].innerHTML;
if(type in content)
content[type] +=1
else
content[type] =1
}
//Anzeigen
var message= "";
for(type in content){
message += "<br> "+type.replace("<br>"," ")+": "+content[type]
}
tr[0].getElementsByTagName("th")[0].innerHTML+= message;
//Update des Status
dn_values = dn_values.substring(0,id) + "d" + dn_values.substring(id+1, dn_values.length);
}
//Einklappen der ausgewählten Tabelle
function hide(tr)
{
var id = parseInt(tr[0].id.substring(2));
var visibility;
var char;
if(tr[1].style.display == "none") {
visibility = "table-row";
char = "v";
}
else{
visibility = "none";
char = "h";
}
//Update des Status
hv_values = hv_values.substring(0,id) + char + hv_values.substring(id+1, hv_values.length);
for(var e = 1; e < tr.length; e++){
tr[e].style.display = visibility;
}
}
//############################# Cookies #############################
function checkCookie(cname,ctext)
{
var cookie = getCookie(cname);
if (cookie === "" || cookie.length != n_values)
{
cookie = ctext;
setCookie(cname, cookie);
}
return cookie;
}
function getCookie(cname)
{
var name = cname + "=";
var ca = document.cookie.split(";");
for(var i = 0; i < ca.length; i++) {
var c = ca[i];
while (c.charAt(0) == ' ') {
c = c.substring(1);
}
if (c.indexOf(name) === 0) {
return c.substring(name.length, c.length);
}
}
return "";
}
function setCookie(cname, cvalue)
{
var d = new Date();
d.setTime(d.getTime() + (365*24*60*60*1000));
var expires = "expires="+d.toUTCString();
document.cookie = cname + "=" + cvalue + ";" + expires + ";path=/" + ";SameSite=Strict;"
}
//############################### Hintergrund Thread #######################################
//Code from: https://github.com/CoeJoder/waitForKeyElements.js
//(Greasy Fork doesn't allow to include this project by "require")
function waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals) {
if (typeof waitOnce === "undefined") {
waitOnce = true;
}
if (typeof interval === "undefined") {
interval = 300;
}
if (typeof maxIntervals === "undefined") {
maxIntervals = -1;
}
var targetNodes = (typeof selectorOrFunction === "function")
? selectorOrFunction()
: document.querySelectorAll(selectorOrFunction);
var targetsFound = targetNodes && targetNodes.length > 0;
if (targetsFound) {
targetNodes.forEach(function(targetNode) {
var attrAlreadyFound = "data-userscript-alreadyFound";
var alreadyFound = targetNode.getAttribute(attrAlreadyFound) || false;
if (!alreadyFound) {
var cancelFound = callback(targetNode);
if (cancelFound) {
targetsFound = false;
}
else {
targetNode.setAttribute(attrAlreadyFound, true);
}
}
});
}
if (maxIntervals !== 0 && !(targetsFound && waitOnce)) {
maxIntervals -= 1;
setTimeout(function() {
waitForKeyElements(selectorOrFunction, callback, waitOnce, interval, maxIntervals);
}, interval);
}
}