// ==UserScript== // @name AO3: Fic's Style, Blacklist, Bookmarks // @namespace https://github.com/Schegge // @version 3.5 // @description Change font, size, width, background... of a work + blacklist/savior: hide works that contain certains tags, have too many fandoms/relations/chapters and other options + fullscreen reading mode + bookmarks: save the position you stopped reading a fic + number of words for each chapter and estimated reading time // @author Schegge // @include http*://archiveofourown.org/* // @grant none // @icon data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAAC4AAAAuCAYAAABXuSs3AAAAGXRFWHRTb2Z0d2FyZQBBZG9iZSBJbWFnZVJlYWR5ccllPAAAAyBpVFh0WE1MOmNvbS5hZG9iZS54bXAAAAAAADw/eHBhY2tldCBiZWdpbj0i77u/IiBpZD0iVzVNME1wQ2VoaUh6cmVTek5UY3prYzlkIj8+IDx4OnhtcG1ldGEgeG1sbnM6eD0iYWRvYmU6bnM6bWV0YS8iIHg6eG1wdGs9IkFkb2JlIFhNUCBDb3JlIDUuMC1jMDYwIDYxLjEzNDc3NywgMjAxMC8wMi8xMi0xNzozMjowMCAgICAgICAgIj4gPHJkZjpSREYgeG1sbnM6cmRmPSJodHRwOi8vd3d3LnczLm9yZy8xOTk5LzAyLzIyLXJkZi1zeW50YXgtbnMjIj4gPHJkZjpEZXNjcmlwdGlvbiByZGY6YWJvdXQ9IiIgeG1sbnM6eG1wPSJodHRwOi8vbnMuYWRvYmUuY29tL3hhcC8xLjAvIiB4bWxuczp4bXBNTT0iaHR0cDovL25zLmFkb2JlLmNvbS94YXAvMS4wL21tLyIgeG1sbnM6c3RSZWY9Imh0dHA6Ly9ucy5hZG9iZS5jb20veGFwLzEuMC9zVHlwZS9SZXNvdXJjZVJlZiMiIHhtcDpDcmVhdG9yVG9vbD0iQWRvYmUgUGhvdG9zaG9wIENTNSBXaW5kb3dzIiB4bXBNTTpJbnN0YW5jZUlEPSJ4bXAuaWlkOjc0REE4QjE3Qjk1ODExRTY4MEZCQTc3RERGMTNGQ0Y0IiB4bXBNTTpEb2N1bWVudElEPSJ4bXAuZGlkOjc0REE4QjE4Qjk1ODExRTY4MEZCQTc3RERGMTNGQ0Y0Ij4gPHhtcE1NOkRlcml2ZWRGcm9tIHN0UmVmOmluc3RhbmNlSUQ9InhtcC5paWQ6NzREQThCMTVCOTU4MTFFNjgwRkJBNzdEREYxM0ZDRjQiIHN0UmVmOmRvY3VtZW50SUQ9InhtcC5kaWQ6NzREQThCMTZCOTU4MTFFNjgwRkJBNzdEREYxM0ZDRjQiLz4gPC9yZGY6RGVzY3JpcHRpb24+IDwvcmRmOlJERj4gPC94OnhtcG1ldGE+IDw/eHBhY2tldCBlbmQ9InIiPz78uZUsAAACeUlEQVR42uxZvW7bMBCWDaFLuih9A3bJrqJLVhVdigwFZGQPIHdpO9aPYC+dGz1CNXXpEiFzFq0NMtjIE0QvYNQ9Bh+BA6Mf0rKkBuABHyxQJPHxdPfdSZ7sdjvvOdrUe6bmiDvijrgj7og/mn/oDX/4T7YMCAkhxLW0nJARNnzip+32v/H4N8KaEIHsipASBMYv2WHG9TizS3haEp1r96S3C8yRT+IdobTZfNLUq1Q89iYLQEJ6Nib8RCi8NjicfBKLsUJFhsUViCwxlrasSdlaMZaqJPi9YCSKljX8fjwG8YQl2Vs2nhusVXPCMYjHB9gjGJq4gNyV3oA2PVCYyFhd1BzK5OAm+dAL8RTarHs9NAiPUYirpFSkM+3+R8Pc2FSs7ZV4BG+XNbp93uD1gOn9fMgYF/BYpukyb5z+oihF2tozwh/CS8KtasBsKnUX4jFI5jXVUO3/G+V/jUPcE37hkCeEL6zqBkMQT2pKuj72gnBMmKEneYXQ+sAOnsLrcd/EY4RKFfFSG1dzC5C80/twliPGHvc7EC8aEm9TMX+F6zcNWp6bEtinrVUvATbW1N5GiG+pLKlpa+t3SMo2CQuZ3An2FsTbYHV/ZePtfT2+RgyvDKrig5a08wY9l8k+I49nfSSnSjSTzfUkTWqSr2ROWPalKklFkTHptZV9RYgENcks+lAVFacLizXqE4Qi9Bma7mmhJmrUqLPHA1S/fYyH1bGm2zxRPRun+AbKECFEBItDVVCKmnZUYG3I3kW5fUfneEM4xfyZTYfYRnxZ0SDxF+Mc30Sqkrgp0Y4I7wnXUJqN7WOcuP+AHHFH3BF3xB1xR9zC/gkwALX7mp/233xAAAAAAElFTkSuQmCC // @downloadURL none // ==/UserScript== (function() { var check = { // Script version version: function() { if (getStorage('ficstyle_version', '1') !== 35) { setStorage('ficstyle_version', 35); return true; } return false; }, // Blacklist: on search pages but not on personal user profile black: function() { let user = document.querySelector('#greeting .user a[href*="/users/"]'); user = user ? window.location.pathname.indexOf(user.href.split('/users/')[1]) !== -1 : false; return document.querySelector('li.blurb.group:not(.collection)') && !user; }, // Fic's style + fullscreen + bookmarking: include /works/(numbers) and /works/(numbers)/chapters/(numbers) and exclude /works/(whatever)navigate work: function() { return /\/works\/\d+(\/chapters\/\d+)?(?!.*navigate)/.test(window.location.pathname); }, // Fullscreen fullScreen: false }; // NEW VERSION NOTIFICATION if (check.version()) { localStorage.removeItem('ficstyle'); document.body.insertAdjacentHTML('beforeend', '
AO3: Fic\'s Style, Blacklist, Bookmarks LAST UPDATE (v3.5)

Fic\'s style tweaks:

- I\'ve changed how you can personalize the styling of a fanfiction, because of this your previous saving is lost. Sorry.
- You can now also change the line spacing and set your own words per minute reading speed (default value is changed to 250). When you change the wpm, the new estimated reading times will appear after refreshing the page.

More information.

close'); document.getElementById('fs-close').addEventListener('click', function() { this.parentElement.style.display = 'none'; }); } // add estimated reading time for every fic found var wpm = getStorage('ficstyle_wpm', '250'); document.querySelectorAll('dl.stats dd.words').forEach(function(s) { let numWords = s.textContent.replace(/,/g, ''); s.insertAdjacentHTML('afterend', '
Time:
' + countTime(numWords) + '
'); }); /** BOOKMARKS **/ addCSS( 'ficstyle-menu', '#menu-bookmarks ul li { display: flex!important; align-items: center; justify-content: space-between; } ' + '#menu-bookmarks ul li a:first-child { flex-grow: 1; font-size: .9em; } ' + 'a.delete-book-menu { color: #900!important; } ' ); var Bookmarks = { list: [], get: function() { this.list = getStorage('ficstyle_bookmarks', []); }, set: function() { setStorage('ficstyle_bookmarks', this.list); }, fromBook: window.location.search === '?bookmark', getUrl: window.location.pathname.split('/works/')[1], getTitle: function() { let title = document.querySelector('#workskin .preface.group h2.title.heading').textContent; title = title.trim().substring(0, 28); // if chapter by chapter, get the number of the chapter if (this.getUrl.indexOf('chapters') !== -1) { let chapter = document.querySelector('#chapters > .chapter > .chapter.preface.group > h3 > a').textContent; chapter = chapter.replace('Chapter ', 'ch'); title += ' (' + chapter + ')'; } return title; }, getNewBook: function() { let newbook = getScroll(); // if chapter by chapter view or work completed (number/number is the same), calculate as a percent if (window.location.pathname.indexOf('chapters') !== -1 || /(\d+)\/\1/.test(document.querySelector('dl.stats dd.chapters').textContent)) { newbook = (newbook / getDocHeight()).toFixed(4) + '%'; } return newbook; }, checkIfExist: function(a, b) { let url = b || this.getUrl; for (let i = 0; i < this.list.length; i++) { // if the same fic if (this.list[i][0].split('/chapters/')[0] === url.split('/chapters/')[0]) { if (a === 'cancel') { return i; // if the same chapter } else if (this.list[i][0] === url) { if (a === 'book') { let book = this.list[i][2]; if (book.toString().indexOf('%') !== -1) { book = parseFloat(book.replace('%', '')); book *= getDocHeight(); } return book; } return true; } } } return false; }, cancel: function(url) { let found = this.checkIfExist('cancel', url); if (found !== false) this.list.splice(found, 1); }, getNew: function() { this.cancel(); this.list.push([this.getUrl, this.getTitle(), this.getNewBook()]); this.set(); }, html: function() { let bmMenu = document.createElement('li'); bmMenu.id = 'menu-bookmarks'; bmMenu.className = 'dropdown'; bmMenu.innerHTML = 'Bookmarks'; let bmMenuDrop = document.createElement('ul'); bmMenuDrop.className = 'menu dropdown-menu'; bmMenu.appendChild(bmMenuDrop); document.querySelector('#header > ul').appendChild(bmMenu); if (this.list.length) { let self = this; let clickDelete = function() { self.cancel(this.getAttribute('data-url')); self.set(); this.style.display = 'none'; this.previousSibling.style.opacity = '.4'; }; for (let i = 0; i < this.list.length; i++) { let bmLi = document.createElement('li'); bmLi.innerHTML = '' + this.list[i][1] + ''; let deleteBookMenu = document.createElement('a'); deleteBookMenu.className = 'delete-book-menu'; deleteBookMenu.title = 'delete bookmark'; deleteBookMenu.setAttribute('data-url', this.list[i][0]); deleteBookMenu.textContent = 'x'; deleteBookMenu.addEventListener('click', clickDelete); bmLi.appendChild(deleteBookMenu); bmMenuDrop.appendChild(bmLi); } } else { bmMenuDrop.innerHTML = '
  • No bookmark yet.
  • '; } } }; Bookmarks.get(); Bookmarks.html(); /** BLACKLIST **/ if (check.black()) { addCSS( 'ficstyle-blacklist', '#menu-blacklist ul li { text-align: center!important; }' + '#menu-blacklist span { font-size: .85em; } ' + '#fs-black-save {color: #900!important; font-weight: bold; } ' + '.fs-black-opts { font-variant: small-caps; display: flex; flex-wrap: wrap; } ' + '.fs-black-opts span { width: 50%; } ' + '.fs-black-opts span:nth-child(5) { width: 100%; } ' + '.fs-black-opts span, #menu-blacklist .fs-black-info span { flex: auto; } ' + '.fs-black-info { font-size: .8em; display: flex; flex-wrap: nowrap; } ' + '#menu-blacklist input[type="checkbox"] { margin-top: .1em; } ' + '#menu-blacklist input[type="number"], #menu-blacklist input[type="text"] { width: 3em; padding: 0 0 0 .2em; background: #fff; border: 0; box-shadow: 0 0 0 1px #888; border-radius: 0; box-sizing: border-box; } ' + '#menu-blacklist input[type="text"] { width: auto; } ' + '#menu-blacklist textarea { font-size: .9em; line-height: 1.2em; min-height: 10em; margin: .5em!important; padding: .3em; box-shadow: 0 0 0 1px #888; width: calc(100% - 1em); border: 0; box-sizing: border-box; resize: vertical; } ' + '[data-visibility="remove"], [data-visibility="hide"] > :not(.header), [data-visibility="hide"] > .header > :not(h4) { display: none!important; } ' + '[data-visibility="hide"] { opacity: .6; } ' + '[data-visibility="hide"] > .header, [data-visibility="hide"] > .header > h4 { margin: 0!important; min-height: auto; font-size: .9em; font-style: italic; }' + '[data-visibility="hide"]::before { content: "\\2573 " attr(data-reasons); font-size: .8em; } ' ); var Blacklist = { list: [], langs: [], opts: { show: true, pause: false, maxFandoms: 0, maxRelations: 0, maxChapters: 0, minIncomplete: 0 }, where: 'li.blurb.group:not(.collection)', what: '.tags .tag, .required-tags span:not(.warnings) span.text, .header .fandoms .tag', get: function() { this.list = getStorage('ficstyle_blacklist', []); this.langs = getStorage('ficstyle_blacklist_langs', []); this.opts = getStorage('ficstyle_blacklist_opts', this.opts); }, set: function(v) { this.list = v.list.trim() ? JSON.parse('["' + v.list.trim().replace(/[\\"]/g, '\\$&').replace(/\n/g, '\\n').split(',').join('","') + '"]') : []; setStorage('ficstyle_blacklist', this.list); this.langs = v.langs.trim() ? JSON.parse('["' + v.langs.trim().replace(/[\\"]/g, '\\$&').split(',').join('","') + '"]') : []; setStorage('ficstyle_blacklist_langs', this.langs); this.opts.show = v.show; this.opts.pause = v.pause; this.opts.maxFandoms = v.maxFandoms ? Math.max(parseInt(v.maxFandoms, 10), 0) : 0; this.opts.maxRelations = v.maxRelations ? Math.max(parseInt(v.maxRelations, 10), 0) : 0; this.opts.maxChapters = v.maxChapters ? Math.max(parseInt(v.maxChapters, 10), 0) : 0; this.opts.minIncomplete = v.minIncomplete ? Math.max(parseInt(v.minIncomplete, 10), 0) : 0; setStorage('ficstyle_blacklist_opts', this.opts); }, findFandoms: function(w) { return this.opts.maxFandoms && w.querySelectorAll('.header .fandoms .tag').length > this.opts.maxFandoms; }, findRelations: function(w) { return this.opts.maxRelations && w.querySelectorAll('.tags .relationships .tag').length > this.opts.maxRelations; }, findChapters: function(w) { return this.opts.maxChapters && w.querySelector('dd.chapters') && parseInt(w.querySelector('dd.chapters').textContent.split('/')[0], 10) > this.opts.maxChapters; }, findIncomplete: function(w) { if (!this.opts.minIncomplete || !w.querySelector('dd.chapters') || /(\d+)\/\1/.test(w.querySelector('dd.chapters').textContent)) return false; let today = new Date(); let last = new Date(w.querySelector('.datetime').textContent); return Math.abs(last.getTime() - today.getTime()) / (1000 * 3600 * 24 * 30.4) > this.opts.minIncomplete; }, findLangs: function(w) { return this.langs.length && w.querySelector('dd.language') && this.langs.join(' ').toLowerCase().indexOf(w.querySelector('dd.language').textContent.toLowerCase().trim()) === -1; }, findTags: function(w) { return Array.prototype.map.call(w.querySelectorAll(this.what), function(t) { return [t.textContent.trim(), t.parentElement.className]; }); }, ifMatch: function(t) { return this.list.some(function(b) { b = b.trim().replace(/[.+?^${}()|[\]\\]/g, '\\$&'); b = b.replace(/\*/g, '.*'); // wildcard b = b.replace(/(.+)&&(.+)/, '(?=.*$1)(?=.*$2).*'); // match 2 words in any order if (t[1] === 'relationships') b = b.replace(/(.+)&!(.+)/, '(?=.*\\/)((?=.*$1)(?!.*$2)|(?=.*$2)(?!.*$1)).*'); // only otp let reg = new RegExp('^' + b + '$', 'i'); return reg.test(t[0]) === true; }); }, findMatch: function() { if (this.opts.pause) return; document.querySelectorAll(this.where).forEach(function(w) { if (this.opts.show) { let reasons = this.findTags(w).filter(this.ifMatch, this).map(function(r) { return r[0]; }); if (this.findRelations(w)) reasons.unshift('[Relationships]'); if (this.findChapters(w)) reasons.unshift('[Chapters]'); if (this.findFandoms(w)) reasons.unshift('[Fandoms]'); if (this.findIncomplete(w)) reasons.unshift('[Incomplete]'); if (this.findLangs(w)) reasons.unshift('[Language]'); if (!reasons.length) return; w.setAttribute('data-visibility', 'hide'); w.setAttribute('data-reasons', reasons.join(' - ')); } else { if (!this.findLangs(w) && !this.findIncomplete(w) && !this.findFandoms(w) && !this.findChapters(w) && !this.findRelations(w) && !this.findTags(w).some(this.ifMatch, this)) return; w.setAttribute('data-visibility', 'remove'); } }, this); }, clear: function() { document.querySelectorAll(this.where + '[data-visibility]').forEach(function(el) { el.removeAttribute('data-visibility'); el.removeAttribute('data-reasons'); }); }, save: function() { this.set({ list: document.getElementById('fs-blacklist').value, langs: document.getElementById('fs-blacklist-langs').value, show: document.getElementById('fs-blacklist-show').checked, pause: document.getElementById('fs-blacklist-pause').checked, maxFandoms: document.getElementById('fs-blacklist-maxFandoms').value, maxRelations: document.getElementById('fs-blacklist-maxRelations').value, maxChapters: document.getElementById('fs-blacklist-maxChapters').value, minIncomplete: document.getElementById('fs-blacklist-minIncomplete').value }); this.clear(); this.findMatch(); }, html: function() { let blMenu = document.createElement('li'); blMenu.id = 'menu-blacklist'; blMenu.className = 'dropdown'; blMenu.innerHTML = 'Blacklist'; document.querySelector('#header > ul').appendChild(blMenu); document.getElementById('fs-black-save').addEventListener('click', function() { Blacklist.save(); let self = this; self.textContent = 'SAVED'; setTimeout(function() { self.textContent = 'SAVE'; }, 1000); }); } }; Blacklist.get(); Blacklist.findMatch(); Blacklist.html(); } /** FIC'S STYLE + FULLSCREEN + BOOKMARKING **/ if (check.work()) { addCSS( 'ficstyle-general', // fic's style '#main div.wrapper { margin-bottom: 1em; } ' + '#workskin { margin: 0; text-align: justify; max-width: none!important; } ' + '#workskin .notes, #workskin .summary, blockquote { font-size: inherit; font-family: inherit; } ' + '.preface a, #chapters a, .preface a:link, #chapters a:link, .preface a:visited, #chapters a:visited, .preface a:visited:hover, #chapters a:visited:hover { color: inherit !important; } ' + '.actions { font-family: \'Lucida Grande\', \'Lucida Sans Unicode\', \'GNU Unifont\', Verdana, Helvetica, sans-serif; font-size: 14px; } ' + '.chapter .preface { border-top: 0; margin-bottom: 0; padding: 0 2em; }' + '.chapter .preface[role="complementary"] { border-width: 0; margin: 0; } ' + '.preface.group, div.preface { color: inherit; background-color: inherit; margin-left: 0; margin-right: 0; padding: 0 2em; } ' + '#workskin #chapters .preface .userstuff p, #workskin .preface .userstuff p { margin: .1em auto; line-height: 1.1em; } ' + 'div.preface .byline a, #workskin #chapters a, #chapters a:link, #chapters a:visited { color: inherit; } ' + 'div.preface .notes, div.preface .summary, div.preface .series, div.preface .children { min-height: 0; } ' + 'div.preface .jump { margin-top: 1em; font-size: .9em; }' + '.preface blockquote { box-shadow: 0 0 0 2px rgba(0, 0, 0, .1), 0 0 0 2px rgba(255, 255, 255, .2); padding: .6em; margin: 0; }' + '.preface h3.title { background: repeating-linear-gradient(45deg, rgba(0, 0, 0, .05), rgba(0, 0, 0, .1) 2px, rgba(255, 255, 255, .2) 2px, rgba(255, 255, 255, .2) 4px); padding: .6em; margin: 0; } ' + '.preface h3.heading { font-size: inherit; border-width: 0; } ' + 'h3.title a { border: 0; font-style: italic; } ' + 'div.preface .associations, .preface .notes h3+p { margin-bottom: 0; font-style: italic; font-size: .8em; } ' + '#workskin #chapters, #workskin #chapters .userstuff { width: 100%!important; box-sizing: border-box; } ' + '#workskin #chapters .userstuff p { font-family: inherit; text-align: justify; } ' + '#workskin #chapters .userstuff { font-family: inherit; text-align: justify; } ' + '#workskin #chapters .userstuff br { display: block; margin-top: .6em; content: " "; } ' + '.userstuff hr { width: 100%; height: 2px; border: 0; margin: 1.5em 0; background-image: linear-gradient(90deg, transparent, rgba(0, 0, 0, .2), transparent), linear-gradient(90deg, transparent, rgba(255, 255, 255, .3), transparent); } ' + '#workskin #chapters .userstuff blockquote { padding-top: 1px; padding-bottom: 1px; margin: 0 .5em; font-size: inherit; } ' + '.userstuff img { max-width: 100%; height: auto; display: block; margin: auto; } ' + // options '#ficstyle-menu, .ficleft { position: fixed; bottom: .8em; margin: 0; padding: 0; color: #000; text-shadow: 0 0 1px rgba(0, 0, 0, .4); border-radius: .3em; z-index: 999; } ' + '#ficstyle-menu { right: 0; background-color: transparent; text-align: right; margin-right: .5em; } ' + '#ficstyle-menu:not(.options-hide) { width: 25em; }' + '.ficleft { font-family: Consolas, monospace; left: 0; padding-left: .5em; } ' + '.ficleft button { padding: 0 .2em; margin: 0 .2em 0 0; } ' + '#ficstyle-menu > button { padding: 0 .3em; }' + '#ficstyle-menu.options-hide > div { display: none; } ' + '#ficstyle-menu > div { background-color: #ddd; padding: .5em; box-shadow: 1px 1px 3px -1px #444; margin: 0; border-radius: .2em; } ' + '#ficstyle-menu label { display: block; border-bottom: 1px solid #888; padding: .2em 0; margin: 0; }' + '#ficstyle-menu input, #ficstyle-menu select { width: 50%; padding: 0; margin: 0 0 0 1em; font-size: 1em; }' + '#ficstyle-menu button { margin: .3em .2em; }' + '.fictop { margin: 1em 0 0; font-size: 80%; text-align: right; padding: 0; } ' + // chapter words '.chapterWords { font-size: .7em; color: inherit; font-family: consolas, monospace; text-transform: uppercase; text-align: center; margin: 3em 0 .5em; }' ); // CSS changes depending on the user var Styling = { def: { fontName: 'inherit', colors: 'light', fontSize: 100, margins: 7, lineSpacing: 5, wpm: 250 }, options: [ ['fontName', 'Font', 'inherit', 'Arial Black', 'Consolas', 'Courier', 'Garamond', 'Georgia', 'Helvetica', 'Segoe UI', 'Times New Roman', 'Verdana'], ['colors', 'Background', 'light', 'grey', 'sepia', 'dark', 'darkblue', 'black'], ['fontSize', 'Text Size', 50, 300], ['margins', 'Page Margins', 5, 40], ['lineSpacing', 'Line Spacing', 3, 15], ['wpm', 'Words per Minute', 100, 500] ], colors: { // background, font color light: ['#ffffff', '#000000'], grey: ['#eeeeee', '#111111'], sepia: ['#fbf0d9', '#54331b'], dark: ['#333333', '#e1e1e1'], darkblue: ['#282a36', '#f8f8e6'], black: ['#000000', '#ffffff'] }, get: function() { return getStorage('ficstyle', this.def); }, setDefs: function() { setStorage('ficstyle', this.def); setStorage('ficstyle_wpm', this.def.wpm); }, set: function(a) { let all = a || this.get(); all.wpm = !all.wpm || all.wpm < this.options[5][2] ? this.options[5][2] : all.wpm; setStorage('ficstyle', all); setStorage('ficstyle_wpm', all.wpm); addCSS( 'ficstyle-user-changes', '#workskin { font-family: ' + all.fontName + '; font-size: ' + all.fontSize + '%; padding: 0 ' + all.margins + '%; color: ' + this.colors[all.colors][1] + '; background-color: ' + this.colors[all.colors][0] + '; } ' + '#workskin #chapters .userstuff p { line-height: ' + all.lineSpacing * 0.3 + 'em; margin: ' + (all.lineSpacing * 0.5 - 1.4) + 'em auto; } #workskin #chapters .userstuff { line-height: ' + all.lineSpacing * 0.3 + 'em } ' ); }, html: function() { let all = this.get(); let pos = 0; // the options displayed on the page let elsC = document.createElement('div'); elsC.id = 'ficstyle-menu'; elsC.className = 'options-hide'; let els = document.createElement('div'); elsC.appendChild(els); for (let i = 0; i < this.options.length; i++) { let el = document.createElement('label'); // i = 0:key 1:text 2:min 3:max let h = this.options[i][1]; if (typeof this.options[i][2] === 'string') { h += ''; } else if (this.options[i][0] === 'wpm') { h += ''; } else { h += ''; } el.innerHTML = h; els.appendChild(el); } let self = this; let save = document.createElement('button'); save.innerHTML = 'save'; save.addEventListener('click', function() { for (let i = 0; i < self.options.length; i++) { let q = els.querySelector('#' + self.options[i][0]); all[self.options[i][0]] = q.tagName === 'SELECT' ? q.value : parseInt(q.value, 10); } self.set(all); setScroll(pos * getDocHeight()); }); els.appendChild(save); let reset = document.createElement('button'); reset.innerHTML = 'reset'; reset.addEventListener('click', function() { self.setDefs(); self.set(); this.parentElement.parentElement.parentElement.removeChild(this.parentElement.parentElement); self.html(); setScroll(pos * getDocHeight()); }); els.appendChild(reset); let elsM = document.createElement('button'); elsM.innerHTML = '☰'; elsM.addEventListener('click', function() { if (this.parentElement.className === 'options-hide') { pos = getScroll() / getDocHeight(); this.parentElement.className = ''; this.innerHTML = 'close'; } else { this.parentElement.className = 'options-hide'; this.innerHTML = '☰'; } }); elsC.appendChild(elsM); document.body.appendChild(elsC); } }; Styling.set(); Styling.html(); // remove all the non-breaking white spaces document.getElementById('chapters').innerHTML = document.getElementById('chapters').innerHTML.replace(/ /g, ' '); // # words and time for every chapter, if the fic has chapters document.querySelectorAll('#chapters > .chapter > div.userstuff.module').forEach(function(el) { let numWords = el.textContent.match(/\S+\b/g).length - 2; // -2 because of hidden

    Chapter Text

    el.parentNode.querySelector('.chapter.preface.group[role="complementary"]').insertAdjacentHTML('beforebegin', '
    this chapter has ' + numWords + ' words (time: ' + countTime(numWords) + ')
    '); }); // FULL SCREEN MODE var workskin = document.getElementById('workskin'); var ficTop = document.createElement('div'); ficTop.className = 'actions fictop'; var toFullScreen = document.createElement('div'); toFullScreen.innerHTML = 'Full Screen'; ficTop.appendChild(toFullScreen); workskin.insertAdjacentElement('afterbegin', ficTop); // changes to create full screen var fullScreen = function() { if (check.fullScreen) { window.location.replace(window.location.pathname); return; } setScroll(); check.fullScreen = true; addCSS( 'ficstyle-fullscreen', '#outer { display: none; } ' + '#workskin .preface { margin: 0; padding-bottom: 0; } ' + 'div.preface .module { padding-bottom: 0; text-align: center; } ' + '.preface .module h3.heading { display: inline; cursor: pointer; text-align: center; opacity: .5; font-style: italic; font-size: 100%; } ' + '.preface .module > :not(h3) { display: none; } ' + '.preface h3 + p { border: 3px solid rgba(0, 0, 0, .1); border-left: 0; border-right: 0; padding: .6em; margin: 0; }' + '.preface .module > h3:hover ~ .userstuff, .preface .module > .userstuff:hover, ' + '.preface .module > h3:hover ~ ul, .preface .module > ul:hover, ' + '.preface .module > h3:hover + p, .preface .module > h3 + p:hover { display: block!important; position: absolute; width: 100%; max-height: 6em; font-size: .8em; transform: translateY(-100%); color: rgb(42, 42, 42); background-color: #fff; padding: 10px; box-shadow: 0 1px 2px 0 rgba(0, 0, 0, .4); margin: 0; overflow: auto; z-index: 999; cursor: pointer; } ' + '.no-book, .actions:not(.fictop) li > a:not([href*="chapters"]):not([href="#workskin"]) { display: none; } ' + '.actions:not(.fictop) { margin-top: 2em; } ' ); document.body.appendChild(workskin); toFullScreen.innerHTML = 'Exit'; let goToBook = document.createElement('div'); goToBook.innerHTML = 'Go to Bookmark'; goToBook.addEventListener('click', function() { setScroll(Bookmarks.checkIfExist('book')); }); let ficLeft = document.createElement('div'); ficLeft.className = 'ficleft'; let deleteBook = document.createElement('button'); deleteBook.title = 'delete bookmark'; deleteBook.textContent = 'x'; deleteBook.addEventListener('click', function() { Bookmarks.cancel(); Bookmarks.set(); this.className = 'no-book'; goToBook.className = 'no-book'; }); let newBook = document.createElement('button'); newBook.title = 'new bookmark'; newBook.textContent = '+'; newBook.addEventListener('click', function() { Bookmarks.getNew(); goToBook.className = ''; deleteBook.className = ''; }); if (!Bookmarks.checkIfExist()) { deleteBook.className = 'no-book'; goToBook.className = 'no-book'; } ficTop.insertBefore(goToBook, toFullScreen); ficLeft.appendChild(newBook); ficLeft.appendChild(deleteBook); document.body.appendChild(ficLeft); document.querySelector('#feedback .actions a[href="#main"]').href = '#workskin'; workskin.appendChild(document.querySelector('#feedback .actions')); }; if (Bookmarks.fromBook) fullScreen(); toFullScreen.addEventListener('click', fullScreen); } /* GLOBAL FUNCTIONS */ function addCSS(id, css) { if (!document.querySelector('style#' + id)) { let style = document.createElement('style'); style.id = id; style.textContent = css; document.getElementsByTagName('head')[0].appendChild(style); } else { document.querySelector('style#' + id).textContent = css; } } function countTime(num) { // estimate reading time let time = (parseInt(num, 10) / wpm / 60).toFixed(2).toString().split('.'); return (time[0] !== '0' ? time[0] + 'hr ' : '') + (time[1] !== '00' ? Math.round(parseInt(time[1], 10) / 100 * 60) + 'min' : '') || '<1min'; } function getScroll() { return Math.max(document.documentElement.scrollTop, window.scrollY, 0); } function setScroll(s) { window.scroll(0, s ? s : 0); } function getDocHeight() { return Math.max(document.documentElement.scrollHeight, document.documentElement.offsetHeight, document.body.scrollHeight, document.body.offsetHeight); } function setStorage(key, value) { localStorage.setItem(key, typeof value !== 'string' ? JSON.stringify(value) : value); } function getStorage(key, def) { if (localStorage.getItem(key)) { return JSON.parse(localStorage.getItem(key)); } else { setStorage(key, def); return def; } } })();