NOTICE: By continued use of this site you understand and agree to the binding Terms of Service and Privacy Policy.
// ==UserScript== // @name Fanfiction and Fictionpress HTML E-Books Downloader // @namespace uo1.net // @description Allows generation of printable HTML, that can be fed to e-book readers // @include https://www.fanfiction.net/* // @include https://www.fictionpress.com/* // @version 1.4 // @grant GM_xmlhttpRequest // @license MIT // ==/UserScript== (function() { let printFicBtn = null; function htmlEntities(str) { return String(str).replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>').replace(/"/g, '"'); } function onAllChapters(author, title, chapters) { let html = ""; html += '<!DOCTYPE html><html><head><meta charset="utf-8">'; html += '<title>' + htmlEntities(title) + '</title>'; html += '<link rel="canonical" href="' + location.href + '">'; html += '<style type="text/css">body,* { background-color:#000;color:#fff; }</style>'; html += '</head><body>'; html += '<h1>' + htmlEntities(title) + '</h1>'; html += 'by <a href="' + author.link + '">' + author.name + '</a>'; html += '<p>'; for(let chapterNo = 0; chapterNo < chapters.count; chapterNo++) { let chapter = chapters[chapterNo]; html += '<a href="#chapter' + chapter.number + '">' + htmlEntities(chapter.title) + '</a><br>'; } html += '</p>'; for(let chapterNo = 0; chapterNo < chapters.count; chapterNo++) { let chapter = chapters[chapterNo]; html += '<h2 style="page-break-before: always" id="chapter' + chapter.number + '">' + htmlEntities(chapter.title) + '</h2>'; html += '<p>' + chapter.html + '</p>'; } html += '</body></html>'; let blob = new Blob([html], {type: "text/html"}); let url = window.URL.createObjectURL(blob); printFicBtn.href = url; printFicBtn.dataset.download = title + '.html'; printFicBtn.innerHTML = '🔗 Printable Version'; printFicBtn.onmousedown = function(ev) { if (ev.button === 2) { printFicBtn.download = title + '.html'; } else { printFicBtn.removeAttribute('download'); } } //printFicBtn.click(); } $('<a style="margin-right: 0.3em" id="printfic_btn" class="btn pull-right icon-">ⳠDownload Printable Version</a>').insertAfter('#profile_top > button'); printFicBtn = document.getElementById('printfic_btn'); printFicBtn.addEventListener('click', function() { if (printFicBtn.hasAttribute('href')) { return; } $.ajaxSetup({ async: false }); var n = 0; let chapters = {count: document.getElementById('chap_select').options.length}; setTimeout(function(){ //timeout to let it get us a progress bar ish thing for (let i = 1; i <= document.getElementById('chap_select').options.length; i++) { let chapTitle = document.getElementById('chap_select').options[i - 1].textContent; n++; // chapter is variable used by fanfiction, it counts what chapter you are at starting at 1. // my own call to get chapter has +1 because it fetches a list starting at 0 instead. console.log("Downloading " + document.location.pathname.substr(3, 8).replace("/", "") + " chapter " + i + " (" + chapTitle + ")"); let $div = $('<div>'); let cn = i - 1; chapters[cn] = {number: i, title: chapTitle}; $div.load('/s/' + document.location.pathname.substr(3, 8).replace("/", "") + '/' + i + ' #storytext', function(content, result, response){ if (response.status !== 200) { location.reload(); return; } chapters[cn].html = $(this)[0].innerHTML; if (n == chapters.count) { let authorA = null; document.querySelectorAll('#profile_top > a[href]').forEach(function(a) { let h = a.getAttribute('href'); if (!h.startsWith('/u/')) { return; } if (a.textContent.trim() === '') { return; } authorA = a; }); onAllChapters({ link: authorA.href, name: authorA.textContent.trim() }, document.querySelector('#profile_top > b').textContent.trim() + ' by ' + document.querySelector('#profile_top > a[href]').textContent.trim(), chapters); } }); } }, 100); // end of timeout }); })();