// ==UserScript==
// @name FanfictionQomplete
// @description Loads all following chapters on fanfiction.net and strips off bloat.
// @namespace https://greasyfork.org/en/users/11891-qon
// @author Qon
// @include https://www.fanfiction.net/s/*/*
// @include https://www.fictionpress.com/s/*/*
// @include https://www.fimfiction.net/story/*/*
// @include http://www.fimfiction.net/story/*/*
// @compatible firefox
// @compatible chrome
// @noframes
// @grant GM_xmlhttpRequest
// @grant GM_getValue
// @grant GM_setValue
// @run-at document-start
// @license Simple Public License 2.0 (SimPL) https://tldrlegal.com/license/simple-public-license-2.0-%28simpl%29
// @version 0.0.1.20151106143113
// ==/UserScript==
/*
TODO
!checkForBadJavascripts! (remove addJS_Node ?)
Add support for other sites
WIP https://www.fictionpress.com
WIP fimfiction.com
Change width by dragging the border? and position?
Add copies of all links at the end of a fanfic.
Save settings?
-- In location bar! && in GM storage!
*/
// javascript:var script=document.createElement("script");var t=new Date(Date());script.src="https://greasyfork.org/en/scripts/10182-fanfictionqomplete/code/fanfictionqomplete.js?"+t.getFullYear()+t.getMonth()+t.getDate();document.body.appendChild(script);window.setTimeout(function(){document.runFFQomplete();},500);
// Source for checkForBadJavascripts: https://gist.github.com/BrockA/2620135
/*--- checkForBadJavascripts()
This is a utility function, meant to be used inside a Greasemonkey script that
has the "@run-at document-start" directive set.
It Checks for and deletes or replaces specific <script> tags.
*/
function checkForBadJavascripts(controlArray) {
/*--- Note that this is a self-initializing function. The controlArray
parameter is only active for the FIRST call. After that, it is an
event listener.
The control array row is defines like so:
[bSearchSrcAttr, identifyingRegex, callbackFunction]
Where:
bSearchSrcAttr True to search the SRC attribute of a script tag
false to search the TEXT content of a script tag.
identifyingRegex A valid regular expression that should be unique
to that particular script tag.
callbackFunction An optional function to execute when the script is
found. Use null if not needed.
Usage example:
checkForBadJavascripts ( [
[false, /old, evil init()/, function () {addJS_Node (init);} ],
[true, /evilExternalJS/i, null ]
] );
*/
if (!controlArray.length) return null;
checkForBadJavascripts = function(zEvent) {
for (var J = controlArray.length - 1; J >= 0; --J) {
var bSearchSrcAttr = controlArray[J][0];
var identifyingRegex = controlArray[J][1];
if (bSearchSrcAttr) {
if (identifyingRegex.test(zEvent.target.src)) {
stopBadJavascript(J);
return false;
}
} else {
if (identifyingRegex.test(zEvent.target.textContent)) {
stopBadJavascript(J);
return false;
}
}
}
function stopBadJavascript(controlIndex) {
zEvent.stopPropagation();
zEvent.preventDefault();
var callbackFunction = controlArray[J][2];
if (typeof callbackFunction == "function")
callbackFunction(zEvent.target);
//--- Remove the node just to clear clutter from Firebug inspection.
zEvent.target.parentNode.removeChild(zEvent.target);
//--- Script is intercepted, remove it from the list.
controlArray.splice(J, 1);
if (!controlArray.length) {
//--- All done, remove the listener.
window.removeEventListener(
'beforescriptexecute', checkForBadJavascripts, true
);
}
}
}
/*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
Note seems to work on scripts that are dynamically created, despite what
the spec says.
*/
window.addEventListener('beforescriptexecute', checkForBadJavascripts, true);
return checkForBadJavascripts;
}
function addJS_Node(text, s_URL, funcToRun) {
var D = document;
var scriptNode = D.createElement('script');
scriptNode.type = "text/javascript";
if (text) scriptNode.textContent = text;
if (s_URL) scriptNode.src = s_URL;
if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
var targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement;
//--- Don't error check here. if DOM not available, should throw error.
targ.appendChild(scriptNode);
}
if (Element.prototype.remove == undefined) {
Element.prototype.remove = function() {
this.parentNode.removeChild(this)
}
}
function injectQompleteButton() {
var lc = document.getElementsByClassName('lc')
if (lc.length) {
lc = lc[0]
var btn = document.createElement('button')
// btn.setAttribute('onclick', 'runFFQomplete();')
btn.addEventListener('click', runFFQomplete)
btn.setAttribute('class', 'btn')
btn.setAttribute('style', 'margin-left:12px;margin-right:2px;')
btn.setAttribute('title', 'Append all following chapters and remove unecessary bloat.')
btn.innerHTML = 'Qomplete!'
lc.appendChild(btn)}}
window.addEventListener('load', injectQompleteButton)
// document.styleSheets[0].cssText = "";
var hash_settings = document.location.hash.slice(1).split('&')
for(var i=0; i<hash_settings.length; ++i){
var kv = hash_settings[i].split('=')
if(kv[0]==='Qomplete') {
checkForBadJavascripts ([
[true, /static\.fimfiction\.net\/js\/scripts\.js\?uupAcQPf/, null],
[false, /\$\(window\)\.scroll\( function\(e\)/ /*)*/, null]
])
window.addEventListener('load', runFFQomplete)}}
var live = true
function readSettings() {
var settings = {Qomplete:1}
if(live){
settings.center = GM_getValue('center',0)
settings.bgcol = GM_getValue('bgcol',0)
settings.edge = GM_getValue('edge',0)
settings.width = GM_getValue('width',0)
}
var hash_settings = document.location.hash.slice(1).split('&')
for(var i=0; i<hash_settings.length; ++i){
var kv = hash_settings[i].split('=')
switch(kv[0]){
case 'center':
settings.center = parseInt(kv[1])
break;
case 'bgcol':
settings.bgcol = parseInt(kv[1])
break;
case 'edge':
settings.edge = parseInt(kv[1])
break;
case 'width':
settings.width = parseInt(kv[1])
break;
}
}
return settings }
function saveSettings(o) {
var q = []
for(var k in o) {
if(o.hasOwnProperty(k)) {
if(live) {GM_setValue(k,o[k])}
q.push(k+'='+o[k])}}
window.location = '#'+q.join('&')}
var settings = readSettings()
console.log(settings)
function runFFQomplete() {
saveSettings(settings)
window.onload = function() {
var a = document.getElementsByClassName('skiptranslate')
for (; a.length;) {
a[0].remove()}
document.body.removeAttribute('style')}
var re = /(^.*?(?:fan|fim)?fiction(?:press\.com|\.net)\/s(?:tory)?\/\d+\/)(\d+)(\/?[^#]*)/
function urlGetChap(url) {
var arr = re.exec(url)
return arr[2]}
function urlSetChap(url, n) {
var arr = re.exec(url)
return arr[1] + n + ((/fimfiction\.net/.exec(document.location)==null) ? arr[3] : arr[3].replace(/\/[^\/]*$/, ""))}
function inc(url) {
var arr = re.exec(url)
return arr[1] + (parseInt(arr[2]) + 1) + ((/fimfiction\.net/.exec(document.location)==null) ? arr[3] : arr[3].replace(/\/[^\/]*$/, ""))}
function chapFromPage(url, page) {
var storytext = page.getElementById('storytext') // fanfiction.net
if(!storytext) {
// fimfiction.net
if(latestChap==1) {
try {
latestChap = page.getElementById('chapter_format').getElementsByClassName('title')[0].getElementsByTagName('ul')[0].getElementsByTagName('li').length
} catch (e) {}}
storytext = page.getElementsByClassName('chapter_content')[0]
storytext.style = ''
var styles = storytext.getElementsByTagName('style')
for(style of styles) {
style.remove()}}
if (storytext) {
// var ps = storytext.getElementsByTagName('p'), d = 0
// for (q of ps) {
// q.style.color = 'hsl(' + d + ' ,20%, 80%)'
// // q.innerHTML = q.innerHTML.replace(/([\.,?!])/g, '<span style="color:hsl(' + d + ' ,100%, 50%);">$1</span>')
// d = (d + 1 / ps.length * 360) % 360}
var wrap = page.createElement('div')
wrap.setAttribute('class', 'wrap col')
wrap.setAttribute('id', urlGetChap(url))
var pad = page.createElement('div')
pad.setAttribute('class', 'pad')
var chapdiv = page.createElement('div')
chapdiv.setAttribute('class', 'chapter')
var chapspan = page.createElement('span')
chapspan.innerHTML = urlGetChap(url) + '. '
var title = page.getElementsByTagName('title')[0]
var chaptitle = page.createElement('a')
chaptitle.setAttribute('href', url.replace(/#.*$/, ""))
chaptitle.setAttribute('class', 'external')
if(/fimfiction\.net/.exec(document.location)==null) {
var cut = /(.*(| [Cc]hapter [^:]+: .*)), a .* fanfic \| FanFiction/
var newChapTitle = cut.exec(title.innerHTML)}
else {
var cut = /(.*) - FIMFiction.net/
var newChapTitle = cut.exec(title.innerHTML)}
chaptitle.innerHTML = newChapTitle ? newChapTitle[1] : title.innerHTML
chapdiv.appendChild(chapspan)
chapdiv.appendChild(chaptitle)
chapdiv.appendChild(document.createElement('hr'))
chapdiv.appendChild(storytext)
pad.appendChild(chapdiv)
wrap.appendChild(pad)
return wrap
}
else return null}
document.body.setAttribute('style', '')
var title = document.getElementsByTagName('title')[0]
var profile_top = document.getElementById('profile_top')
if(!profile_top) profile_top = document.getElementsByClassName('inner')[0] // fimfiction, TODO make better
var statusCompleteRE = /Status: Complete/
var statusComplete = statusCompleteRE.exec(profile_top.innerHTML) ? true : false
var chap_select = document.getElementById('chap_select')
var latestChap = chap_select ? chap_select.children.length : 1
var activeChap = parseInt(urlGetChap(document.location))
var appendedNow = 1
var notAppendedYet = 0
var chapArr = []
var aaa=0
var bbb=1
if(/fimfiction\.net/.exec(document.location)==null) {aaa=2; bbb=5;}
// else {aaa=0; bbb=1;}
var favicon = [].slice.call(document.head.getElementsByTagName('link')).slice(aaa,bbb)
var chap = chapFromPage(document.location.href, document)
var ptbuttons = profile_top.getElementsByTagName('button')
if (ptbuttons.length) {ptbuttons[0].remove()}
for (; document.head.firstElementChild;) document.head.firstElementChild.remove();
for (; document.body.firstElementChild;) document.body.firstElementChild.remove();
document.body.removeAttribute('style')
var style = document.createElement('style')
style.setAttribute('type', 'text/css')
/*
def gencss():
css = ''
wps = 5
wpe = 1
pps = 50
ppe = 5
mwe = 650
wpr = wpl = wpe
ppr = ppl = ppe
mw = mwe
while ppl+ppr<=pps*2 or wpl+wpr<=wps*2:
if ppl+ppr<=pps*2:
cs = r'@media (max-width: '+str( mw) +r'px) {div.pad{padding-left:'+str(ppl)+r'px;padding-right:'+str(ppr)+'px;}}\\n\\\n' #'
if ppl<=ppr:
ppl += 1
else:
ppr += 1
elif wpl+wpr<=wps*2:
cs = r'@media (max-width: '+str( mw) +r'px) {div.wrap{padding-left:'+str(wpl)+r'px;padding-right:'+str(wpr)+'px;}}\\n\\\n' #'
if wpl<=wpr:
wpl += 1
else:
wpr += 1
css = cs+css
mw = mw+1
return css
*/
console.log(activeChap)
style.innerHTML =
'body{background-color:#000;color:#ccc;margin:0;padding:0;font-family:"Verdana";}\n\
ul.tags > li{display:inline;}\n\
ul.tags > li::before{content:" #"}\n\
#loading{position:inherit;width:100%;height:5px;}\n\
button, select{border-radius:4px;padding:4px 12px;background: linear-gradient(to bottom, #333, #000);border-width: 1px;color:#ccc;background-color:#000;}\n\
button:hover{background-image:none;}\n\
.panel{text-align:center;}\n\
a.external, option.external{background: transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZ\
G9iZSBJbWFnZVJlYWR5ccllPAAAAFZJREFUeF59z4EJADEIQ1F36k7u5E7ZKXeUQPACJ3wK7UNokVxVk9kHnQH7bY9hbDyDhNXgjpRLqFlo4M2GgfyJHhjq8V4agfrgPQX3JtJQGbofmCHgA/nAKks+JAjFA\
AAAAElFTkSuQmCC") no-repeat scroll right center;padding-right: 13px;}\n\
div.wrap{max-width:1300px;margin:auto;padding:0px 5px 0px 5px;}\n\
div.wrap:nth-of-type(2){padding-top:5px;margin-top:50px;}\n\
div.wrap:last-child{padding-bottom:5px;margin-bottom:50px;}\n\
div.pad{background-color:#222;padding:50px;}\n\
.chapter{}#profile_top{}img{float:left;}canvas{float:left;}\n\
a:link{color:#555;}a:visited{color:#555;}a:hover{color:#aaa;}a:active{color:#aaa;}\n\
option{}\n\
option:nth-of-type(6n+1)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #f00, #ff0);}\n\
option:nth-of-type(6n+2)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #ff0, #0f0);}\n\
option:nth-of-type(6n+3)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #0f0, #0ff);}\n\
option:nth-of-type(6n+4)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #0ff, #00f);}\n\
option:nth-of-type(6n+5)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #00f, #f0f);}\n\
option:nth-of-type(6n+0)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #f0f, #f00);}\n\
.col:nth-of-type(6n+'+ ((4- activeChap+60000)%6) +'){background-color:#f00;background:linear-gradient(to bottom, #f00, #ff0);}\n\
.col:nth-of-type(6n+'+ ((5- activeChap+60000)%6) +'){background-color:#ff0;background:linear-gradient(to bottom, #ff0, #0f0);}\n\
.col:nth-of-type(6n+'+ ((6- activeChap+60000)%6) +'){background-color:#0f0;background:linear-gradient(to bottom, #0f0, #0ff);}\n\
.col:nth-of-type(6n+'+ ((7- activeChap+60000)%6) +'){background-color:#0ff;background:linear-gradient(to bottom, #0ff, #00f);}\n\
.col:nth-of-type(6n+'+ ((8- activeChap+60000)%6) +'){background-color:#00f;background:linear-gradient(to bottom, #00f, #f0f);}\n\
.col:nth-of-type(6n+'+ ((9- activeChap+60000)%6) +'){background-color:#f0f;background:linear-gradient(to bottom, #f0f, #f00);}\n'
+'@media (max-width: 700px) {div.wrap{padding-left:1px;padding-right:1px;} div.pad{padding-left:5px;padding-right:5px;}}\n'
switch ((urlGetChap(document.location.href) % 6)) {
case 0:
style.innerHTML += '.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #f0f);}\n'
break;
case 1:
style.innerHTML += '.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #f00);}\n'
break;
case 2:
style.innerHTML += '.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #ff0);}\n'
break;
case 3:
style.innerHTML += '.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #0f0);}\n'
break;
case 4:
style.innerHTML += '.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #0ff);}\n'
break;
case 5:
style.innerHTML += '.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #00f);}\n'
break;
}
document.head.appendChild(style)
if(/fimfiction\.net/.exec(document.location)==null) {
var settitle = /(.*)[Cc]hapter .*, a (.*) fanfic \| FanFiction/
var storyname = settitle.exec(title.innerHTML)
if(!storyname) {
var settitle = /(.*), a (.*) fanfic \| FanFiction/
var storyname = settitle.exec(title.innerHTML) }
if(storyname) {
title.innerHTML = storyname[1]+' - '+storyname[2]+(statusComplete && activeChap==1 ? ' - Qomplete' : (' - chapter '+activeChap+(latestChap!=activeChap?'-'+latestChap:'')))}
} else {
var settitle = /.* - (.*) - FIMFiction.net/
var storyname = settitle.exec(title.innerHTML)
// console.log(storyname)
if(storyname) {
title.innerHTML = storyname[1] + (' - chapter '+activeChap+(latestChap!=activeChap?'-'+latestChap:'')) } }
document.head.appendChild(title)
for (i=0;i<favicon.length;i+=1) {document.head.appendChild(favicon[i])}
var ficpic = profile_top.getElementsByTagName('img')[0]
if(ficpic){
// http://pastebin.ca/1425789
function mime_from_data(data) // Simple function that checks for JPG, GIF and PNG from image data. Otherwise returns false.
{
if('GIF'==data.substr(0,3))return 'image/gif';
else if('PNG'==data.substr(1,3))return 'image/png';
else if('JFIF'==data.substr(6,4))return 'image/jpg';
return false;
};
function data_string(data) // Generates the binary data string from character / multibyte data
{
var data_string='';
for(var i=0,il=data.length;i<il;i++)data_string+=String.fromCharCode(data[i].charCodeAt(0)&0xff);
return data_string;
};
function load_image(tries) {
GM_xmlhttpRequest({
method: 'GET',
url: ficpic.src,
overrideMimeType: 'text/plain; charset=x-user-defined',
onload: function(xhr) {
var data = data_string(xhr.responseText);
if(mime_from_data(data)) {
var base64_data = btoa(data); // Encode to base64 string
var data_url = 'data:' + mime_from_data(data) + ';base64,' + base64_data; // Make the data url
ficpic.src = data_url
} else if (tries>0) {
load_image(tries-1)
}
}
});
return;
};
load_image(5)
}
// window.addEventListener('load_image',load_image,false); // Make sure image has properly loaded
var loadwrap = document.createElement('div')
loadwrap.setAttribute('class', 'wrap')
loadwrap.style.position = 'sticky'
loadwrap.style.top = '0px'
var loading = document.createElement('div')
loading.style.float = 'left'
loading.setAttribute('id', 'loading')
// loading.style.margin = '0px'
// loading.innerHTML = String.fromCharCode(160)
loadwrap.appendChild(loading)
document.body.appendChild(loadwrap)
function updateLoading(ignore, appended, downloaded, total) {
// var loading = document.getElementById('loading')
var p0 = parseInt(ignore / total * 100)
var p1 = parseInt((appended + ignore) / total * 100)
if (p1 == 100) {
setTimeout(function() {
loading.style.display = 'none'
}, (activeChap != latestChap) * 100)
}
var p2 = parseInt((downloaded + appended + ignore) / total * 100)
loading.style.background = 'linear-gradient(to right' +
', white 0%' +
', #555 ' + p0/2 +
'%, white ' + p0 +
'%, lime ' + p0 +
'%, lime ' + p1 +
'%, blue ' + p1 +
'%, blue ' + p2 +
'%, white ' + p2 +
'%, white 100%)'
}
updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)
var profile = document.createElement('div')
profile.setAttribute('class', 'wrap profile')
profile.setAttribute('id', 'profile')
var pad = document.createElement('div')
pad.setAttribute('class', 'pad')
pad.appendChild(profile_top)
profile.appendChild(pad)
document.body.appendChild(profile)
var panel = document.createElement('div')
panel.setAttribute('class', 'panel')
{
var posbtn = document.createElement('button')
posbtn.setAttribute('id', 'posbtn')
function centerclick() {
var settings = readSettings()
var e = document.getElementById('position-style')
if (e) {
if (e.innerHTML == 'div.wrap{margin-left:0px;}') {
settings.center = 2
e.innerHTML = 'div.wrap{margin-right:0px;}'
document.getElementById('posbtn').innerHTML = 'Right'}
else {
settings.center = 0
e.remove()
document.getElementById('posbtn').innerHTML = 'Centered'}}
else {
settings.center = 1
var s = document.createElement('style')
s.setAttribute('id', 'position-style')
s.innerHTML = 'div.wrap{margin-left:0px;}'
document.head.appendChild(s)
document.getElementById('posbtn').innerHTML = 'Left'}
saveSettings(settings)}
posbtn.setAttribute('onclick', 'centerclick()')
posbtn.setAttribute('class', 'center;')
posbtn.innerHTML = 'Centered'
var bgcolbtn = document.createElement('button')
bgcolbtn.setAttribute('id', 'bgcolbtn')
function bgcolclick() {
var settings = readSettings()
var e = document.getElementById('bgcol-style')
if (e) {
if (e.innerHTML == 'body{color:#000;}a:hover{color:#000;}div.pad{background-color:#fff;}') {
settings.bgcol = 2
e.innerHTML = 'body{color:#fff;}div.pad{background-color:#000;}'
document.getElementById('bgcolbtn').innerHTML = 'Background: Black'}
else {
settings.bgcol = 0
e.remove()
document.getElementById('bgcolbtn').innerHTML = 'Background: Dark'}}
else {
settings.bgcol = 1
var s = document.createElement('style')
s.setAttribute('id', 'bgcol-style')
s.innerHTML = 'body{color:#000;}a:hover{color:#000;}div.pad{background-color:#fff;}'
document.head.appendChild(s)
document.getElementById('bgcolbtn').innerHTML = 'Background: White'}
saveSettings(settings)}
bgcolbtn.setAttribute('onclick', 'bgcolclick()')
bgcolbtn.setAttribute('style', 'float:left;')
bgcolbtn.innerHTML = 'Background: Dark'
var edgebtn = document.createElement('button')
edgebtn.setAttribute('id', 'edgebtn')
function edgeclick() {
var settings = readSettings()
var e = document.getElementById('edge-style')
if (e) {
settings.edge = 0
e.remove()
document.getElementById('edgebtn').innerHTML = 'Edge: Rainbow'}
else {
settings.edge = 1
var s = document.createElement('style')
s.setAttribute('id', 'edge-style')
s.innerHTML = '.col:nth-of-type(n){background-color:#333;background:#333;}.profile{background-color:#333;background:linear-gradient(to bottom, #fff, #333);}'
document.head.appendChild(s)
document.getElementById('edgebtn').innerHTML = 'Edge: Gray'}
saveSettings(settings)}
edgebtn.setAttribute('onclick', 'edgeclick()')
edgebtn.setAttribute('style', 'float:left;')
edgebtn.innerHTML = 'Edge: Rainbow'
var widthbtn = document.createElement('button')
widthbtn.setAttribute('id', 'widthbtn')
function widthclick() {
var settings = readSettings()
var e = document.getElementById('width-style')
if (e) {
if (e.innerHTML == 'div.wrap{max-width:777px;}') {
settings.width = 2
e.innerHTML = 'div.wrap{max-width:100%;}'
document.getElementById('widthbtn').innerHTML = 'Width: Wide'}
else {
settings.width = 0
e.remove()
document.getElementById('widthbtn').innerHTML = 'Width: Default'}}
else {
settings.width = 1
var s = document.createElement('style')
s.setAttribute('id', 'width-style')
s.innerHTML = 'div.wrap{max-width:777px;}'
document.head.appendChild(s)
document.getElementById('widthbtn').innerHTML = 'Width: Narrow'}
saveSettings(settings)}
widthbtn.setAttribute('onclick', 'widthclick()')
widthbtn.setAttribute('class', 'center')
widthbtn.innerHTML = 'Width: Default'
function setSettings(){
var live = false
if (Element.prototype.remove == undefined) {
Element.prototype.remove = function() {
this.parentNode.removeChild(this)}}
var e,settings = readSettings()
if((e=document.getElementById('position-style'))&&settings.center!=null) {e.remove(); document.getElementById('posbtn').innerHTML='Centered'}
if((e=document.getElementById('bgcol-style'))&&settings.bgcol!=null) {e.remove(); document.getElementById('bgcolbtn').innerHTML='Background: Dark'}
if((e=document.getElementById('edge-style'))&&settings.edge!=null) {e.remove(); document.getElementById('edgebtn').innerHTML='Edge: Rainbow'}
if((e=document.getElementById('width-style'))&&settings.width!=null) {e.remove(); document.getElementById('widthbtn').innerHTML='Width: Default'}
for(var i=0; i<settings.center; ++i) {setTimeout(centerclick,0)}
for(var i=0; i<settings.bgcol; ++i) {setTimeout(bgcolclick,0)}
for(var i=0; i<settings.edge; ++i) {setTimeout(edgeclick,0)}
for(var i=0; i<settings.width; ++i) {setTimeout(widthclick,0)}}
var scripttag = document.createElement('script')
scripttag.setAttribute('id','button-helper')
scripttag.innerHTML = [
,setSettings.toString().slice(23,-1)
,readSettings.toString()
,saveSettings.toString()
,centerclick.toString()
,bgcolclick.toString()
,edgeclick.toString()
,widthclick.toString()
].join('\n')
posbtn.addEventListener('click',_=>{saveSettings(readSettings())})
widthbtn.addEventListener('click',_=>{saveSettings(readSettings())})
bgcolbtn.addEventListener('click',_=>{saveSettings(readSettings())})
edgebtn.addEventListener('click',_=>{saveSettings(readSettings())})
panel.appendChild(posbtn)
panel.appendChild(widthbtn)
panel.appendChild(bgcolbtn)
panel.appendChild(edgebtn)
document.body.appendChild(scripttag)
}
if (chap_select) {
chap_select.setAttribute('onchange', 'if(this.options[this.selectedIndex].value < ' + urlGetChap(document.location.href) + '){' +
chap_select.getAttribute('onchange') +
'}' + ' else {document.getElementById(\'\'+this.options[this.selectedIndex].value).scrollIntoView();}')
chap_select.setAttribute('style', 'float:right;')
var os = chap_select.getElementsByTagName('option')
for (i = 0; i < urlGetChap(document.location) - 1; i += 1) {os[i].setAttribute('class', 'external')}
panel.appendChild(chap_select)}
document.getElementById('profile').firstChild.appendChild(panel)
// document.body.insertBefore(panel, document.body.firstChild)
document.body.appendChild(chap)
function loadQomplete() {
var a = document.getElementsByClassName('skiptranslate')
for (; a.length;) {a[0].remove()}
a = document.getElementsByClassName('ad_container')
for (; a.length;) {a[0].remove()}
element.next = function() {
if(this.firstChild) return this.firstChild
if(this.nextSibling) return this.nextSibling
return this.parentNode.nextSibling}
// window.parentNode.replaceChild(window.cloneNode(false), window)
// var el = document
// while(el) {
// e.parentNode.replaceChild(el.cloneNode(false), el)
// el = el.next()}
// document.head.parentNode.replaceChild(document.head.cloneNode(true), document.head)
// document.body.parentNode.replaceChild(document.body.cloneNode(true), document.body)
// var el = document.body
// elClone = el.cloneNode(true);
// el.parentNode.replaceChild(elClone, el);
document.body.removeAttribute('style')
updateLoading(activeChap - 1, latestChap - (activeChap - 1), 0, latestChap)}
function appendChapterFromURL(url) {
var oReq = new XMLHttpRequest();
oReq.onload = function() {
var xmlDoc = new DOMParser().parseFromString(this.responseText, "text/html")
var url = this.responseURL ? this.responseURL : this.responseURLfallback
var chap = chapFromPage(url, xmlDoc)
if (chap) {
document.body.appendChild(chap)
appendedNow += 1
updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)
window.setTimeout(function() {
appendChapterFromURL(inc(url))
}, 0)}
else loadQomplete()}
oReq.responseURLfallback = url
oReq.open("get", url, true)
oReq.send()}
function appendChapterFromURL2(url) {
var oReq = new XMLHttpRequest();
oReq.onload = function() {
// setTimeout(function(this2){
var this2 = this // for debug purposes, use comment above as code to simulate delays (and its matching closing part)
var xmlDoc = new DOMParser().parseFromString(this2.responseText, "text/html")
var url = this2.responseURL ? this2.responseURL : this2.responseURLfallback
var chap = chapFromPage(url, xmlDoc)
if (chap) {
chapArr[parseInt(urlGetChap(url))] = chap
notAppendedYet += 1
updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)
if (appendedNow + notAppendedYet + activeChap - 1 == latestChap) {
// consolog("all downloaded", performance.now())
}
}
// }, Math.random()*8000+50*urlGetChap(this.responseURL), this)
}
oReq.responseURLfallback = url
oReq.open("get", url, true)
oReq.send()}
function appendNextChap(n) {
if (chapArr[n]) {
document.body.appendChild(chapArr[n])
appendedNow += 1
notAppendedYet -= 1
updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)
if (n < latestChap) {
appendNextChap(n + 1)}
else {
loadQomplete() }}
else {
window.setTimeout(function() {
appendNextChap(n)
}, 50) }}
if (true) {
// Asynchronous chapter load. Very fast for big fanfics.
for (i = activeChap; i < latestChap; i += 1)
appendChapterFromURL2(urlSetChap(document.location.href, i + 1));
if (activeChap == latestChap) {
loadQomplete()}
else {
appendNextChap(activeChap + 1)}}
else {
// Synchronous chapter load. Slow for big fanfics but doesn't hit the server as hard.
appendChapterFromURL(inc(document.location.href))}
}