// ==UserScript==
// @name Nyaa Linker
// @namespace https://github.com/po5
// @version 2.3.0
// @description Adds a button to Anime and Manga database websites that opens a relevant Nyaa search
// @author Metacor, eva
// @match *://*.myanimelist.net/*
// @match *://*.anilist.co/*
// @match *://*.kitsu.app/*
// @match *://*.anime-planet.com/*
// @match *://*.animenewsnetwork.com/encyclopedia/*
// @match *://*.anidb.net/*
// @match *://*.livechart.me/*
// @grant GM.getValue
// @grant GM.setValue
// @grant GM.registerMenuCommand
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @license GPL-3.0
// @run-at document-end
// ==/UserScript==
// GreaseMonkey 4.x shim
let getValue;
if (typeof GM_getValue === 'undefined' && typeof GM !== 'undefined') {
self.GM_setValue = GM.setValue;
self.GM_registerMenuCommand = GM.registerMenuCommand;
getValue = GM.getValue;
} else {
getValue = function(key, fallback) {
return new Promise(function(resolve, reject) {
try {
resolve(GM_getValue(key, fallback));
}
catch(e) {
reject(e);
}
});
};
}
let settings;
const defaultSettings = {
filter_setting: '0',
category_setting: '1_2',
query_setting: 'default',
sort_setting: 'seeders',
order_setting: 'desc',
hide_button_setting: false,
focus_setting: false,
custom_text_toggle_setting: false,
custom_text_setting: '',
hotkey_key_setting: '',
hotkey_modifier_setting: '',
hotkey_query_setting: 'inherit',
};
if (typeof GM_registerMenuCommand !== 'undefined') {
GM_registerMenuCommand('Nyaa Linker Settings', () => {
if (document.getElementById('nyaa-linker-settings')) return;
const settingsPanel = document.createElement('div');
settingsPanel.id = 'nyaa-linker-settings';
settingsPanel.style.position = 'fixed';
settingsPanel.style.top = '50%';
settingsPanel.style.left = '50%';
settingsPanel.style.transform = 'translate(-50%, -50%)';
settingsPanel.style.backgroundColor = 'var(--clrDark, hsl(0, 0%, 10%))';
settingsPanel.style.color = 'var(--clrLight, hsl(0, 0%, 87%))';
settingsPanel.style.padding = '20px';
settingsPanel.style.border = '1px solid var(--clrAccent, hsl(210, 100%, 60%))';
settingsPanel.style.zIndex = '10000';
settingsPanel.style.overflow = 'hidden';
settingsPanel.style.fontFamily = 'Verdana, Arial';
settingsPanel.style.fontSize = '11px';
settingsPanel.style.lineHeight = '16px';
settingsPanel.innerHTML = `
<style>
#nyaa-linker-settings {
--clrDark: hsl(0, 0%, 10%);
--clrLight: hsl(0, 0%, 87%);
--clrAccent: hsl(210, 100%, 60%);
}
#nyaa-linker-settings,
#nyaa-linker-settings::before,
#nyaa-linker-settings::after,
#nyaa-linker-settings *,
#nyaa-linker-settings *::before,
#nyaa-linker-settings *::after {
all: revert;
}
#nyaa-linker-settings *,
#nyaa-linker-settings *::before,
#nyaa-linker-settings *::after {
box-sizing: border-box;
margin: 0;
padding: 0;
border: none;
font-size: 11px;
}
#nyaa-linker-settings-parametersPage,
#nyaa-linker-settings-settingsPage {
display: grid;
grid-template-columns: auto auto;
gap: 2px;
padding: 2px 0 2px 2px;
text-align: right;
}
#nyaa-linker-settings-settingsPage {
border: 1px solid var(--clrAccent);
border-top: none;
padding-left: 25px;
}
#nyaa-linker-settings button,
#nyaa-linker-settings input,
#nyaa-linker-settings select,
#nyaa-linker-settings option {
cursor: pointer;
text-align: center;
height: 21px;
}
#nyaa-linker-settings select,
#nyaa-linker-settings-saveButton,
#nyaa-linker-settings-hotkey_key_select,
#nyaa-linker-settings-custom_text_select {
background: var(--clrAccent);
color: var(--clrDark);
}
#nyaa-linker-settings option,
#nyaa-linker-settings-settingsPage {
background: var(--clrDark);
color: var(--clrAccent);
}
#nyaa-linker-settings-bottomButtons {
display: flex;
gap: 2px;
}
#nyaa-linker-settings-saveButton {
flex: 11;
}
#nyaa-linker-settings-settingsButton {
background: var(--clrAccent) url('https://upload.wikimedia.org/wikipedia/commons/5/58/Ic_settings_48px.svg') no-repeat 0 0 / contain;
flex: 1;
}
#nyaa-linker-settings-hotkey_key_select,
#nyaa-linker-settings input[type='checkbox'] {
width: 21px;
justify-self: left;
}
#nyaa-linker-settings-hotkey_modifier_select,
#nyaa-linker-settings-hotkey_query_select,
#nyaa-linker-settings-custom_text_select {
width: 98%;
}
</style>
<div id="nyaa-linker-settings-parametersPage">
<label for="filter_select">Filter:</label>
<select id="filter_select">
<option value="0">No Filter</option>
<option value="1">No Remakes</option>
<option value="2">Trusted Only</option>
</select>
<label for="category_select">Category:</label>
<select id="category_select">
<option value="0_0">All Categories</option>
<option value="1_2">English-Translated</option>
<option value="1_3">Non-English-Translated</option>
<option value="1_4">Raw</option>
</select>
<label for="query_select">Query:</label>
<select id="query_select">
<option value="default" title="Creates a search using both the 'Exact' and 'Base' options">Default</option>
<option value="fuzzy" title="Searches for the site's default title only, without quotes — allows fuzzy matching">Fuzzy</option>
<option value="exact" title="Japanese and English full titles — searches for exact title names as written">Exact</option>
<option value="base" title="Japanese and English base titles — searches with Seasons and Parts removed">Base</option>
</select>
<label for="sort_select">Sort:</label>
<select id="sort_select">
<option value="comments">Comments</option>
<option value="size">Size</option>
<option value="id">Date</option>
<option value="seeders">Seeders</option>
<option value="leechers">Leechers</option>
<option value="downloads">Downloads</option>
</select>
<label for="order_select">Order:</label>
<select id="order_select">
<option value="desc">Descending</option>
<option value="asc">Ascending</option>
</select>
</div>
<div id="nyaa-linker-settings-bottomButtons">
<button id="nyaa-linker-settings-saveButton">Save and Close</button>
</div>
<div id="nyaa-linker-settings-settingsPage">
<label for="hide_button_select">Hide Button:</label>
<input type="checkbox" id="hide_button_select" title="Stops the 'Search on Nyaa' button from being rendered">
<label for="focus_select">Maintain Focus:</label>
<input type="checkbox" id="focus_select" title="Changes Tab Focus behavior when using the Hotkey">
<label for="custom_text_toggle_select">Include Text:</label>
<input type="checkbox" id="custom_text_toggle_select" title="Decides if text in 'Custom Query' is included">
<label for="custom_text_select">Custom Text:</label>
<input type="text" placeholder="?" id="custom_text_select" title="User defined text to be added at the end of the search query">
<label for="hotkey_key_select">Hotkey:</label>
<input type="text" maxlength="1" placeholder="?" id="hotkey_key_select">
<label for="hotkey_modifier_select">Hotkey Modifier:</label>
<select id="hotkey_modifier_select">
<option value="">None</option>
<option value="shiftKey">Shift</option>
<option value="ctrlKey">Control</option>
<option value="altKey">Alt</option>
</select>
<label for="hotkey_query_select">Hotkey Query:</label>
<select id="hotkey_query_select">
<option value="inherit" title="Inherits its behavior from the Query option on the primary settings page">Inherit</option>
<option value="default" title="Creates a search using both the 'Exact' and 'Base' options">Default</option>
<option value="fuzzy" title="Searches for the site's default title only, without quotes — allows fuzzy matching">Fuzzy</option>
<option value="exact" title="Japanese and English full titles — searches for exact title names as written">Exact</option>
<option value="base" title="Japanese and English base titles — searches with Seasons and Parts removed">Base</option>
</select>
</div>
`;
document.body.appendChild(settingsPanel);
document.getElementById('filter_select').value = settings.filter_setting;
document.getElementById('category_select').value = settings.category_setting;
document.getElementById('query_select').value = settings.query_setting;
document.getElementById('sort_select').value = settings.sort_setting;
document.getElementById('order_select').value = settings.order_setting;
document.getElementById('hide_button_select').checked = settings.hide_button_setting;
document.getElementById('focus_select').checked = settings.focus_setting;
document.getElementById('custom_text_toggle_select').checked = settings.custom_text_toggle_setting;
document.getElementById('custom_text_select').value = settings.custom_text_setting;
document.getElementById('hotkey_key_select').value = settings.hotkey_key_setting;
document.getElementById('hotkey_modifier_select').value = settings.hotkey_modifier_setting;
document.getElementById('hotkey_query_select').value = settings.hotkey_query_setting;
document.getElementById('nyaa-linker-settings-saveButton').onclick = () => {
const newSettings = {
filter_setting: document.getElementById('filter_select').value,
category_setting: document.getElementById('category_select').value,
query_setting: document.getElementById('query_select').value,
sort_setting: document.getElementById('sort_select').value,
order_setting: document.getElementById('order_select').value,
hide_button_setting: document.getElementById('hide_button_select').checked,
focus_setting: document.getElementById('focus_select').checked,
custom_text_toggle_setting: document.getElementById('custom_text_toggle_select').checked,
custom_text_setting: document.getElementById('custom_text_select').value,
hotkey_key_setting: document.getElementById('hotkey_key_select').value.toLowerCase(),
hotkey_modifier_setting: document.getElementById('hotkey_modifier_select').value,
hotkey_query_setting: document.getElementById('hotkey_query_select').value,
};
GM_setValue('settings', newSettings);
settings = newSettings;
settingsPanel.remove();
document.querySelectorAll('.nyaaBtn').forEach((e) => e.remove());
init();
};
});
}
let btn, currentPage, hotkeyListener;
function init() {
searchNyaa(settings);
}
function searchNyaa(settings) {
const domain = window.location.href;
let media = window.location.pathname.includes('manga') ? 'manga' : 'anime';
let titleJap, titleEng, btnSpace, cardType, cardFlag, isSpicy;
let categorySetting = settings.category_setting;
let queryType = settings.query_setting;
let customQuery = settings.custom_text_toggle_setting ? settings.custom_text_setting : '';
const setCategory = (cat) => {
if (media === 'manga') {
const categories = { '0_0': '3_0', '1_2': '3_1', '1_3': '3_2', '1_4': '3_3' };
return categories[cat];
} else {
return cat;
}
};
categorySetting = setCategory(settings.category_setting);
function createBtn(btnSpace) {
!cardFlag && document.querySelector('.nyaaBtn') && document.querySelectorAll('.nyaaBtn').forEach((e) => e.remove()), (cardFlag = true);
btn = btnSpace.appendChild(document.createElement('a'));
btn.classList.add('nyaaBtn');
settings.hide_button_setting && (btn.style.display = 'none');
!cardType && settings.hotkey_key_setting && startHotkeyListener();
}
function createSearch(query) {
let subDomain, siteText;
isSpicy
? ((subDomain = 'sukebei.'), (siteText = 'Sukebei'), media === 'manga' ? (categorySetting = '0_0') : (categorySetting = '1_1'))
: ((subDomain = ''), (siteText = 'Nyaa'));
!btn.title && (btn.textContent = `Search on ${siteText}`);
(query.includes('&') || query.includes('+')) && (query = query.replace(/&/g, '%26').replace(/\+/g, '%2B'));
btn.href = `https://${subDomain}nyaa.si/?f=${settings.filter_setting}&c=${categorySetting}&q=${query}${customQuery}&s=${settings.sort_setting}&o=${settings.order_setting}`;
btn.target = '_blank';
}
function startHotkeyListener() {
hotkeyListener && document.removeEventListener('keydown', hotkeyListener);
hotkeyListener = (e) => {
if (
(btn && e[settings.hotkey_modifier_setting] && e.key.toLowerCase() === settings.hotkey_key_setting) ||
(btn && settings.hotkey_modifier_setting === '' && !e.ctrlKey && !e.shiftKey && !e.altKey && e.key === settings.hotkey_key_setting)
) {
if (settings.hotkey_query_setting !== 'inherit') {
queryType = settings.hotkey_query_setting;
createSearch(getQuery(titleJap, titleEng, queryType));
}
btn.dispatchEvent(new MouseEvent('click', { ctrlKey: settings.focus_setting }));
e.preventDefault();
queryType = settings.query_setting;
createSearch(getQuery(titleJap, titleEng, queryType));
}
};
document.addEventListener('keydown', hotkeyListener);
}
switch (true) {
case domain.includes(`myanimelist.net`):
media = window.location.href.split('/')[3];
categorySetting = setCategory(settings.category_setting);
const malMain = new RegExp(`myanimelist\\.net/${media}/\\d+`);
if (malMain.test(domain)) {
const engCheck = document.querySelector('.title-english');
engCheck && (titleEng = engCheck.textContent);
if (media === 'manga') {
const titleElm = document.querySelector('[itemprop="name"]');
titleJap = titleElm.textContent;
if (engCheck) {
engCheck.textContent = '';
titleJap = titleElm.textContent;
engCheck.textContent = titleEng;
}
} else {
titleJap = document.querySelector('.title-name').textContent;
}
isSpicy = [...document.querySelectorAll('span[itemprop="genre"]')].some((el) => el.textContent.trim().toLowerCase() === 'hentai');
btnSpace = document.getElementById('broadcast-block') || document.querySelector('.leftside').children[0];
createBtn(btnSpace);
btn.style.marginTop = '4px';
btn.classList.add('left-info-block-broadcast-button');
createSearch(getQuery(titleJap, titleEng, queryType));
}
const cardPaths = ['/genre', '/season', '/magazine', '/adapted'];
if (cardPaths.some((path) => domain.includes(path))) {
if (domain.includes('/adapted') && document.querySelector('.list.on')) return;
for (const card of document.querySelectorAll('.seasonal-anime')) {
cardType = true;
titleJap = card.querySelector('.title h2').innerText;
titleEng = card.querySelector('.title h3')?.innerText;
isSpicy = [...card.querySelectorAll('.explicit a')].some((el) => el.title.toLowerCase().includes('hentai'));
!isSpicy && (categorySetting = setCategory(settings.category_setting));
createBtn(card.querySelector('.broadcast'));
btn.title = 'Search on Nyaa';
btn.style.background = 'url(https://i.imgur.com/9Fr2BRG.png) center/20px no-repeat';
btn.style.padding = '0 11px';
isSpicy && ((btn.title = 'Search on Sukebei'), (btn.style.border = '2px solid red'), (btn.style.borderRadius = '50%'));
createSearch(getQuery(titleJap, titleEng, queryType));
}
}
break;
case (domain.includes(`anime-planet.com/anime/`) || domain.includes(`anime-planet.com/manga/`)) && domain.split("/").pop() !== '':
media = window.location.href.split('/')[3];
categorySetting = setCategory(settings.category_setting);
const skipPages = ['all', 'top-', 'recommendations', 'tags'];
let skipExtra =
media == 'anime' ? ['seasons', 'watch-online', 'studios'] : ['read-online', 'publishers', 'magazines', 'webtoons', 'light-novels'];
if (skipPages.some((page) => domain.includes(`/${media}/${page}`)) || skipExtra.some((page) => domain.includes(`/${media}/${page}`))) {
break;
}
setTimeout(() => {
const titleMain = document.querySelector('[itemprop=name]').textContent;
const titleAlt = document.getElementsByClassName('aka')[0];
titleEng = titleMain;
titleAlt ? (titleJap = titleAlt.innerText.split(': ').pop()) : (titleJap = titleMain);
createBtn(document.querySelector('.mainEntry'));
btn.classList.add('button');
document.querySelectorAll('.mainEntry > .button').forEach((button) => {
typeof button === 'object' && (button.style.width = '180px');
});
createSearch(getQuery(titleJap, titleEng, queryType));
}, 50);
break;
case domain.includes(`animenewsnetwork.com/encyclopedia/anime.php?id=`) || domain.includes(`animenewsnetwork.com/encyclopedia/manga.php?id=`):
media = domain.includes(`animenewsnetwork.com/encyclopedia/anime.php?id=`) ? 'anime' : 'manga';
categorySetting = setCategory(settings.category_setting);
setTimeout(() => {
titleEng = document.getElementById('page_header').innerText.split(' (').shift();
for (const altTitle of document.querySelectorAll('#infotype-2 > .tab')) {
altTitle.textContent.includes('Japanese') && !titleJap && (titleJap = altTitle.textContent.split(' (').shift());
}
!titleJap && titleEng && (titleJap = titleEng);
btnSpace = document.querySelector('.fright') ? document.querySelector('.fright') : document.querySelector('#big-video');
createBtn(btnSpace);
btn.style.display !== 'none' && (btn.style.display = 'flex');
btn.style.alignItems = 'center';
btn.style.justifyContent = 'center';
btn.style.height = '35px';
btn.style.borderRadius = '3px';
btn.style.background = '#2d50a7';
btn.style.color = '#fff';
btn.style.border = '1px solid black';
btn.style.textDecoration = 'none';
btnSpace.children[0].tagName === 'TABLE' && (btn.style.marginTop = '4px');
createSearch(getQuery(titleJap, titleEng, queryType));
}, 50);
break;
case domain.includes(`anidb.net/anime/`) || domain.includes(`anidb.net/manga/`):
media = window.location.href.split('/')[3];
categorySetting = setCategory(settings.category_setting);
const hasID = /anidb\.net\/\w+\/(\d+)/;
if (domain.match(hasID)) {
titleJap = document.querySelector(".value > [itemprop='name']").textContent;
titleEng = document.querySelector(".value > [itemprop='alternateName']").textContent;
isSpicy = [...document.querySelectorAll('.tagname')].some((el) => el.textContent.trim().toLowerCase() === '18 restricted');
btnSpace = document.querySelector('.resources > .value .english').appendChild(document.createElement('div'));
btnSpace.classList.add('icons');
createBtn(btnSpace);
btn.classList.add('i_icon');
btn.style.backgroundImage = "url('https://i.imgur.com/YG6H2nF.png')";
btn.style.backgroundSize = 'contain';
isSpicy ? (btn.title = 'Search on Sukebei') : (btn.title = 'Search on Nyaa');
createSearch(getQuery(titleJap, titleEng, queryType));
}
break;
case domain.includes(`anilist.co/anime/`) || domain.includes(`anilist.co/manga/`):
media = window.location.href.split('/')[3];
categorySetting = setCategory(settings.category_setting);
awaitLoadOf('.sidebar .type', 'Romaji', () => {
for (const data of document.getElementsByClassName('type')) {
const setTitle = data.parentNode.children[1].textContent;
data.textContent.includes('Romaji') && (titleJap = setTitle);
data.textContent.includes('English') && (titleEng = setTitle);
data.textContent.includes('Genres') ? (isSpicy = setTitle.toLowerCase().includes('hentai')) : null;
}
createBtn(document.querySelector('.cover-wrap-inner'));
btn.style.display !== 'none' && (btn.style.display = 'flex');
btn.style.alignItems = 'center';
btn.style.justifyContent = 'center';
btn.style.height = '35px';
btn.style.borderRadius = '3px';
btn.style.marginBottom = '20px';
btn.style.background = 'rgb(var(--color-blue))';
btn.style.color = 'rgb(var(--color-white))';
createSearch(getQuery(titleJap, titleEng, queryType));
});
break;
case domain.includes(`kitsu.app/anime/`) || domain.includes(`kitsu.app/manga/`):
media = window.location.href.split('/')[3];
categorySetting = setCategory(settings.category_setting);
awaitLoadOf('.media--information', 'Status', () => {
let titleUsa;
document.querySelector('a.more-link')?.click();
for (const data of document.querySelectorAll('.media--information > ul > li')) {
const usaCheck = data.textContent.includes('English (American)');
const setTitle = data.getElementsByTagName('span')[0];
data.textContent.includes('Japanese (Romaji)') && (titleJap = setTitle.textContent);
data.textContent.includes('English') && !usaCheck && (titleEng = setTitle.textContent);
usaCheck && (titleUsa = setTitle.textContent);
if (data.textContent.includes('Rating')) {
isSpicy = data.querySelector('span')?.textContent.replace(/\s+/g, ' ').trim() === 'R18 - Hentai';
}
}
document.querySelector('a.more-link')?.click();
!titleEng && titleUsa && (titleEng = titleUsa);
!titleJap && titleEng && (titleJap = titleEng);
createBtn(document.querySelector('.library-state'));
btn.classList.add('button', 'button--secondary');
btn.style.background = '#f5725f';
btn.style.marginTop = '10px';
createSearch(getQuery(titleJap, titleEng, queryType));
});
break;
case domain.includes('livechart.me'):
media = "anime";
categorySetting = setCategory(settings.category_setting);
if (domain.includes(`livechart.me/${media}/`)) {
titleJap = document.querySelector('.grow .text-xl').innerText;
titleEng = document.querySelector('.grow .text-lg').innerText;
createBtn(document.querySelector('.lc-poster-col'));
btn.classList.add('lc-btn', 'lc-btn-sm', 'lc-btn-outline');
createSearch(getQuery(titleJap, titleEng, queryType));
} else {
let cardSelector, cardSpace;
domain.includes('livechart.me/franchises/') ? (cardSelector = '.lc-anime') : (cardSelector = '.anime');
domain.includes('livechart.me/franchises/') ? (cardSpace = '.lc-anime-card--related-links') : (cardSpace = '.related-links');
for (const card of document.querySelectorAll(cardSelector)) {
cardType = true;
titleJap = card.getAttribute('data-romaji');
card.getAttribute('data-english') ? (titleEng = card.getAttribute('data-english')) : (titleEng = undefined);
createBtn(card.querySelector(cardSpace));
btn.style.background = 'url(https://i.imgur.com/9Fr2BRG.png) center/20px no-repeat';
btn.style.padding = '15px';
btn.style.margin = 0;
btn.classList.add('action-button');
btn.title = 'Search on Nyaa';
createSearch(getQuery(titleJap, titleEng, queryType));
}
}
break;
}
}
function getQuery(titleJap, titleEng, queryType) {
!titleJap && !titleEng && init();
titleJap && (titleJap = titleJap.replace(/["]/g, ''));
titleEng && (titleEng = titleEng.replace(/["]/g, ''));
let query = `"${titleJap}"|"${titleEng}"`;
if (!titleEng || titleJap.toLowerCase() === titleEng.toLowerCase()) {
query = titleJap;
return query;
} else {
let baseJap = getBaseTitle(titleJap);
let baseEng = getBaseTitle(titleEng);
if (queryType == 'default') {
baseJap == titleJap && baseEng == titleEng ? (query = query) : (query = `"${titleJap}"|"${titleEng}"|"${baseJap}"|"${baseEng}"`);
}
if (queryType == 'base') {
baseJap == baseEng ? (query = query) : (query = `"${baseJap}"|"${baseEng}"`);
}
queryType == 'fuzzy' && (query = titleJap);
return query;
}
}
function getBaseTitle(baseTitle) {
const hasSeason = /(?<![\w])(season)(?![\w])/i;
const hasNum = /(?<![\w])[0-9]+(?:st|[nr]d|th)(?![\w])/i;
const hasWord = /(?<![\w])(first|second|third|fourth|fifth|(the final|final))(?![\w])/i;
const hasPart = /(?<![\w])(part )/i;
const hasEndPunc = /[?!.]$/;
baseTitle = baseTitle
.replace(/[\(\)\[\]\{\}][^()\[\]\{\}]*[\)\]\{\}]/g, '')
.replace(/([♡♥☆★♪∞])(?=\w)/g, ' ')
.replace(/[♡♥☆★♪∞](?!\w)/g, '')
.trim();
baseTitle.includes(': ') && (baseTitle = baseTitle.split(': ').shift());
baseTitle.includes(' - ') && (baseTitle = baseTitle.split(' - ').pop());
hasPart.test(baseTitle) && (baseTitle = baseTitle.split(/( part)/i).shift());
if (hasSeason.test(baseTitle)) {
if (hasNum.test(baseTitle) || hasWord.test(baseTitle)) {
let titleNum, titleWord;
hasNum.test(baseTitle) && (titleNum = baseTitle.match(hasNum)[0]);
hasWord.test(baseTitle) && (titleWord = baseTitle.match(hasWord)[0]);
titleNum && (baseTitle = baseTitle.split(` ${titleNum}`).shift());
titleWord && (baseTitle = baseTitle.split(` ${titleWord}`).shift());
} else {
baseTitle = baseTitle.split(/( season)/i).shift();
}
}
while (hasEndPunc.test(baseTitle)) {
baseTitle = baseTitle.split(baseTitle.match(hasEndPunc)[0]).shift();
}
return baseTitle;
}
const awaitLoadOf = (selector, text, func) => {
return new Promise((resolve) => {
let found = false;
const elmspre = document.querySelectorAll(selector);
elmspre.forEach((elm) => {
if (found) return;
if (elm.textContent.includes(text)) {
found = true;
resolve(elm);
func();
}
});
if (found) return;
const mutObs = new MutationObserver(() => {
const elms = document.querySelectorAll(selector);
elms.forEach((elm) => {
if (found) return;
if (elm.textContent.includes(text)) {
found = true;
resolve(elm);
mutObs.disconnect();
func();
}
});
});
mutObs.observe(document.body, { childList: true, subtree: true });
});
};
getValue('settings', defaultSettings).then((v) => {
settings = v;
currentPage = window.location.href.split('/')[4];
init();
const observer = new MutationObserver(() => {
if (window.location.href.split('/')[4] !== currentPage) {
currentPage = window.location.href.split('/')[4];
document.querySelectorAll('.nyaaBtn').forEach((e) => e.remove());
init();
}
});
observer.observe(document.body, { childList: true, subtree: true });
});