NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript==
// @name Light Novel Loader
// @namespace https://openuserjs.org/users/XDHx86/scripts
// @version 4.3
// @description BEST novel loader for your favorite Light Novel site! Works on Android & IOS!! Supports: (www.lightnovelworld.com, www.readlightnovel.me, www.readwn.com, www.wuxiax.com, www.lnreader.org, novelonlinefull.com, www.novels.pl, novelfull.com, boxnovel.com, bestlightnovel.com)
// @author XDHx86
// @icon https://i.imgur.com/HfGcZ5V.png
// @updateURL https://openuserjs.org/meta/XDHx86/Light_Novel_Loader.meta.js
// @downloadURL https://openuserjs.org/install/XDHx86/Light_Novel_Loader.user.js
// @copyright 2020, XDHx86 (https://openuserjs.org/users/XDHx86)
// @license MIT
// @match http*://*/*
// @connect fonts.gstatic.com
// @connect fonts.googleapis.com
// @connect www.lightnovelworld.com
// @connect static.lightnovelworld.com
// @connect avatar.novelonlinefree.com
// @connect www.readlightnovel.me
// @connect www.wuxiax.com
// @connect www.lnreader.org
// @connect lnreader.org
// @connect www.novels.pl
// @connect readlightnovel.me
// @connect www.novelhall.com
// @connect www.readwn.com
// @connect novelonlinefull.com
// @connect novels.pl
// @connect novelfull.com
// @connect boxnovel.com
// @connect bestlightnovel.com
// @require https://cdnjs.cloudflare.com/ajax/libs/jszip/3.9.1/jszip.min.js
// @require https://cdnjs.cloudflare.com/ajax/libs/jspdf/1.3.3/jspdf.min.js
// @grant GM_setValue
// @grant GM_getValue
// @grant GM_addStyle
// @grant GM_xmlhttpRequest
// @grant GM_registerMenuCommand
// @grant GM_notification
// @grant GM_listValues
// @noframes
// ==/UserScript==
(function() {
'use strict';
{
const version = GM_getValue('scriptversion', '');
if (!version || version != GM_info.script.version) {
GM_setValue('scriptversion', GM_info.script.version);
window.open('https://openuserjs.org/scripts/XDHx86/Light_Novel_Loader#changelog', '_blank');
}
}
const SITES = {
'www.lightnovelworld.com': {
novel: '.titles a',
chaptertitle: '.chapter-title',
chapter: '.chapter-content',
next: '.chnav.next',
prev: '.chnav.prev',
title: '.titles',
annon: [0, ['div', 'script', 'p[class]', 'ins', 'button', 'sub', 'input']],
namer: ['\n', ':'],
name: [' - ', ''],
othert: '',
favobj: {
noveltitle: '.novel-title.text2row',
link: '/chapters',
lastc: '.container p a',
status: ''
},
indexobj: {
max: '.PagedList-skipToLast a',
nav: '.pagination a',
list: '.chapter-list',
page: '/page-1',
chapters: '.chapter-list a',
},
search: {
input: '#inputContent',
link: 'https://www.lightnovelworld.com/search'
}
},
'readlightnovel.me': {
novel: '.black-link',
chaptertitle: '.block-title h1',
chapter: '.desc',
next: '.next.next-link',
prev: '.prev.prev-link',
title: '.block-title h1',
annon: [0, ['span', 'div', 'hr', 'small', 'a', 'script']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.block-title h1',
link: '',
lastc: '',
status: ''
},
indexobj: {
max: '',
nav: '',
list: '.tab-content',
page: '',
chapters: '.tab-content a',
},
search: {
input: '.form-control.search-form',
link: 'https://www.readlightnovel.me/'
}
},
'www.readlightnovel.me': {
novel: '.black-link',
chaptertitle: '.block-title h1',
chapter: '.desc',
next: '.next.next-link',
prev: '.prev.prev-link',
title: '.block-title h1',
annon: [0, ['span', 'div', 'hr', 'small', 'a', 'script']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.block-title h1',
link: '',
lastc: '',
status: ''
},
indexobj: {
max: '',
nav: '',
list: '.tab-content',
page: '',
chapters: '.tab-content a',
},
search: {
input: '.form-control.search-form',
link: 'https://www.readlightnovel.me/'
}
},
'www.novelhall.com': {
novel: '#bookname',
chaptertitle: '.single-header h1',
chapter: '#htmlContent',
next: '.nav-single a:nth-of-type(3)',
prev: '.nav-single a',
title: '.single-header h1',
annon: [0, ['div']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.book-info h1',
link: '',
lastc: '.book-catalog.inner.mt20 a',
status: 'span:nth-of-type(2)'
},
indexobj: {
max: '',
nav: '',
list: '#morelist',
page: '',
chapters: '#morelist a',
},
search: {
input: '',
link: 'https://www.novelhall.com/index.php?s=so&module=book&keyword=${term}'
}
},
'novelonlinefull.com': {
novel: '.lem_bem_top a',
chaptertitle: '.name_chapter',
chapter: '.vung_doc',
next: '.LN_nextchbtn',
prev: '.LN_prevchbtn',
title: '.name_chapter',
annon: [0, ['div']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.truyen_info_right h1',
link: '',
lastc: '.chapter-list a',
status: '.truyen_info_right li:nth-of-type(4) a'
},
indexobj: {
max: '',
nav: '',
list: '.chapter-list',
page: '',
chapters: '.chapter-list a',
},
search: {
input: '',
link: 'https://novelonlinefull.com/search_novels/${term}'
}
},
'www.novels.pl': {
novel: '.panel-title.pull-left a',
chaptertitle: '',
chapter: '.panel-body.article',
next: '.next a',
prev: '.previous a',
title: '.panel-title.pull-left',
annon: [0, ['a', 'div']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.panel-title.pull-left',
link: '',
lastc: '#chapters a',
status: ''
},
indexobj: {
max: '',
nav: '',
list: '',
page: '',
chapters: ''
},
search: {
input: '',
link: 'https://www.novels.pl/?search=${term}'
}
},
'novels.pl': {
novel: '.panel-title.pull-left a',
chaptertitle: '',
chapter: '.panel-body.article',
next: '.next a',
prev: '.previous a',
title: '.panel-title.pull-left',
annon: [0, ['a', 'div']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.panel-title.pull-left',
link: '',
lastc: '#chapters a',
status: ''
},
indexobj: {
max: '',
nav: '',
list: '',
page: '',
chapters: ''
},
search: {
input: '',
link: 'https://www.novels.pl/?search=${term}'
}
},
'novelfull.com': {
novel: '.truyen-title',
chaptertitle: '.chapter-text',
chapter: '#chapter-content',
next: '#next_chap',
prev: '#prev_chap',
title: '.chapter-text',
annon: [0, ['div', 'script', 'ins', 'hr', 'h2', 'ol']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.title',
link: '',
lastc: '.l-chapters a',
status: '.info div:nth-of-type(5) a'
},
indexobj: {
max: '.pagination:nth-of-type(1) .last a',
nav: '.pagination:nth-of-type(1) a',
list: '.list-chapter',
page: '?page=1',
chapters: '.list-chapter a'
},
search: {
input: '',
link: 'https://novelfull.com/search?keyword=${term}'
}
},
'boxnovel.com': {
novel: '.breadcrumb li:nth-of-type(2) a',
chaptertitle: '.breadcrumb .active',
chapter: '.text-left',
next: '.next_page.btn',
prev: '.prev_page.btn',
title: '.breadcrumb .active',
annon: [0, ['div', 'script', 'p[class]', 'ins', 'button', 'sub', 'input']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.post-title h1',
link: '',
lastc: '.wp-manga-chapter a',
status: '.post-status .summary-content'
},
indexobj: {
max: '',
nav: '',
list: '.main.version-chap',
page: '',
chapters: '.main.version-chap a',
},
search: {
input: '',
link: 'https://boxnovel.com/?s=${term}&post_type=wp-manga'
}
},
'bestlightnovel.com': {
novel: '.lem_bem_top a',
chaptertitle: '.name_chapter',
chapter: '.vung_doc',
next: '.LN_nextchbtn',
prev: '.LN_prevchbtn',
title: '.name_chapter',
annon: [0, ['div']],
namer: '',
name: '',
othert: '',
favobj: {
noveltitle: '.truyen_info_right h1',
link: '',
lastc: '.chapter-list a',
status: '.truyen_info_right li:nth-of-type(4) a'
},
indexobj: {
max: '',
nav: '',
list: '.chapter-list',
page: '',
chapters: '.chapter-list a',
},
search: {
input: '',
link: 'https://bestlightnovel.com/search_novels/${term}'
}
},
'www.wuxiax.com': {
novel: '.titles a',
chaptertitle: '.titles h2',
chapter: '.chapter-content',
next: '.chnav.next',
prev: '.chnav.prev',
title: '.titles',
annon: [0, ['div', 'script', 'p[class]', 'ins', 'button', 'sub']],
namer: ['\n', ':'],
name: [' - ', ''],
othert: '',
search: '',
favobj: {
noveltitle: '.novel-title',
link: '',
lastc: '.intro a',
status: '.header-stats strong[class]'
},
indexobj: {
max: '',
nav: '',
list: '',
page: '',
chapters: ''
},
search: {
input: '',
link: ''
}
},
'lnreader.org': {
novel: '.section-header-title a',
chaptertitle: '.section-header-title span',
chapter: '#chapterText',
next: '.cm-button:nth-of-type(2)',
prev: '.cm-button:nth-of-type(1)',
title: '.section-header-title',
annon: [0, ['div', 'script', 'p[class]', 'ins', 'button', 'sub', 'center']],
namer: ['\n', ':'],
name: [' - ', ''],
othert: '',
search: '',
favobj: {
noveltitle: '.novel-title',
link: '',
lastc: '.novels-detail-chapters a',
status: 'li:nth-of-type(2) .novels-detail-right-in-right'
},
indexobj: {
max: '',
nav: '',
list: '',
page: '',
chapters: '.novels-detail-chapters a'
},
search: {
input: '.search-boxx-input',
link: 'http://lnreader.org/'
}
},
'www.readwn.com': {
novel: '.titles a',
chaptertitle: '.titles h2',
chapter: '.chapter-content',
next: '.chnav.next',
prev: '.chnav.prev',
title: '.titles',
annon: [0, ['div', 'script', 'p[class]', 'ins', 'button', 'sub']],
namer: ['\n', ':'],
name: [' - ', ''],
othert: '',
search: '',
favobj: {
noveltitle: '.novel-title',
link: '',
lastc: '.intro a',
status: '.header-stats span:nth-of-type(2)'
},
indexobj: {
max: '',
nav: '',
list: '',
page: '',
chapters: '.chapter-list a',
},
search: {
input: '',
link: ''
}
},
};
Object.defineProperty(Object.prototype, 'LN_search', {
value: function(value, options) {
let output = { found: false },
altv,
checkit = (inp, opt) => {
let output = false;
if (opt) {
if (opt instanceof RegExp && opt.test(inp)) output = true;
else if (typeof opt == 'string' && inp && inp.includes && inp.includes(value)) output = true;
}
else if (inp == value) output = true;
return output;
};
if (Array.isArray(this)) {
for (let i = 0; i < this.length; i++) {
if (typeof this[i] == 'object') {
let obj = this[i];
for (let w in obj) {
if (checkit(obj[w], options)) {
output = { found: true, obj: obj, child: w, index: i, value: obj[w] };
break;
}
}
if (output.found) break;
} else if (Array.isArray(this[i])) {
let arr = this[i];
for (let j = 0; j < arr.length; j++) {
if (checkit(arr[j], options)) {
output = { found: true, array: arr, index: j, parentindex: i, value: arr[j] };
break;
}
}
if (output.found) break;
}
else if (checkit(this[i], options)) {
output = { found: true, array: this, index: i, value: this[i], obj: this };
break;
}
}
}
else {
for (let e in this) {
if (typeof this[e] == 'object') {
let obj = this[e];
for (let w in obj) {
if (checkit(obj[w], options)) {
output = { found: true, obj: obj, sub: w, child: e, value: obj[w]};
break;
}
}
if (output.found) break;
}
else if (Array.isArray(this[e])) {
let arr = this[e];
for (let i = 0; i < arr.length; i++) {
if (checkit(arr[i])) {
output = { found: true, array: arr, index: i, child: e, value: arr[i] };
break;
}
}
if (output.found) break;
}
else if (checkit(this[e], options)) {
output = { found: true, obj: this, child: e, value: this[e] };
break;
}
}
}
return output;
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'LN_deepSearch', {
value: function(value) {
let output = { found: false };
if (Array.isArray(this)) {
for (let i = 0; i < this.length; i++) {
if (this[i] == value) {
output = { found: true, array: this, index: i };
break;
} else if (typeof this[i] == 'object' || Array.isArray(this[i])) {
let search = this[i].LN_deepSearch(value);
if (search.found) {
output = search;
output.parentIndex = i;
break;
}
}
}
} else if (typeof this == 'object') {
for (let e in this) {
if (this[e] == value) {
output = { found: true, obj: this, child: e };
break;
} else if (typeof this[e] == 'object' || Array.isArray(this[e])) {
let search = this[e].LN_deepSearch(value);
if (search.found) {
output = search;
output.parentObj = e;
break;
}
}
}
}
return output;
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'LN_hasClass', {
value: function(classes) {
let output = false;
if (!this.classList) return output;
if (!Array.isArray(classes)) classes = [classes];
for (let i = 0; i < classes.length; i++) if (this.classList.contains(classes[i])) output = true;
return output;
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'LN_index', {
value: function() {
let el = this,
i = 0;
do {
i++;
if (i > 12) break;
} while (el = el.previousElementSibling);
return i - 1;
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'getType', {
value: function() {
if (this.search('jpeg') > 0) return 'jpeg';
if (this.search('jpg') > 0) return 'jpeg';
if (this.search('png') > 0) return 'png';
if (this.search('webp') > 0) return 'webp';
},
enumerable: false
});
Object.defineProperty(Object.prototype, 'replacer', {
value: function() {
return this.replace(/’/g, "'").replace(':', ' -').normalize('NFD').replace(/([\u0300-\u036f]|[^0-9a-zA-Z ()\-_!'])/g, '').replace(/[^\x00-\x7F]/g, '');
},
enumerable: false
});
String.prototype.LN_platform = function() {
if (this.includes('lightnovelworld')) return 'Light Novel World';
else if (this.includes('readlightnovel.me')) return 'Read Light Novel';
else if (this.includes('readwn')) return 'Read WN';
else if (this.includes('www.wuxiax.com')) return 'Wuxia X';
else if (this.includes('lnreader.org')) return 'Light Novel Reader';
else if (this.includes('novelonlinefull')) return 'Novel Online Full';
else if (this.includes('bestlightnovel')) return 'Best Light Novel';
else if (this.includes('novels.pl')) return 'Novels PL';
else if (this.includes('novelfull')) return 'Novel Full';
else if (this.includes('boxnovel')) return 'Box Novel';
else if (this.includes('novelhall')) return 'Novel Hall';
}
String.prototype.LN_host = function() {
if (this.includes('lightnovelworld')) return 'www.lightnovelworld.com';
else if (this.includes('www.readlightnovel.me')) return 'www.readlightnovel.me';
else if (this.includes('readlightnovel.me')) return 'readlightnovel.me';
else if (this.includes('readwn')) return 'www.readwn.com';
else if (this.includes('www.wuxiax.com')) return 'www.wuxiax.com';
else if (this.includes('www.lnreader.org')) return 'www.lnreader.org';
else if (this.includes('lnreader.org')) return 'lnreader.org';
else if (this.includes('novelonlinefull')) return 'novelonlinefull.com';
else if (this.includes('bestlightnovel')) return 'bestlightnovel.com';
else if (this.includes('novels.pl')) return 'novels.pl';
else if (this.includes('novelfull')) return 'novelfull.com';
else if (this.includes('boxnovel.com')) return 'boxnovel.com';
else if (this.includes('novelhall')) return 'www.novelhall.com';
}
String.prototype.LN_fix = function(url) {
let s = this.split('/');
s.shift(); s.shift(); s.shift();
s.unshift('https:', '', url.LN_host());
s = s.join('/');
return s;
}
String.prototype.LN_replaceAt = function(index, replacement) {
let a = this.split('');
a[index] = replacement;
return a.join('');
}
String.prototype.LN_reverseString = function() {
const a = this.split(''),
b = a.reverse(),
c = b.join('');
return c;
}
if (GM_getValue('firsttime', true)) {
GM_setValue('next', 'KeyN');
GM_setValue('nexta', 'ArrowRight');
GM_setValue('nextaa', 'KeyD');
GM_setValue('prev', 'KeyB');
GM_setValue('preva', 'ArrowLeft');
GM_setValue('prevaa', 'KeyA');
GM_setValue('darkmode', 'KeyL');
GM_setValue('fullscreen', 'KeyF');
GM_setValue('autoscroll', 'KeyQ');
GM_setValue('removean', 'KeyE');
GM_setValue('download', 'KeyS');
GM_setValue('decfs', 'Minus');
GM_setValue('incfs', 'Equal');
GM_setValue('autonc', false);
GM_setValue('autora', true);
GM_setValue('autodl', true);
GM_setValue('autoresume', true);
GM_setValue('defaultv', false);
GM_setValue('scrollt', false);
GM_setValue('swipeg', true);
GM_setValue('nnotfound', true);
GM_setValue('autoch', false);
GM_setValue('autodmode', false);
GM_setValue('decensore', true);
GM_setValue('allowconfirm', true);
GM_setValue('uselastr', false);
GM_setValue('checkcomp', false);
GM_setValue('downloadt', true);
GM_setValue('autoretry', true);
GM_setValue('laststop', ['placeholder']);
GM_setValue('autoncd', 40);
GM_setValue('dmtime', 18);
GM_setValue('lmtime', 9);
GM_setValue('pmargin', 20);
GM_setValue('lheight', 1.4);
GM_setValue('autoscrolls', 90);
GM_setValue('autoretryno', 20);
GM_setValue('showfavs', 'Digit3');
GM_setValue('autodown', false);
GM_setValue('defaultf', "'Comic Sans MS'");
GM_setValue('defaultfs', 18);
GM_setValue('checkd', '09:00');
GM_setValue('lastcheck', '');
GM_setValue('autobackupp', 0);
GM_setValue('bgc', '#c95');
GM_setValue('txc', '#000');
GM_setValue('dbgc', '#111');
GM_setValue('dtxc', '#550');
GM_setValue('CCSS', '');
GM_setValue('ECCSS', '');
GM_setValue('FAVS', ['placeholder']);
GM_setValue('HISTORY', ['placeholder']);
GM_setValue('RULES', ['placeholder']);
GM_setValue('DHISTORY', ['placeholder']);
GM_setValue('TIMES', ['placeholder']);
GM_setValue('CHECKED', [new Date]);
GM_setValue('firsttime', false);
}
let next = GM_getValue('next', 'KeyN'),
nexta = GM_getValue('nexta', 'ArrowRight'),
nextaa = GM_getValue('nextaa', 'KeyD'),
prev = GM_getValue('prev', 'KeyB'),
preva = GM_getValue('preva', 'ArrowLeft'),
prevaa = GM_getValue('prevaa', 'KeyA'),
darkmode = GM_getValue('darkmode', 'KeyL'),
fullscreen = GM_getValue('fullscreen', 'KeyF'),
autoscroll = GM_getValue('autoscroll', 'KeyQ'),
showfavs = GM_getValue('showfavs', 'Digit3'),
removean = GM_getValue('removean', 'KeyE'),
download = GM_getValue('download', 'KeyS'),
decfs = GM_getValue('decfs', 'Minus'),
incfs = GM_getValue('incfs', 'Equal'),
autonc = GM_getValue('autonc', false),
autora = GM_getValue('autora', true),
autodl = GM_getValue('autodl', true),
autoresume = GM_getValue('autoresume', true),
defaultv = GM_getValue('defaultv', false),
scrollt = GM_getValue('scrollt', false),
swipeg = GM_getValue('swipeg', true),
nnotfound = GM_getValue('nnotfound', true),
autoch = GM_getValue('autoch', false),
autodmode = GM_getValue('autodmode', false),
decensore = GM_getValue('decensore', true),
allowconfirm = GM_getValue('allowconfirm', true),
uselastr = GM_getValue('uselastr', false),
checkcomp = GM_getValue('checkcomp', false),
downloadt = GM_getValue('downloadt', true),
autoretry = GM_getValue('autoretry', true),
laststop = GM_getValue('laststop', ['placeholder']),
autoncd = parseFloat(GM_getValue('autoncd', 40)),
dmtime = parseFloat(GM_getValue('dmtime', 18)),
lmtime = parseFloat(GM_getValue('lmtime', 9)),
pmargin = parseFloat(GM_getValue('pmargin', 20)),
lheight = parseFloat(GM_getValue('lheight', 1.4)),
autoscrolls = parseFloat(GM_getValue('autoscrolls', 90)),
autoretryno = GM_getValue('autoretryno', 20),
autodown = GM_getValue('autodown', false),
defaultf = GM_getValue('defaultf', '"Comic Sans MS"'),
defaultfs = GM_getValue('defaultfs', 18),
checkd = GM_getValue('checkd', '09:00'),
lastcheck = GM_getValue('lastcheck', ''),
autobackupp = parseFloat(GM_getValue('autobackupp', 0)),
bgc = GM_getValue('bgc', '#c95'),
txc = GM_getValue('txc', '#000'),
dbgc = GM_getValue('dbgc', '#111'),
dtxc = GM_getValue('dtxc', '#550'),
CCSS = GM_getValue('CCSS', ''),
ECCSS = GM_getValue('ECCSS', ''),
HISTORY = GM_getValue('HISTORY', ['placeholder']),
FAVS = GM_getValue('FAVS', ['placeholder']),
RULES = GM_getValue('RULES', ['placeholder']),
DHISTORY = GM_getValue('DHISTORY', ['placeholder']),
TIMES = GM_getValue('TIMES', ['placeholder']),
CHECKED = GM_getValue('CHECKED', [JSON.stringify(new Date()).replaceAll('"', '')]),
KEY = {
code: fullscreen,
altKey: false,
ctrlKey: false,
shiftKey: false,
metaKey: false,
target: {
tagName: 'DIV'
}
};
if (!Array.isArray(laststop)) {
laststop = JSON.parse(laststop);
GM_setValue('laststop', laststop);
}
if (!Array.isArray(HISTORY)) {
HISTORY = JSON.parse(HISTORY);
GM_setValue('HISTORY', HISTORY);
}
if (!Array.isArray(FAVS)) {
FAVS = JSON.parse(FAVS);
GM_setValue('FAVS', FAVS);
}
if (!Array.isArray(RULES)) {
RULES = JSON.parse(RULES);
GM_setValue('RULES', RULES);
}
if (!Array.isArray(DHISTORY)) {
DHISTORY = JSON.parse(DHISTORY);
GM_setValue('DHISTORY', DHISTORY);
}
if (!Array.isArray(TIMES)) {
TIMES = JSON.parse(TIMES);
GM_setValue('TIMES', TIMES);
}
{
for (let i = 1; i < HISTORY.length; i++ ) if (typeof HISTORY[i] == 'string') HISTORY.splice(i, 1);
for (let i = 1; i < DHISTORY.length; i++ ) if (typeof DHISTORY[i] == 'string') DHISTORY.splice(i, 1);
for (let i = 1; i < laststop.length; i++ ) if (typeof laststop[i] == 'string') laststop.splice(i, 1);
let o = 'lightnovelworld';
for (let i = 1; i < FAVS.length; i++) {
FAVS[i].lastc.href = FAVS[i].lastc.href.replace('lightnovelpub', o);
FAVS[i].url.href = FAVS[i].url.href.replace('lightnovelpub', o);
}
while (DHISTORY.LN_search('lightnovelpub', 'includes').found) DHISTORY[DHISTORY.LN_search('lightnovelpub', 'includes').index] = DHISTORY.LN_search('lightnovelpub', 'includes').value.replace('lightnovelpub', o);
while (HISTORY.LN_search('lightnovelpub', 'includes').found) HISTORY[HISTORY.LN_search('lightnovelpub', 'includes').index] = HISTORY.LN_search('lightnovelpub', 'includes').value.replace('lightnovelpub', o);
while (laststop.LN_search('lightnovelpub', 'includes').found) laststop[laststop.LN_search('lightnovelpub', 'includes').index] = laststop.LN_search('lightnovelpub', 'includes').value.replace('lightnovelpub', o);
GM_setValue('FAVS', FAVS);
GM_setValue('DHISTORY', DHISTORY);
GM_setValue('HISTORY', HISTORY);
GM_setValue('laststop', laststop);
}
if (SITES[location.host]) {
FAVS.forEach((elm, id) => {
if (id != 0) {
let {novel, url, url: {href, text}, status, lastc} = elm
if (!novel || !url || !href || !text || !status || !lastc) FAVS.splice(id, 1);
}
});
GM_setValue('FAVS', FAVS);
rvals();
}
const s = document.createElement('div'),
d = document.createElement('div'),
o = document.createElement('div'),
m = document.createElement('div'),
c = document.createElement('div'),
f = document.createElement('div'),
r = document.createElement('div'),
n = document.createElement('aside'),
a = document.createElement('div'),
l = document.createElement('div'),
svga = '<svg style="transform: rotate(-90deg); height: 22px; width: 22px; display: inline; transition: inherit;" fill="#090" viewBox="0 0 26 26"> <path d="M13,15.405l8.764-8.584c0.392-0.383,1.019-0.38,1.406,0.008l1.536,1.536c0.391,0.391,0.39,1.026-0.002,1.417L13.707,20.707 C13.512,20.902,13.256,21,13,21c-0.256,0-0.512-0.098-0.707-0.293L1.296,9.782c-0.393-0.39-0.394-1.025-0.002-1.417L2.83,6.829 c0.387-0.387,1.015-0.391,1.406-0.008L13,15.405z"></path> </svg>',
svgs = '<svg style="height: 20px; width: 50px;" viewBox="0 0 48 48"> <circle style="fill:#F44336;" cx="24" cy="24" r="19"/> <rect x="22" y="14" transform="matrix(0.7071 0.7071 -0.7071 0.7071 24 -9.9411)" style="fill:#FFFFFF;" width="4" height="20"/> <rect x="22" y="14" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 57.9411 24)" style="fill:#FFFFFF;" width="4" height="20"/> </svg>',
svgi = '<svg style="height: 20px; width: 50px; flex: 0 0 auto;" viewBox="0 0 48 48"> <circle style="fill:#FFCA28;" cx="24" cy="24" r="19"/> <rect x="22" y="21" style="fill:#37474F;" width="4" height="13"/> <circle style="fill:#37474F;" cx="24" cy="16" r="2"/> </svg>',
svgreader = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 50 50"> <path d="M 6 4 L 6 39 L 7 39 C 16.733333 39 24.552734 42.894531 24.552734 42.894531 L 25 43.117188 L 25.447266 42.894531 C 25.447266 42.894531 33.266667 39 43 39 L 44 39 L 44 4 L 43 4 C 33.160341 4 25.642079 7.6218492 25 7.9355469 C 24.357921 7.6218492 16.839659 4 7 4 L 6 4 z M 8 6.0898438 C 16.244795 6.3254498 22.653699 9.0479802 24 9.65625 L 24 40.503906 C 21.872898 39.585697 15.887188 37.302104 8 37.087891 L 8 6.0898438 z M 42 6.0898438 L 42 37.087891 C 34.112812 37.302104 28.127102 39.585697 26 40.503906 L 26 9.65625 C 27.346301 9.0479802 33.755205 6.3254498 42 6.0898438 z M 2 9 L 2 43 L 3 43 C 15.794872 43 24.59375 46.914062 24.59375 46.914062 L 25 47.09375 L 25.40625 46.914062 C 25.40625 46.914062 34.205128 43 47 43 L 48 43 L 48 9 L 46 9 L 46 41.070312 C 33.798944 41.276157 25.556846 44.711648 25 44.953125 C 24.443154 44.711648 16.201056 41.276157 4 41.070312 L 4 9 L 2 9 z"/> </svg>',
svgsettings = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 24 24"> <path d="M 12.0625 0 C 9.7745 -1.9428903e-16 9.8125 0.21875 9.8125 0.21875 C 9.5405 1.53775 9.2555 2.43275 9.0625 2.96875 C 7.8965 3.34975 6.82525 3.94775 5.90625 4.71875 C 5.35225 4.60975 4.45775 4.40675 3.21875 3.96875 C 3.21875 3.96875 3.052 3.8235 1.875 5.8125 C 0.696 7.7995 0.875 7.90625 0.875 7.90625 C 1.788 8.76425 2.387 9.426 2.75 9.875 C 2.593 10.559 2.5 11.268 2.5 12 C 2.5 12.663 2.5585 13.3125 2.6875 13.9375 C 2.3145 14.3785 1.70525 15.043 0.78125 15.875 C 0.78125 15.875 0.613 15.95675 1.75 17.96875 C 2.886 19.98075 3.03125 19.84375 3.03125 19.84375 C 4.28225 19.43075 5.193 19.25325 5.75 19.15625 C 6.666 19.95925 7.72925 20.56375 8.90625 20.96875 C 9.10025 21.49675 9.4065 22.41425 9.6875 23.78125 C 9.6875 23.78125 9.6505 24 11.9375 24 C 14.2215 24 14.21875 23.78125 14.21875 23.78125 C 14.49075 22.46325 14.7435 21.56625 14.9375 21.03125 C 16.1035 20.65025 17.17575 20.05225 18.09375 19.28125 C 18.64875 19.39025 19.54325 19.59225 20.78125 20.03125 C 20.78125 20.03125 20.944 20.1755 22.125 18.1875 C 23.302 16.1985 23.125 16.09375 23.125 16.09375 C 22.211 15.23575 21.613 14.573 21.25 14.125 C 21.406 13.439 21.5 12.732 21.5 12 C 21.5 11.337 21.4415 10.6875 21.3125 10.0625 C 21.6855 9.6225 22.29375 8.958 23.21875 8.125 C 23.21875 8.125 23.387 8.04425 22.25 6.03125 C 21.114 4.01825 20.96875 4.15625 20.96875 4.15625 C 19.71875 4.56925 18.807 4.74675 18.25 4.84375 C 17.334 4.04175 16.26975 3.43625 15.09375 3.03125 C 14.90075 2.50425 14.5945 1.58475 14.3125 0.21875 C 14.3125 0.21875 14.3465 0 12.0625 0 z M 12 4 C 16.418 4 20 7.582 20 12 C 20 16.418 16.418 20 12 20 C 7.582 20 4 16.418 4 12 C 4 7.582 7.582 4 12 4 z M 12 5 C 8.134 5 5 8.134 5 12 C 5 15.866 8.134 19 12 19 C 15.866 19 19 15.866 19 12 C 19 8.134 15.866 5 12 5 z M 12 8 C 14.209 8 16 9.791 16 12 C 16 14.209 14.209 16 12 16 C 9.791 16 8 14.209 8 12 C 8 9.791 9.791 8 12 8 z"/> </svg>',
svgarrowr = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 26 26"> <path d="M2.72,11.023l13.28-1V6.874c0-0.328,0.16-0.574,0.48-0.82c0.32-0.082,0.64-0.082,0.88,0.082 c3.28,2.296,6.4,6.144,6.48,6.308c0.078,0.159,0.152,0.393,0.157,0.556c0,0.005,0.003,0.012,0.003,0.017c0,0,0,0,0,0s0,0,0,0 c0,0.164-0.08,0.41-0.16,0.492c-0.08,0.164-3.12,4.023-6.48,6.319c-0.32,0.164-0.64,0.246-0.88,0.082 c-0.32-0.164-0.48-0.41-0.48-0.738v-3.15l-13.28-1C2.32,14.703,2,13.897,2,13.017C2,12.217,2.24,11.343,2.72,11.023z"/> </svg>',
svgarrowl = '<svg style="height: 20px; width: 20px; transform: rotate(180deg);" fill="#090" viewBox="0 0 26 26"> <path d="M2.72,11.023l13.28-1V6.874c0-0.328,0.16-0.574,0.48-0.82c0.32-0.082,0.64-0.082,0.88,0.082 c3.28,2.296,6.4,6.144,6.48,6.308c0.078,0.159,0.152,0.393,0.157,0.556c0,0.005,0.003,0.012,0.003,0.017c0,0,0,0,0,0s0,0,0,0 c0,0.164-0.08,0.41-0.16,0.492c-0.08,0.164-3.12,4.023-6.48,6.319c-0.32,0.164-0.64,0.246-0.88,0.082 c-0.32-0.164-0.48-0.41-0.48-0.738v-3.15l-13.28-1C2.32,14.703,2,13.897,2,13.017C2,12.217,2.24,11.343,2.72,11.023z"/> </svg>',
svgdarkm = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 24 24"> <path d="M 11.09375 0 C 11.070976 0.018366465 11.053905 0.043928916 11.03125 0.0625 C 4.8826005 0.56284675 0 5.7281066 0 12 C 0 18.271893 4.8826005 23.437153 11.03125 23.9375 C 11.053905 23.956071 11.070976 23.981634 11.09375 24 L 12 24 C 14.475 24 16.771484 23.234766 18.6875 21.9375 C 19.006836 21.721289 19.328369 21.495068 19.625 21.25 C 22.294678 19.044385 24 15.7125 24 12 C 24 11.5875 23.978662 11.18335 23.9375 10.78125 C 23.567041 7.1623535 21.561523 4.0083984 18.6875 2.0625 C 18.368164 1.8462891 18.05874 1.6540527 17.71875 1.46875 C 16.358789 0.72753906 14.827148 0.22714844 13.21875 0.0625 C 12.81665 0.021337891 12.4125 0 12 0 L 11.09375 0 z M 8.8125 2.53125 C 7.0677963 5.031543 6 8.3197873 6 12 C 6 15.680213 7.0677963 18.968457 8.8125 21.46875 C 4.8662498 20.128313 2 16.38442 2 12 C 2 7.6155795 4.8662498 3.8716869 8.8125 2.53125 z"/> </svg>',
svgsh = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 50 50"> <path d="M 5 6 C 3.346 6 2 7.346 2 9 L 2 41 C 2 42.654 3.346 44 5 44 L 45 44 C 46.654 44 48 42.654 48 41 L 48 9 C 48 7.346 46.654 6 45 6 L 5 6 z M 5 8 L 29 8 L 29 42 L 5 42 C 4.448 42 4 41.552 4 41 L 4 9 C 4 8.448 4.448 8 5 8 z M 13.917969 18.990234 A 1.0001 1.0001 0 0 0 13.232422 19.296875 L 7.59375 25 L 13.232422 30.703125 A 1.0001 1.0001 0 1 0 14.654297 29.296875 L 11.394531 26 L 23.943359 26 A 1.0001 1.0001 0 1 0 23.943359 24 L 11.394531 24 L 14.654297 20.703125 A 1.0001 1.0001 0 0 0 13.917969 18.990234 z" /> </svg>',
svgfd = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 50 50"> <path d="M 17.84375 10 C 17.43175 10 17.05525 10.27225 16.90625 10.65625 L 4.125 43.5 C 4.005 43.809 4.03275 44.13425 4.21875 44.40625 C 4.40575 44.67925 4.7325 44.84375 5.0625 44.84375 L 9.5 44.84375 C 9.918 44.84375 10.2915 44.5795 10.4375 44.1875 L 13.875 34.96875 L 26.375 34.96875 L 29.71875 44.1875 C 29.86175 44.5825 30.2665 44.84375 30.6875 44.84375 L 35.5 44.84375 L 35.53125 44.84375 C 36.08425 44.84375 36.53025 44.39675 36.53125 43.84375 C 36.53125 43.64675 36.474 43.4675 36.375 43.3125 L 23.59375 10.65625 C 23.44375 10.27225 23.06825 10 22.65625 10 L 17.84375 10 z M 20.1875 17.78125 L 24.28125 29.28125 L 16 29.28125 L 20.1875 17.78125 z M 37 24 C 36.447 24 36 24.448 36 25 C 36 25.552 36.447 26 37 26 L 47 26 C 47.553 26 48 25.552 48 25 C 48 24.448 47.553 24 47 24 L 37 24 z"/> </svg>',
svgfi = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 50 50"> <path d="M 18.4375 4.28125 C 18.0255 4.28125 17.649 4.5535 17.5 4.9375 L 2.46875 43.5 C 2.34875 43.809 2.40775 44.13425 2.59375 44.40625 C 2.78075 44.67925 3.07625 44.84375 3.40625 44.84375 L 8.65625 44.84375 C 9.07425 44.84375 9.44775 44.5795 9.59375 44.1875 L 13.65625 33.25 L 28.5625 33.25 L 32.5625 44.1875 C 32.7055 44.5825 33.079 44.84375 33.5 44.84375 L 39.15625 44.84375 L 39.1875 44.84375 C 39.7395 44.84375 40.1875 44.39675 40.1875 43.84375 C 40.1875 43.64675 40.13025 43.4675 40.03125 43.3125 L 25.03125 4.9375 C 24.88125 4.5535 24.50575 4.28125 24.09375 4.28125 L 18.4375 4.28125 z M 21.15625 12.75 L 26.25 26.90625 L 16.03125 26.90625 L 21.15625 12.75 z M 42 19 C 41.447 19 41 19.448 41 20 L 41 24 L 37 24 C 36.447 24 36 24.448 36 25 C 36 25.552 36.447 26 37 26 L 41 26 L 41 30 C 41 30.553 41.447 31 42 31 C 42.553 31 43 30.553 43 30 L 43 26 L 47 26 C 47.553 26 48 25.552 48 25 C 48 24.448 47.553 24 47 24 L 43 24 L 43 20 C 43 19.448 42.553 19 42 19 z"/> </svg>',
svgscroll = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 24 24"> <path d="M 12 2 C 7.5700855 2 4 5.5700855 4 10 L 4 14 C 4 18.429915 7.5700855 22 12 22 C 16.429915 22 20 18.429915 20 14 L 20 10 C 20 5.5700855 16.429915 2 12 2 z M 12 4 C 15.370085 4 18 6.6299145 18 10 L 18 14 C 18 17.370085 15.370085 20 12 20 C 8.6299145 20 6 17.370085 6 14 L 6 10 C 6 6.6299145 8.6299145 4 12 4 z M 12 6 L 9 9 L 11 9 L 11 11 L 9 11 L 12 14 L 15 11 L 13 11 L 13 9 L 15 9 L 12 6 z" /> </svg>',
svgrema = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 32 32"> <path d="M 4 5 L 4 6 L 4 26 L 4 27 L 5 27 L 19 27 L 19 25 L 6 25 L 6 11 L 26 11 L 26 17 L 28 17 L 28 6 L 28 5 L 27 5 L 5 5 L 4 5 z M 6 7 L 26 7 L 26 9 L 6 9 L 6 7 z M 9 14 L 9 16 L 11 16 L 11 14 L 9 14 z M 13 14 L 13 16 L 23 16 L 23 14 L 13 14 z M 9 18 L 9 20 L 11 20 L 11 18 L 9 18 z M 13 18 L 13 20 L 19 20 L 19 18 L 13 18 z M 25 19 C 24.448 19 24 19.448 24 20 L 20 20 L 20 22 L 32 22 L 32 20 L 28 20 C 28 19.448 27.552 19 27 19 L 25 19 z M 21 23 L 21 30 C 21 31.093063 21.906937 32 23 32 L 29 32 C 30.093063 32 31 31.093063 31 30 L 31 23 L 29 23 L 29 30 L 23 30 L 23 23 L 21 23 z"/> </svg>',
svgcontent = '<svg style="height: 20px; width: 20px;" fill="#090" viewBox="0 0 50 50"> <path d="M 43 7 C 42.448 7 42 7.447 42 8 L 42 12 C 42 12.553 42.448 13 43 13 L 47 13 C 47.552 13 48 12.553 48 12 L 48 8 C 48 7.447 47.552 7 47 7 L 43 7 z M 4 8 A 2.0002 2.0002 0 1 0 4 12 L 36 12 A 2.0002 2.0002 0 1 0 36 8 L 4 8 z M 43 17 C 42.448 17 42 17.447 42 18 L 42 22 C 42 22.553 42.448 23 43 23 L 47 23 C 47.552 23 48 22.553 48 22 L 48 18 C 48 17.447 47.552 17 47 17 L 43 17 z M 4 18 A 2.0002 2.0002 0 1 0 4 22 L 36 22 A 2.0002 2.0002 0 1 0 36 18 L 4 18 z M 43 27 C 42.448 27 42 27.448 42 28 L 42 32 C 42 32.552 42.448 33 43 33 L 47 33 C 47.552 33 48 32.552 48 32 L 48 28 C 48 27.448 47.552 27 47 27 L 43 27 z M 4 28 A 2.0002 2.0002 0 1 0 4 32 L 36 32 A 2.0002 2.0002 0 1 0 36 28 L 4 28 z M 43 37 C 42.448 37 42 37.448 42 38 L 42 42 C 42 42.552 42.448 43 43 43 L 47 43 C 47.552 43 48 42.552 48 42 L 48 38 C 48 37.448 47.552 37 47 37 L 43 37 z M 4 38 A 2.0002 2.0002 0 1 0 4 42 L 36 42 A 2.0002 2.0002 0 1 0 36 38 L 4 38 z"/> </svg>',
svgdd = '<svg viewBox="0 0 60 60" style="height: 20px; width: 20px; margin: 0 2px 0 0; animation: LN_rotate 2s linear infinite;"><path d="M46.03,32c0,-2.751 2.233,-4.985 4.985,-4.985c2.751,0 4.985,2.234 4.985,4.985c0,2.751 -2.234,4.985 -4.985,4.985c-2.752,0 -4.985,-2.234 -4.985,-4.985Z" style="fill: #030;"></path> <path d="M41.92,41.92c1.946,-1.945 5.105,-1.945 7.051,0c1.945,1.946 1.945,5.105 0,7.051c-1.946,1.945 -5.105,1.945 -7.051,0c-1.945,-1.946 -1.945,-5.105 0,-7.051Z" style="fill: #060;"></path> <circle cx="32" cy="51.015" r="4.985" style="fill: #090;"></circle><path d="M22.08,41.92c1.945,1.946 1.945,5.105 0,7.051c-1.946,1.945 -5.105,1.945 -7.051,0c-1.945,-1.946 -1.945,-5.105 0,-7.051c1.946,-1.945 5.105,-1.945 7.051,0Z" style="fill: #090b;"></path> <path d="M17.97,32c0,2.751 -2.233,4.985 -4.985,4.985c-2.751,0 -4.985,-2.234 -4.985,-4.985c0,-2.751 2.234,-4.985 4.985,-4.985c2.752,0 4.985,2.234 4.985,4.985Z" style="fill: #0909;"></path> <path d="M22.08,22.08c-1.946,1.945 -5.105,1.945 -7.051,0c-1.945,-1.946 -1.945,-5.105 0,-7.051c1.946,-1.945 5.105,-1.945 7.051,0c1.945,1.946 1.945,5.105 0,7.051Z" style="fill: #0906;"></path><circle cx="32" cy="12.985" r="4.985" style="fill: #0903;"></circle></svg>',
BOTCHECK = B => {
if (B.innerText.includes('Cloudflare') || B.innerText.includes('cloudflare') || B.innerText.includes('Verifying that you are not a robot...') || B.innerText.includes('Your access to this site has been limited by the site owner')) return true;
else return false;
};
let sch, pch, och, dch, nch, bch, fch, tah, etah, portcss, CSS, ccss, fontcss, liningcss, CS;
if (SITES[location.host]) CS = SITES[location.host];
let LF = 'https://fonts.googleapis.com/css2?family=Asap&family=Berkshire+Swash&family=Comic+Neue&family=Dosis&family=Nunito+Sans&family=Maven+Pro&family=Quicksand&family=Squada+One&family=Ubuntu&display=swap',
fonts = ['comic sans ms', 'comic neue', 'squada one', 'maven pro', 'berkshire swash', 'dosis', 'cursive', 'ubuntu', 'asap', 'helvetiva', 'quicksand', 'verdana'],
NLF = document.createElement('link');
NLF.rel = 'stylesheet';
NLF.href = LF;
document.head.appendChild(NLF);
a.classList.add('LN_alert');
document.body.appendChild(a);
GM_addStyle(` .LN_alert { line-height: initial; position: fixed; z-index: 2147483647; background-color: #000; color: #090; font-size: 14px; font-family: 'Nunito Sans', sans-serif; transition: all 0.5s ease; user-select: none; } .LN_alert { bottom: 3%; margin: 0 auto; width: 500px; height: fit-content; height: -moz-fit-content; border-radius: 20px; border: 1px #090 solid; display: none; opacity: 0; user-select: none; cursor: default; text-align: center; padding: 25px; font-size: 25px; font-weight: 700; } `);
{
const arr1 = ['』', '」', '〉', '『', '「', '〈', '♬', '☆', '⑨', 'ē', '[email protected]', '→', '←', '↔',],
arr2 = [']', ']', ']', '[', '[', '[', '', '', '', 'e', '@', ' > ', ' < ', ' <-> '];
if (!RULES.LN_search('&.{0,5};').found) {
const obj = {
word: '&.{0,5};',
replaced: '',
type: 'contains',
cond: 'word'
};
RULES.push(obj);
}
for (let i = 0; i < arr1.length; i++) {
if (!RULES.LN_search(arr1[i]).found) {
const obj = {
word: arr1[i],
replaced: arr2[i],
type: 'equals',
cond: 'word'
};
RULES.push(obj);
}
}
GM_setValue('RULES', RULES);
}
function ui() {
GM_addStyle(` .LN_swiper { height: 0; width: 100%; overflow: hidden; background-color: inherit; color: inherit; transition: inherit; } .LN_swiper span { background-color: inherit; color: inherit; transition: inherit; display: block; font-size: 30px; text-align: center; height: 250px; width: 400px; margin: auto; font-family: Helvetica Neue, Segoe UI, Helvetica, Arial, sans-serif; } .LN_icob:hover, .LN_icob2:hover { opacity: 0.3; } .LN_icob, .LN_icob2 { background-color: transparent !important; border: none; padding: 10px 0; margin: 30px 0; display: block ruby; width: 40px; cursor: pointer; outline: 0; font-family: sans-serif; font-weight: bolder; text-align: center; color: #090 !important; } .LN_icob2 { display: none !important; } .LN_sidebar { margin-left: -200px; margin-bottom: -200px; cursor: default !important; background-color: #000 !important; color: #090 !important; border-radius: 0 20px 20px 0; } .LN_filter h4 { color: #090; font-size: 18px; font-family: 'Nunito Sans', sans-serif; display: inline; } .LN_btn[disabled] { opacity: 0.6 !important; cursor: not-allowed !important; color: #000 !important; background-color: #090 !important; } .LN_note { align-content: center; display: flex; text-transform: none; } .LN_current { border: 1px dashed #090; padding: 2px; cursor: pointer; } .LN_styler:not(input), .LN_filter:not(input), .LN_menu:not(input, span) { text-transform: capitalize; } .LN_menu input, .LN_menu select, { appearance: auto; font-family: 'Nunito Sans', sans-serif; display: inline; width: auto; } .LN_help { cursor: help; border-bottom: 2px #aaa dotted; margin-bottom: 2px; display: inline-block; } .LN_sidebar *, .LN_settings *, .LN_downloadCounter *, .LN_menu *, .LN_overlay *, .LN_chapter *, .LN_styler *, .LN_filter *, .LN_chapterin * { transition: inherit; } .LN_settings, .LN_sidebar, .LN_menu, .LN_overlay, .LN_chapter, .LN_styler, .LN_filter, .LN_chapterin, .LN_downloadCounter { line-height: initial; position: fixed; z-index: 9999999999999999999; background-color: #000; color: #090; font-size: 14px; font-family: 'Nunito Sans', sans-serif; transition: all 0.5s ease; user-select: none; } .LN_settings, .LN_downloadCounter { bottom: 0; right: 0; padding: 5px; border-top-left-radius: 7px; cursor: pointer; border-right: 7px #090 solid; opacity: 0.7; font-family: cursive; font-weight: 700; } .LN_downloadCounter { right: -600px; cursor: default;} .LN_settings:hover, .LN_downloadCounter:hover { opacity: 1; } .LN_menu::-webkit-resizer, .LN_menu::-webkit-scrollbar-corner, .LN_menu::-webkit-scrollbar-button, .LN_chapterin::-webkit-resizer, .LN_chapterin::-webkit-scrollbar-corner, .LN_chapterin::-webkit-scrollbar-button { transition: all 0.5s; width: 0; height: 0; display: none; } .LN_menu::-webkit-scrollbar-thumb, .LN_chapterin::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: #090; } .LN_menu::-webkit-scrollbar-track, .LN_menu::-webkit-scrollbar, .LN_chapterin::-webkit-scrollbar-track, .LN_chapterin::-webkit-scrollbar { transition: all 0.5s; background-color: #000; width: 10px; } .LN_menu { top: 2%; right: 0; padding: 20px; width: 500px; height: -moz-fit-content; height: fit-content; margin-right: -600px; border-radius: 15px 0px 0px 15px; scrollbar-color: #090 #000; scrollbar-width: thin; } .LN_menu h2, .LN_styler h2, .LN_filter h2 { font-size: 22px; margin: 0; display: inline-block; line-height: 30px; transition: inherit; color: #090; cursor: pointer; font-weight: bolder; font-family: Helvetica Neue, Segoe UI, Helvetica, Arial, sans-serif; } .LN_menu input[type=radio] { display: inline-block; appearance: radio; } .LN_menu input[type=number], .LN_menu input[type=text], .LN_filter input[type=text], .LN_menu input[type=time], .LN_styler input { width: auto; min-width: 100px; margin: 2px; outline: 0; background-color: #111; color: #777; border: none; padding: 2px; margin-left: 15px; text-align: center; font-weight: bolder; outline: 0; transition: inherit; appearance: auto; display: inline; } .LN_menu input[type=number]:focus, .LN_menu input[type=text]:focus, .LN_menu input[type=time]:focus, .LN_styler input:focus, .LN_filter input:focus { box-shadow: 0 0 5px 2px #090; padding: 3px; margin: 5px 1px 5px 10px; outline: 0; border: 1px solid #090; } .LN_menu input:invalid, .LN_filter input:invalid, .LN_styler input:invalid, .LN_main textarea:invalid { box-shadow: 0 0 5px 2px #900; outline: 0; border: 1px solid #900; } .LN_menu select, .LN_filter select { border: 1px solid #090; margin: 2px; background-color: #000; color: #090; outline: 0; cursor: pointer; font-size: 20px; padding: 3px 10px; appearance: auto; width: auto; height: auto; display: inline; } .LN_s, .LN_p, .LN_o, .LN_d, .LN_n, .LN_b, .LN_f { overflow-y: scroll; height: 0px; transition: inherit; } .LN_btn:hover { color: #090; background-color: #000; } .LN_cancel { display: inline-flex; vertical-align: middle; cursor: pointer; } .LN_btn { transition: inherit; border: none; color: #000; background-color: #090; outline: 0; font-family: sans-serif; cursor: pointer; padding: 5px 20px; font-weight: bolder; margin-left: 10px; margin-top: 10px; } .LN_overlay { z-index: 2147483640 !important; top: 0; right: 0; position: fixed; background-color: #000; opacity: 0.7; display: none; height: 100%; width: 100%; } .LN_chapter { user-select: text; display: none; top: 0; left: 0; width: 100%; height: 100%; z-index: 2147483635 !important; overflow: scroll; scroll-behavior: smooth; scrollbar-color: #000 #090; scrollbar-width: thin; } .LN_chapter div { background-color: #090; color: #000; padding: 50px 250px; border: none; } .LN_chapter::-webkit-resizer, .LN_chapter::-webkit-scrollbar-corner, .LN_chapter::-webkit-scrollbar-button { transition: all 0.5s; width: 0; height: 0; display: none; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: #000; } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: #090; width: 10px; } .LN_styler, .LN_filter, .LN_chapterin { box-shadow: 0 0 20px 20px #090; top: 20px; display: none; padding: 20px; margin: 20px auto; border-radius: 20px; height: -moz-fit-content; width: -moz-fit-content; height: fit-content; width: fit-content; } .LN_styler textarea { color: #090; background-color: #222; border: none; outline: 0; padding: 2px; width: 500px; height: 300px; font-family: roboto; } .LN_styler h2, .LN_filter h2 { cursor: default; } .LN_b input[type=file] { cursor: pointer; background-color: #000; color: #090; padding: 3px; border: 1px solid #090; } .LN_loading { width: 50px; height: 50px; text-align: center; margin: auto; border-radius: 50%; border: 8px #000 solid; border-color: #090 transparent; animation: LN_rotate 2s linear infinite; } .LN_chapterin { width: -moz-fit-content; width: fit-content; height: 70%; margin: auto; overflow-y: scroll; display: none; z-index: 2147483646 !important; background-color: #000; top: 30px; padding: 20px; border-radius: 15px; scrollbar-color: #090 #000; scrollbar-width: thin; align-self: center; scroll-behavior: smooth; } .LN_chapteran { border: 1px #090 solid; margin: 10px 0; width: 100%; text-align: center; color: #090; transition: all 0.3s; padding: 5px 50px; box-sizing: border-box; } .LN_chapteran:hover { color: #ccc !important; text-decoration: underline; border: 1px #ccc solid !important; } .LN_chapteran:visited { color: #c83; border: 1px #c83 solid; } @keyframes LN_rotate { from {transform: rotate(0deg);} to {transform:rotate(360deg);} `);
portcss = GM_addStyle(` .LN_cmob { width: 100% !important; right: 0 !important; left: 0 !important; } .LN_fmob { width: 100% !important; right: 0 !important; left: 0 !important; } .LN_cmob textarea { width: 100% !important; } .LN_amob { font-size: 14px !important; max-width: 300px !important; margin: 0 ${(window.innerWidth - 300) / 2}px !important; } .LN_mob { padding: 20px !important; } .LN_bot { border-radius: 20px 20px 0 0 !important; } .LN_bot .LN_icob, .LN_bot .LN_icob2 { display: inline; padding: 0; margin: 10px 0; } `);
CSS = GM_addStyle(` .LN_chapter, .LN_chapter div, .LN_chapter p { background-color: ${bgc} !important; color: ${txc} !important; scrollbar-color: ${txc} ${bgc}; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: ${txc} } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: ${bgc}; width: 10px; }`);
fontcss = GM_addStyle(` .LN_chapter, .LN_chapter div, .LN_chapter p { font-size: ${defaultfs}px !important; font-family: ${defaultf} !important; }`);
liningcss = GM_addStyle(` .LN_chapter * { line-height: ${lheight}; } .LN_chapter p { margin: ${pmargin}px 0; text-align: start; }`);
ccss = GM_addStyle(CCSS);
s.className = 'LN_settings';
d.className = 'LN_downloadCounter';
o.className = 'LN_overlay';
m.className = 'LN_menu';
c.className = 'LN_styler';
f.className = 'LN_filter';
r.className = 'LN_chapter';
n.className = 'LN_sidebar';
l.className = 'LN_chapterin';
s.innerHTML = 'XDHx86 - LN Loader v4.3 | Settings';
d.innerHTML = svgdd + ' Downloading Chapters: 1';
m.innerHTML = `<h2> ${svga} Shortcuts: </h2> <div class="LN_s"> Reader Mode: <input type="text"> <br> Next Chapter: <input type="text"> <br> Next Chapter: <input type="text"> <br> Next Chapter: <input type="text"> <br> Prev Chapter: <input type="text"> <br> Prev Chapter: <input type="text"> <br> Prev Chapter: <input type="text"> <br> Dark Mode: <input type="text"> <br> Increase Font Size: <input type="text"> <br> Decrease Font Size: <input type="text"> <br> Remove Annoyances: <input type="text"> <br> Download: <input type="text"> <br> Auto Scroll: <input type="text"> <br> Show Favorites: <input type="text"> <br> </div> <h2> ${svga} Switches: </h2> <div class="LN_p"> <span title="What is this? Click to see notes." class="LN_help"> Auto Remove Annoyances: </span> <br> Yes <input type="radio" name="rn"> No <input type="radio" name="rn"> <br> Auto Next Chapter: <br> Yes <input type="radio" name="nc"> No <input type="radio" name="nc"> <br> <span title="What is this? Click to see notes." class="LN_help"> Use Dynamic Loading: </span> <br> Yes <input type="radio" name="dl"> No <input type="radio" name="dl"> <br> Download Type: <br> EPUB <input type="radio" name="ep"> PDF <input type="radio" name="ep"> <br> Auto Resume from Last Stop: <br> Yes <input type="radio" name="ar"> No <input type="radio" name="ar"> <br> Default Reader Mode: <br> Dark Mode <input type="radio" name="dr"> Light Mode <input type="radio" name="dr"> <br> Auto Scroll Type: <br> Page-Like <input type="radio" name="st"> Continuous <input type="radio" name="st"> <br> <span class="LN_help" title="What is this? click to see notes"> Swipe gestures: </span> <br> Yes <input type="radio" name="sg"> No <input type="radio" name="sg"> <br> Dynamic light-dark mode: <br> Yes <input type="radio" name="ld"> No <input type="radio" name="ld"> <br> decensore words: <br> Yes <input type="radio" name="uc"> No <input type="radio" name="uc"> <br> Confirm on auto download: <br> Yes <input type="radio" name="ca"> No <input type="radio" name="ca"> <br> Auto Download updates from: <br> Last read chapter <input type="radio" name="du"> Last downloaded chapter <input type="radio" name="du"> <br> Check Completed Novels: <br> Yes <input type="radio" name="cn"> No <input type="radio" name="cn"> <br> Auto Retry: <br> Yes <input type="radio" name="rr"> No <input type="radio" name="rr"> </div> <h2> ${svga} General: </h2> <div class="LN_o"> Default Font: <br> <select class="LN_dfs"> <option value="'Comic Sans MS'">Comic Sans MS</option> <option value="'Comic Neue'">Comic Neue</option> <option value="'Squada One'">Squada One</option> <option value="'Maven Pro'">Maven Pro</option> <option value="'Berkshire Swash'">Berkshire Swash</option> <option value="Dosis">Dosis</option> <option value="Cursive">Cursive</option> <option value="Ubuntu">Ubuntu</option> <option value="Asap">Asap</option> <option value="Helvetica">Helvetica</option> <option value="Quicksand">Quicksand</option> <option value="Verdana">Verdana</option> <option value="Custom">Custom</option> </select> <input class="LN_dft" style="display: none;" type="text"> <span class="LN_cancel" title="Cancel" style="display: none;"> ${svgs} </span> <span class="LN_note" style="display: none;"> ${svgi} Note that: <br> - The value has to be inbetween quotes and capitalized to work. <br> - The font has to be from Google Fonts. </span> <br> Auto Scroll Speed: <br> <select class="LN_ass"> <option value="60">Fastest</option> <option value="70">Faster</option> <option value="80">Fast</option> <option value="90">Medium</option> <option value="100">Slow</option> <option value="110">Slower</option> <option value="120">Slowest</option> <option value="Custom">Custom</option> </select> <input class="LN_ast" style="display: none;" type="number" min="10" max="200" step="1"> <span class="LN_cancel" title="Cancel" style="display: none;"> ${svgs} </span> <span class="LN_note" style="display: none;"> ${svgi} Notes: <br> - The lower the value the faster the speed. <br> - If you're using page-like scroll, then this represents seconds to wait before scrolling.</span> <br> Default Font Size: <input type="number" min="8" max="30"> <span class="LN_current"> Current Size </span> <br> Space between each paragraph: <input type="number" min="10" max="60"> <br> Line height: <input type="number" min="1" max="3" step="0.01"> <br> <span title="What is this? Click to see notes." class="LN_help"> Auto Next Chapter Delay: </span> <input type="number" min="20" max="100" step="1"> <br> Auto dark-mode hour: <input type="number" min="0" max="24" step="1"> <br> <span title="Click to see important note!" class=LN_help> Auto light-mode hour: </span> <input type="number" min="0" max="24" step="1"> <br> Auto retry times: <input type="number" min="0" max="1000" step="1"> <br> <br> Customize Styles: <button class="LN_btn"> Open Styler </button> <br> <span title="What is this? Click to see notes." class="LN_help"> Word Filtering: </span> <button class="LN_btn"> Open Filter </button> </div> <h2> ${svga} Download: </h2> <div class="LN_d"> Download <input type="number" min="1" max="10000" step="1"> chapter(s) <br> <button style="margin-top: 10px" class="LN_btn"> Download </button> <br> <span class=LN_note style="margin: 5px 0;"> ${svgi} Please note that: <br> - If you see random letters or incomprehensible words in a downloaded chapter, then you need to filter the letter that caused this issue such as:『 』「 」〈 〉 and other similar non-ASCII characters. </span> <br> Cancel current download: <button style="margin-top: 10px" class="LN_btn"> Cancel </button> <br> Cancel auto download: <button style="margin-top: 10px" class="LN_btn"> Cancel </button> </div> <h2> ${svga} Notifications & Favorites: </h2> <div class="LN_n"> Add this novel to favorites: <button class="LN_btn"> Add </button> <br> Notify when no chapters found: <br> Yes <input type="radio" name="nf"> No <input type="radio" name="nf"> <br> Auto check for updates: <br> Enabled <input type="radio" name="nn"> Disabled <input type="radio" name="nn"> <br> <span class=LN_help title="Not sure of something? Click to see notes"> Auto download updates: </span> <br> Yes <input type="radio" name="ad"> No <input type="radio" name="ad"> <br> <span class=LN_help> Auto check updates after: </span> <input type="time"> <br> Check for updates now: <button class="LN_btn"> Check </button> <br> Cancel current check: <button class="LN_btn"> Cancel </button> <br> Favorite novels list: <button class="LN_btn"> Show </button> <br> <span class="LN_note"> ${svgi} Please note that: <br> - If the window is already open it won't open again. <br> - You need to allow popups to use this. </span> </div> <h2> ${svga} Backup & Restore: </h2> <div class="LN_b"> Backup: <button class="LN_btn"> Backup </button> <br> Restore: <input type="file"> <button class="LN_btn"> Restore </button> <br> <br> Auto backup period: <input type=number min=0 max=20 step=1> <span class=LN_note> ${svgi} Notes: <br> - Set to zero to disable. <br> - This value determines the days before an auto backup. </span> </div> <h2> ${svga} Feedback: </h2> <div class="LN_f"> <button class=LN_btn> Send an email </button> </div> <br> <br> <button class="LN_btn"> Save </button> <button class="LN_btn"> Reset </button> `;
c.innerHTML = 'Reader Text Color: <input type="text"> <br> Reader Background Color: <input type="text"> <br> Reader Text Color (Dark Mode): <input type="text"> <br> Reader Background Color (Dark Mode): <input type="text"> <br> <br> <h2>' + svga + ' Advanced Styling: </h2> <br> <textarea spellcheck="false" placeholder="Type CSS Code">' + CCSS + '</textarea> <br> <h2>' + svga + ' EPUB Styling: </h2> <br> <textarea spellcheck="false" placeholder="Type CSS Code">' + ECCSS + '</textarea> <br> <br> <button class="LN_btn"> Save </button>';
f.innerHTML = '<div> <h4> Replace </h4> <select> <option value="line"> Whole Line </option> <option value="word"> Word Only </option> </select> <h4> That </h4> <select> <option value="contains"> Contains </option> <option value="equals"> Equals </option> </select> <h4> This Value: </h4> <input type="text"> <h4> With: </h4> <input type="text" placeholder="Leave Empty to Remove"> <span class="LN_note"> ' + svgi + 'Note: <br> - Replaced word and the to be replaced word are both case-sensitive. <br> - "Word only" replacer with modifier "contains" uses RegExp. <br> - "Word only" replacer with modifier "equals" can contain HTML code, such as "< BR >". </span> </div> <h2> Rules: </h2> <br> <select size="6" style="width: 100%; padding: 0px;"> </select> <div style="display: inline-block;height: 50px;"> <button class="LN_btn" style="margin: 10px 2px;"> Add Rule </button> <button class="LN_btn" disabled="" style="right: 20px;position: absolute; margin: 10px -2px;"> Remove </button> </div>';
n.innerHTML = `<button class="LN_icob" title="Reader mode"> ${svgreader} </button> <button class="LN_icob" title="Settings"> ${svgsettings} </button> <button class="LN_icob" title="Previous Chapter"> ${svgarrowl} </button> <button class="LN_icob" title="Dark/Light Mode"> ${svgdarkm} </button> <button class="LN_icob" title="Next Chapter"> ${svgarrowr} </button> <button class="LN_icob" title="Extra actions"> ${svgsh} </button> </button> <button class="LN_icob2" title="Increase font size"> ${svgfi} </button> </button> <button class="LN_icob2" title="Decrease font size"> ${svgfd} </button> </button> <button class="LN_icob2" title="Auto scroll"> ${svgscroll} </button> </button> <button class="LN_icob2" title="Remove annoyances"> ${svgrema} </button> <button class="LN_icob2" title="Show novel's chapter index"> ${svgcontent} </button> `;
l.innerHTML = '<div class=LN_loading></div>';
r.appendChild(document.querySelector(CS.chapter).cloneNode(true));
document.body.appendChild(s);
document.body.appendChild(d);
document.body.appendChild(m);
document.body.appendChild(o);
document.body.appendChild(c);
document.body.appendChild(f);
document.body.appendChild(r);
document.body.appendChild(n);
document.body.appendChild(l);
if (location.host == 'www.lightnovelworld.com') document.querySelector('.navigation-bar').style.zIndex = 2, document.querySelector('.control-action').style.zIndex = 5;
{
let p = 50;
m.querySelector('.LN_s').style.height = '-moz-fit-content';
m.querySelector('.LN_s').style.height = 'fit-content';
sch = m.querySelector('.LN_s').offsetHeight + p;
m.querySelector('.LN_s').style.height = '0';
pch = 400;
m.querySelector('.LN_p').style.height = '0';
m.querySelector('.LN_o').style.height = '-moz-fit-content';
m.querySelector('.LN_o').style.height = 'fit-content';
och = m.querySelector('.LN_o').offsetHeight + 130;
m.querySelector('.LN_o').style.height = '0';
m.querySelector('.LN_d').style.height = '-moz-fit-content';
m.querySelector('.LN_d').style.height = 'fit-content';
dch = m.querySelector('.LN_d').offsetHeight + p;
m.querySelector('.LN_d').style.height = '0';
m.querySelector('.LN_n').style.height = '-moz-fit-content';
m.querySelector('.LN_n').style.height = 'fit-content';
nch = m.querySelector('.LN_n').offsetHeight + p;
m.querySelector('.LN_n').style.height = '0';
m.querySelector('.LN_b').style.height = '-moz-fit-content';
m.querySelector('.LN_b').style.height = 'fit-content';
bch = m.querySelector('.LN_b').offsetHeight + p;
m.querySelector('.LN_b').style.height = '0';
m.querySelector('.LN_f').style.height = '-moz-fit-content';
m.querySelector('.LN_f').style.height = 'fit-content';
fch = m.querySelector('.LN_f').offsetHeight + p;
m.querySelector('.LN_f').style.height = '0';
c.querySelector('textarea').style.height = '-moz-fit-content';
c.querySelector('textarea').style.height = 'fit-content';
tah = c.querySelector('textarea').offsetHeight || 200;
c.querySelector('textarea').style.height = '0';
c.querySelector('textarea').style.display = 'none';
c.querySelectorAll('textarea')[1].style.height = '-moz-fit-content';
c.querySelectorAll('textarea')[1].style.height = 'fit-content';
etah = c.querySelectorAll('textarea')[1].offsetHeight || 200;
c.querySelectorAll('textarea')[1].style.height = '0';
c.querySelectorAll('textarea')[1].style.display = 'none';
}
document.body.onclick = document.body.ontouchstart = e => {
let path = e.target;
for (let i = 0; i < 6; i++) {
if (path.LN_hasClass(['LN_menu', 'LN_alert', 'LN_styler', 'LN_overlay', 'LN_settings', 'LN_filter', 'LN_sidebar', 'LN_decincfs', 'LN_chapterin', '.LN_downloadCounter'])) return;
else if (path.parentElement && path.parentElement.classList) path = path.parentElement;
if (i == 5) {
sh('sf');
sh('pf');
sh('of');
sh('df');
sh('nf');
sh('bf');
sh('ff');
sh('stf');
sh('cif');
sh('wff');
sh('menuf');
sh('etahf');
sh('tahf');
}
}
}
s.onclick = () => {
if (m.style.marginRight == '0' || m.style.marginRight == '0px') {
sh('menuf');
sh('sf');
sh('of');
sh('df');
sh('nf');
sh('bf');
sh('ff');
sh('stf');
sh('wff');
} else sh('menut');
}
m.querySelectorAll('h2')[0].onclick = () => {
if (m.querySelector('.LN_s').style.height == '0' || m.querySelector('.LN_s').style.height == '0px') {
sh('st');
sh('pf');
sh('of');
sh('df');
sh('nf');
sh('bf');
sh('ff');
} else sh('sf');
}
m.querySelectorAll('h2')[1].onclick = () => {
if (m.querySelector('.LN_p').style.height == '0' || m.querySelector('.LN_p').style.height == '0px') {
sh('sf');
sh('pt');
sh('of');
sh('df');
sh('nf');
sh('bf');
sh('ff');
} else sh('pf');
}
m.querySelectorAll('h2')[2].onclick = () => {
if (m.querySelector('.LN_o').style.height == '0' || m.querySelector('.LN_o').style.height == '0px') {
sh('sf');
sh('pf');
sh('ot');
sh('df');
sh('nf');
sh('bf');
sh('ff');
} else sh('of');
}
m.querySelectorAll('h2')[3].onclick = () => {
if (m.querySelector('.LN_d').style.height == '0' || m.querySelector('.LN_d').style.height == '0px') {
sh('sf');
sh('pf');
sh('of');
sh('dt');
sh('nf');
sh('bf');
sh('ff');
} else sh('df');
}
m.querySelectorAll('h2')[4].onclick = () => {
if (m.querySelector('.LN_n').style.height == '0' || m.querySelector('.LN_n').style.height == '0px') {
sh('sf');
sh('pf');
sh('of');
sh('df');
sh('nt');
sh('bf');
sh('ff');
} else sh('nf');
}
m.querySelectorAll('h2')[5].onclick = () => {
if (m.querySelector('.LN_b').style.height == '0' || m.querySelector('.LN_b').style.height == '0px') {
sh('sf');
sh('pf');
sh('of');
sh('df');
sh('nf');
sh('bt');
sh('ff');
} else sh('bf');
}
m.querySelectorAll('h2')[6].onclick = () => {
if (m.querySelector('.LN_f').style.height == '0' || m.querySelector('.LN_f').style.height == '0px') {
sh('sf');
sh('pf');
sh('of');
sh('df');
sh('nf');
sh('bf');
sh('ft');
} else sh('ff');
}
m.querySelector('.LN_btn').onclick = () => {
if (c.style.display == 'block') {
sh('wff');
sh('stf');
sh('cif');
} else {
sh('wff');
sh('cif');
sh('stt');
}
}
m.querySelectorAll('.LN_btn')[1].onclick = () => {
if (f.style.display == 'block') {
sh('wff');
sh('stf');
sh('cif');
} else {
sh('stf');
sh('cif');
sh('wft');
}
}
c.querySelectorAll('h2')[0].onclick = () => {
if (c.querySelectorAll('textarea')[0].style.display == 'block') sh('tahf');
else {
sh('etahf');
sh('taht');
}
}
c.querySelectorAll('h2')[1].onclick = () => {
if (c.querySelectorAll('textarea')[1].style.display == 'block') sh('etahf');
else {
sh('tahf');
sh('etaht');
}
}
m.querySelector('.LN_dfs').style.fontFamily = m.querySelector('.LN_dfs').value;
m.querySelector('.LN_dfs').addEventListener('change', function() {
this.style.fontFamily = this.value;
if (this.value == 'Custom') sh('dft');
m.querySelector('.LN_cancel').onclick = () => sh('dfs');
});
m.querySelector('.LN_ass').addEventListener('change', function() {
if (this.value == 'Custom') sh('ast');
m.querySelectorAll('.LN_cancel')[1].onclick = () => sh('ass');
});
o.onclick = e => {
sh('stf');
sh('wff');
sh('cif');
sh('tahf');
sh('etahf');
}
let idlc = 0;
r.addEventListener('click', () => {
idlc = 0;
r.querySelector(CS.chapter).style.cursor = 'text';
r.style.cursor = 'text';
});
r.addEventListener('mousemove', () => {
idlc = 0;
r.querySelector(CS.chapter).style.cursor = 'text';
r.style.cursor = 'text';
});
window.setInterval(() => {
idlc++;
if (idlc >= 5) {
r.querySelector(CS.chapter).style.cursor = 'none';
r.style.cursor = 'none';
}
}, 1000);
window.addEventListener('click', e => {
let found, elm = e.target;
for (let i = 0; i < 6; i++) {
if (elm.LN_hasClass(['LN_menu', 'LN_alert', 'LN_styler', 'LN_overlay', 'LN_settings', 'LN_filter', 'LN_sidebar', 'LN_decincfs', '.LN_chapterin', '.LN_downloadCounter'])) {
found = true;
break;
}
if (elm.parentElement) elm = elm.parentElement;
}
if (!found) sh('sidebar');
});
let currentfont = defaultf.replace(/'|"/g, '');
if (!fonts.includes(currentfont.toLowerCase())) {
NLF.href = 'https://fonts.googleapis.com/css2?family=' + currentfont.replace(' ', '+') + '&display=swap';
let sl = m.querySelector('select');
document.head.appendChild(NLF);
sl.lastElementChild.remove();
sl.options.add(new Option(currentfont, defaultf));
sl.options.add(new Option('Custom', 'Custom'));
}
let mobcss,
mobcssf = () => {
portcss.innerHTML = ` .LN_cmob { width: 100% !important; right: 0 !important; left: 0 !important; } .LN_fmob { width: 100% !important; right: 0 !important; left: 0 !important; } .LN_cmob textarea { width: 100% !important; } .LN_amob { font-size: 14px !important; max-width: 300px !important; margin: 0 ${(window.innerWidth - 300) / 2}px !important; } .LN_mob { padding: 20px !important; } .LN_bot { border-radius: 20px 20px 0 0 !important; } .LN_bot .LN_icob, .LN_bot .LN_icob2 { display: inline; padding: 0; margin: 10px 0; } `;
if (!mobcss) mobcss = GM_addStyle(` .LN_menu { width: 300px !important; font-size: 14px !important; } .LN_menu .LN_s, .LN_menu h2:first-child, .LN_menu br:first-of-type { display: none !important; } .LN_menu h2 { font-size: 18px !important; } .LN_menu select { font-size: 16px; } `);
},
inputsizes = (mode) => {
if (mode) {
for (let e of m.querySelectorAll('input')) e.size = 1;
for (let e of c.querySelectorAll('input')) e.size = 1;
for (let e of f.querySelectorAll('input')) e.size = 1;
} else {
for (let e of m.querySelectorAll('input')) e.size = 20;
for (let e of c.querySelectorAll('input')) e.size = 20;
for (let e of f.querySelectorAll('input')) e.size = 20;
}
},
robserver = new ResizeObserver(e => {
let aside = n;
if (window.innerWidth < window.innerHeight) {
inputsizes(true);
mobcssf();
r.querySelector(CS.chapter).classList.add('LN_mob');
a.classList.add('LN_amob');
c.classList.add('LN_cmob');
f.classList.add('LN_fmob');
aside.classList.remove('LN_side');
aside.classList.add('LN_bot');
s.style = 'display: none !important;';
aside.style = `bottom: 0; left: ${(r.offsetWidth - aside.offsetWidth) / 2}px;`;
} else {
inputsizes(false);
r.querySelector(CS.chapter).classList.remove('LN_mob');
a.classList.remove('LN_amob');
c.classList.remove('LN_cmob');
f.classList.remove('LN_fmob');
aside.classList.add('LN_side');
aside.classList.remove('LN_bot');
s.style = 'display: block !important;';
aside.style = `top: ${(r.offsetHeight - aside.offsetHeight) / 2}px; left: 0;`;
if (mobcss) {
mobcss.remove();
mobcss = false;
}
}
});
robserver.observe(r);
if (window.innerWidth < window.innerHeight) {
inputsizes(true);
mobcssf();
r.querySelector(CS.chapter).classList.add('LN_mob');
a.classList.add('LN_amob');
c.classList.add('LN_cmob');
f.classList.add('LN_fmob');
n.classList.remove('LN_side');
n.classList.add('LN_bot');
s.style = 'display: none !important;';
setTimeout(() => { n.style = `bottom: 0; left: ${(window.innerWidth - n.offsetWidth) / 2}px;` }, 1000);
}
else {
inputsizes(false);
r.querySelector(CS.chapter).classList.remove('LN_mob');
a.classList.remove('LN_amob');
c.classList.remove('LN_cmob');
f.classList.remove('LN_fmob');
n.classList.add('LN_side');
n.classList.remove('LN_bot');
s.style = 'display: block !important;';
setTimeout(() => { n.style = `top: ${(window.innerHeight - n.offsetHeight) / 2}px; left: 0;` }, 1000);
if (mobcss) {
mobcss.remove();
mobcss = false;
}
}
window.onresize = () => {
if (window.innerWidth < window.innerHeight) {
inputsizes(true);
mobcssf();
r.querySelector(CS.chapter).classList.add('LN_mob');
a.classList.add('LN_amob');
c.classList.add('LN_cmob');
f.classList.add('LN_fmob');
n.classList.remove('LN_side');
n.classList.add('LN_bot');
s.style = 'display: none !important;';
n.style = `bottom: 0; left: ${(window.innerWidth - n.offsetWidth) / 2}px;`;
}
else {
inputsizes(false);
r.querySelector(CS.chapter).classList.remove('LN_mob');
a.classList.remove('LN_amob');
c.classList.remove('LN_cmob');
f.classList.remove('LN_fmob');
n.classList.add('LN_side');
n.classList.remove('LN_bot');
s.style = 'display: block !important;';
n.style = `top: ${(window.innerHeight - n.offsetHeight) / 2}px; left: 0;`;
if (mobcss) {
mobcss.remove();
mobcss = false;
}
}
}
fsbtn();
}
function fsbtn() {
if (!document.querySelectorAll('#LN_decincfs button')[0]) {
const div = document.createElement('div');
r.querySelector(CS.chapter).style = '';
div.id = 'LN_decincfs';
div.classList.add('LN_decincfs');
div.style.display = 'none';
div.innerHTML = '<button>inc</button><button>dec</button>';
document.body.appendChild(div);
const btn1 = div.querySelector('button'),
btn2 = div.querySelectorAll('button')[1];
r.style.fontSize = defaultfs + 'px';
document.querySelector(CS.chapter).style.fontSize = defaultfs + 'px';
btn1.onclick = () => {
document.querySelector(CS.chapter).style.fontSize = parseInt(document.querySelector(CS.chapter).style.fontSize) + 2 + 'px';
r.querySelector(CS.chapter).style = '';
fontcss.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { font-size: ${parseInt(getComputedStyle(r).fontSize) + 2}px !important; font-family: ${defaultf} !important; }`;
}
btn2.onclick = () => {
document.querySelector(CS.chapter).style.fontSize = parseInt(document.querySelector(CS.chapter).style.fontSize) - 2 + 'px';
r.querySelector(CS.chapter).style = '';
fontcss.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { font-size: ${parseInt(getComputedStyle(r).fontSize) - 2}px !important; font-family: ${defaultf} !important; }`;
}
} else {
r.querySelector(CS.chapter).style = '';
r.style.fontSize = defaultfs + 'px';
document.querySelector(CS.chapter).style.fontSize = defaultfs + 'px';
}
}
function sh(w) {
switch (w) {
case 'rt':
r.style.display = 'block';
break;
case 'rf':
r.style.display = 'none';
break;
case 'menut':
m.style.marginRight = '0';
break;
case 'menuf':
m.style.marginRight = '-600px';
break;
case 'st':
m.querySelector('.LN_s').style.height = sch + 'px';
m.querySelectorAll('h2 svg')[0].style.transform = 'rotate(0deg)';
break;
case 'sf':
m.querySelector('.LN_s').style.height = '0';
m.querySelectorAll('h2 svg')[0].style.transform = 'rotate(-90deg)';
break;
case 'pt':
m.querySelector('.LN_p').style.height = pch + 'px';
m.querySelectorAll('h2 svg')[1].style.transform = 'rotate(0deg)';
break;
case 'pf':
m.querySelector('.LN_p').style.height = '0';
m.querySelectorAll('h2 svg')[1].style.transform = 'rotate(-90deg)';
break;
case 'ot':
m.querySelector('.LN_o').style.height = och + 'px';
m.querySelectorAll('h2 svg')[2].style.transform = 'rotate(0deg)';
break;
case 'of':
m.querySelector('.LN_o').style.height = '0';
m.querySelectorAll('h2 svg')[2].style.transform = 'rotate(-90deg)';
break;
case 'dt':
m.querySelector('.LN_d').style.height = dch + 'px';
m.querySelectorAll('h2 svg')[3].style.transform = 'rotate(0deg)';
break;
case 'df':
m.querySelector('.LN_d').style.height = '0';
m.querySelectorAll('h2 svg')[3].style.transform = 'rotate(-90deg)';
break;
case 'nt':
m.querySelector('.LN_n').style.height = nch + 'px';
m.querySelectorAll('h2 svg')[4].style.transform = 'rotate(0deg)';
break;
case 'nf':
m.querySelector('.LN_n').style.height = '0';
m.querySelectorAll('h2 svg')[4].style.transform = 'rotate(-90deg)';
break;
case 'bt':
m.querySelector('.LN_b').style.height = bch + 'px';
m.querySelectorAll('h2 svg')[5].style.transform = 'rotate(0deg)';
break;
case 'bf':
m.querySelector('.LN_b').style.height = '0';
m.querySelectorAll('h2 svg')[5].style.transform = 'rotate(-90deg)';
break;
case 'ft':
m.querySelector('.LN_f').style.height = fch + 'px';
m.querySelectorAll('h2 svg')[6].style.transform = 'rotate(0deg)';
break;
case 'ff':
m.querySelector('.LN_f').style.height = '0';
m.querySelectorAll('h2 svg')[6].style.transform = 'rotate(-90deg)';
break;
case 'stt':
c.style.display = 'block';
o.style.display = 'block';
c.style.right = (document.body.offsetWidth - c.offsetWidth) / 2 + 'px';
break;
case 'stf':
c.style.display = 'none';
o.style.display = 'none';
break;
case 'wft':
f.style.display = 'block';
o.style.display = 'block';
f.style.right = (document.body.offsetWidth - f.offsetWidth) / 2 + 'px';
break;
case 'wff':
f.style.display = 'none';
o.style.display = 'none';
break;
case 'dct':
d.style.right = '0px';
d.style.bottom = s.offsetHeight + 'px';
break;
case 'dcf':
d.style.right = '-600px';
d.style.bottom = s.offsetHeight + 'px';
break;
case 'taht':
c.querySelectorAll('h2 svg')[0].style.transform = 'rotate(0deg)';
c.querySelectorAll('textarea')[0].style.display = 'block';
c.querySelectorAll('textarea')[0].style.height = tah + 'px';
break;
case 'tahf':
c.querySelectorAll('h2 svg')[0].style.transform = 'rotate(-90deg)';
c.querySelectorAll('textarea')[0].style.display = 'none';
c.querySelectorAll('textarea')[0].style.height = '0';
break;
case 'etaht':
c.querySelectorAll('h2 svg')[1].style.transform = 'rotate(0deg)';
c.querySelectorAll('textarea')[1].style.display = 'block';
c.querySelectorAll('textarea')[1].style.height = etah + 'px';
break;
case 'etahf':
c.querySelectorAll('h2 svg')[1].style.transform = 'rotate(-90deg)';
c.querySelectorAll('textarea')[1].style.display = 'none';
c.querySelectorAll('textarea')[1].style.height = '0';
break;
case 'cit':
sh('wff');
sh('stf');
l.style.display = 'grid';
o.style.display = 'block';
l.style.left = (document.body.offsetWidth - l.offsetWidth) / 2 + 'px';
for (let e of l.querySelectorAll('a')) {
let h = location.href.LN_fix(location.href);
if (h.endsWith('/')) h = h.substr(0, h.length-1);
if (e.href.LN_fix(location.href) == h) {
l.scrollTo(0, e.offsetTop - e.offsetHeight);
break;
}
}
break;
case 'cif':
l.style.display = 'none';
o.style.display = 'none';
break;
case 'dfs':
m.querySelector('.LN_dfs').style.display = 'block';
m.querySelectorAll('.LN_note')[0].style.display = 'none';
m.querySelector('.LN_dfs').value = 'block';
m.querySelector('.LN_cancel').style.display = 'none';
m.querySelector('.LN_dft').style.display = 'none';
setTimeout(() => {
for (let e of m.querySelector('.LN_dfs').options) {
if (e.value == defaultf){
e.selected = true;
m.querySelector('.LN_dfs').style.fontFamily = e.value;
}
}
}, 100);
break;
case 'dft':
m.querySelector('.LN_dfs').style.display = 'none';
m.querySelectorAll('.LN_note')[0].style.display = 'flex';
m.querySelector('.LN_cancel').style.display = 'inline-flex';
m.querySelector('.LN_dft').style.display = 'inline';
m.querySelector('.LN_dft').value = defaultf;
break;
case 'ass':
m.querySelector('.LN_ass').style.display = 'block';
m.querySelectorAll('.LN_note')[1].style.display = 'none';
m.querySelectorAll('.LN_cancel')[1].style.display = 'none';
m.querySelector('.LN_ast').style.display = 'none';
setTimeout(() => { for (let e of m.querySelector('.LN_ass').options) if (e.value == autoscrolls) e.selected = true; }, 100);
break;
case 'ast':
m.querySelector('.LN_ass').style.display = 'none';
m.querySelectorAll('.LN_note')[1].style.display = 'flex';
m.querySelectorAll('.LN_cancel')[1].style.display = 'inline-flex';
m.querySelector('.LN_ast').style.display = 'inline';
m.querySelector('.LN_ast').value = autoscrolls;
break;
case 'sidebar':
if ((n.LN_hasClass('LN_side') && (n.style.marginLeft == '0' || n.style.marginLeft == '0px')) || (n.LN_hasClass('LN_bot') && (n.style.marginBottom == '0' || n.style.marginBottom == '0px'))) {
if (n.LN_hasClass('LN_bot')) {
n.style.marginLeft = '0px';
n.style.marginBottom = '-200px';
} else {
n.style.marginBottom = '0px';
n.style.marginLeft = '-200px';
}
} else {
if (n.LN_hasClass('LN_bot')) {
n.style.marginLeft = '0px';
n.style.marginBottom = '0px';
} else {
n.style.marginBottom = '0px';
n.style.marginLeft = '0px';
}
}
break;
}
}
function alt(msg, time=3000) {
a.innerText = msg;
a.style.cursor = 'default';
a.style.margin = '0 ' + (document.body.offsetWidth - 500) / 2 + 'px';
a.style.display = 'block';
setTimeout(() => { a.style.opacity = '1'; }, 100);
setTimeout(() => { a.style.opacity = '0'; }, time);
setTimeout(() => { a.style.display = 'none'; }, time + 600);
}
function getcontent(url, PL=CS, OBJ) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
try {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
OBJ.BODY = doc.body;
OBJ.CHAPTER = OBJ.BODY.querySelector(PL.chapter);
OBJ.NOVEL = OBJ.BODY.querySelector(PL.novel).innerText;
if (url.includes('readlightnovel.me')) OBJ.CHAPTERTITLE = OBJ.BODY.querySelector('.block-title h1').lastChild.data;
else if (url.includes('novelonlinefull') || url.includes('bestlightnovel')) OBJ.CHAPTERTITLE = OBJ.BODY.querySelector('.rdfa-breadcrumb p').lastChild.data;
else if (url.includes('novels.pl')) OBJ.CHAPTERTITLE = OBJ.BODY.querySelector('.panel-title.pull-left').lastChild.data;
else OBJ.CHAPTERTITLE = OBJ.BODY.querySelector(PL.chaptertitle).innerText;
let title = OBJ.BODY.querySelector(PL.title).innerText;
if (url.includes('novelfull.com') || url.includes('boxnovel.com')) title = OBJ.BODY.querySelector(PL.novel).innerText + ' - ' + OBJ.BODY.querySelector(PL.title).innerText;
if (PL.othert != '') title = OBJ.BODY.querySelector(PL.othert).innerText + ' - ' + title;
if (PL.name != '') for (let i = 0; i < PL.namer; i++) title = title.replace(PL.namer[i], PL.name[i]);
OBJ.TITLE = [doc.title, OBJ.BODY.querySelector(PL.title).innerText, title];
} catch (e) {
if (!OBJ.BODY) {
alt('Error: NETWORK_ERROR\nDetails: Response cannot be null! Please try again later.\nReason: Might be request timeout, bad internet connection, spammed requests in a small time frame.', 10000);
console.error(e);
}
else if (BOTCHECK(OBJ.BODY)) {
if (confirm('Caught by bot verification.\nOpen url (' + url + ') in new tab?')) window.open(url, '_blank');
} else {
alt('Error:\nDetails: Unexpected error has occured. Please try again later.\nReason: Might be request timeout, bad internet connection, another unexpected reason.', 10000);
console.error(e);
}
}
}
});
}
let oldtitle, oldurl, firsttitle, AUTHOR, IMAGE = {}, FONT = {}, NURL, CCC = 0,
Dgetcover = async url => {
GM_xmlhttpRequest({
method: 'GET',
responseType: 'blob',
url: url,
onload: r => {
IMAGE.code = new Blob([r.response], {type: 'image/' + IMAGE.link.getType()});
IMAGE.blob = window.URL.createObjectURL(IMAGE.code);
}
})
},
Dgetauthor = (url, PL) => {
if (url.includes('lightnovelworld')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = doc.querySelector(PL.novel).href.LN_fix(url);
let s = doc.querySelector('#chapter'),
j = JSON.parse(s.innerHTML);
for (let e in j) {
for (let ee in j[e]) {
for (let eee in j[e][ee]) if (eee == 'schema:author') AUTHOR = j[e][ee][eee];
}
}
}
});
} else if (url.includes('wuxiax.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR = doc.querySelector('[itemprop="author"]').innerText;
}
});
} else if (url.includes('www.readwn.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR = doc.querySelector('[itemprop="author"]').innerText;
}
});
} else if (url.includes('lnreader.org')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc= new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR = doc.querySelector('a[href*=author]').innerText;
}
});
} else if (url.includes('readlightnovel.me')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
doc.querySelectorAll('.novel-detail-body a').forEach(item => {
if (item.href.search('author') > 1) AUTHOR = item.innerText;
});
}
});
} else if (url.includes('novels.pl')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR = doc.querySelector('a[href*=author]').innerText;
}
});
} else if (url.includes('novelhall')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR = doc.querySelector('#author').innerText;
}
});
} else if (url.includes('novelfull.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR.link = doc.body.getAttribute('.info a');
}
});
} else if (url.includes('novelonlinefull.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR.link = doc.body.querySelector('.truyen_info_right li:nth-of-type(2) a').innerText;
}
});
} else if (url.includes('bestlightnovel.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
AUTHOR.link = doc.body.querySelector('.truyen_info_right li:nth-of-type(2) a').innerText;
}
});
}
},
Dlink = (url, PL) => {
if (url.includes('lightnovelworld')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
IMAGE.link = doc.querySelector('[itemprop="image"]').href;
}
});
}
if (url.includes('wuxiax')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
IMAGE.link = "https://www.wuxiax.com" + doc.querySelector('.cover img').getAttribute('data-src');
}
});
}
if (url.includes('readwn')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
IMAGE.link = "https://www.readwn.com" + doc.querySelector('.cover img').getAttribute('data-src');
}
});
}
if (url.includes('readlightnovel.me')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
IMAGE.link = doc.querySelector('[property="og:image"]').content;
}
});
}
if (url.includes('lnreader.org')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
IMAGE.link = doc.querySelector('.novels-detail-left img').src.LN_fix(url);
}
});
}
if (url.includes('novels.pl')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
IMAGE.link = doc.querySelector('[property="og:image"]').content;
}
});
}
if (url.includes('novelhall')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
IMAGE.link = doc.body.getAttribute('.article-thumb');
}
});
}
if (url.includes('novelfull.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
IMAGE.link = doc.querySelector('.book img').src.LN_fix(url);
}
});
}
if (url.includes('novelonlinefull.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
IMAGE.link = doc.querySelector('[property="og:image"]').content;
}
});
}
if (url.includes('bestlightnovel.com')) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html');
NURL = url;
IMAGE.link = doc.querySelector('[property="og:image"]').content;
}
});
}
},
getfont = () => {
GM_xmlhttpRequest({
url: 'https://fonts.googleapis.com/css2?family='+defaultf.replaceAll(' ', '+').replaceAll("'", ''),
method: 'GET',
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
body = doc.body;
let a = body.innerText.substr(body.innerText.search('latin ') + 9),
b = a.substr(a.search('url')+4, a.search('format') - 6 - a.search('url'));
GM_xmlhttpRequest({
url: b,
method: 'GET',
responseType: 'blob',
onload: r => {
FONT.font = defaultf.replace("'", '');
FONT.code = new Blob([r.response], {type: 'font/woff2'});
FONT.blob = window.URL.createObjectURL(new Blob([r.response], {type: 'font/woff2'}));
}
});
}
});
},
Dcont = [],
Dalt = false;
function genEPUB(options, content, url, auto=false, index, single, title, OBJ) {
let NOVEL = OBJ.NOVEL,
zip = new JSZip(),
metainf = zip.folder('META-INF'),
oeb = zip.folder('OEBPS'),
chapters = oeb.folder('Chapters'),
images = oeb.folder('Images'),
fonts = oeb.folder('Fonts'),
styles = oeb.folder('Styles'),
printMeta = () => {
let text = '';
content.forEach((obj, i) => {
text += `<navPoint id="body${i}" playOrder="${i}"><navLabel><text>${obj.title}</text></navLabel><content src="Chapters/Section_${i}.xhtml"/></navPoint>`;
});
return text;
},
items = () => {
let text = '';
content.forEach((obj, i) => {
text += `<item id="xhtml${i}" href="Chapters/Section_${i}.xhtml" media-type="application/xhtml+xml"/>`;
});
return text;
},
spine = () => {
let text = '';
content.forEach((obj, i) => {
text += `<itemref idref="xhtml${i}"/>`;
});
return text;
},
list = () => {
let text = '';
content.forEach((obj, i) => {
text += `<li> <a href="../Chapters/Section_${i}.xhtml">${obj.title}</a> </li>`;
});
return text;
},
date = () => {
let bdate = new Date;
bdate.setMilliseconds(0);
return bdate.toISOString().replace('.000', '');
},
xmls = {
container: `<?xml version="1.0" encoding="utf-8"?> <container version="1.0" xmlns="urn:oasis:names:tc:opendocument:xmlns:container"> <rootfiles> <rootfile full-path="OEBPS/content.opf" media-type="application/oebps-package+xml"/> </rootfiles> </container>`,
contentopf: `<?xml version="1.0" encoding="utf-8"?> <package version="3.0" unique-identifier="BookId" xmlns="http://www.idpf.org/2007/opf" xmlns:dc="http://purl.org/dc/elements/1.1/"> <metadata> <dc:creator>EPUB generated by XDHx86 script LN LOADER check it out on https://openuserjs.org/users/XDHx86/</dc:creator> <dc:source>${NURL}</dc:source> <dc:title id="title">${options.title.replaceAll('\n', '')}</dc:title> <dc:language>en-US</dc:language> <dc:identifier id="BookId">urn:uuid:${options.uid}</dc:identifier> <dc:creator id="creator">XDHx86</dc:creator> <meta refines="#title" property="title-type">main</meta> <meta refines="#creator" property="file-as">${options.author || 'Unknown'}</meta> <meta property="dcterms:modified">${date()}</meta> <meta name="calibre:series" content="${NOVEL}"/> <meta name="calibre:series_index" content="00"/> <meta name="cover" content="cover.${options.cover.type}"/> </metadata> <manifest> <item id="css" href="Styles/css.css" media-type="text/css"/> ${options.font ? `<item id="font" href="Fonts/font.woff2" media-type="font/woff2"/>` : ''} <item id="cover.${options.cover.type}" href="Images/Cover.${options.cover.type}" media-type="image/${options.cover.type}" properties="cover-image"/> ${items()} <item id="ncx" href="toc.ncx" media-type="application/x-dtbncx+xml"/> <item id="cover.xhtml" href="Chapters/cover.xhtml" media-type="application/xhtml+xml"/> <item id="nav" href="Chapters/toc.xhtml" media-type="application/xhtml+xml" properties="nav"/> </manifest> <spine toc="ncx"> <itemref idref="cover.xhtml"/> ${spine()} </spine> <guide> <reference type="cover" title="Cover" href="Chapters/cover.xhtml"/> </guide> </package>`,
tocncx: `<?xml version="1.0" encoding="utf-8"?> <ncx version="2005-1" xml:lang="en-US" xmlns="http://www.daisy.org/z3986/2005/ncx/"> <head> <meta content="urn:uuid:${options.uid}" name="dtb:uid"/> <meta content="2" name="dtb:depth"/> <meta content="0" name="dtb:totalPageCount"/> <meta content="0" name="dtb:maxPageNumber"/> </head> <docTitle> <text>${NOVEL}</text> </docTitle> <navMap>${printMeta()}</navMap> </ncx>`,
tocxml: `<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en-US" xmlns:epub="http://www.idpf.org/2007/ops" lang="en-US"> <head> <title>Table of Contents</title> </head> <body> <nav epub:type="toc" id="toc"> <h1>Table of Contents</h1> <ol> ${list()} </ol> </nav> <nav epub:type="landmarks" id="landmarks" hidden=""> <h1>Landmarks</h1> <ol> <li> <a epub:type="cover" href="../Chapters/cover.xhtml">Cover</a> </li> </ol> </nav> </body> </html>`,
cover: `<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml" xmlns:epub="http://www.idpf.org/2007/ops"> <head> <title> Cover </title> </head> <body> <img src="../Images/Cover.${options.cover.type}"/> </body> </html>`,
css: `${options.font ? `@font-face {font-family: '${options.font}'; src: url('../Fonts/font.woff2') format('woff2'); } p {font-family: '${options.font}'; }` : ''} p { text-indent: 3%; }`
},
down = (f, n) => {
const a = document.createElement('a');
a.href = window.URL.createObjectURL(f);
a.download = n + '.epub';
document.body.appendChild(a);
a.click();
setTimeout(() => { a.remove(); }, 10000);
}
zip.file('mimetype', 'application/epub+zip', {
compression: 'STORE',
compressionOptions: {
level: 0
}
});
oeb.file('content.opf', xmls.contentopf);
oeb.file('toc.ncx', xmls.tocncx);
chapters.file('toc.xhtml', xmls.tocxml);
chapters.file('cover.xhtml', xmls.cover);
metainf.file('container.xml', xmls.container);
styles.file('css.css', xmls.css);
options.font ? fonts.file('font.woff2', options.woff2) : '';
content.forEach((obj, i) => {
let text = `<?xml version="1.0" encoding="utf-8"?> <!DOCTYPE html> <html xmlns="http://www.w3.org/1999/xhtml"> <head> <link href="../Styles/css.css" type="text/css" rel="stylesheet"/> <title> ${obj.title} </title> </head> <body> <h1> ${obj.title} </h1> <hr /> ${obj.content} </body> </html>`;
chapters.file('Section_' + i + '.xhtml', text);
});
images.file('Cover.' + options.cover.type, options.cover.code);
zip.generateAsync({
type: 'blob',
mimeType: 'application/epub+zip',
compression: 'DEFLATE',
compressionOptions: {
level: 9
}
}).then(function(file) {
setTimeout(() => {
GM_notification('EPUB generating is complete (' + NOVEL +'), Enjoy!\nIf you encountered any issues feedback is very appreciated! EPUB downloading is still in experimental phase.', 'EPUB generating is complete (' + NOVEL + '), Enjoy!')
window.LN_downloading = false;
if (m.querySelectorAll('.LN_btn')[2]) {
m.querySelectorAll('.LN_btn')[3].disabled = false;
m.querySelectorAll('.LN_btn')[2].disabled = false;
m.querySelectorAll('.LN_btn')[2].innerText = 'Download';
}
down(file, options.title);
d.innerHTML = 'Download complete. Enjoy!'
setTimeout(() => {
sh('dcf');
}, 4000);
setTimeout(() => {
if (!auto) {
if (location.href != url) {
if (confirm('Download complete.\nRedirecting to last chapter downloaded (' + title.replace(/\n|\t|«/gm, '') + ')')) window.open(url, '_self');
} else alert('Download compelete.');
} else if (!single) downnew(index);
else alert('Download compelete.');
}, 10000);
}, 10000);
});
}
let downchp = 0, warned,
epubsups = ['www.readlightnovel.me', 'readlightnovel.me', 'www.lightnovelworld.com', 'www.wuxiax.com', 'lnreader.org', 'www.lnreader.org', 'novels.pl', 'www.readwn.com', 'www.novelhall.com', 'novelfull.com', 'novelonlinefull.com', 'bestlightnovel.com'];
let ogn;
function Download(url, n=1, retries=0, auto=false, index, PL=CS, single, OBJ={}) {
let allowbtn = () => {
m.querySelectorAll('.LN_btn')[2].disabled = false;
m.querySelectorAll('.LN_btn')[2].innerText = 'Download';
m.querySelectorAll('.LN_btn')[3].disabled = false;
m.querySelectorAll('.LN_btn')[4].disabled = false;
}, cancel = () => {
window.LN_downloading = false;
window.LN_canceldownload = false;
alt('Download canceled.');
if (auto && !single && !window.LN_cancelauto) downnew(index + 1);
window.LN_cancelauto = false;
if (m.querySelector('.LN_btn')) allowbtn();
warned = false;
}
if (url.includes('lightnovelworld') && !warned && document.querySelector('.login-wrap-nav').innerText.toUpperCase() == 'SIGN IN') {
warned = true;
if (!confirm('To download from LNW you have to be logged in since many chapters are only avialable to the signed-in user\nTo continue press OK else sign in and try again')) {
cancel();
return;
}
}
window.LN_downloading = true;
if (window.LN_canceldownload) {
cancel();
return;
}
if (!ogn) ogn = n;
n--;
if (CCC < 10) Dgetcover(IMAGE.link), CCC++;
getcontent(url, PL, OBJ);
downchp++;
if (d) sh('dct'), d.innerHTML = svgdd + ' Downloading Chapters: ' + downchp + '/' + ogn;
if (downchp%100 == 0 && downloadt && epubsups.includes(url.LN_host())) alt(downchp+' chapters finished, it\'s not stuck. Don\'t worry.');
const action = setTimeout(() => {
try {
let NOVEL = OBJ.NOVEL, BODY = OBJ.BODY, CHAPTER = OBJ.CHAPTER, TITLE = OBJ.TITLE, CHAPTERTITLE = OBJ.CHAPTERTITLE;
const down = () => {
if (downloadt && epubsups.includes(url.LN_host())) {
GM_notification('Chapters Loading is complete (' + NOVEL + ')\nGenerating EPUB this may take up to 5 minutes depending on the number of chapters downloaded.\nJust Wait for the download to begin.', 'Chapters loading is complete. (' + url.LN_platform() + ')');
let Dopts = {
author: AUTHOR,
cover: {
link: IMAGE.link,
type: IMAGE.link.getType(),
code: IMAGE.code
},
uid: ([1e7]+-1e3+-4e3+-8e3+-1e11).replace(/[018]/g, c => (c ^ crypto.getRandomValues(new Uint8Array(1))[0] & 15 >> c / 4).toString(16)),
title: `${NOVEL.replacer()} (${firsttitle.replace(NOVEL, '').replace('-', '')} - ${title.replace(NOVEL, '').replace('-', '')})`,
font: FONT.font.replaceAll("'", ''),
woff2: FONT.code
}
OBJ.url = a;
genEPUB(Dopts, Dcont, url, auto, index, single, title, OBJ);
d.innerHTML = svgdd + ' Generating EPUB'
}
else {
if (downloadt && !confirm('EPUB downloading isn\'t supported for this site, it will be downloaded as PDF. Continue?')) {
cancel();
return;
}
pdf.save(title.replaceAll('«', '') + '.pdf');
setTimeout(() => {
window.LN_downloading = false;
if (m.querySelectorAll('.LN_btn')[2]) allowbtn();
setTimeout(() => {
if (!auto) {
if (location.href != url) {
if (confirm('Download complete.\nRedirecting to last chapter downloaded (' + title.replace(/\n|\t|«/gm, '') + ')')) window.open(url, '_self');
} else alert('Download compelete.');
} else if (!single) downnew(index);
else alert('Download compelete.');
}, 5000);
}, 3000);
}
}
if (TITLE) {
if (TITLE[2].includes('Teaser')) {
window.LN_downloading = false;
window.LN_canceldownload = false;
window.LN_cancelauto = false;
if (m.querySelector('.LN_btn')) allowbtn();
if (!auto) {
if (location.href != url) {
if (confirm('Download complete.\nRedirecting to last chapter downloaded (' + oldtitle.replace(/\n|\t|«/gm, '') + ')')) window.open(BODY.querySelector(PL.prev).href, '_self');
} else alert('Download compelete.');
} else if (!single) downnew(index);
else alert('Download compelete.');
return;
}
if (TITLE[2] == oldtitle && n >= 0) {
alt('Network latency high or server denied access, retrying...');
retry({
callback: Download,
url: url,
n: n,
auto: auto,
dindex: index,
PL: PL,
single: single,
retries: retries + 1,
origin: url,
OBJ: OBJ
});
return;
}
}
let body = BODY,
chapter = CHAPTER,
title = TITLE[2];
oldtitle = title;
oldurl = url;
if (!firsttitle) firsttitle = title;
removeannon(chapter, true, PL);
record('', body, PL, url, true);
let content = chapter.innerText,
econtent = chapter.innerHTML,
pdf = new jsPDF();
if (!downloadt || !epubsups.includes(url.LN_host())) {
if (content.search(/\w+/gm) > 2) content = content.substr(content.search(/\w+/gm));
function wrap(text, textWidth, doc, fontSize = 10, fontType = 'normal', lineSpacing = 7, xPosition = 10, initialYPosition = 20, pageWrapInitialYPosition = 20) {
let textLines = doc.splitTextToSize(text, textWidth),
pageHeight = doc.internal.pageSize.height,
cursorY = initialYPosition;
doc.setFontType(fontType);
doc.setFontSize(fontSize);
textLines.forEach(lineText => {
if (cursorY > pageHeight) {
doc.addPage();
cursorY = pageWrapInitialYPosition;
}
doc.text(xPosition, cursorY, lineText);
cursorY += lineSpacing;
});
}
wrap(content, 210 - 10 - 10, pdf, 16);
} else {
let t = n*2,
m = (t/60).toFixed(),
s = ((t/60 % 1) * 60).toFixed();
if (downloadt && !Dalt && epubsups.includes(url.LN_host())) {
setTimeout(() => alt('Please wait, downloading chapters then generating Epub. This will take approximately ' + m + ' minute(s) ' + s + ' seconds. (Time is calculated based on number of chapters requested to be downloaded (' + parseInt(n + 1) +') NOT how many chapters the novel has.)\nIt will notify when it\'s done.', 10000), 6000);
let url2;
if (url.includes('wuxiax') || url.includes('lnreader') || url.includes('novels.pl') || url.includes('www.readwn.com') || url.includes('novelfull.com')) url2 = BODY.querySelector(PL.novel).href.LN_fix(url);
Dgetauthor(url2 || url, PL);
if (url.includes('readlightnovel') || url.includes('novels.pl')) url2 = undefined;
Dlink(url2 || url);
getfont();
}
Dalt = true;
let fcontent = econtent;
if (url.includes('readlightnovel.me')) {
let b = document.createElement('div');
for (let e of chapter.childNodes) {
if (e.nodeName != '#text') e.remove();
else {
let p = document.createElement('p');
p.innerText = e.data;
b.appendChild(p);
}
}
b.innerHTML = b.innerHTML.replaceAll('<br>', '');
for (let e of b.childNodes) if (e.innerText.search(/\w/) == -1) e.remove();
fcontent = b.innerHTML;
}
Dcont.push({
title: CHAPTERTITLE.replacer(),
content: fcontent
});
}
if (n == 0) down();
if (n >= 1) {
let nurl = body.querySelector(PL.next);
if (url.includes('novelonlinefull') || url.includes('bestlightnovel')) {
let nn;
for (let e of body.querySelectorAll('a')) if (/NEXT CHAPTER/ig.test(e.innerText) || e.innerText == 'Next »') nn = e;
nurl = nn;
}
if (nurl && nurl.href) nurl.href = nurl.href.LN_fix(url);
if (nurl && nurl.href && (!(nurl.href.endsWith('#') && nurl.href.includes(url))) && (nurl.href.split(url.LN_host())[1] && nurl.href.split(url.LN_host())[1].split('/')[1].toLowerCase() == url.split(url.LN_host())[1].split('/')[1].toLowerCase()) && nurl.href != url) {
nurl = nurl.href;
Download(nurl, n, 0, auto, index, PL, single);
} else down();
}
} catch (e) {
console.error(e);
clearTimeout(action);
if (!OBJ.BODY) {
alt('Error: NETWORK_ERROR\nDetails: Response cannot be null! Retrying...', 10000);
retry({
callback: Download,
url: url,
n: n,
auto: auto,
index: index,
PL: PL,
single: single,
retries: retries + 1,
origin: url,
OBJ: OBJ
});
}
else if (BOTCHECK(OBJ.BODY)) {
if (m.querySelector('.LN_btn')) allowbtn();
if (confirm('Caught by bot verification.\nClick OK to open url (' + url + ') in new tab, the script will try again in 5 seconds.\nYou can also press cancel to cancel the download.')) {
window.open(url, '_blank');
Download(url, n, auto, index, PL, single, retries, OBJ)
}
} else {
alt('Error:\nDetails: Unexpected error has occured. Retrying...', 10000);
retry({
callback: Download,
url: url,
n: n,
auto: auto,
index: index,
PL: PL,
single: single,
retries: retries + 1,
origin: url,
OBJ: OBJ
});
}
}
}, 2000);
}
function set() {
let inputst = m.querySelectorAll('.LN_s input[type=text]'),
inputsr = m.querySelectorAll('input[type=radio]'),
inputsn = m.querySelectorAll('input[type=number]:not([max="10000"])'),
current = m.querySelector('.LN_current'),
selects = m.querySelectorAll('select'),
down = m.querySelectorAll('.LN_btn')[2],
canceldown = m.querySelectorAll('.LN_btn')[3],
cancelauto = m.querySelectorAll('.LN_btn')[4],
addtof = m.querySelectorAll('.LN_btn')[5],
updates = m.querySelectorAll('.LN_btn')[6],
cancelupdate = m.querySelectorAll('.LN_btn')[7],
fav = m.querySelectorAll('.LN_btn')[8],
backup = m.querySelectorAll('.LN_btn')[9],
restore = m.querySelectorAll('.LN_btn')[10],
feba = m.querySelectorAll('.LN_btn')[11],
save = m.querySelectorAll('.LN_btn')[12],
reset = m.querySelectorAll('.LN_btn')[13],
mreader = n.querySelectorAll('button')[0],
msettings = n.querySelectorAll('button')[1],
mprev = n.querySelectorAll('button')[2],
mdarkm = n.querySelectorAll('button')[3],
mnext = n.querySelectorAll('button')[4],
msh = n.querySelectorAll('button')[5],
mif = n.querySelectorAll('button')[6],
mdf = n.querySelectorAll('button')[7],
mas = n.querySelectorAll('button')[8],
mra = n.querySelectorAll('button')[9],
mdn = n.querySelectorAll('button')[10],
inputsc = c.querySelectorAll('input'),
savec = c.querySelector('.LN_btn'),
textc = c.querySelectorAll('textarea')[0],
etextc = c.querySelectorAll('textarea')[1],
selectl = f.querySelectorAll('select')[0],
selectt = f.querySelectorAll('select')[1],
selectr = f.querySelectorAll('select')[2],
word = f.querySelectorAll('input')[0],
rword = f.querySelectorAll('input')[1],
addwf = f.querySelectorAll('.LN_btn')[0],
remwf = f.querySelectorAll('.LN_btn')[1],
radios = [autora, autonc, autodl, downloadt, autoresume, defaultv, scrollt, swipeg, autodmode, decensore, allowconfirm, uselastr, checkcomp, autoretry, nnotfound, autoch, autodown],
radiosn = ['autora', 'autonc', 'autodl', 'downloadt', 'autoresume', 'defaultv', 'scrollt', 'swipeg', 'autodmode', 'decensore', 'allowconfirm', 'uselastr', 'checkcomp', 'autoretry', 'nnotfound', 'autoch', 'autodown'],
valst = [fullscreen, next, nexta, nextaa, prev, preva, prevaa, darkmode, incfs, decfs, removean, download, autoscroll, showfavs],
valstn = ['fullscreen', 'next', 'nexta', 'nextaa', 'prev', 'preva', 'prevaa', 'darkmode', 'incfs', 'decfs', 'removean', 'download', 'autoscroll', 'showfavs'],
valsn = [autoscrolls, defaultfs, pmargin, lheight, autoncd, dmtime, lmtime, autoretryno, autobackupp],
valsnn = ['autoscrolls', 'defaultfs', 'pmargin', 'lheight', 'autoncd', 'dmtime', 'lmtime', 'autoretryno', 'autobackupp'],
valsc = [txc, bgc, dtxc, dbgc],
valscn = ['txc', 'bgc', 'dtxc', 'dbgc'],
help = m.querySelectorAll('.LN_help'),
downinput = m.querySelector('input[max="10000"]'),
checktinput = m.querySelector('input[type=time]');
downinput.onkeydown = function(e) {
if (e.key != 'Enter') return;
this.blur();
down.click();
}
checktinput.value = checkd;
checktinput.setAttribute('ln-name', 'checkd');
checktinput.onchange = checktinput.onblur = function() {
if (!this.value) {
this.value = checkd;
alt('Invalid value!');
this.setCustomValidity('Invalid value!');
this.reportValidity();
}
}
checktinput.onfocus = function() {
this.setCustomValidity('');
}
for (let i = 0; i < inputst.length; i++) {
inputst[i].value = valst[i];
inputst[i].setAttribute('ln-og', inputst[i].value);
inputst[i].setAttribute('ln-name', valstn[i]);
inputst[i].onkeydown = e => {
if ((e.shiftKey && e.code != 'Tab') || /tab/i.test(e.code)) return;
else e.preventDefault();
}
inputst[i].onkeyup = function(e) {
if (/enter|capslock|f[0-9]|home|end|pageup|pagedown|insert|numlock|backquote/i.test(e.code)) alt('No special characters allowed!');
else if (e.ctrlKey || e.altKey || e.metaKey || (e.shiftKey && e.code != 'Tab')) alt('No special characters allowed!');
else if (e.code == 'Delete' || e.code == 'Backspace') this.value = this.getAttribute('ln-og');
else if (e.code == 'Escape') this.blur();
else if (!/control|shift|alt|meta|osl|osr/i.test(e.code) && e.code != 'Tab') this.value = e.code;
}
inputst[i].onblur = function() {
if (!/digit|numpad|key|space|bracket|semi|comma|period|slash|(^quote)|minus|arrow|equal/i.test(this.value)) {
alt('No special characters allowed!');
this.value = this.getAttribute('ln-og');
}
let v = this.value.toLowerCase(),
a = inputst,
c = [];
for (let e of a) c.push(e.value.toLowerCase());
if (c.indexOf(v) != c.lastIndexOf(v)) {
alt('This value is already assigned!');
this.value = this.getAttribute('ln-og');
}
}
}
for (let i = 0; i < inputsn.length; i++) {
inputsn[i].value = valsn[i];
inputsn[i].setAttribute('ln-og', inputsn[i].value);
inputsn[i].setAttribute('ln-name', valsnn[i]);
inputsn[i].onblur = function() {
if (isNaN(parseInt(this.value)) || this.value > parseInt(this.max) || this.value < parseInt(this.min) || (!this.step && this.value % 1 != 0) || (this.step && this.value / this.step % 1 != 0)) {
this.blur();
this.onfocus = function() {
this.onfocus = null;
this.setCustomValidity('');
}
alt(`Invalid value! No more than ${this.max} nor less than ${this.min} ${this.step ? `and is divisible by ${this.step}` : 'and no decimals'}.`, 7000);
this.value = this.getAttribute('ln-og');
}
}
}
{
for (let i = 0; i < radios.length; i++) {
if (radios[i] === true) inputsr[i*2].checked = true;
else inputsr[i*2+1].checked = true;
}
let search = selects[0].options.LN_search(defaultf);
if (search.found) {
search.obj.selected = true;
selects[0].style.fontFamily = search.obj.value;
}
search = selects[1].options.LN_search(autoscrolls);
if (search.found) search.obj.selected = true;
else {
selects[1].lastElementChild.remove();
selects[1].options.add(new Option(autoscrolls, autoscrolls));
selects[1].options.add(new Option('Custom', 'Custom'));
search = selects[1].options.LN_search(autoscrolls);
search.obj.selected = true;
}
for (let i = 1; i < RULES.length; i++) selectr.add(new Option(RULES[i].word, RULES[i].word), selectr[0]);
}
for (let i = 0; i < inputsc.length; i++) {
inputsc[i].value = valsc[i];
inputsc[i].setAttribute('ln-og', inputsc[i].value);
inputsc[i].setAttribute('ln-name', valscn[i]);
}
for (let elm of help) elm.onclick = () => window.open('https://openuserjs.org/scripts/XDHx86/Light_Novel_Loader#notes');
current.onclick = () => { current.previousElementSibling.value = getComputedStyle(r).fontSize.split('px')[0]; }
savec.onclick = () => {
let a = [],
validate = c => {
let img = document.createElement('img');
img.style = 'background: rgb(0, 0, 0)';
img.style = 'background: ' + c;
if (img.style.background != 'rgb(0, 0, 0)' && img.style.background != '') return true;
img.style = 'background: rgb(255, 255, 255)';
img.style = 'background: ' + c;
return (img.style.background != 'rgb(255, 255, 255)' && img.style.background != '');
};
for (let e of inputsc) {
if (!validate(e.value)) {
e.setCustomValidity('Invalid value!');
e.reportValidity();
e.onfocus = function() { this.setCustomValidity('Invalid value!'); this.reportValidity(); }
e.onchange = function() {
this.onchange = null;
this.onfocus = null;
this.setCustomValidity('');
}
} else a.push(e.value);
}
if (a.length == 4) {
for (let e of inputsc) GM_setValue(e.getAttribute('ln-name'), e.value);
GM_setValue('CCSS', textc.value);
GM_setValue('ECCSS', etextc.value);
rvals();
ucss(0);
alt('Settings will take effect immediately!');
}
}
feba.onclick = feedback;
save.onclick = () => {
if (m.querySelector(':invalid')) {
alt('There are some invalid values, cannot save settings. Please check the values again.');
return;
}
for (let e of inputst) if (e.getAttribute('ln-name')) GM_setValue(e.getAttribute('ln-name'), e.value);
for (let e of inputsn) if (e.getAttribute('ln-name')) GM_setValue(e.getAttribute('ln-name'), e.value);
for (let i = 0; i < radios.length; i++) {
if (inputsr[i*2].checked) GM_setValue(radiosn[i], true);
else GM_setValue(radiosn[i], false);
}
if (selects[0].style.display == '' || selects[0].style.display == 'block') GM_setValue('defaultf', selects[0].value);
else GM_setValue('defaultf', m.querySelector('.LN_dft').value);
if (selects[1].style.display == '' || selects[1].style.display == 'block') GM_setValue('autoscrolls', selects[1].value);
else GM_setValue('autoscrolls', parseInt(m.querySelector('.LN_ast').value));
rvals();
let currentfont = defaultf.replace(/'|"/g, '');
if (!fonts.includes(currentfont.toLowerCase())) {
NLF.href = 'https://fonts.googleapis.com/css2?family=' + currentfont.replace(' ', '+') + '&display=swap';
document.head.appendChild(NLF);
const search = selects[0].options.LN_search(defaultf);
if (!search.found) {
selects[0].lastElementChild.remove();
selects[0].options.add(new Option(currentfont, defaultf));
selects[0].options.add(new Option('Custom', 'Custom'));
}
}
liningcss.innerHTML = ` .LN_chapter * { line-height: ${lheight}; } .LN_chapter p { margin: ${pmargin}px 0; text-align: start; }`;
ucss(2);
alt('Settings will take effect immediately!');
}
down.onclick = () => {
let link;
if (CS.novel != '') link = document.querySelector(CS.novel).href;
else {
link = location.href.split('/');
if (link[link.length - 1] == '') {
link.pop();
link.pop();
} else link.pop();
link = link.join('/');
}
if (!FAVS.LN_deepSearch(link).found) {
if (confirm('This novel is not in favorites, do you want to add it?')) addtof.click();
}
if (!isNaN(parseInt(downinput.value)) && downinput.value >= 1 && downinput.value <= 10000 && downinput.value % 1 == 0) {
alt('Download started.\nYou can still read from this page while downloading.');
down.disabled = true;
down.innerText = 'Downloading..';
Dcont = [];
Dalt = false;
Download(location.href, downinput.value);
} else {
downinput.setCustomValidity('Invalid value!');
downinput.reportValidity();
downinput.onfocus = function() {
downinput.onfocus = null;
downinput.setCustomValidity('');
}
alt(`Invalid value! No more than ${downinput.max} nor less than ${downinput.min} and NO decimals.`, 7000);
return;
}
}
canceldown.onclick = () => {
window.LN_canceldownload = true;
window.LN_cancelauto = false;
alt('Please wait.');
canceldown.disabled = true;
}
cancelauto.onclick = () => {
window.LN_canceldownload = true;
window.LN_cancelauto = true;
alt('Please wait.');
cancelauto.disabled = true;
}
reset.onclick = () => {
if (confirm('Are you sure you want to reset the script? This will reset ALL the settings and records!')) {
GM_setValue('firsttime', true);
if (confirm('Settings will take effect after reload.\nReload now?')) location.reload();
}
}
window.addEventListener('keyup', e => { if (e.code == 'Escape') dofull(true); });
let swiperhp = false;
r.onscroll = () => {
{
let a = r.scrollTop,
b = r.scrollHeight - r.clientHeight - 100,
c = {
pos: a,
link: location.href
};
const search = laststop.LN_search(location.href);
if (a > 100) {
if (!search.found) {
laststop.push(c);
GM_setValue('laststop', laststop);
} else {
laststop[search.index].pos = a;
GM_setValue('laststop', laststop);
}
} else {
if (search.found) {
laststop.splice(search.index, 1);
GM_setValue('laststop', laststop);
}
}
if (a >= b) {
laststop.splice(laststop.indexOf(c), 1);
GM_setValue('laststop', laststop);
setTimeout(() => {
KEY.code = next;
if (autonc) handler(KEY);
}, (autoncd * 1000));
}
}
{
let swiper = r.querySelector('.LN_swiper');
if (!swiper && !swiperhp) {
let d = document.createElement('div');
d.className = 'LN_swiper';
d.innerHTML = `<span> Scroll down to go to next chapter </span>`;
r.appendChild(d);
return;
}
if (swipeg) {
if (autonc) autonc = false;
if (r.firstElementChild.LN_hasClass('LN_swiper')) r.appendChild(swiper);
const swiperh = n => {
if (swiperhp) return;
let h = swiper.offsetHeight;
swiper.style.height = (h + n) + 'px';
}
if ((r.scrollHeight - r.clientHeight) - r.scrollTop <= 5 && !swiperhp) swiperh(40);
if (swiper && (swiper.offsetHeight / r.clientHeight) > 0.45) {
swiper.remove();
swiperhp = true;
alt('Loading the next chapter.', 1000);
KEY.code = next;
handler(KEY);
setTimeout(() => { swiperhp = false; }, 5000);
}
}
}
}
addtof.onclick = () => {
let link;
if (CS.novel != '') link = document.querySelector(CS.novel).href;
else {
link = location.href.split('/');
if (link[link.length - 1] == '') {
link.pop();
link.pop();
} else link.pop();
link = link.join('/');
}
favthis(link);
alt('Please Wait.');
}
fav.onclick = favs;
updates.onclick = () => {
alt('Please wait while the script searches for new chapters.', 5000);
updates.innerText = 'Checking...';
updates.disabled = true;
notify();
}
cancelupdate.onclick = () => {
window.LN_cancelcheck = true;
alt('Please wait.');
cancelupdate.disabled = true;
}
backup.onclick = () => br(0);
restore.onclick = () => br(1);
mreader.onclick = () => dofull();
msettings.onclick = () => s.click();
mprev.onclick = () => {
KEY.code = prev;
handler(KEY);
}
mdarkm.onclick = () => {
KEY.code = darkmode;
handler(KEY);
}
mnext.onclick = () => {
KEY.code = next;
handler(KEY);
}
msh.onclick = () => {
if (msh.querySelector('svg').style.transform == 'rotate(180deg)') {
msh.querySelector('svg').style.transform = 'rotate(0deg)';
mdf.classList.remove('LN_icob'); mif.classList.remove('LN_icob'); mas.classList.remove('LN_icob'); mra.classList.remove('LN_icob'); mdn.classList.remove('LN_icob');
mdf.classList.add('LN_icob2'); mif.classList.add('LN_icob2'); mas.classList.add('LN_icob2'); mra.classList.add('LN_icob2'); mdn.classList.add('LN_icob2');
mreader.classList.add('LN_icob'); msettings.classList.add('LN_icob'); mprev.classList.add('LN_icob'); mdarkm.classList.add('LN_icob'); mnext.classList.add('LN_icob');
mreader.classList.remove('LN_icob2'); msettings.classList.remove('LN_icob2'); mprev.classList.remove('LN_icob2'); mdarkm.classList.remove('LN_icob2'); mnext.classList.remove('LN_icob2');
} else {
msh.querySelector('svg').style.transform = 'rotate(180deg)';
mdf.classList.add('LN_icob'); mif.classList.add('LN_icob'); mas.classList.add('LN_icob'); mra.classList.add('LN_icob'); mdn.classList.add('LN_icob');
mdf.classList.remove('LN_icob2'); mif.classList.remove('LN_icob2'); mas.classList.remove('LN_icob2'); mra.classList.remove('LN_icob2'); mdn.classList.remove('LN_icob2');
mreader.classList.remove('LN_icob'); msettings.classList.remove('LN_icob'); mprev.classList.remove('LN_icob'); mdarkm.classList.remove('LN_icob'); mnext.classList.remove('LN_icob');
mreader.classList.add('LN_icob2'); msettings.classList.add('LN_icob2'); mprev.classList.add('LN_icob2'); mdarkm.classList.add('LN_icob2'); mnext.classList.add('LN_icob2');
}
}
mdf.onclick = () => {
KEY.code = decfs;
handler(KEY);
}
mif.onclick = () => {
KEY.code = incfs;
handler(KEY);
}
mas.onclick = () => {
KEY.code = autoscroll;
handler(KEY);
sh('sidebar');
}
mra.onclick = () => {
KEY.code = removean;
handler(KEY);
}
mdn.onclick = () => {
if (!window.LN_indexslow) {
getindex();
if (!l.style.display || l.style.display == 'none') sh('cit');
else sh('cif');
} else alt('Novel index is loading, please wait');
}
record();
addwf.onclick = () => {
let value = word.value,
replaced = rword.value,
type = selectt.value,
condition = selectl.value,
search = RULES.LN_search(value),
obj = {
word: value,
replaced: replaced.includes('\\n') ? replaced.replaceAll('\\n', '\n') : replaced,
type: type,
cond: condition
};
if (search.found && search.child == 'word' && selectr.value != value) {
if (!word.value || word.value == '') {
alt('Invalid rule configurations!');
return;
}
selectr.options.LN_search(value).obj.selected = true;
selectt.options.LN_search(search.obj.type).obj.selected = true;
selectl.options.LN_search(search.obj.condition).obj.selected = true;
word.value = search.obj.word;
rword.value = search.obj.replaced;
remwf.disabled = false;
alt('Rule already exist!');
} else if (search.found && search.child == 'word' && selectr.value == value) {
if (!word.value || word.value == '') {
alt('Invalid rule configurations!');
return;
}
search.obj.type = selectt.value;
search.obj.condition = selectl.value;
search.obj.word = word.value;
search.obj.replaced = rword.value;
GM_setValue('RULES', RULES);
rvals();
alt('Rule edited successfully.');
} else if (!search.found || search.child != 'word') {
if (!word.value || word.value == '') {
alt('Invalid rule configurations!');
return;
}
selectr.add(new Option(word.value, word.value), selectr[0]);
RULES.push(obj);
GM_setValue('RULES', RULES);
rvals();
alt('Rule added successfully.');
}
}
remwf.onclick = () => {
if (selectr.selectedIndex > -1) {
if (confirm('Are you sure you want to remove this filter rule?')) {
RULES.splice(RULES.LN_search(selectr.value).index, 1);
GM_setValue('RULES', RULES);
rvals();
selectr.selectedOptions[0].remove();
}
}
}
selectr.onchange = () => {
let search = RULES.LN_search(selectr.value);
selectt.options.LN_search(search.obj.type).obj.selected = true;
selectl.options.LN_search(search.obj.cond).obj.selected = true;
word.value = search.obj.word;
rword.value = search.obj.replaced;
remwf.disabled = false;
}
}
let asked = false, dloldtitle;
function dynamicload(url, retries=0, OBJ={}) {
window.LN_loading = true;
getcontent(url, CS, OBJ);
if (location.href != url) asked = false;
if (timers) {
clearInterval(timers);
timers = 'on';
}
setTimeout(() => {
try {
const body = OBJ.BODY,
chapter = OBJ.CHAPTER,
title = OBJ.TITLE,
cp = document.querySelector(CS.chapter).parentElement,
c = document.querySelector(CS.chapter);
let next = body.querySelector(CS.next),
prev = body.querySelector(CS.prev),
cpos = '';
if (title[2] == dloldtitle) {
window.LN_loading = false;
alt('Network latency high or server denied access, retrying...');
retry({
callback: dynamicload,
url: url,
retries: retries + 1,
origin: url,
OBJ: OBJ
});
return;
}
dloldtitle = title[2];
if (location.host.includes('novelonlinefull') || location.host.includes('bestlightnovel')) {
let nn, pp;
for (let e of body.querySelectorAll('a')) {
if (/NEXT CHAPTER/ig.test(e.innerText) || e.innerText == 'Next »') nn = e;
if (/PREV CHAPTER/ig.test(e.innerText) || e.innerText == '« Previous') pp = e;
}
next = nn || null;
prev = pp || null;
}
if (next !== null) next = next.href;
if (prev !== null) prev = prev.href;
cp.insertBefore(chapter.cloneNode(true), Array.from(cp.children)[c.LN_index()]);
r.appendChild(chapter.cloneNode(true));
r.querySelector(CS.chapter).remove();
c.remove();
if (location.host.includes('readlightnovel.me')) document.querySelector('.block-title h1').lastChild.data = OBJ.CHAPTERTITLE;
else if (location.host.includes('novelonlinefull') || location.host.includes('bestlightnovel')) document.querySelector('.rdfa-breadcrumb p').lastChild.data = OBJ.CHAPTERTITLE;
else if (location.host.includes('novels.pl')) document.querySelector('.panel-title.pull-left').lastChild.data = OBJ.CHAPTERTITLE;
else document.querySelector(CS.chaptertitle).innerText = OBJ.CHAPTERTITLE;
if (document.querySelector(CS.next)) document.querySelector(CS.next).href = next;
else if (document.querySelector('.LN_nextchbtn')) document.querySelector('.LN_nextchbtn').href = next;
else {
let a = document.createElement('a');
a.className = CS.next.replace(/\.|\#/, '').replace('.', ' ');
a.id = CS.next.replace(/\.|\#/, '').replace('.', ' ');
a.href = next;
a.classList.add('LN_nextchbtn');
a.style.display = 'none';
document.body.appendChild(a);
}
if (document.querySelector(CS.prev)) document.querySelector(CS.prev).href = prev;
else if (document.querySelector('.LN_prevchbtn')) document.querySelector('.LN_prevchbtn').href = prev;
else {
let a = document.createElement('a');
a.className = CS.prev.replace(/\.|\#/, '').replace('.', ' ');
a.id = CS.prev.replace(/\.|\#/, '').replace('.', ' ');
a.href = prev;
a.classList.add('LN_prevchbtn');
a.style.display = 'none';
document.body.appendChild(a);
}
document.title = title[0];
alt(title[1].replace(/\n|\t|«/gm, ''));
if (CS.title && (location.host.includes('novelonlinefull') || location.host.includes('bestlightnovel'))) document.querySelector(CS.title).innerText = title[2];
fsbtn();
try {
history.pushState('', title[0], url);
} catch (e) {
url = url.LN_fix(location.href);
history.pushState('', title[0], url);
}
if (autora) removeannon();
let ls = laststop.LN_search(location.href);
if (ls.found) resume();
else r.scrollTo(0, 0);
if (window.innerWidth < window.innerHeight) {
r.querySelector(CS.chapter).classList.add('LN_mob');
a.classList.add('LN_amob');
n.classList.remove('LN_side');
n.classList.add('LN_bot');
s.style = 'display: none !important;';
n.style = `bottom: 0; left: ${(window.innerWidth - n.offsetWidth) / 2}px;`;
}
else {
r.querySelector(CS.chapter).classList.remove('LN_mob');
a.classList.remove('LN_amob');
n.classList.add('LN_side');
n.classList.remove('LN_bot');
s.style = 'display: block !important;';
n.style = `top: ${(window.innerHeight - n.offsetHeight) / 2}px; left: 0;`;
}
dynamicmode();
record();
window.LN_loading = false;
setTimeout(() => {
if (timers == 'on') {
timers = false;
doautoscroll();
}
}, 3000);
} catch (e) {
window.LN_loading = false;
console.error(e);
if (!OBJ.BODY) {
alt('Error: NETWORK_ERROR\nDetails: Response cannot be null! Retrying...', 10000);
retry({
callback: dynamicload,
retries: retries + 1,
url: url,
origin: url,
OBJ: OBJ
});
}
else if (BOTCHECK(OBJ.BODY)) {
if (confirm('Caught by bot verification.\nOpen url (' + url + ') in new tab?')) window.open(url, '_blank');
} else {
alt('Error:\nDetails: Unexpected error has occured. Retrying...', 10000);
retry({
callback: dynamicload,
retries: retries + 1,
url: url,
origin: url,
OBJ: OBJ
});
}
}
}, 3000);
}
function removeannon(c, m=false, PL=CS) {
let chapter;
if (c) chapter = c;
else chapter = r;
let replaced,
replacew,
list = PL.annon[1];
if (PL.annon[0]) {
replaced = PL.annon[0][0];
replacew = PL.annon[0][1];
}
if (replacew) chapter.innerHTML = chapter.innerHTML.replace(replaced, replacew);
if (list && chapter.querySelector(PL.chapter)) for (let e of chapter.querySelector(PL.chapter).querySelectorAll(list)) e.remove();
else if (list && chapter.querySelector(list)) for (let e of chapter.querySelectorAll(list)) e.remove();
if (chapter.querySelectorAll('p').length > 25 && PL.chapter != SITES['novels.pl'].chapter) {
chapter.innerHTML = chapter.innerHTML.replaceAll('<\/p>', '</p>\n\n');
for (let e of chapter.querySelectorAll('br')) e.remove();
}
if (decensore) {
while (chapter.innerHTML.search(/[a-zA-Z](\.[a-zA-Z]){1,}/) > -1) {
let a = chapter.innerHTML,
b = a.substr(a.search(/[a-zA-Z](\.[a-zA-Z]){1,}/)),
c = b.substr(0, b.indexOf(' ')),
i1 = a.search(/[a-zA-Z](\.[a-zA-Z]){1,}/),
i2 = c.length;
for (let i = i1; i < i1 + c.length; i++) a = a.LN_replaceAt(i1, '');
a = a.LN_replaceAt(i1, c.replaceAll('.', '') + ' ');
chapter.innerHTML = a;
}
}
if (chapter.querySelector('br') && m) chapter.innerHTML = chapter.innerHTML.replaceAll('<br>', '\n');
if (chapter.querySelector(PL.chapter)) filter(chapter.querySelector(PL.chapter));
else filter(chapter);
}
let timers = false;
function doautoscroll() {
let del, scrolly;
if (scrollt) {
del = (autoscrolls / 2) * 1000;
let div = document.createElement('div');
div.style = 'display: block; z-index: -1; position: fixed; top: 0; left: 0; width: 100%; height: 100%;';
document.body.appendChild(div);
scrolly = div.clientHeight - 120;
div.remove();
}
else {
del = autoscrolls;
scrolly = 2;
}
if (!timers) timers = setInterval(() => r.scrollBy(0, scrolly), del);
else {
clearInterval(timers);
timers = false;
}
r.ontouchstart = r.onclick = r.onauxclick = () => {
if (timers) {
clearInterval(timers);
timers = false;
}
}
let cond = false;
window.onblur = () => {
if (timers) {
cond = true;
clearInterval(timers);
timers = false;
}
window.onfocus = () => {
if (!timers && cond) {
cond = false;
timers = setInterval(() => r.scrollBy(0, scrolly), del);
}
}
}
}
let darked = false;
function ucss(u=1) {
if (u == 0) {
ccss.innerHTML = CCSS;
if (darked) CSS.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { background-color: ${dbgc} !important; color: ${dtxc} !important; scrollbar-color: ${dtxc} ${dbgc}; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: ${dtxc} } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: ${dbgc}; width: 10px; }`;
else CSS.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { background-color: ${bgc} !important; color: ${txc} !important; scrollbar-color: ${txc} ${bgc}; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: ${txc} } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: ${bgc}; width: 10px; }`;
} else if (u == 1) {
if (!darked) {
CSS.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { background-color: ${dbgc} !important; color: ${dtxc} !important; scrollbar-color: ${dtxc} ${dbgc}; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: ${dtxc} } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: ${dbgc}; width: 10px; }`;
darked = true;
}
else {
CSS.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { background-color: ${bgc} !important; color: ${txc} !important; scrollbar-color: ${txc} ${bgc}; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: ${txc} } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: ${bgc}; width: 10px; }`;
darked = false;
}
} else if (u == 5) {
CSS.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { background-color: ${dbgc} !important; color: ${dtxc} !important; scrollbar-color: ${dtxc} ${dbgc}; } .LN_chapter::-webkit-scrollbar-thumb { transition: all 0.5s; background-color: ${dtxc} } .LN_chapter::-webkit-scrollbar-track, .LN_chapter::-webkit-scrollbar { transition: all 0.5s; background-color: ${dbgc}; width: 10px; }`;
darked = true;
}
else fontcss.innerHTML = `.LN_chapter, .LN_chapter div, .LN_chapter p { font-size: ${defaultfs}px !important; font-family: ${defaultf} !important; }`;
}
function rvals() {
next = GM_getValue('next', 'KeyN');
nexta = GM_getValue('nexta', 'ArrowRight');
nextaa = GM_getValue('nextaa', 'KeyD');
prev = GM_getValue('prev', 'KeyB');
preva = GM_getValue('preva', 'ArrowLeft');
prevaa = GM_getValue('prevaa', 'KeyA');
darkmode = GM_getValue('darkmode', 'KeyL');
fullscreen = GM_getValue('fullscreen', 'KeyF');
autoscroll = GM_getValue('autoscroll', 'KeyQ');
showfavs = GM_getValue('showfavs', 'Digit3');
removean = GM_getValue('removean', 'KeyE');
download = GM_getValue('download', 'KeyS');
decfs = GM_getValue('decfs', 'Minus');
incfs = GM_getValue('incfs', 'Equal');
autonc = GM_getValue('autonc', true);
autora = GM_getValue('autora', true);
autodl = GM_getValue('autodl', true);
autoresume = GM_getValue('autoresume', true);
defaultv = GM_getValue('defaultv', false);
scrollt = GM_getValue('scrollt', false);
swipeg = GM_getValue('swipeg', true);
nnotfound = GM_getValue('nnotfound', true);
autoch = GM_getValue('autoch', false);
autodmode = GM_getValue('autodmode', false);
decensore = GM_getValue('decensore', true);
allowconfirm = GM_getValue('allowconfirm', true);
uselastr = GM_getValue('uselastr', false);
checkcomp = GM_getValue('checkcomp', false);
downloadt = GM_getValue('downloadt', true);
autoretry = GM_getValue('autoretry', true);
laststop = GM_getValue('laststop', ['placeholder']);
dmtime = parseFloat(GM_getValue('dmtime', 18));
lmtime = parseFloat(GM_getValue('lmtime', 9));
pmargin = parseFloat(GM_getValue('pmargin', 20));
lheight = parseFloat(GM_getValue('lheight', 1.4));
autoncd = parseFloat(GM_getValue('autoncd', 40));
autoscrolls = parseFloat(GM_getValue('autoscrolls', 90));
autoretryno = GM_getValue('autoretryno', 20);
autodown = GM_getValue('autodown', false);
defaultf = GM_getValue('defaultf', '"Comic Sans MS"');
defaultfs = GM_getValue('defaultfs', 18);
checkd = GM_getValue('checkd', '09:00');
lastcheck = GM_getValue('lastcheck', '');
autobackupp = parseFloat(GM_getValue('autobackupp', 3));
bgc = GM_getValue('bgc', '#c95');
txc = GM_getValue('txc', '#000');
dbgc = GM_getValue('dbgc', '#111');
dtxc = GM_getValue('dtxc', '#550');
CCSS = GM_getValue('CCSS', '');
ECCSS = GM_getValue('ECCSS', '');
FAVS = GM_getValue('FAVS', ['placeholder']);
HISTORY = GM_getValue('HISTORY', ['placeholder']);
RULES = GM_getValue('RULES', ['placeholder']);
DHISTORY = GM_getValue('DHISTORY', ['placeholder']);
TIMES = GM_getValue('TIMES', ['placeholder']);
CHECKED = GM_getValue('CHECKED', [JSON.stringify(new Date()).replaceAll('"', '')]);
}
let alreadyc = false;
function dofull(exit) {
let isfull = (document.fullscreenElement && document.fullscreenElement !== null) || (document.webkitFullscreenElement && document.webkitFullscreenElement !== null) || (document.mozFullScreenElement && document.mozFullScreenElement !== null) || (document.msFullscreenElement && document.msFullscreenElement !== null),
docElm = document.documentElement,
exitf = () => {
sh('rf');
document.querySelector('html').style.cssText = document.body.style.cssText = '';
if (!isfull) return;
if (document.exitFullscreen) document.exitFullscreen();
else if (document.webkitExitFullscreen) document.webkitExitFullscreen();
else if (document.mozCancelFullScreen) document.mozCancelFullScreen();
else if (document.msExitFullscreen) document.msExitFullscreen();
},
gof = () => {
document.querySelector('html').style.cssText = document.body.style.cssText = 'min-height: 0px !important; height: 0px !important; overflow: hidden !important;';
if (docElm.requestFullscreen) docElm.requestFullscreen();
else if (docElm.mozRequestFullScreen) docElm.mozRequestFullScreen();
else if (docElm.webkitRequestFullScreen) docElm.webkitRequestFullScreen();
else if (docElm.msRequestFullscreen) docElm.msRequestFullscreen();
}
if (exit) exitf();
else if (!isfull) {
sh('rt');
if (autora) removeannon();
if (defaultv && !alreadyc) {
ucss(5);
alreadyc = true;
}
resume();
dynamicmode();
gof();
} else exitf();
}
function resume() {
if (!asked) {
let ls = laststop.LN_search(location.href);
if (ls.found) {
if (autoresume) r.scrollTo(0, ls.obj.pos);
else if (confirm('Continue from last stop?')) {
r.scrollTo(0, ls.obj.pos);
if (confirm('Don\'t ask again?')) {
GM_setValue('autoresume', true);
m.querySelectorAll('input[type=radio]')[6].checked = true;
}
}
}
asked = true;
}
}
function handler(e) {
if (e.ctrlKey || e.shiftKey || e.altKey || e.metaKey || e.target.tagName == 'INPUT' || e.target.tagName == 'TEXTAREA') return;
let ncb = document.querySelector(CS.next),
pcb = document.querySelector(CS.prev),
key = e.code,
inc = document.querySelectorAll('#LN_decincfs button')[0],
dec = document.querySelectorAll('#LN_decincfs button')[1];
if (location.host.includes('novelonlinefull') || location.host.includes('bestlightnovel')) {
let nn, pp;
for (let e of document.querySelectorAll('a')) {
if (/NEXT CHAPTER/ig.test(e.innerText) || e.innerText.includes('Next »')) nn = e;
if (/PREV CHAPTER/ig.test(e.innerText) || e.innerText.includes('« Previous')) pp = e;
}
ncb = document.querySelector('.LN_nextchbtn') || nn || null;
pcb = document.querySelector('.LN_prevchbtn') || pp || null;
}
if (key == next || key == nexta || key == nextaa) {
if (!ncb || !ncb.href || ncb.href.endsWith('null') || (ncb.href.endsWith('#') && ncb.href.includes(location.href)) || !ncb.href.split(location.host)[1] || ncb.href.split(location.host)[1].split('/')[1].toLowerCase() != location.href.split(location.host)[1].split('/')[1].toLowerCase() || ncb.href == location.href) alt('Last Chapter.');
else {
if (autodl) dynamicload(ncb.href);
else ncb.click();
}
} else if (key == prev || key == preva || key == prevaa) {
if (!pcb || !pcb.href || pcb.href.endsWith('null') || (pcb.href.endsWith('#') && pcb.href.includes(location.href)) || !pcb.href.split(location.host)[1] || pcb.href.split(location.host)[1].split('/')[1].toLowerCase() != location.href.split(location.host)[1].split('/')[1].toLowerCase() || pcb.href == location.href) alt('No chapter Found!');
else {
if (autodl) dynamicload(pcb.href);
else pcb.click();
}
} else if (key == fullscreen) dofull();
else if (key == autoscroll) doautoscroll();
else if (key == showfavs) {
if (WIN === undefined) favs();
else {
WIN.close();
WIN = undefined;
}
}
else if (key == removean) removeannon();
else if (key == darkmode) ucss();
else if (key == download) {
sh('menut');
sh('sf');
sh('pf');
sh('of');
sh('dt');
sh('nf');
sh('bf');
sh('stf');
sh('wff');
m.querySelector('input[max="10000"]').focus();
}
else if (key == 'Escape') {
sh('stf');
sh('wff');
}
else if (key == incfs) inc.click();
else if (key == decfs) dec.click();
}
let URL, LASTC, STATUS, lasturl, WIN;
function favs() {
rvals();
WIN = window.open('', 'LN_favs', 'menubar=no,status=no,titlebar=no,location=no,top=0,left=0,height=' + window.innerHeight + 'width=' + (window.innerWidth / 10) * 8);
const b = WIN.document.body,
h = WIN.document.head;
h.innerHTML = `<style> body { color: #090; background-color: #111; } table, th, td { border: 1px solid #090; text-align: center; } th { width: 200px; height: 30px; font-family: 'Nunito Sans', sans-serif; } a { color: #00f; text-decoration: none; transition: color 0.5s; } a:hover { text-decoration: underline; } body { color: #090; background-color: #111; } .main { width: -moz-fit-content; width: fit-content; margin: 50px auto; } h1 { width: -moz-fit-content; width: fit-content; margin: 50px auto; font-size: 35px; font-weight: bolder; font-family: Helvetica Neue, Segoe UI, Helvetica, Arial, sans-serif; } td { height: 50px; font-family: Helvetica; } td svg { cursor: pointer } a:visited { color: #d2f !important; } h4 { font-family: arial; margin: 0px; } h2 { font-family: Helvetica Neue, Segoe UI, Helvetica, Arial, sans-serif; font-size: 22px; margin: 0 25px 30px; } .notes { padding: 0 40px; } .updates { font-family: Helvetica Neue, Segoe UI, Helvetica, Arial, sans-serif; font-size: 22px; margin: 0 25px 30px; display: inline; } button:hover { color: #090; background-color: #111; } button { transition: all 0.5s; border: none; color: #111; background-color: #090; outline: 0; font-family: sans-serif; cursor: pointer; padding: 5px 20px; font-weight: bolder; margin-left: 10px; margin-top: 10px; } :disabled { opacity: 0.6 !important; cursor: not-allowed !important; color: #000 !important; background-color: #090 !important; } .picker { top: 0; left: 0; position: fixed; width: 100%; height: 100%; background: #0009; display: flex; } .pbody { margin: auto; align-self: center; padding: 40px; background-color: #111; width: 50%; border-radius: 15px; user-select: none; font-family: arial; font-weight: 700; font-size: 20px; } .source { border: 1px solid #090; margin: 2px; background-color: #111; color: #090; cursor: pointer; font-size: 20px; padding: 3px 10px; appearance: auto; width: auto; height: auto; display: inline; } .LN_help { cursor: help; border-bottom: 2px #aaa dotted; margin-bottom: 2px; display: inline-block; } input[type="time"]:focus { box-shadow: 0 0 5px 2px #090; outline: 0; border: 1px solid #090; } input[type=time] { background-color: #111; border: none; color: #090; cursor: pointer; transition: all 0.5s; padding: 3px; } </style>`;
b.innerHTML = `<div class="main"> <h1> Favorites </h1> <table> <tbody> <tr> <th> Novel </th> <th> URL </th> <th> Last Chapter </th> <th> Last Read </th> <th> Last Downloaded </th> <th> Status </th> <th> <span class=LN_help title="If auto check is enabled, the script checks daily at this time for each novel individually" onclick="javascript:window.open('https://openuserjs.org/scripts/XDHx86/Light_Novel_Loader#notes')"> Update Time </th> <th> Migrate </th> <th> Download </th> <th> Check </th> <th> Remove </th> </tr> </tbody> </table> <br> <br> <h2 class="updates"> Check for Updates: </h2> <button class="check"> Check </button> <br> <h2 class=updates> Save updates times: </h2> <button class=savetime> Save </button> <br> <h2> Notes: </h2> <div class="notes"> <h4> - Press CTRL+Z to undo removal. </h4> <br> <h4> - Press CTRL+R to show new changes. </h4> <br> <h4> - Press Escape to close the window. </h4> <br> <h4> - All novels in favorites will receive notifications, to disable notifications go to "Notifications & Favorites" in settings menu. </h4> <br> <h4> - If the window is already open it won't open again. </h4> </div> </div>`;
const table = b.querySelector('table');
if (WIN.innerWidth < (window.innerWidth / 10) * 8) {
try { WIN.innerWidth = (window.innerWidth / 10) * 8; }
catch (e) { }
}
for (let i = 1; i < FAVS.length; i++) {
let fav = FAVS[i],
novel = fav.novel.replace(/\n|\t|«/gm, ''),
url = fav.url,
status = fav.status,
lastc = fav.lastc,
lastr = HISTORY.LN_search(url.href).obj,
lastd = DHISTORY.LN_search(url.href).obj,
time = TIMES.LN_search(url.href).obj || '',
svgs = '<div title="Remove from favorites"> <svg class="remove" style="height: 20px; width: 20px;" viewBox="0 0 48 48"> <circle style="fill:#F44336;" cx="24" cy="24" r="19"/> <rect x="22" y="14" transform="matrix(0.7071 0.7071 -0.7071 0.7071 24 -9.9411)" style="fill:#FFFFFF;" width="4" height="20"/> <rect x="22" y="14" transform="matrix(-0.7071 0.7071 -0.7071 -0.7071 57.9411 24)" style="fill:#FFFFFF;" width="4" height="20"/> </svg> </div>',
svgd = '<div title="Download to latest chapter"> <svg class="download" style="height: 20px; width: 20px; fill: #ccc;" viewBox="0 0 26 26"> <path d="M 11 0 C 9.344 0 8 1.343 8 3 L 8 11 L 4.75 11 C 3.34 11 3.043 11.2265 4.25 12.4375 L 10.84375 19.03125 C 13.04475 21.23125 13.01375 21.23725 15.21875 19.03125 L 21.78125 12.4375 C 22.98925 11.2275 22.5855 11 21.3125 11 L 18 11 L 18 3 C 18 1.343 16.656 0 15 0 L 11 0 z M 0 19 L 0 23 C 0 24.656 1.344 26 3 26 L 23 26 C 24.656 26 26 24.656 26 23 L 26 19 L 24 19 L 24 23 C 24 23.551 23.552 24 23 24 L 3 24 C 2.448 24 2 23.551 2 23 L 2 19 L 0 19 z"/> </svg> </div>',
svgc = '<div title="Check this novel for updates"> <svg class="update" style="height: 20px; width: 20px; fill: #ccc;" viewBox="0 0 24 24"> <path d="M 7.59375 3 L 9.0625 5 L 12 5 L 13 5 C 16.325562 5 19 7.674438 19 11 L 19 15 L 16 15 L 20 20.46875 L 24 15 L 21 15 L 21 11 C 21 6.593562 17.406438 3 13 3 L 12 3 L 7.59375 3 z M 4 3.53125 L 0 9 L 3 9 L 3 13 C 3 17.406438 6.593562 21 11 21 L 12 21 L 14 21 L 16.40625 21 L 14.9375 19 L 14 19 L 12 19 L 11 19 C 7.674438 19 5 16.325562 5 13 L 5 9 L 8 9 L 4 3.53125 z"/> </svg> </div>',
svgm = '<div title="Migrate this novel to another source"> <svg class="migrate" style="height: 20px; width: 20px; fill: #ccc;" viewBox="0 0 50 50"> <path d="M 2 2 L 2 3 L 2 39 L 2 40 L 3 40 L 19 40 L 19 47 L 19 48 L 20 48 L 47 48 L 48 48 L 48 47 L 48 22 L 48 21.59375 L 47.71875 21.28125 L 36.71875 10.28125 L 36.40625 10 L 36 10 L 27.4375 10 L 19.71875 2.28125 L 19.40625 2 L 19 2 L 3 2 L 2 2 z M 4 4 L 18 4 L 18 14 L 18 15 L 19 15 L 29 15 L 29 32 L 29 34 L 29 38 L 4 38 L 4 4 z M 20 5.4375 L 26 11.40625 L 26 11.4375 A 1.0043849 1.0043849 0 0 0 26.03125 11.5 A 1.0043849 1.0043849 0 0 0 26.375 11.84375 A 1.0043849 1.0043849 0 0 0 26.46875 11.90625 L 27.5625 13 L 20 13 L 20 5.4375 z M 29.4375 12 L 35 12 L 35 22 L 35 23 L 36 23 L 46 23 L 46 46 L 21 46 L 21 40 L 30 40 L 31 40 L 31 39 L 31 36.4375 L 33.28125 38.71875 L 34.71875 37.28125 L 31.4375 34 L 41 34 L 41 32 L 31.4375 32 L 34.71875 28.71875 L 33.28125 27.28125 L 31 29.5625 L 31 14 L 31 13.59375 L 30.71875 13.28125 L 29.4375 12 z M 37 13.4375 L 44.5625 21 L 37 21 L 37 13.4375 z M 17.71875 20.28125 L 16.28125 21.71875 L 19.5625 25 L 10 25 L 10 27 L 19.5625 27 L 16.28125 30.28125 L 17.71875 31.71875 L 22.71875 26.71875 L 23.40625 26 L 22.71875 25.28125 L 17.71875 20.28125 z"/> </svg> </div>';
if (!lastr) lastr = { href: '', text: 'No Record Yet', target: '_self' };
else lastr.target = '_blank';
if (!lastd) lastd = { href: '', text: 'No Record Yet', target: '_self' };
else lastd.target = '_blank';
let timecode = ` <input type=time value="${time.time}">`;
if (status.toLowerCase().includes('complete')) timecode = '<span style="cursor: default;"> Completed </span>';
let code = `<tr data="${i}"> <td> ${novel} </td> <td> <a href="${url.href}" target="_blank"> ${url.text} </a> </td> <td> <a href="${lastc.href}" target="_blank"> ${lastc.text} </a> </td> </td> <td> <a href="${lastr.href}" target="${lastr.target}"> ${lastr.text} </a> </td> <td> <a href="${lastd.href}" target="${lastd.target}"> ${lastd.text} </a> </td> <td style="text-transform: capitalize;"> ${status.toLowerCase()} </td> <td> ${timecode} </td> <td> ${svgm} </td> <td> ${svgd} </td> <td> ${svgc} </td> <td> ${svgs} </td> </tr>`;
table.innerHTML += code;
}
let ROW, DATA;
for (let i = 1; i < FAVS.length; i++) {
let svg = b.querySelectorAll('svg.remove')[i-1];
svg.onclick = function() {
let p = this.parentElement.parentElement.parentElement,
d = p.getAttribute('data');
ROW = p.cloneNode(true);
DATA = FAVS[d];
FAVS.splice(d, 1);
GM_setValue('FAVS', FAVS);
rvals();
p.remove();
}
svg = b.querySelectorAll('svg.migrate')[i-1];
if (svg) {
svg.onclick = function() {
let tr = this.parentElement.parentElement.parentElement,
d = tr.getAttribute('data'),
novel = FAVS[d],
div = document.createElement('div');
div.className = 'picker';
div.innerHTML = `<div class="pbody"> <h2> How It Works: </h2> <ul> <li> After clicking migrate the script will attempt to search for the novel in the specified source for the user to select </li> <br> <li> After selecting the novel, add it to favorites. FIRST novel added will be the target to migrate to - Whether it's already favored or not - and the script won't check for names </li> <br> <li> Migrating a novel doesn't delete the old one </li> <br> <li> Migrating a novel doesn't delete records of the old one but only adds the current records to the new source</li> </ul> Migrating novel: ${novel.novel} <br> From: ${novel.url.href.LN_host()} <br> To: <select class="source"> </select> <br> <button> Migrate </button> </div>`;
for (let e in SITES) {
let s = div.querySelector('select');
if (e != 'www.readlightnovel.me' && e != 'www.novels.pl' && e != novel.url.href.LN_host()) s.options.add(new Option(e, e));
}
WIN.document.body.appendChild(div);
div.querySelector('button').onclick = () => {
let site = div.querySelector('.source').value;
search(novel.novel, site);
GM_setValue('migrated', novel.url.href);
}
div.onclick = e => { if (e.target.className == 'picker') div.remove(); }
}
}
svg = b.querySelectorAll('svg.download')[i-1];
svg.onclick = () => downnew(i, true);
svg = b.querySelectorAll('svg.update')[i-1];
svg.onclick = () => notify(i, 0, true);
}
b.querySelector('.check').onclick = function() {
notify();
this.disabled = true;
this.innerText = 'Checking...';
setTimeout(() => {
b.querySelector('.check').disabled = false;
b.querySelector('.check').innerText = 'Check';
}, 10000);
}
b.querySelector('.savetime').onclick = function() {
let ins = b.querySelectorAll('[type=time]');
for (let i = 0; i < ins.length; i++) {
if (ins[i].value) {
let h = FAVS[ins[i].parentElement.parentElement.getAttribute('data')].url.href,
obj = {
href: h,
time: ins[i].value
}
if (TIMES.LN_search(h).obj) TIMES.LN_search(h).obj.time = ins[i].value;
else TIMES.push(obj);
GM_setValue('TIMES', TIMES);
}
}
}
WIN.onkeyup = e => {
if (e.code == 'KeyZ' && e.ctrlKey && ROW) {
let t = b.querySelector('table');
ROW.setAttribute('data', FAVS.length);
t.appendChild(ROW);
FAVS.push(DATA);
GM_setValue('FAVS', FAVS);
rvals();
favs();
} else if (e.code == 'KeyR' && e.ctrlKey) {
e.preventDefault();
favs();
} else if (e.code == 'Escape' || (e.code == showfavs && e.target.tagName != 'INPUT')) {
WIN.close();
WIN = undefined;
}
}
}
let FNOVEL
function favthis(url, mode=0, PL=CS) {
GM_xmlhttpRequest({
method: 'GET',
url: url,
onload: r => {
try {
let BODY, CHAPTER, TITLE, CHAPTERTITLE;
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body,
getdata = () => {
for (let e of b.querySelectorAll('script')) {
if (e.innerHTML.includes('function load(page)')) {
let str = e.innerHTML;
str = str.substr(str.indexOf('data: {'));
let id = str.substr(10, 4);
id = id.trim();
if (id.endsWith(',')) id = id.substr(0, id.length-1);
str = str.substr(str.indexOf(',')+2);
let novel = str.substr(6, str.indexOf(',')-6);
novel = novel.replace(/'|"'/g, '');
str = str.substr(str.indexOf(',')+2);
let max = str.substr(4,4);
max = max.trim();
if (max.endsWith(',')) max = max.substr(0, max.length-1);
let string = `id=${id}&novel=${novel}&max=${max}&page=1`;
return string;
}
}
},
lastcnovels = () => {
GM_xmlhttpRequest({
method: 'POST',
url: 'https://www.novels.pl/ajax/ajaxGetChapters.php',
data: getdata(),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body;
LASTC = {
href: b.querySelector('a').href.LN_fix(url),
text: b.querySelector('a').innerText
}
}
});
},
adding = () => {
let index = {
novel: FNOVEL,
url: URL,
status: STATUS.toLowerCase(),
lastc: LASTC
}, err, {novel, url, status, lastc} = index;
if (!novel || !url || !status || !lastc) {
setTimeout(() => { alt('Error retreving data, can’t add favorite. Please report this via Email to xdhx86@gmail.com', 5000) }, 5000);
err = true;
}
if (err) return;
FAVS.push(index);
GM_setValue('FAVS', FAVS);
rvals();
alt('Added to favorites!');
setTimeout(() => {
if (GM_getValue('migrated')) {
migrate(GM_getValue('migrated'), url);
GM_setValue('migrated', false);
}
}, 7000);
}
if (mode == 0 || mode == 2) {
if (mode == 0 && FAVS.LN_deepSearch(url).found) {
if (GM_getValue('migrated')) {
migrate(GM_getValue('migrated'), url);
GM_setValue('migrated', false);
}
else alt('This novel is already in favorites!');
return;
}
URL = { href: url, text: url.LN_platform() };
let link;
if (url.includes('lightnovelworld')) {
FNOVEL = b.querySelector(PL.favobj.noveltitle).innerText;
STATUS = b.querySelectorAll('.header-stats span')[3].querySelector('strong').innerText;
link = url + PL.favobj.link;
}
else if (url.includes('readlightnovel.me')) {
FNOVEL = b.querySelector(PL.favobj.noveltitle).innerText;
let t = b.querySelectorAll('.novel-detail-item');
for (let e of t) {
if (e.innerText.includes('Status')) STATUS = e.querySelector('li').innerText;
else if (e.innerText.includes('Latest Chapters')) LASTC = { href: e.querySelector('a').href.LN_fix(url), text: e.querySelector('a').innerText };
}
}
else if (url.includes('novels.pl')) {
FNOVEL = b.querySelector(PL.favobj.noveltitle).innerText;
STATUS = ' - ';
lastcnovels();
}
else {
FNOVEL = b.querySelector(PL.favobj.noveltitle).innerText.replaceAll(/\n|HOT|NEW/gm, '');
if (PL.favobj.status) STATUS = b.querySelector(PL.favobj.status).innerText;
else STATUS = ' - ';
LASTC = {
href: b.querySelector(PL.favobj.lastc).href.LN_fix(url),
text: b.querySelector(PL.favobj.lastc).innerText
};
}
if (link && mode == 0) favthis(link, 1);
else if (link && mode == 2) favthis(link, 3, PL);
else if (!link && mode == 0) {
if (url.includes('novels.pl')) setTimeout(adding, 3000);
else adding();
}
} else if (mode == 1 || mode == 3) {
if (url.includes('novels.pl')) lastcnovels();
else {
LASTC = {
href: b.querySelector(PL.favobj.lastc).href.LN_fix(url),
text: b.querySelector(PL.favobj.lastc).innerText
};
}
if (mode == 3) return;
if (url.includes('novels.pl')) setTimeout(adding, 3000);
else adding();
}
} catch (e) {
const doc = new DOMParser().parseFromString(r.responseText, "text/html"),
b = doc.body;
if (!b) {
alt('Error: NETWORK_ERROR\nDetails: Response cannot be null! Please try again later.\nReason: Might be request timeout, bad internet connection, spammed requests in a small time frame.', 10000);
console.error(e);
}
else if (BOTCHECK(b)) {
if (confirm('Caught by bot verification.\nOpen url (' + url + ') in new tab?')) window.open(url, '_blank');
} else {
alt('Error:\nDetails: Unexpected error has occured. Please try again later.\nReason: Might be request timeout, bad internet connection, another unexpected reason.', 10000);
console.error(e);
}
}
}
});
}
function notify(index, retries=0, single=false, allowdown=false) {
if (window.LN_checking) {
alt('The script is already checking for updates, please wait patiently or cancel it in notifications menu.');
return;
}
if (window.LN_cancelcheck) {
window.LN_cancelcheck = false;
if (m.querySelectorAll('.LN_btn')[5]) {
m.querySelectorAll('.LN_btn')[5].disabled = false;
m.querySelectorAll('.LN_btn')[5].innerText = 'Check';
m.querySelectorAll('.LN_btn')[6].disabled = false;
}
alt('Checking canceled.');
return;
}
window.LN_checking = true;
let i = index || 1,
curl,
checkc = obj => {
let c = [], output = false;
for (let i = 1; i < FAVS.length; i++) c.push({lastc: FAVS[i].lastc, novel: FAVS[i].novel});
for (let i = 0; i < c.length; i++) {
let e = c[i];
if (obj.lastc.text == e.lastc.text && obj.lastc.href == e.lastc.href && obj.novel != e.novel) {
output = true;
break;
}
}
return output;
},
clear = () => {
if (notification) clearInterval(notification);
window.LN_checking = false;
if (allowdown) setTimeout(() => downnew(index, single), 7000);
if (m.querySelectorAll('.LN_btn')[5]) {
m.querySelectorAll('.LN_btn')[5].disabled = false;
m.querySelectorAll('.LN_btn')[5].innerText = 'Check';
m.querySelectorAll('.LN_btn')[6].disabled = false;
}
},
notification = setInterval(() => {
try {
if (!FAVS[i]) {
clear();
setTimeout(downnew, 3000);
return;
}
if (window.LN_cancelcheck) {
window.LN_cancelcheck = false;
clear();
alt('Checking canceled.');
return;
}
let obj = FAVS[i],
PL = SITES[obj.url.href.LN_host()];
curl = obj.url.href;
if (obj.status.toLowerCase().includes('complete') && !checkcomp && !single) {
clear();
alt('Novel is complete, skipping.');
notify(i+1);
return;
}
favthis(obj.url.href, 2, PL);
let action = setTimeout(() => {
if ((!LASTC || obj.url.href.LN_host() != LASTC.href.LN_host() || checkc({lastc: LASTC, novel: obj.novel}))) {
alt('Network latency high or server denied access, retrying...');
clear();
retry({
callback: notify,
index: i,
single: single,
retries: retries + 1,
origin: obj.url.href
});
return;
}
if (LASTC.text != obj.lastc.text) {
if (STATUS != obj.status && STATUS.toLowerCase().includes('complete')) {
GM_notification({
title: 'Novel is completed!',
text: `${obj.novel.replace(/\n|\t|«/gm, '')}\nIs completed!\nLast chapter: ${LASTC.text}.\nClick to show favorites.`,
onclick: favs
});
obj.lastc = LASTC;
obj.status = STATUS;
GM_setValue('FAVS', FAVS);
rvals();
} else {
GM_notification({
title: 'New chapter found!',
text: `${obj.novel.replace(/\n|\t|«/gm, '')}\n\nHas new chapter: ${LASTC.text}.\nClick to show favorites.`,
onclick: favs
});
obj.lastc = LASTC;
GM_setValue('FAVS', FAVS);
rvals();
}
} else {
if (nnotfound) {
GM_notification({
title: 'No chapter found!',
text: `${obj.novel.replace(/\n|\t|«/gm, '')}\n\nHas no new chapter.\nClick to show favorites.`,
onclick: favs
});
}
}
}, 6500);
if (!single) i++;
else clear();
} catch (e) {
window.LN_checking = false;
console.error(e);
if (!OBJ.BODY) {
alt('Error: NETWORK_ERROR\nDetails: Response cannot be null! Retrying...', 10000);
retry({
callback: notify,
index: i,
single: single,
retries: retries + 1,
origin: FAVS[i].url.href
});
}
else if (BOTCHECK(OBJ.BODY)) {
if (m.querySelectorAll('.LN_btn')[5]) {
m.querySelectorAll('.LN_btn')[5].disabled = false;
m.querySelectorAll('.LN_btn')[5].innerText = 'Check';
m.querySelectorAll('.LN_btn')[6].disabled = false;
}
if (confirm('Caught by bot verification.\nOpen url (' + FAVS[i].url.href + ') in new tab?')) window.open(FAVS[i].url.href, '_blank');
} else {
alt('Error:\nDetails: Unexpected error has occured. Retrying...', 10000);
retry({
callback: notify,
index: i,
single: single,
retries: retries + 1,
origin: FAVS[i].url.href
});
}
}
}, 7000);
}
function check() {
if (autoch && (new Date).toTimeString().substr(0, 5) > checkd && (!lastcheck || lastcheck != (new Date).getDate())) {
alt('Auto checking for updates..');
lastcheck = (new Date).getDate();
GM_setValue('lastcheck', lastcheck);
notify();
}
}
let aurcc = 0;
function retry(obj) {
let callback = obj.callback,
retries = obj.retries,
origin = obj.origin,
single = obj.single,
index = obj.index,
auto = obj.auto,
OBJ = obj.OBJ,
url = obj.url,
PL = obj.PL,
n = obj.n;
if (retries < 3) {
if (callback.name == 'notify') callback(index, retries, single);
else if (callback.name == 'Download') callback(url, n, retries, auto, index, PL, single, OBJ);
else if (callback.name == 'dynamicload') callback(url, retries, OBJ);
}
else {
if (autoretry && aurcc <= autoretryno) {
setTimeout(() => {
aurcc++;
obj.retries = 0;
retry(obj);
}, 5000);
}
else if (!autoretry || aurcc > autoretryno) {
if (confirm('Refused to connect to (' + origin + ') after ' + (autoretry ? autoretryno : '3') + ' retries.\nClick ok to open the url in a new tab.\nThe script will retry after 5 seconds after you click ok.')) {
window.open(origin, '_blank');
setTimeout(() => {
aurcc = 0;
obj.retries = 0;
retry(obj);
}, 5000);
} else if (!confirm('Are you sure you want to cancel? Press OK to cancel')) {
window.open(origin, '_blank');
setTimeout(() => {
aurcc = 0;
obj.retries = 0;
retry(obj);
}, 5000);
}
else if (callback.name == 'notify') {
window.LN_checking = false;
m.querySelectorAll('.LN_btn')[5].disabled = false;
m.querySelectorAll('.LN_btn')[6].disabled = false;
m.querySelectorAll('.LN_btn')[5].innerText = 'Check';
}
else if (callback.name == 'Download') {
window.LN_downloading = false;
Dalt = false;
m.querySelectorAll('.LN_btn')[2].disabled = false;
m.querySelectorAll('.LN_btn')[3].disabled = false;
m.querySelectorAll('.LN_btn')[2].innerText = 'Download';
}
else if (callback.name == 'dynamicload') window.LN_loading = false;
}
}
}
function downnew(index=1, single=false) {
if (!autodown && !single) return;
let obj = FAVS[index];
if (!obj) {
alt('Auto download finished.');
return;
}
let lastr = HISTORY.LN_search(obj.url.href),
lastd = DHISTORY.LN_search(obj.url.href),
PL = SITES[obj.url.href.LN_host()],
con = () => {
if (allowconfirm) return (confirm('Attempting to download following chapters from (' + TITLE[2].replace(/\n|«/gm, '') + ') to last chapter uploaded.\nConfirm?'));
else return true;
},
lastm = () => {
if (uselastr) return lastr;
else return lastd;
},
lastt = () => {
if (lastm() == lastr) return 'read';
else return 'downloaded';
};
if (!lastm().found && !single) {
alt('Last '+ lastt() +' chapter not found, skipping.');
downnew(index + 1);
}
else if (!lastm().found && single) alt('Last '+ lastt() +' chapter not found!');
else if (obj.lastc.href == lastm().obj.href && !single) {
alt('Last '+ lastt() +' chapter is last the chapter, skipping.');
downnew(index + 1);
}
else {
const OBJ = {};
getcontent(lastm().obj.href, PL, OBJ);
setTimeout(() => {
let NOVEL = OBJ.NOVEL, BODY = OBJ.BODY, CHAPTER = OBJ.CHAPTER, TITLE = OBJ.TITLE, CHAPTERTITLE = OBJ.CHAPTERTITLE;
let current = BODY.querySelector(PL.next);
if (obj.lastc.href.includes('novelonlinefull') || obj.lastc.href.includes('bestlightnovel')) {
let nn;
for (let e of BODY.querySelectorAll('a')) if (/NEXT CHAPTER/ig.test(e.innerText) || e.innerText == 'Next »') nn = e;
current = nn;
}
if (current && current.href) current.href = current.href.LN_fix(obj.lastc.href);
if (current && current.href && (!(current.href.endsWith('#') && current.href.includes(obj.lastc.href))) && (current.href.split(obj.url.href.LN_host())[1] && current.href.split(obj.url.href.LN_host())[1].split('/')[1].toLowerCase() == obj.lastc.href.split(obj.url.href.LN_host())[1].split('/')[1].toLowerCase())) {
current = current.href;
if (con()) {
let int = setInterval(() => {
if (!window.LN_checking) {
Dcont = [];
Dalt = false;
Download(current, 9999, 0, true, index, PL, single);
clearInterval(int);
}
}, 1000);
} else if (!single) downnew(index + 1);
}
else {
alt('Last '+ lastt() +' chapter is last the chapter, skipping.');
if (!single) downnew(index + 1);
}
}, 5000);
}
}
function br(mode) {
let fr = new FileReader(),
res;
if (!fr) alt('File API not supported!');
else if (mode == 0) {
const a = document.createElement('a'),
date = (new Date).toDateString().split(' '),
time = date[2] + '-' + date[1],
names = GM_listValues(),
values = [],
parsed = [];
names.forEach(e => values.push(GM_getValue(e)));
parsed.push(names);
parsed.push(values);
a.href = 'data:text/plain;charset=utf-8,' + encodeURIComponent(JSON.stringify(parsed));
a.download = '(' + time + ').XDHx86_LightNovelLoader.data.txt';
a.click();
a.remove();
GM_setValue('lastbackup', JSON.stringify(new Date).replace('"', ''));
} else if (mode == 1) {
const i = document.querySelector('.LN_b input'),
f = i.files[0];
if (!f) {
alt('Please select a file!');
return;
}
fr.onload = () => { res = fr.result; }
fr.readAsText(f);
setTimeout(() => {
let arr = JSON.parse(res),
g = arr[0],
v = arr[1];
if (!g || !g.LN_search('firsttime').found) alt('This is not a backup file, please check the uploaded file.');
else {
for (let i = 0; i < g.length; i++) GM_setValue(g[i], v[i]);
if (confirm('Settings will take effect after reload.\nReload now?')) location.reload();
}
}, 1500);
}
}
function record(e, doc = document, PL=CS, url, download=false) {
rvals();
let novel, chapter;
url = url || doc.URL || location.href;
if (PL.novel != '') novel = doc.querySelector(PL.novel).href.LN_fix(url);
else {
novel = location.href.LN_fix(url).split('/');
if (novel[novel.length - 1] == '') {
novel.pop();
novel.pop();
} else novel.pop();
novel = novel.join('/');
}
if (url.includes('readlightnovel.me')) chapter = doc.querySelector('.block-title h1').lastChild.data.replace(' - ', '');
else if (url.includes('novelonlinefull') || url.includes('bestlightnovel')) chapter = doc.querySelector('.rdfa-breadcrumb p').lastChild.data;
else if (url.includes('novels.pl')) chapter = doc.querySelector('.panel-title.pull-left').lastChild.data;
else chapter = doc.querySelector(PL.chaptertitle).innerText;
let result1 = HISTORY.LN_search(novel),
result2 = DHISTORY.LN_search(novel);
if (!download) {
if (result1.found) {
let obj = result1.obj;
if (chapter != obj.text) {
obj.text = chapter;
obj.href = url;
GM_setValue('HISTORY', HISTORY);
rvals();
}
}
else {
let obj = {
novel: novel,
href: url,
text: chapter
};
HISTORY.push(obj);
GM_setValue('HISTORY', HISTORY);
rvals();
}
}
else {
if (result2.found) {
let obj = result2.obj;
if (chapter != obj.text) {
obj.text = chapter;
obj.href = url;
GM_setValue('DHISTORY', DHISTORY);
rvals();
}
}
else {
let obj = {
novel: novel,
href: url,
text: chapter
};
DHISTORY.push(obj);
GM_setValue('DHISTORY', DHISTORY);
rvals();
}
}
}
let cleanedr = false;
function filter(c) {
let clean = () => {
let tbr = c.querySelectorAll('*'),
elements = [];
for (let e of tbr) if (e.parentElement == c && e.parentElement.tagName == 'P' && e.tagName == 'P') elements.push(e);
for (let i = 0; i < elements.length; i++) {
let e = elements[i];
if (e.children.length > 0) {
do e.parentElement.appendChild(e.children[0]);
while (e.children.length > 0);
}
}
};
if (!cleanedr || window.LN_downloading) {
clean();
clean();
if (!window.LN_downloading) cleanedr = true;
}
for (let i = 1; i < RULES.length; i++) {
let obj = RULES[i];
if (obj.cond == 'line') {
if (obj.type == 'contains') {
for (let e of c.childNodes) {
if (e.data) {
if (obj.replaced == '' && e.data.includes(obj.word)) {
if (e.previousSibling && e.previousSibling.tagName == 'BR') e.previousSibling.remove();
if (e.previousSibling && e.previousSibling.tagName == 'BR') e.previousSibling.remove();
e.data = '';
}
else if (e.data.includes(obj.word)) e.data = obj.replaced;
} else if (e.childNodes[0]) {
for (let m of e.childNodes) {
if (m.data) {
if (obj.replaced == '' && m.data.includes(obj.word)) {
m.data = '';
if (m.parentElement.tagName == 'P') m.parentElement.remove();
}
else if (m.data.includes(obj.word)) m.data = obj.replaced;
}
}
}
}
} else {
for (let e of c.childNodes) {
if (e.data) {
if (obj.replaced == '' && e.data == (obj.word)) {
if (e.previousSibling && e.previousSibling.tagName == 'BR') e.previousSibling.remove();
if (e.previousSibling && e.previousSibling.tagName == 'BR') e.previousSibling.remove();
e.data = '';
}
else if (e.data.includes(obj.word)) e.data = obj.replaced;
} else if (e.childNodes[0]) {
for (let m of e.childNodes) {
if (m.data) {
if (obj.replaced == '' && m.data == (obj.word)) {
m.data = '';
if (m.parentElement.tagName == 'P') m.parentElement.remove();
}
else if (m.data.includes(obj.word)) m.data = obj.replaced;
}
}
}
}
}
} else {
if (obj.type == 'equals') c.innerHTML = c.innerHTML.replaceAll(obj.word, obj.replaced);
else c.innerHTML = c.innerHTML.replaceAll(new RegExp(obj.word, 'gmi'), obj.replaced);
}
}
}
let chapters = [], confirmed = false, pagecc = 1, wxpage = 0;
function novelindex(url, maximum, elm) {
let nurl = url;
GM_xmlhttpRequest({
url: url,
method: 'GET',
onload: r => {
try {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body,
PL = SITES[url.LN_host()],
getmax = () => {
let anc;
if (b.querySelector(PL.indexobj.max)) anc = b.querySelector(PL.indexobj.max);
else if (b.querySelectorAll(PL.indexobj.nav).length > 0) anc = b.querySelectorAll(PL.indexobj.nav)[b.querySelectorAll(PL.indexobj.nav).length-2];
else return 1;
let x = new window.URL(anc.href);
if (x.search) {
let y = x.search.substr(x.search.indexOf('page=')),
z = y;
if (y.indexOf('&') > -1) z = y.substr(0, y.indexOf('&'));
return parseInt(z.substr(z.indexOf('=') + 1));
} else return parseInt(x.href.substr(x.href.indexOf('page')+5));
},
getpage = () => {
let x = new window.URL(url);
if (x.search) {
let y = x.search.substr(x.search.indexOf('page=')),
z = y;
if (y.indexOf('&') > -1) z = y.substr(0, y.indexOf('&'));
return parseInt(z.substr(z.indexOf('=') + 1));
} else return parseInt(x.href.substr(x.href.indexOf('page')+5));
},
add = (elms=chapters) => {
let addto = elm || l;
if (addto.querySelector('div')) addto.querySelector('div').remove();
if (!Array.isArray(elms)) elms = Array.from(elms);
if (!url.includes('lightnovelworld') && !url.includes('readlightnovel') && !url.includes('novelfull') && !url.includes('wuxiax.com') && !url.includes('novelhall')) elms.reverse();
for (let i = 0; i < elms.length; i++) {
let el = elms[i],
a = document.createElement('a');
a.className = 'LN_chapteran';
a.innerText = el.title || el.innerText;
if (a.querySelectorAll('*')) for (let e of a.querySelectorAll('*')) e.remove();
a.href = el.href.LN_fix(url);
a.onclick = e => {
e.preventDefault();
if (location.href != a.href) {
alt('Loading ' + a.innerText);
dynamicload(a.href.LN_fix(location.href));
}
}
addto.appendChild(a);
}
if (addto.style.display == 'grid') sh('cit');
window.LN_indexslow = false;
},
getdata = () => {
if (url.includes('novels.pl')) {
for (let e of b.querySelectorAll('script')) {
if (e.innerHTML.includes('function load(page)')) {
let str = e.innerHTML;
str = str.substr(str.indexOf('data: {'));
let id = str.substr(10, 4);
id = id.trim();
if (id.endsWith(',')) id = id.substr(0, id.length-1);
str = str.substr(str.indexOf(',')+2);
let novel = str.substr(6, str.indexOf(',')-6);
novel = novel.replace(/'|"'/g, '');
str = str.substr(str.indexOf(',')+2);
let max = str.substr(4,4);
max = max.trim();
if (max.endsWith(',')) max = max.substr(0, max.length-1);
return `id=${id}&novel=${novel}&max=${max}&page=${maximum || 1}`;;
}
}
}
}
if (BOTCHECK(b)) {
if (confirm('Caught by bot verification.\nOpen url (' + url + ') in new tab?')) {
window.open(url, '_blank');
setTimeout(() => { novelindex(nurl, maximum, elm) }, 10000);
return;
} else {
add();
alt('Cancelled!');
return;
}
}
if (url.includes('lightnovelworld')) {
if (getmax() > 10 && !confirmed) {
if (!confirm('Loading index might slow the browser or cause errors due to large amount of chapters, are you sure?')) {
sh('cif');
confirmed = false;
return;
}
window.LN_indexslow = true;
confirmed = true;
}
let max = maximum || getmax(),
currentpage = parseInt(getpage()),
a = b.querySelectorAll(PL.indexobj.chapters);
for (let e of a) chapters.push(e);
if (currentpage == max) add();
else {
nurl = url.replace(/page-[0-9]?[0-9]/, 'page-' + (currentpage + 1));
novelindex(nurl, max, elm);
}
}
else if (url.includes('novelfull')) {
let cid = document.querySelector('#chapter_error').getAttribute('data-chapter-id'),
nid;
for (let i = 0; i < document.scripts.length; i++) {
let sc = document.scripts[i].innerHTML.toString();
if (sc.indexOf('var novel = {') > -1) nid = parseInt(sc.substr(sc.indexOf('var novel = {'), 40).substr(sc.substr(sc.indexOf('var novel = {'), 40).indexOf('id:')+4));
}
GM_xmlhttpRequest({
method: 'GET',
url: `https://novelfull.com/ajax-chapter-option?novelId=${nid}¤tChapterId=${cid}`,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
sel = doc.body.querySelector('select');
for (let i = 0; i < sel.options.length; i++) {
let t = sel.options[i].innerText,
h = 'https://novelfull.com' + sel.options[i].value,
a = document.createElement('a');
a.href = h;
a.innerText = t;
chapters.push(a);
}
add();
}
});
}
else if (url.includes('wuxiax.com')) {
let novellink = url.split('/')[4].replace('.html', '');
GM_xmlhttpRequest({
method: 'POST',
url: 'https://www.wuxiax.com/e/extend/fy.php?page=' + wxpage + '&wjm=' + novellink,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body,
a = b.querySelectorAll('.chapter-list a');
for (let e of a) {
const text = e.querySelector('strong').innerText;
e.innerHTML = text;
chapters.push(e);
}
wxpage++;
if (b.querySelector('.chapter-list a')) novelindex(url, (maximum + 1) || 2, elm);
else add();
}
});
}
else if (url.includes('www.readwn.com')) {
let novellink = url.split('/')[4].replace('.html', '');
GM_xmlhttpRequest({
method: 'POST',
url: 'https://www.readwn.com/e/extend/fy.php?page=' + wxpage + '&wjm=' + novellink,
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body,
a = b.querySelectorAll('.chapter-list a');
for (let e of a) {
const text = e.querySelector('strong').innerText;
e.innerHTML = text;
chapters.push(e);
}
wxpage++;
if (b.querySelector('.chapter-list a')) novelindex(url, (maximum + 1) || 2, elm);
else add();
}
});
}
else if (url.includes('novels.pl')) {
GM_xmlhttpRequest({
method: 'POST',
url: 'https://www.novels.pl/ajax/ajaxGetChapters.php',
data: getdata(),
headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body,
a = b.querySelectorAll('a');
for (let e of a) chapters.push(e);
if (b.querySelector('a')) novelindex(url, (maximum + 1) || 2, elm);
else add();
}
});
}
else if (url.includes('boxnovel.com')) {
GM_xmlhttpRequest({
method: 'POST',
url: url + 'ajax/chapters/',
onload: r => {
const doc = new DOMParser().parseFromString(r.responseText, 'text/html'),
b = doc.body,
a = b.querySelectorAll('a:not([class])');
for (let e of a) chapters.push(e);
add();
}
});
}
else add(b.querySelectorAll(PL.indexobj.chapters));
} catch (e) {
const doc = new DOMParser().parseFromString(r.responseText, "text/html"),
b = doc.body;
if (!b) {
alt('Error: NETWORK_ERROR\nDetails: Response cannot be null! Please try again later.\nReason: Might be request timeout, bad internet connection, spammed requests in a small time frame.', 10000);
console.error(e);
setTimeout(() => { novelindex(nurl, maximum, elm) }, 5000);
}
else if (BOTCHECK(b)) {
if (confirm('Caught by bot verification.\nOpen url (' + url + ') in new tab?')) {
window.open(url, '_blank');
setTimeout(() => { novelindex(nurl, maximum, elm) }, 10000);
} else {
alt('Cancelled!');
return;
}
} else {
alt('Error:\nDetails: Unexpected error has occured. Please try again later.\nReason: Might be request timeout, bad internet connection, another unexpected reason.', 10000);
console.error(e);
setTimeout(() => { novelindex(nurl, maximum, elm) }, 5000);
}
}
}
});
}
function getindex(url, elm) {
let link = url || location.href,
PL = SITES[link.LN_host()];
if (l.querySelector('a')) return;
if (document.querySelector(PL.novel)) link = document.querySelector(PL.novel).href;
else if (!elm) {
link = location.href.split('/');
if (link[link.length - 1] == '') {
link.pop();
link.pop();
} else link.pop();
link = link.join('/');
}
if (PL.favobj.link) link += PL.favobj.link;
if (PL.indexobj.page) link += PL.indexobj.page;
novelindex(link, undefined, elm);
}
function migrate(url1, url2) {
alt('Please wait for migration process.');
window.LN_migrating = true;
let index1 = document.createElement('div'),
index2 = document.createElement('div'),
history = HISTORY.LN_search(url1),
distory = DHISTORY.LN_search(url1);
getindex(url1, index1);
getindex(url2, index2);
let interval1 = setInterval(() => {
if (index1.querySelector('a') && index2.querySelector('a')) {
if (history.found) {
let getpos = () => {
for (let i = 0; i < index1.querySelectorAll('a').length; i++) if (index1.querySelectorAll('a')[i].href == history.obj.href) return i;
},
i = getpos();
if (!index2.querySelectorAll('a')[i]) alt('Couldn\'t find chapter ' + index1.querySelectorAll('a')[i].innerText + ' in ' + url2.LN_host() + '.\nPlease register the novel read history manually :(.');
else {
let obj = {
novel: url2,
href: index2.querySelectorAll('a')[i].href,
text: index2.querySelectorAll('a')[i].innerText
};
if (!HISTORY.LN_search(url2).found) HISTORY.push(obj);
else {
HISTORY.LN_search(url2).obj.href = index2.querySelectorAll('a')[i].href;
HISTORY.LN_search(url2).obj.text = index2.querySelectorAll('a')[i].innerText;
}
GM_setValue('HISTORY', HISTORY);
}
}
if (distory.found) {
let getpos = () => {
for (let i = 0; i < index1.querySelectorAll('a').length; i++) if (index1.querySelectorAll('a')[i].href == distory.obj.href) return i;
},
i = getpos();
if (!index2.querySelectorAll('a')[i]) alt('Couldn\'t find chapter ' + index1.querySelectorAll('a')[i].innerText + ' in ' + url2.LN_host() + '.\nPlease register the novel download history manually :(.');
else {
let obj = {
novel: url2,
href: index2.querySelectorAll('a')[i].href,
text: index2.querySelectorAll('a')[i].innerText
};
if (!DHISTORY.LN_search(url2).found) DHISTORY.push(obj);
else {
DHISTORY.LN_search(url2).obj.href = index2.querySelectorAll('a')[i].href;
DHISTORY.LN_search(url2).obj.text = index2.querySelectorAll('a')[i].innerText;
}
GM_setValue('DHISTORY', DHISTORY);
}
}
clearInterval(interval1);
rvals();
window.LN_migrating = false;
setTimeout(() => alt('Migration successful.'), 7000);
}
}, 1000);
}
function search(inp, s) {
if (typeof inp != 'string') inp = undefined;
let input = inp || prompt('Search term'),
binp = ''
for (let s in SITES) {
if (s.includes('novelonlinefull') || s.includes('bestlightnovel')) binp = input.replaceAll(' ', '_');
else binp = input;
if (SITES[s].search.link.includes('${term}')) window.open(SITES[s].search.link.replace('${term}', binp), '_blank');
else GM_setValue('searchterm', binp), window.open(SITES[s].search.link, '_blank');
}
}
function dynamicmode() {
if (autodmode) {
let time = (new Date).getHours();
if (time > dmtime && !darked) ucss();
else if (time > lmtime && time < dmtime && darked) ucss();
}
}
function autobackup() {
if (autobackupp != 0) {
let date = GM_getValue('lastbackup'),
age = (new Date((new Date) - (new Date(date)))).getDate();
if (!date || age >= autobackupp + 1) br(0);
}
}
function timedcheck() {
let time = (new Date).toTimeString().substr(0, 5);
for (let i = 0; i < TIMES.length; i++) {
let tbc = TIMES[i].time;
if (time >= tbc) {
if (CHECKED[0] && (new Date(CHECKED[0])).getDate() - (new Date).getDate() == 0) {
if (!CHECKED.LN_search(TIMES[i].href).found) {
let index = FAVS.LN_deepSearch(TIMES[i].href).parentIndex;
CHECKED.push(TIMES[i].href);
setTimeout(() => {
alt('Auto checking for ' + FAVS[index].novel);
notify(index, 0, true, true);
}, (i - 1) * 15000);
}
}
else {
CHECKED = [JSON.stringify(new Date()).replaceAll('"', '')];
CHECKED.push(TIMES[i].href);
let index = FAVS.LN_deepSearch(TIMES[i].href).parentIndex;
setTimeout(() => {
alt('Auto checking for ' + FAVS[index].novel);
notify(index, 0, true, true);
}, (i - 1) * 15000);
}
GM_setValue('CHECKED', CHECKED);
}
}
}
function feedback() {
let html = document.createElement('div'),
css = document.createElement('style');
html.className = 'LN_container';
html.innerHTML = '<div class="LN_main"> <h3 class="LN_head"> Subject: </h3> <input class="LN_sub"> <br> <h3 class="LN_head"> Message Body: </h3> <span> Please don\'t actually use this to write your message and use an editor then paste here. </span> <br> <textarea class="LN_msg" spellcheck="true" style=" "></textarea> <br> <br> <button class="LN_send"> Send </button> </div>';
css.innerHTML = '.LN_container { z-index: 2147483644; top: 0; left: 0; position: fixed; background-color: #000c; color: #090; width: 100%; height: 100%; display: flex; transition: all 0.5s; user-select: none; } .LN_main { margin: auto; transition: inherit; background-color: #000; height: 70%; padding: 20px; border-radius: 15px; width: 50%; box-shadow: 0 0 20px 20px #090; position: fixed; translate: -50% -50%; top: 50%; left: 50%; } .LN_head { margin: 15px 0 0; } .LN_container input { width: auto; min-width: 100px; margin: 2px; background-color: #111; color: #777; border: none; padding: 2px; outline: 0; margin-left: 15px; font-weight: bolder; outline: 0; transition: inherit; } .LN_container input:focus { box-shadow: 0 0 5px 2px #090; padding: 3px; margin: 5px 1px 5px 10px; outline: 0; border: 1px solid #090; } .LN_container span { margin-left: 15px; } .LN_msg { width: calc(100% - 40px); height: calc(100% - 250px); min-width: 100px; margin: 2px; background-color: #111; color: #777; border: none; padding: 2px; outline: 0; margin-left: 15px; font-weight: bolder; outline: 0; transition: inherit; } .LN_send:hover { color: #090; background-color: #000; } .LN_send { transition: inherit; border: none; color: #000; background-color: #090; outline: 0; font-family: sans-serif; cursor: pointer; padding: 5px 20px; font-weight: bolder; margin-left: 10px; margin-top: 10px; }';
document.body.appendChild(html);
document.head.appendChild(css);
html.onclick = e => {
if (e.target.LN_hasClass('LN_container')) {
html.remove();
css.remove();
}
}
html.querySelector('.LN_send').onclick = () => {
let sub = html.querySelector('.LN_sub'),
msg = html.querySelector('.LN_msg'),
validate = () => {
let output = true;
if (!msg.value) {
msg.setCustomValidity('Message body is required');
msg.reportValidity();
msg.onfocus = () => msg.setCustomValidity('');
alt('Message body is required!');
output = false;
}
return output;
};
if (validate()) {
if (!sub.value) sub.value = 'LN Loader v' + GM_info.script.version + ' feedback';
let link = 'mailto:xdhx86@gmail.com&subject=' + encodeURIComponent(sub.value) + '&body=' + encodeURIComponent(msg.value);
window.open(link, '_blank');
html.remove();
css.remove();
alt('Thanks for the feedback!');
}
}
}
const stopit = () => {
if (window.LN_canceldownload) {
alt('No download found!');
window.LN_canceldownload = false;
if (document.querySelector('.LN_btn')) document.querySelectorAll('.LN_btn')[3].disabled = true;
}
if (window.LN_cancelauto) {
alt('No auto download found!');
window.LN_cancelauto = false;
if (document.querySelector('.LN_btn')) document.querySelectorAll('.LN_btn')[4].disabled = true;
}
}
GM_registerMenuCommand('Show Favorites', favs);
GM_registerMenuCommand('Check for updates', () => {
alt('Please wait while the script searches for new chapters.', 5000);
notify();
});
GM_registerMenuCommand('Search all sources for a novel', search);
GM_registerMenuCommand('Cancel current download', () => {
window.LN_canceldownload = true;
window.LN_cancelauto = false;
alt('Please wait.');
if (document.querySelector('.LN_btn')) document.querySelectorAll('.LN_btn')[3].disabled = true;
setTimeout(stopit, 10000);
});
GM_registerMenuCommand('Cancel auto download', () => {
window.LN_canceldownload = true;
window.LN_cancelauto = true;
alt('Please wait.');
if (document.querySelector('.LN_btn')) document.querySelectorAll('.LN_btn')[4].disabled = true;
setTimeout(stopit, 10000);
});
GM_registerMenuCommand('Feedback', feedback);
let dispatch = (inp, value) => {
inp.focus();
inp.click();
inp.value = value;
inp.dispatchEvent(new KeyboardEvent('keyup', {
bubbles: true,
cancelable: true,
char: 'Q',
key: 'q',
keyCode: 81,
code: 'KeyQ'
}));
},
searchinterval = setInterval(() => {
try {
let searchterm = GM_getValue('searchterm'),
inp = document.querySelector(CS.search.input);
if (!inp || !searchterm) {
clearInterval(searchinterval);
return;
}
dispatch(inp, searchterm);
GM_setValue('searchterm', '');
clearInterval(searchinterval);
} catch { clearInterval(searchinterval); }
}, 1000),
timedcheckint = setInterval(() => {
rvals();
if (TIMES.length == CHECKED.length) clearInterval(timedcheckint);
if (autoch) timedcheck();
else clearInterval(timedcheckint);
}, 60000);
if (!CS || !document.querySelector(CS.chapter)) {
check();
autobackup();
}
else {
ui();
set();
window.onkeyup = handler;
check();
window.onunload = window.onbeforeunload = () => WIN.close();
dynamicmode();
autobackup();
}
for (let i = 1; i < FAVS.length; i++) if (FAVS[i].url.href == '') alert(`Error encountered while checking the novel ${FAVS[i].novel}\nPlease remove it and re-add it again.`);
window.addEventListener('beforeunload', e => {
if (window.LN_checking || window.LN_downloading || window.LN_migrating) {
e.preventDefault();
return 'The script is currently checking, downloading, or migrating content. Are you sure you want to leave the page?';
if (window.LN_checking) alt('The script is currently checking updates, please wait.');
else if (window.LN_downloading) alt('The script is currently downloading content, please wait.');
else if (window.LN_migrating) alt('The script is currently migrating a novel to another source, please wait.');
}
});
})();