NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name WatchAnything
// @namespace https://openuserjs.org/users/Pasha13666
// @version 1.1.3
// @description [shikimori.one] Кнопка открытия случайного аниме из списка
// @author NekoNekoNyan
// @match http://shikimori.me/*
// @match https://shikimori.me/*
// @match http://shikimori.one/*
// @match https://shikimori.one/*
// @match http://shikimori.org/*
// @match https://shikimori.org/*
// @updateURL https://openuserjs.org/meta/Pasha13666/WatchAnything.meta.js
// @homepageURL https://github.com/neko-neko-nyan/WatchAnything
// @run-at document-body
// @license MIT
// @grant GM_addStyle
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_log
// @copyright 2018-2023, NekoNekoNyan (https://github.com/neko-neko-nyan)
// ==/UserScript==
const LIST_NAMES = ['planned', 'watching', 'completed', 'on_hold', 'dropped'];
function WatchAnything(){
this.onRandomClicked = this.onRandomClicked.bind(this);
this.triggerChange = this.triggerChange.bind(this);
}
WatchAnything.prototype.triggerChange = function(){
if (!document.body || !document.body.classList.contains("p-user_rates"))
return;
GM_log("[WatchAnything] Starting / loading...");
if (location.href !== this.prevUrl) {
this.prevUrl = location.href;
this.onUrlChanged();
}
this.bindToLists();
}
WatchAnything.prototype.onUrlChanged = function (){
const url = window.location.pathname.split('/');
this.data = {};
this.kind = url[3];
for (let i = 4; i < url.length; i+=2) {
const k = url[i], v = url[i + 1];
if (k !== 'order-by')
this.data[k] = v;
}
this.data.censored = false;
this.data.limit = 50;
this.csrf = document.querySelector('meta[name=csrf-token]').content;
}
WatchAnything.prototype.bindToLists = function (){
for (let i = 0; i < 5; i++){
const $header = document.querySelector('.status-' + i);
if (!$header || $header.children[1].classList.contains('wa-link'))
continue;
const $link = document.createElement('div');
$link.classList = "b-options-floated wa-link";
$link.innerHTML = '<a href="#" class="action">рандом</a>';
$link.dataset.listName = LIST_NAMES[i];
$link.addEventListener('click', this.onRandomClicked);
$link.addEventListener('auxclick', this.onRandomClicked);
$header.children[1].before($link);
}
}
WatchAnything.prototype.onRandomClicked = function(e) {
const newTab = e.which !== 1;
const $link = e.currentTarget;
e.preventDefault();
this.loadList(1, $link.dataset.listName, function(c){
c = c[Math.floor(Math.random() * c.length)];
GM_log("[WatchAnything] Opening random selected:", c.russian || c.name);
if (newTab) window.open(location.origin + c.url, '_blank', '');
else window.location.pathname = c.url;
});
if (newTab) e.stopImmediatePropagation();
}
WatchAnything.prototype.loadList = function(page, mylist, fn){
const ajax = new XMLHttpRequest();
const data = new Object(this.data);
data.page = page;
data.mylist = mylist;
ajax.open("GET", location.origin + '/api/' + this.kind + 's?' + Object.keys(data).map(key => key + '=' + data[key]).join('&'), true);
ajax.setRequestHeader('X-CSRF-Token', this.csrf);
ajax.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
ajax.setRequestHeader('X-Userscript', 'WatchAnything');
ajax.onreadystatechange = () => {
if (ajax.readyState !== 4 || ajax.status !== 200)
return;
const cnt = JSON.parse(ajax.responseText);
if (cnt.length === 50) {
if (page % 5 === 0) setTimeout(() => this.loadList(page + 1, mylist, function(c){
fn(cnt.concat(c));
}), 1000);
else this.loadList(page + 1, mylist, function(c){
fn(cnt.concat(c));
});
} else fn(cnt);
}
ajax.send();
}
document.head.append(GM_addStyle(".wa-link { right: 90px; }"));
const wa = new WatchAnything;
document.addEventListener('ready', wa.triggerChange);
document.addEventListener('page:load', wa.triggerChange);
document.addEventListener('turbolinks:load', wa.triggerChange);
wa.triggerChange();