// ==UserScript==
// @name AO3: Fic's Style, Blacklist, Bookmarks
// @namespace https://github.com/Schegge
// @version 3.5.1
// @description Change font, size, width, background... of a work + blacklist/savior: hide works that contain certains tags, have too many fandoms/relations/chapters/words 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') !== 351) {
setStorage('ficstyle_version', 351);
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()) {
document.body.insertAdjacentHTML('beforeend', '
AO3: Fic\'s Style, Blacklist, Bookmarks UPDATES (v3.5.1)
Blacklist:
Added the options to hide a work that is under a min or above a max number of words. The values are in thousands, so if you put "5" it refers to 5k words.
More info. 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:last-of-type:nth-of-type(odd) { 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: 4em; 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: [],
opts: {},
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', '{"show":true,"pause":false,"maxFandoms":0,"maxRelations":0,"minIncomplete":0,"maxChapters":0,"minWords":0,"maxWords":0}');
},
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.minIncomplete = v.minIncomplete ? Math.max(parseInt(v.minIncomplete, 10), 0) : 0;
this.opts.maxChapters = v.maxChapters ? Math.max(parseInt(v.maxChapters, 10), 0) : 0;
this.opts.minWords = v.minWords ? Math.max(parseInt(v.minWords, 10), 0) : 0;
this.opts.maxWords = v.maxWords ? Math.max(parseInt(v.maxWords, 10), 0) : 0;
if (this.opts.maxWords > 0 && this.opts.minWords >= this.opts.maxWords) this.opts.maxWords = 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;
},
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;
},
findChapters: function(w) {
return this.opts.maxChapters && w.querySelector('dd.chapters') && parseInt(w.querySelector('dd.chapters').textContent.split('/')[0], 10) > this.opts.maxChapters;
},
findWords: function(w) {
if ((this.opts.minWords || this.opts.maxWords) && w.querySelector('dd.words')) {
let numWords = parseInt(w.querySelector('dd.words').textContent.replace(/,/g, ''), 10) / 1000;
if ((this.opts.minWords && numWords <= this.opts.minWords) || (this.opts.maxWords && numWords >= this.opts.maxWords)) return true;
}
return false;
},
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.findWords(w)) reasons.unshift('[Words]');
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.findWords(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,
minIncomplete: document.getElementById('fs-blacklist-minIncomplete').value,
maxChapters: document.getElementById('fs-blacklist-maxChapters').value,
minWords: document.getElementById('fs-blacklist-minWords').value,
maxWords: document.getElementById('fs-blacklist-maxWords').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', 100, 50, 300],
['margins', 'Page Margins', 7, 5, 40],
['lineSpacing', 'Line Spacing', 5, 3, 10],
['wpm', 'Words per Minute', 250, 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', '250');
},
set: function(a) {
let all = a || this.get();
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() {
var all = this.get();
// 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');
let h = this.options[i][1];
if (typeof this.options[i][2] === 'string') {
h += '
';
for (let j = 2; j < this.options[i].length; j++) {
h += '' + this.options[i][j] + ' ';
}
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 == 'SELECT' ? q.options[e.selectedIndex].text : q.value;
}
self.set(all);
});
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();
});
els.appendChild(reset);
let elsM = document.createElement('button');
elsM.innerHTML = '☰';
elsM.addEventListener('click', function() {
if (this.parentElement.className === 'options-hide') {
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
if (!num) return '?';
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);
}
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 getStorage(key, def) {
return JSON.parse(localStorage.getItem(key) || def);
}
function setStorage(key, value) {
localStorage.setItem(key, typeof value !== 'string' ? JSON.stringify(value) : value);
}
})();