您需要先安装一个扩展,例如 篡改猴、Greasemonkey 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 暴力猴,之后才能安装此脚本。
您需要先安装一个扩展,例如 篡改猴 或 Userscripts ,之后才能安装此脚本。
您需要先安装一款用户脚本管理器扩展,例如 Tampermonkey,才能安装此脚本。
您需要先安装用户脚本管理器扩展后才能安装此脚本。
Greasy Fork is available in English.
Loads all following chapters on fanfiction.net and strips off bloat.
当前为
// ==UserScript== // @name FanfictionQomplete // @description Loads all following chapters on fanfiction.net and strips off bloat. // @namespace http://greasyfork.icu/en/users/11891-qon // @author Qon // @include https://www.fanfiction.net/s/*/* // @include https://www.fictionpress.com/s/*/* // @compatible firefox // @compatible chrome // @noframes // @grant GM_xmlhttpRequest // @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.20150923175850 // ==/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? -- Loading bar modified slightly. -- Super narrow mode for small devices like phones. Thinner rainbow edge and less padding. -- Convert images to data url. Add copies of all links at the end of a fanfic. Automatically qomplete fanfics? -- if the url ends with #Qomplete Save settings? In location bar! -- :nth-of-type instead of col1..6 -- Chapters qolor qoded in the chapter selector. */ // javascript:var script=document.createElement("script");var t=new Date(Date());script.src="http://greasyfork.icu/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 injectRunButton() { 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', injectRunButton) // injectRunButton() // document.styleSheets[0].cssText = ""; function runFFQomplete() { document.location = '#Qomplete' 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') // var 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') posbtn.setAttribute('onclick', '('/*)*/+ (function() { var e = document.getElementById('position-style') if (e) { if (e.innerHTML == 'div.wrap{margin-left:0px;}') { e.innerHTML = 'div.wrap{margin-right:0px;}' document.getElementById('posbtn').innerHTML = 'Right' } else { e.remove() document.getElementById('posbtn').innerHTML = 'Centered' } } else { 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' } }).toString()+')()'/*(*/) posbtn.setAttribute('class', 'center;') posbtn.innerHTML = 'Centered' var bgcolbtn = document.createElement('button') bgcolbtn.setAttribute('id', 'bgcolbtn') bgcolbtn.setAttribute('onclick', '('/*)*/+ (function() { var e = document.getElementById('bgcol-style') if (e) { if (e.innerHTML == 'body{color:#000;}a:hover{color:#000;}div.pad{background-color:#fff;}') { e.innerHTML = 'body{color:#fff;}div.pad{background-color:#000;}' document.getElementById('bgcolbtn').innerHTML = 'Background: Black' } else { e.remove() document.getElementById('bgcolbtn').innerHTML = 'Background: Dark' } } else { 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' } }).toString()+')()'/*(*/) bgcolbtn.setAttribute('style', 'float:left;') bgcolbtn.innerHTML = 'Background: Dark' var edgebtn = document.createElement('button') edgebtn.setAttribute('id', 'edgebtn') edgebtn.setAttribute('onclick', '('/*)*/+ (function() { var e = document.getElementById('edge-style') if (e) { e.remove() document.getElementById('edgebtn').innerHTML = 'Edge: Rainbow' } else { 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' } }).toString()+')()'/*(*/) edgebtn.setAttribute('style', 'float:left;') edgebtn.innerHTML = 'Edge: Rainbow' var widthbtn = document.createElement('button') widthbtn.setAttribute('id', 'widthbtn') widthbtn.setAttribute('onclick', '('/*)*/+ (function() { var e = document.getElementById('width-style') if (e) { if (e.innerHTML == 'div.wrap{max-width:777px;}') { e.innerHTML = 'div.wrap{max-width:100%;}' document.getElementById('widthbtn').innerHTML = 'Width: Wide' } else { e.remove() document.getElementById('widthbtn').innerHTML = 'Width: Default' } } else { 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' } }).toString()+')()'/*(*/) widthbtn.setAttribute('class', 'center') widthbtn.innerHTML = 'Width: Default' panel.appendChild(posbtn) panel.appendChild(widthbtn) panel.appendChild(bgcolbtn) panel.appendChild(edgebtn) } 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) // consolog("qomplete", performance.now()) } 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)) } } if(/\#Qomplete/.exec(document.location)[1]!=''){ checkForBadJavascripts ( [ [true, /static\.fimfiction\.net\/js\/scripts\.js\?uupAcQPf/, null], [false, /\$\(window\)\.scroll\( function\(e\)/ /*)*/, null] ] ) window.addEventListener('load', runFFQomplete) }