QonQon

FanfictionReviewQompleteAlpha

Dec 8th, 2015
111
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
  1. // ==UserScript==
  2. // @name          FanfictionQomplete
  3. // @description   Proper multi-chapter reading mode with less clutter.
  4. // @namespace     https://greasyfork.org/en/users/11891-qon
  5. // @author        Qon
  6. // @include       https://www.fanfiction.net/s/*/*
  7. // @include       https://www.fanfiction.net/r/*/*
  8. // @include       https://www.fictionpress.com/s/*/*
  9. // @include       https://www.fimfiction.net/story/*/*
  10. // @include       http://www.fimfiction.net/story/*/*
  11. // @compatible    firefox
  12. // @compatible    chrome
  13. // @noframes
  14. // @grant         GM_xmlhttpRequest
  15. // @grant         GM_getValue
  16. // @grant         GM_setValue
  17. // @run-at        document-start
  18. // @license       Simple Public License 2.0 (SimPL) https://tldrlegal.com/license/simple-public-license-2.0-%28simpl%29
  19. // ==/UserScript==
  20.  
  21. // 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);
  22.  
  23. /*
  24. TODO
  25.   --Font size
  26.   Minimize Qontrol panel or something to reduce button amount
  27.   Add support for other sites
  28.     fimfiction.com chapter selector
  29.   Change width by dragging the border? and position?
  30.   Add copies of all links at the end of a fanfic.
  31.   Save scroll, because the browser built in one doesn't work between browser restarts
  32. */
  33.  
  34. var live = true
  35. polyfill()
  36. var siteMatch = /^.*?www\.(.*?)\..*?\//.exec(document.location.href)[1]
  37.  
  38. var hash_settings = document.location.hash.slice(1).split('&')
  39. if(hash_settings.map(c=>c.split('=')[0]).includes('Qomplete')) {
  40.   checkForBadJavascripts ([
  41.     [true, /static\.fimfiction\.net\/js\/scripts\.js\?uupAcQPf/, null]
  42.    ,[false, /\$\(window\)\.scroll\( function\(e\)/ /*)*/, null]])
  43.   window.addEventListener('load', runFFQomplete)}
  44. else {window.addEventListener('load', injectQompleteButton)}
  45.  
  46. function injectQompleteButton() {
  47.   switch(siteMatch){
  48.   case 'fimfiction':
  49.     var lc = document.getElementById('chapter_title')
  50.     lc = lc&&lc.parentNode
  51.     lc = lc&&lc.getElementsByClassName('button-group')[0]
  52.     var btn = document.createElement('a')
  53.     btn.addEventListener('click', runFFQomplete)
  54.     btn.setAttribute('class', 'styled_button styled_button_grey button-icon-only')
  55.     btn.setAttribute('title', 'Append all following chapters and remove unecessary bloat.')
  56.     btn.innerHTML = 'Qomplete!'
  57.     lc.insertBefore(btn,lc.lastChild.previousSibling)
  58.     break;
  59.   case 'fanfiction':
  60.   case 'fictionpress':
  61.     var lc = document.getElementsByClassName('lc')[0] || document.getElementById('gui_table1i').firstElementChild.firstElementChild.firstElementChild
  62.     var btn = document.createElement('button')
  63.     // btn.setAttribute('onclick', 'runFFQomplete();')
  64.     btn.addEventListener('click', runFFQomplete)
  65.     btn.setAttribute('class', 'btn')
  66.     btn.setAttribute('style', 'margin-left:12px;margin-right:2px;')
  67.     btn.setAttribute('title', 'Append all following chapters and remove unecessary bloat.')
  68.     btn.innerHTML = 'Qomplete!'
  69.     lc.appendChild(btn)
  70.     break;
  71.   }}
  72. function runFFQomplete() {
  73.   // Add the loaded settings to url bar so that they don't get changed for this tab if the settings are changed elsewhere.
  74.   var settings = readSettings()
  75.   saveSettings(settings)
  76.  
  77.   var re = getChapRE()
  78.   // TODO remove vvv?
  79.   window.addEventListener('load', ()=>{
  80.     var a = document.getElementsByClassName('skiptranslate')
  81.     for (; a.length; a[0].remove());
  82.     document.body.removeAttribute('style')})
  83.  
  84.   // Initialisations
  85.   var appendedNow = 1
  86.   var notAppendedYet = 0
  87.   var chapArr = []
  88.   var activeChap = parseInt(urlGetChap(document.location.href))
  89.   // Grab the elements we want
  90.   var title = document.getElementsByTagName('title')[0]
  91.   var profile_top = getProfileTop()
  92.   var ficpic = profile_top&&profile_top.getElementsByTagName('img')[0]
  93.   var statusComplete = !!(/Status: Complete/.exec(profile_top&&profile_top.innerHTML))
  94.   var chap_select = document.getElementById('chap_select')
  95.   var latestChap = getLatestChap()
  96.   var favicons = getFavicons()
  97.   var chap = chapFromPage(document.location.href, document) // Get the chapter that is already loaded
  98.  
  99.   clean()
  100.   // Now, add items. The ones we kept and modified and new ones.
  101.   addStyle()
  102.   title.innerHTML = fixTitleText(title.innerHTML)
  103.   document.head.appendChild(title)
  104.   for (i=0;i<favicons.length;i+=1) {document.head.appendChild(favicons[i])}
  105.   if(ficpic){dataURLify(ficpic)} // window.addEventListener('load_image',load_image,false); // Make sure image has properly loaded
  106.   addProgressBar()
  107.   updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)
  108.   addProfile()
  109.   addQontrolPanel()
  110.   document.body.appendChild(chap)
  111.   addFollowingChapters()
  112.   console.log('WQDSAD')
  113.  
  114.   function addFollowingChapters(){
  115.     console.log('SADASD')
  116.     for (i = activeChap; i < latestChap; i += 1)
  117.       appendChapterFromURL(urlSetChap(document.location.href, i + 1));
  118.     if (activeChap == latestChap) {
  119.       loadQomplete()}
  120.     else {
  121.       appendNextChap(activeChap + 1)}}
  122.   function addQontrolPanel(){
  123.     var panel = document.createElement('div')
  124.     panel.setAttribute('class', 'panel')
  125.     addButtons(panel)
  126.     document.getElementById('profile').firstChild.appendChild(panel)}
  127.   function getLatestChap(){
  128.     if(siteMatch==='fanfiction'||siteMatch==='fictionpress'){
  129.       var lc = chap_select ? chap_select.children.length : 1
  130.       console.log('JHHLL:License')
  131.       if(/(?:fanfiction\.net|fictionpress\.com)\/r/.test(document.location.href)) {
  132.         var as = document.getElementById('content_wrapper_inner').firstElementChild.nextElementSibling
  133.         if(as) lc = [].slice.call(as.getElementsByTagName('a')).map(q=>urlGetChap(q.href)).reduce((p,q)=>Math.max(p,q),1)
  134.       }
  135.       return lc
  136.     }
  137.     if(siteMatch==='fimfiction') {
  138.       try {
  139.         return document.getElementById('chapter_format').getElementsByClassName('title')[0].getElementsByTagName('ul')[0].getElementsByTagName('li').length }
  140.       catch (e) {}}}
  141.   function getChapRE(){if(siteMatch==='fanfiction'||siteMatch==='fictionpress'||siteMatch==='fimfiction') return /(^.*?\/.*?\/\d+\/(?:.*\/)?)(\d+)(\/?[^#]*)/}
  142.   function getProfileTop() {
  143.     if(siteMatch==='fanfiction'||siteMatch==='fictionpress'){return document.getElementById('profile_top')}
  144.     if(siteMatch==='fimfiction') {return document.getElementsByClassName('inner')[0]}}
  145.   function getFavicons() {
  146.     return [].slice.call(document.head.getElementsByTagName('link')).filter(c=>(/favicon/.test(c.href)))}
  147.   function readSettings() {
  148.     var settings = {Qomplete:1}
  149.     var parr = ['center','bgcol','edge','width','fontsz']
  150.     if(live){
  151.       for(var i in parr) {
  152.         settings[parr[i]] = GM_getValue(parr[i],0)}}
  153.     var hash_settings = document.location.hash.slice(1).split('&')
  154.     for(var i=0; i<hash_settings.length; ++i){
  155.       var kv = hash_settings[i].split('=')
  156.       if(parr.includes(kv[0])) {
  157.         settings[kv[0]] = parseInt(kv[1])}}
  158.     return settings}
  159.   function saveSettings(o) {
  160.     var q = []
  161.     var parr = ['Qomplete','center','bgcol','edge','width','fontsz']
  162.     for(var k in o) {
  163.       if(o.hasOwnProperty(k) && parr.includes(k)) {
  164.         if(live) {GM_setValue(k,o[k])}
  165.         q.push(k+'='+o[k])}}
  166.     window.location = '#'+q.join('&')}
  167.   function addProgressBar(){
  168.     var loadwrap = document.createElement('div')
  169.     loadwrap.setAttribute('class', 'wrap')
  170.     loadwrap.style.position = 'sticky'
  171.     loadwrap.style.top = '0px'
  172.     var loading = document.createElement('div')
  173.     loading.style.float = 'left'
  174.     loading.setAttribute('id', 'loading')
  175.     loadwrap.appendChild(loading)
  176.     document.body.appendChild(loadwrap)}
  177.   function addProfile(){
  178.     var profile = document.createElement('div')
  179.     profile.setAttribute('class', 'wrap profile')
  180.     profile.setAttribute('id', 'profile')
  181.     var pad = document.createElement('div')
  182.     pad.setAttribute('class', 'pad')
  183.     if(profile_top) pad.appendChild(profile_top)
  184.     profile.appendChild(pad)
  185.     document.body.appendChild(profile)}
  186.   function dataURLify(img){
  187.     // http://pastebin.ca/1425789
  188.     // Simple function that checks for JPG, GIF and PNG from image data. Otherwise returns false.
  189.     function mime_from_data(data) {
  190.       if     ('GIF' ==data.substr(0,3))return 'image/gif';
  191.       else if('PNG' ==data.substr(1,3))return 'image/png';
  192.       else if('JFIF'==data.substr(6,4))return 'image/jpg';
  193.       return false; }
  194.     // Generates the binary data string from character / multibyte data
  195.     function data_string(data) {
  196.       var data_string='';
  197.       for(var i=0,il=data.length;i<il;i++)data_string+=String.fromCharCode(data[i].charCodeAt(0)&0xff);
  198.       return data_string; }
  199.     function load_image(tries) {
  200.       GM_xmlhttpRequest({
  201.         method: 'GET',
  202.         url: img.src,
  203.         overrideMimeType: 'text/plain; charset=x-user-defined',
  204.         onload: function(xhr) {
  205.           var data = data_string(xhr.responseText);
  206.           if(mime_from_data(data)) {
  207.             var base64_data = btoa(data); // Encode to base64 string
  208.             var data_url = 'data:' + mime_from_data(data) + ';base64,' + base64_data; // Make the data url
  209.             img.src = data_url }
  210.           else if (tries>0) {
  211.             load_image(tries-1) }}})}
  212.     load_image(5)}
  213.   function urlGetChap(url) {
  214.     var arr = re.exec(url)
  215.     return arr&&arr[2]||1}
  216.   function urlSetChap(url, n) {
  217.     var arr = re.exec(url)
  218.     if(!arr) {
  219.       arr = re.exec(url+'0/1/')
  220.       if(/(?:fanfiction\.net|fictionpress\.com)\/r/.test(document.location.href)) {
  221.         arr[1] = arr[1] + arr[1].split('\/').length==5?'':'0\/'
  222.       }
  223.     }
  224.     return arr[1] + n + ((siteMatch==='fimfiction') ? arr[3] : arr[3].replace(/\/[^\/]*$/, ""))}
  225.   function inc(url) {
  226.     var arr = re.exec(url)
  227.     return urlSetChap(url, ++parseInt(urlGetChap(url))) }
  228.   function chapFromPage(url, page) {
  229.     var storytext;
  230.     if(siteMatch==='fanfiction'||siteMatch==='fictionpress'){
  231.       storytext = page.getElementById('storytext') || page.getElementById('gui_table1i').firstElementChild.nextElementSibling }
  232.     else if(siteMatch==='fimfiction') {
  233.       storytext = page.getElementsByClassName('chapter_content')[0]
  234.       storytext.style = ''
  235.       var styles = storytext.getElementsByTagName('style')
  236.       for(style of styles) {
  237.         style.remove()}}
  238.  
  239.     if( storytext) {
  240.       // 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}
  241.       var wrap = page.createElement('div')
  242.       wrap.setAttribute('class', 'wrap col')
  243.       wrap.setAttribute('id', urlGetChap(url))
  244.       var pad = page.createElement('div')
  245.       pad.setAttribute('class', 'pad')
  246.       var chapdiv = page.createElement('div')
  247.       chapdiv.setAttribute('class', 'chapter')
  248.       var chapspan = page.createElement('span')
  249.       chapspan.innerHTML = urlGetChap(url) + '. '
  250.       var title = page.getElementsByTagName('title')[0]
  251.       var chaptitle = page.createElement('a')
  252.       chaptitle.setAttribute('href', url.replace(/#.*$/, ""))
  253.       chaptitle.setAttribute('class', 'external')
  254.       if     (siteMatch==='fanfiction' || siteMatch==='fictionpress') {
  255.         var newChapTitle = /(.*(| [Cc]hapter [^:]+: .*)), a .*/.exec(title.innerHTML)}
  256.       else if(siteMatch==='fimfiction') {
  257.         var newChapTitle = /(.*) - FIMFiction.net/.exec(title.innerHTML)}
  258.       chaptitle.innerHTML = newChapTitle ? newChapTitle[1] : title.innerHTML
  259.       chapdiv.appendChild(chapspan)
  260.       chapdiv.appendChild(chaptitle)
  261.       chapdiv.appendChild(document.createElement('hr'))
  262.       chapdiv.appendChild(storytext)
  263.       pad.appendChild(chapdiv)
  264.       wrap.appendChild(pad)
  265.       return wrap
  266.     }
  267.     else return null}
  268.   function clean(){
  269.     var ptbuttons = profile_top&&profile_top.getElementsByTagName('button')
  270.     if (ptbuttons&&ptbuttons.length) {ptbuttons[0].remove()}
  271.     for (; document.head.firstElementChild;) document.head.firstElementChild.remove();
  272.     for (; document.body.firstElementChild;) document.body.firstElementChild.remove();
  273.     document.body.removeAttribute('style')}
  274.   function addStyle(){
  275.     var style = document.createElement('style')
  276.     style.setAttribute('type', 'text/css')
  277.     style.innerHTML =
  278.       `body{background-color:#000;color:#ccc;margin:0;padding:0;font-family:"Verdana";}
  279.       ul.tags > li{display:inline;}
  280.       ul.tags > li::before{content:" #"}
  281.       #loading{position:inherit;width:100%;height:5px;}
  282.       button, select{border-radius:4px;padding:4px 12px;background: linear-gradient(to bottom, #333, #000);border-width: 1px;color:#ccc;background-color:#000;}
  283.       button:hover{background-image:none;}
  284.       .panel{text-align:center;}
  285.       a.external, option.external{background: transparent url("data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAAoAAAAKCAYAAACNMs+9AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAFZJREFUeF59z4EJADEIQ1F36k7u5E7ZKXeUQPACJ3wK7UNokVxVk9kHnQH7bY9hbDyDhNXgjpRLqFlo4M2GgfyJHhjq8V4agfrgPQX3JtJQGbofmCHgA/nAKks+JAjFAAAAAElFTkSuQmCC") no-repeat scroll right center;padding-right: 13px;}
  286.       div.wrap{max-width:1300px;margin:auto;padding:0px 5px 0px 5px;}
  287.       div.wrap:nth-of-type(2){padding-top:5px;margin-top:50px;}
  288.       div.wrap:last-child{padding-bottom:5px;margin-bottom:50px;}
  289.       div.pad{background-color:#222;padding:50px;}
  290.       .chapter{}#profile_top{}img{float:left;}canvas{float:left;}
  291.       a:link{color:#555;}a:visited{color:#555;}a:hover{color:#aaa;}a:active{color:#aaa;}
  292.       option{}
  293.       option:nth-of-type(6n+1)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #f00, #ff0);}
  294.       option:nth-of-type(6n+2)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #ff0, #0f0);}
  295.       option:nth-of-type(6n+3)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #0f0, #0ff);}
  296.       option:nth-of-type(6n+4)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #0ff, #00f);}
  297.       option:nth-of-type(6n+5)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #00f, #f0f);}
  298.       option:nth-of-type(6n+0)::before{content:"";padding-left:3px;margin-left:-3px;margin-right:4px;background:linear-gradient(to bottom, #f0f, #f00);}
  299.       .col:nth-of-type(6n+${((4-activeChap+60000)%6)}){background-color:#f00;background:linear-gradient(to bottom, #f00, #ff0);}
  300.       .col:nth-of-type(6n+${((5-activeChap+60000)%6)}){background-color:#ff0;background:linear-gradient(to bottom, #ff0, #0f0);}
  301.       .col:nth-of-type(6n+${((6-activeChap+60000)%6)}){background-color:#0f0;background:linear-gradient(to bottom, #0f0, #0ff);}
  302.       .col:nth-of-type(6n+${((7-activeChap+60000)%6)}){background-color:#0ff;background:linear-gradient(to bottom, #0ff, #00f);}
  303.       .col:nth-of-type(6n+${((8-activeChap+60000)%6)}){background-color:#00f;background:linear-gradient(to bottom, #00f, #f0f);}
  304.       .col:nth-of-type(6n+${((9-activeChap+60000)%6)}){background-color:#f0f;background:linear-gradient(to bottom, #f0f, #f00);}
  305.       @media (max-width: 700px) {div.wrap{padding-left:1px;padding-right:1px;} div.pad{padding-left:5px;padding-right:5px;}}
  306.       ${['.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #f0f);}'
  307.         ,'.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #f00);}'
  308.         ,'.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #ff0);}'
  309.         ,'.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #0f0);}'
  310.         ,'.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #0ff);}'
  311.         ,'.profile{background-color:#777;background:linear-gradient(to bottom, #fff, #00f);}'][urlGetChap(document.location.href) % 6]}`
  312.     document.head.appendChild(style)}
  313.   function fixTitleText(titleText){
  314.     if(siteMatch==='fanfiction') {
  315.       var settitle = /(.*?)(?:[Cc]hapter .*, a |, a )(.*) fanfic \| FanFiction/
  316.       var storyname = settitle.exec(titleText)
  317.       if(storyname) {
  318.         return storyname[1]
  319.           +(statusComplete&&activeChap==1?' - Qomplete':(' - chapter '+activeChap+(latestChap!=activeChap?'-'+latestChap:'')))
  320.           +' - '+storyname[2]}}
  321.     if(siteMatch==='fictionpress') {
  322.       var settitle = /(.*?)(?:[Cc]hapter .*, a |, a )(.*) fiction \| FictionPress/
  323.       var storyname = settitle.exec(titleText)
  324.       if(storyname) {
  325.         return storyname[1]
  326.           +(statusComplete&&activeChap==1?' - Qomplete':(' - chapter '+activeChap+(latestChap!=activeChap?'-'+latestChap:'')))
  327.           +' - '+storyname[2]}}
  328.     if(siteMatch==='fimfiction') {
  329.       var settitle = /.* - (.*) - FIMFiction.net/
  330.       var storyname = settitle.exec(titleText)
  331.       if(storyname) {
  332.         return storyname[1] + (' - chapter '+activeChap+(latestChap!=activeChap?'-'+latestChap:''))}}}
  333.   function updateLoading(ignore, appended, downloaded, total) {
  334.     var loading = document.getElementById('loading')
  335.     var p0 = parseInt(ignore / total * 100)
  336.     var p1 = parseInt((appended + ignore) / total * 100)
  337.     if (p1 == 100) {
  338.       setTimeout(function() {
  339.         loading.style.display = 'none'
  340.       }, (activeChap != latestChap) * 100)
  341.     }
  342.     var p2 = parseInt((downloaded + appended + ignore) / total * 100)
  343.     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%)`}
  344.   function addButtons(panel){
  345.     var posbtn = document.createElement('button')
  346.     posbtn.setAttribute('id', 'posbtn')
  347.     function centerclick() {
  348.       var settings = readSettings()
  349.       var e = document.getElementById('position-style')
  350.       if (e) {
  351.         if (e.innerHTML == 'div.wrap{margin-left:0px;}') {
  352.           settings.center = 2
  353.           e.innerHTML = 'div.wrap{margin-right:0px;}'
  354.           document.getElementById('posbtn').innerHTML = 'Right'}
  355.         else {
  356.           settings.center = 0
  357.           e.remove()
  358.           document.getElementById('posbtn').innerHTML = 'Centered'}}
  359.       else {
  360.         settings.center = 1
  361.         var s = document.createElement('style')
  362.         s.setAttribute('id', 'position-style')
  363.         s.innerHTML = 'div.wrap{margin-left:0px;}'
  364.         document.head.appendChild(s)
  365.         document.getElementById('posbtn').innerHTML = 'Left'}
  366.       saveSettings(settings)}
  367.     posbtn.setAttribute('onclick', 'centerclick()')
  368.     posbtn.setAttribute('class', 'center;')
  369.     posbtn.innerHTML = 'Centered'
  370.  
  371.     var bgcolbtn = document.createElement('button')
  372.     bgcolbtn.setAttribute('id', 'bgcolbtn')
  373.     function bgcolclick() {
  374.       var settings = readSettings()
  375.       var e = document.getElementById('bgcol-style')
  376.       if (e) {
  377.         if (e.innerHTML == 'body{color:#000;}a:hover{color:#000;}div.pad{background-color:#fff;}') {
  378.           settings.bgcol = 2
  379.           e.innerHTML = 'body{color:#fff;}div.pad{background-color:#000;}'
  380.           document.getElementById('bgcolbtn').innerHTML = 'Black'}
  381.         else {
  382.           settings.bgcol = 0
  383.           e.remove()
  384.           document.getElementById('bgcolbtn').innerHTML = 'Dark'}}
  385.       else {
  386.         settings.bgcol = 1
  387.         var s = document.createElement('style')
  388.         s.setAttribute('id', 'bgcol-style')
  389.         s.innerHTML = 'body{color:#000;}a:hover{color:#000;}div.pad{background-color:#fff;}'
  390.         document.head.appendChild(s)
  391.         document.getElementById('bgcolbtn').innerHTML = 'White'}
  392.       saveSettings(settings)}
  393.     bgcolbtn.setAttribute('onclick', 'bgcolclick()')
  394.     bgcolbtn.setAttribute('style', 'float:left;')
  395.     bgcolbtn.innerHTML = 'Dark'
  396.  
  397.     var edgebtn = document.createElement('button')
  398.     edgebtn.setAttribute('id', 'edgebtn')
  399.     function edgeclick() {
  400.       var settings = readSettings()
  401.       var e = document.getElementById('edge-style')
  402.       if (e) {
  403.         settings.edge = 0
  404.         e.remove()
  405.         document.getElementById('edgebtn').innerHTML = 'Rainbow'}
  406.       else {
  407.         settings.edge = 1
  408.         var s = document.createElement('style')
  409.         s.setAttribute('id', 'edge-style')
  410.         s.innerHTML = '.col:nth-of-type(n){background-color:#333;background:#333;}.profile{background-color:#333;background:linear-gradient(to bottom, #fff, #333);}'
  411.         document.head.appendChild(s)
  412.         document.getElementById('edgebtn').innerHTML = 'Edge: Gray'}
  413.       saveSettings(settings)}
  414.     edgebtn.setAttribute('onclick', 'edgeclick()')
  415.     edgebtn.setAttribute('style', 'float:left;')
  416.     edgebtn.innerHTML = 'Rainbow'
  417.  
  418.     var widthbtn = document.createElement('button')
  419.     widthbtn.setAttribute('id', 'widthbtn')
  420.     function widthclick() {
  421.       var settings = readSettings()
  422.       var e = document.getElementById('width-style')
  423.       if (e) {
  424.         if (e.innerHTML == 'div.wrap{max-width:777px;}') {
  425.           settings.width = 2
  426.           e.innerHTML = 'div.wrap{max-width:100%;}'
  427.           document.getElementById('widthbtn').innerHTML = 'Wide'}
  428.         else {
  429.           settings.width = 0
  430.           e.remove()
  431.           document.getElementById('widthbtn').innerHTML = 'Width: Default'}}
  432.       else {
  433.         settings.width = 1
  434.         var s = document.createElement('style')
  435.         s.setAttribute('id', 'width-style')
  436.         s.innerHTML = 'div.wrap{max-width:777px;}'
  437.         document.head.appendChild(s)
  438.         document.getElementById('widthbtn').innerHTML = 'Narrow'}
  439.       saveSettings(settings)}
  440.     widthbtn.setAttribute('onclick', 'widthclick()')
  441.     widthbtn.setAttribute('class', 'center')
  442.     widthbtn.innerHTML = 'Width: Default'
  443.  
  444.     var fontszSelect = document.createElement('select')
  445.     fontszSelect.setAttribute('id', 'fontzsselect');
  446.     fontszSelect.setAttribute('style', 'float:left;')
  447.     function setFontSize(pt) {
  448.       var settings = readSettings()
  449.       settings.fontsz = pt||0
  450.       saveSettings(settings)
  451.       var e = document.getElementById('fontsz-style')
  452.       if(pt!=0 && pt!=null && pt!=''){
  453.         var css = `body{font-size:${pt}pt}`
  454.         if (!e) {
  455.           e = document.createElement('style')
  456.           e.setAttribute('id', 'fontsz-style')
  457.           document.head.appendChild(e)}
  458.         e.innerHTML = css}
  459.       else {
  460.         if(e) e.remove()}}
  461.     for(var i = 0; i<=50; i+=1){
  462.       var option = document.createElement('option')
  463.       if(i==settings.fontsz){option.setAttribute('selected','')}
  464.       // option.setAttribute('style',`font-size:${i==0?12:Math.max(3,i)}pt;`)
  465.       option.value = i.toString()
  466.       option.innerHTML = `${i||''}pt`
  467.       fontszSelect.appendChild(option)}
  468.     // fontszSelect.setAttribute('style', `max-height:4em; padding-top:-8em;`)
  469.     fontszSelect.setAttribute('onchange', `setFontSize(this.options[this.selectedIndex].value)`)
  470.  
  471.     function setSettings(){
  472.       polyfill()
  473.       var live = false
  474.       var e,settings = readSettings()
  475.       if((e=document.getElementById('position-style'))&&settings.center!=null) {e.remove(); document.getElementById('posbtn').innerHTML='Centered'}
  476.       if((e=document.getElementById('bgcol-style'))&&settings.bgcol!=null) {e.remove(); document.getElementById('bgcolbtn').innerHTML='Dark'}
  477.       if((e=document.getElementById('edge-style'))&&settings.edge!=null) {e.remove(); document.getElementById('edgebtn').innerHTML='Rainbow'}
  478.       if((e=document.getElementById('width-style'))&&settings.width!=null) {e.remove(); document.getElementById('widthbtn').innerHTML='Width: Default'}
  479.       for(var i=0; i<settings.center; ++i) {setTimeout(centerclick,0)}
  480.       for(var i=0; i<settings.bgcol; ++i) {setTimeout(bgcolclick,0)}
  481.       for(var i=0; i<settings.edge; ++i) {setTimeout(edgeclick,0)}
  482.       for(var i=0; i<settings.width; ++i) {setTimeout(widthclick,0)}
  483.       setTimeout(setFontSize.bind(null,settings.fontsz),0)}
  484.     var scripttag = document.createElement('script')
  485.     scripttag.setAttribute('id','button-helper')
  486.     scripttag.innerHTML = [
  487.       ,polyfill.toString()
  488.       ,setSettings.toString().slice(23,-1)
  489.       ,readSettings.toString()
  490.       ,saveSettings.toString()
  491.       ,centerclick.toString()
  492.       ,bgcolclick.toString()
  493.       ,edgeclick.toString()
  494.       ,widthclick.toString()
  495.       ,setFontSize.toString()
  496.       ].join('\n')
  497.  
  498.     posbtn.addEventListener('click',_=>saveSettings(readSettings()))
  499.     widthbtn.addEventListener('click',_=>saveSettings(readSettings()))
  500.     bgcolbtn.addEventListener('click',_=>saveSettings(readSettings()))
  501.     edgebtn.addEventListener('click',_=>saveSettings(readSettings()))
  502.     fontszSelect.addEventListener('change',_=>saveSettings(readSettings()))
  503.  
  504.     panel.appendChild(posbtn)
  505.     panel.appendChild(widthbtn)
  506.     panel.appendChild(bgcolbtn)
  507.     panel.appendChild(edgebtn)
  508.     panel.appendChild(fontszSelect)
  509.     document.body.appendChild(scripttag)
  510.  
  511.     if (chap_select) {
  512.       chap_select.setAttribute('onchange', 'if(this.options[this.selectedIndex].value < ' + urlGetChap(document.location.href) + '){' +
  513.         chap_select.getAttribute('onchange') +
  514.         '}' + ' else {document.getElementById(\'\'+this.options[this.selectedIndex].value).scrollIntoView();}')
  515.       chap_select.setAttribute('style', 'float:right;')
  516.       var os = chap_select.getElementsByTagName('option')
  517.       for (i = 0; i < urlGetChap(document.location) - 1; i += 1) {os[i].setAttribute('class', 'external')}
  518.       panel.appendChild(chap_select)}}
  519.   function loadQomplete() {
  520.     var a = document.getElementsByClassName('skiptranslate')
  521.     for (; a.length; a[0].remove());
  522.     a = document.getElementsByClassName('ad_container')
  523.     for (; a.length; a[0].remove());
  524.     document.body.removeAttribute('style')
  525.     updateLoading(activeChap - 1, latestChap - (activeChap - 1), 0, latestChap)}
  526.   function appendChapterFromURL(url) {
  527.     console.log('SCCCC')
  528.     var oReq = new XMLHttpRequest();
  529.     oReq.onload = function() {
  530.       // setTimeout(function(this2){
  531.       var this2 = this // for debug purposes, use comment above as code to simulate delays (and its matching closing part)
  532.       var xmlDoc = new DOMParser().parseFromString(this2.responseText, "text/html")
  533.       var url = this2.responseURL ? this2.responseURL : this2.responseURLfallback
  534.       var chap = chapFromPage(url, xmlDoc)
  535.       if (chap) {
  536.         chapArr[parseInt(urlGetChap(url))] = chap
  537.         notAppendedYet += 1
  538.         updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)}
  539.       // }, Math.random()*8000+50*urlGetChap(this.responseURL), this)
  540.     }
  541.     oReq.responseURLfallback = url
  542.     oReq.open("get", url, true)
  543.     oReq.send()}
  544.   function appendNextChap(n) {
  545.     if (chapArr[n]) {
  546.       document.body.appendChild(chapArr[n])
  547.       appendedNow += 1
  548.       notAppendedYet -= 1
  549.       updateLoading(activeChap - 1, appendedNow, notAppendedYet, latestChap)
  550.       if (n < latestChap) {
  551.         appendNextChap(n + 1)}
  552.       else {
  553.         loadQomplete() }}
  554.     else {
  555.       window.setTimeout(function() {
  556.         appendNextChap(n)
  557.       }, 50) }}
  558. }
  559. function polyfill(){
  560.   if (Element.prototype.remove == undefined) {
  561.     Element.prototype.remove = function() {
  562.       this.parentNode.removeChild(this) } }
  563.   if (!Array.prototype.includes) {
  564.     Array.prototype.includes = function(searchElement /*, fromIndex*/ ) {
  565.       'use strict';
  566.       var O = Object(this);
  567.       var len = parseInt(O.length) || 0;
  568.       if (len === 0) {return false; }
  569.       var n = parseInt(arguments[1]) || 0;
  570.       var k;
  571.       if (n >= 0) {k = n; }
  572.       else {
  573.         k = len + n;
  574.         if (k < 0) {k = 0;} }
  575.       var currentElement;
  576.       while (k < len) {
  577.         currentElement = O[k];
  578.         if (searchElement === currentElement || (searchElement !== searchElement && currentElement !== currentElement)) {
  579.           return true; }
  580.         k++; }
  581.       return false; }; }}
  582. // Source for checkForBadJavascripts: https://gist.github.com/BrockA/2620135
  583. /*--- checkForBadJavascripts()
  584.     This is a utility function, meant to be used inside a Greasemonkey script that
  585.     has the "@run-at document-start" directive set.
  586.     It Checks for and deletes or replaces specific <script> tags. */
  587. function checkForBadJavascripts(controlArray) {
  588.     /*--- Note that this is a self-initializing function.  The controlArray
  589.         parameter is only active for the FIRST call.  After that, it is an
  590.         event listener.
  591.  
  592.         The control array row is  defines like so:
  593.         [bSearchSrcAttr, identifyingRegex, callbackFunction]
  594.         Where:
  595.             bSearchSrcAttr      True to search the SRC attribute of a script tag
  596.                                 false to search the TEXT content of a script tag.
  597.             identifyingRegex    A valid regular expression that should be unique
  598.                                 to that particular script tag.
  599.             callbackFunction    An optional function to execute when the script is
  600.                                 found.  Use null if not needed.
  601.         Usage example:
  602.             checkForBadJavascripts ( [
  603.                 [false, /old, evil init()/, function () {addJS_Node (init);} ],
  604.                 [true,  /evilExternalJS/i,  null ]
  605.             ] );
  606.     */
  607.     if (!controlArray.length) return null;
  608.     checkForBadJavascripts = function(zEvent) {
  609.         for (var J = controlArray.length - 1; J >= 0; --J) {
  610.             var bSearchSrcAttr = controlArray[J][0];
  611.             var identifyingRegex = controlArray[J][1];
  612.             if (bSearchSrcAttr) {
  613.                 if (identifyingRegex.test(zEvent.target.src)) {
  614.                     stopBadJavascript(J);
  615.                     return false; } }
  616.             else {
  617.                 if (identifyingRegex.test(zEvent.target.textContent)) {
  618.                     stopBadJavascript(J);
  619.                     return false; } } }
  620.         function stopBadJavascript(controlIndex) {
  621.             zEvent.stopPropagation();
  622.             zEvent.preventDefault();
  623.             var callbackFunction = controlArray[J][2];
  624.             if (typeof callbackFunction == "function")
  625.                 callbackFunction(zEvent.target);
  626.             //--- Remove the node just to clear clutter from Firebug inspection.
  627.             zEvent.target.parentNode.removeChild(zEvent.target);
  628.             //--- Script is intercepted, remove it from the list.
  629.             controlArray.splice(J, 1);
  630.             if (!controlArray.length) {
  631.                 //--- All done, remove the listener.
  632.                 window.removeEventListener(
  633.                     'beforescriptexecute', checkForBadJavascripts, true ); } } }
  634.     /*--- Use the "beforescriptexecute" event to monitor scipts as they are loaded.
  635.         See https://developer.mozilla.org/en/DOM/element.onbeforescriptexecute
  636.         Note seems to work on scripts that are dynamically created, despite what
  637.         the spec says. */
  638.     window.addEventListener('beforescriptexecute', checkForBadJavascripts, true);
  639.     return checkForBadJavascripts;}
  640. function addJS_Node(text, s_URL, funcToRun) {
  641.     var D = document;
  642.     var scriptNode = D.createElement('script');
  643.     scriptNode.type = "text/javascript";
  644.     if (text) scriptNode.textContent = text;
  645.     if (s_URL) scriptNode.src = s_URL;
  646.     if (funcToRun) scriptNode.textContent = '(' + funcToRun.toString() + ')()';
  647.     var targ = D.getElementsByTagName('head')[0] || D.body || D.documentElement;
  648.     //--- Don't error check here. if DOM not available, should throw error.
  649.     targ.appendChild(scriptNode);}
  650.  
  651. // element.next = function() {
  652. //   if(this.firstChild) return this.firstChild
  653. //   if(this.nextSibling) return this.nextSibling
  654. //   return this.parentNode.nextSibling}
  655. // 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.bodyj elClone = el.cloneNode(true); el.parentNode.replaceChild(elClone, el);
  656. // document.styleSheets[0].cssText = "";
Add Comment
Please, Sign In to add comment