// ==UserScript==
// @name FanFictionNavigator
// @name:ru FanFictionNavigator
// @namespace window
// @description:en Mark and hide fanfics or authors
// @description:ru Выделяет цветом/скрывает фанфики или авторов
// @include https://ficbook.net/*
// @include https://www.fanfiction.net/*
// @include https://archiveofourown.org/*
// @include http://archiveofourown.org/*
// @include https://tbooklist.org/*
// @run-at document-end
// @require https://code.jquery.com/jquery-1.7.2.min.js
// @require https://greasyfork.org/scripts/17419-underscore-js-1-8-3/code/Underscorejs%20183.js?version=109803
// @version 53
// @grant GM_addStyle
// @description Mark and hide fanfics or authors
// @downloadURL none
// ==/UserScript==
// Based on https://chrome.google.com/webstore/detail/fanfiction-organizer/adlnghnicfngjnofbljedmclmdoepcbe
// by Stefan Hayden
// Fics:
const FIC_LIKED = 0;
const FIC_DISLIKED = 1;
const FIC_MARKED = 2;
const FIC_INLIBRARY = 3;
// Authors:
const AUTHOR_LIKED = 0;
const AUTHOR_DISLIKED = 1;
// Communities:
const COMMUNITY_LIKED = 0;
const COMMUNITY_DISLIKED = 1;
// colors. now used for like/dislike/etc links
const COLOR_LIKED = '#C4FFCA';
const COLOR_DISLIKED = '#FCB0B0';
const COLOR_MARKED = '#CCCCCC';
const COLOR_INLIBRARY = '#F1D173';
const COLOR_CLEARED = '#FFF';
const COLOR_FB_CLEAR = '#FFF';
// styles for author/story links; links should have only one of these so order doesn't matter
GM_addStyle("a.ffn_dislike_fic {text-decoration: line-through; font-weight: bold;}");
GM_addStyle("a.ffn_like_fic {font-weight: bold;} ");
GM_addStyle("a.ffn_dislike_author {text-decoration: line-through; font-weight: bold;}");
GM_addStyle("a.ffn_like_author {font-weight: bold;} ");
GM_addStyle("a.ffn_mark {font-weight: bold;}");
GM_addStyle("a.ffn_inlibrary {font-weight: bold;}");
GM_addStyle("a.ffn_dislike_community {text-decoration: line-through; font-weight: bold;}");
GM_addStyle("a.ffn_like_community {font-weight: bold;} ");
// styles for box background; fic style should overwrite author style
GM_addStyle(".ffn_like_author:not(a) {background-color:#C4FFCA !important;}");
GM_addStyle(".ffn_dislike_author:not(a) {background-color:#FCB0B0 !important;}");
GM_addStyle(".ffn_like_fic:not(a) {background-color:#C4FFCA !important;}");
GM_addStyle(".ffn_dislike_fic:not(a) {background-color:#FCB0B0 !important;}");
GM_addStyle(".ffn_mark:not(a) {background-color:#CCCCCC !important;}");
GM_addStyle(".ffn_inlibrary:not(a) {background-color:#F1D173 !important;}");
GM_addStyle(".ffn_like_community:not(a) {background-color:#C4FFCA !important;}");
GM_addStyle(".ffn_dislike_community:not(a) {background-color:#FCB0B0 !important;}");
// styles for boxes, they differ between sites
/*
switch(window.location.hostname){
case "www.fanfiction.net":
case "tbooklist.org":
GM_addStyle("div.ffn_dislike {background-color:#FCB0B0 !important;}");
GM_addStyle("div.ffn_like {background-color:#C4FFCA !important;}");
GM_addStyle("div.ffn_mark {background-color:#CCCCCC !important;}");
GM_addStyle("div.ffn_inlibrary {background-color:#F1D173 !important;}");
break
case "archiveofourown.org":
GM_addStyle(".ffn_dislike {background-color:#FCB0B0 !important;}");
GM_addStyle(".ffn_like {background-color:#C4FFCA !important;}");
GM_addStyle(".ffn_mark {background-color:#CCCCCC !important;}");
GM_addStyle(".ffn_inlibrary {background-color:#F1D173 !important;}");
break
case "ficbook.net":
GM_addStyle("div.ffn_dislike {background-color:#FCB0B0 !important;}");
GM_addStyle("div.ffn_like {background-color:#C4FFCA !important;}");
GM_addStyle("div.ffn_mark {background-color:#CCCCCC !important;}");
GM_addStyle("div.ffn_inlibrary {background-color:#F1D173 !important;}");
GM_addStyle("ul.ffn_dislike {background-color:#FCB0B0 !important;}");
GM_addStyle("ul.ffn_like {background-color:#C4FFCA !important;}");
GM_addStyle("ul.ffn_mark {background-color:#CCCCCC !important;}");
GM_addStyle("ul.ffn_inlibrary {background-color:#F1D173 !important;}");
break
}
*/
// prevent conflicts with websites' jQuery version
this.ffn$ = this.jQuery = jQuery.noConflict(true);
var db = JSON.parse(localStorage.getItem("FFLikerAA") || '{}');
db.options = db.options || {};
db.version = db.version || '0.2';
//
// APP
//
// Main
var patharr = window.location.pathname.split("/"); // moved to main block to save on split functions elsewhere
var Application = function Application(optionsin) {
var a = {};
var options = optionsin || {};
if(!options.namespace) { throw new Error("namespace is required"); }
if(!options.db) { throw new Error("database object is required"); }
a.namespace = options.namespace;
var db = options.db;
db[a.namespace] = db[a.namespace] || { fic: {}, author:{}, community:{} };
a.collection = [];
a.color = {
link_default: ffn$("ol.work.index.group > li:first a:first").css("color"),
like_link:'',
like_background:'',
dislike_link:'',
dislike_background:'',
};
a.save = function(type,id,value){
if(type == "fic" || type == "author" || type == "community") {
a.saveNameSpaced(type,id,value);
} else {
if(value === "clear") {
delete db[type][id];
} else {
db[type][id] = value;
}
localStorage.setItem("FFLikerAA", JSON.stringify(db));
}
};
a.saveNameSpaced = function(type,id,value){
if(value === "clear") {
delete db[a.namespace][type][id];
} else {
if (typeof(db[a.namespace][type]) == 'undefined') {
db[a.namespace][type] = {};
}
db[a.namespace][type][id] = value;
}
localStorage.setItem("FFLikerAA", JSON.stringify(db));
};
a.author = {};
a.author.get = function(id){
return db[a.namespace].author[id];
};
a.author.like = function(id) {
a.save("author",id,AUTHOR_LIKED);
_.each(a.author.getFics(id), function(story){
story.author = AUTHOR_LIKED;
story.like_author();
});
};
a.author.dislike = function(id) {
a.save("author",id,AUTHOR_DISLIKED);
//ga('set', 'metric3', 1);
_.each(a.author.getFics(id), function(story){
story.author = AUTHOR_DISLIKED;
story.dislike_author();
});
};
a.author.clear = function(id) {
a.save("author",id,"clear");
_.each(a.author.getFics(id), function(story){
story.author = '';
story.clear_author();
});
};
a.author.getFics = function(id) {
return _.filter(a.collection,function(story){
return story.authorId() == id;
});
};
a.fic = {};
a.fic.get = function(id) {
return db[a.namespace].fic[id];
};
a.fic.like = function(id) {
a.save("fic",id,FIC_LIKED);
var story = _.find(a.collection,function(story){
return story.ficId() == id;
});
story.fic = FIC_LIKED;
story.like_story();
};
a.fic.dislike = function(id) {
a.save("fic",id,FIC_DISLIKED);
var story = _.find(a.collection,function(story){
return story.ficId() == id;
});
story.fic = FIC_DISLIKED;
story.dislike_story();
};
a.fic.mark = function(id) {
a.save("fic",id,FIC_MARKED);
var story = _.find(a.collection,function(story){
return story.ficId() == id;
});
story.fic = FIC_MARKED;
story.mark_story();
};
a.fic.inlibrary = function(id) {
a.save("fic",id,FIC_INLIBRARY);
var story = _.find(a.collection,function(story){
return story.ficId() == id;
});
story.fic = FIC_INLIBRARY;
story.inlibrary_story();
};
a.fic.clear = function(id) {
a.save("fic",id,"clear");
var story = _.find(a.collection,function(story){
return story.ficId() == id;
});
story.fic = '';
story.clear_story();
};
a.community = {};
a.community.get = function(id) {
if (typeof(db[a.namespace].community) == "undefined") {
db[a.namespace].community = {}
}
return db[a.namespace].community[id];
};
a.community.like = function(id) {
a.save("community",id,COMMUNITY_LIKED);
var community = _.find(a.collection,function(community){
return community.communityId() == id;
});
community.community = COMMUNITY_LIKED;
community.like_community();
};
a.community.dislike = function(id) {
a.save("community",id,COMMUNITY_DISLIKED);
var community = _.find(a.collection,function(community){
return community.communityId() == id;
});
community.community = COMMUNITY_DISLIKED;
community.dislike_community();
};
a.community.clear = function(id) {
a.save("community",id,"clear");
var community = _.find(a.collection,function(community){
return community.communityId() == id;
});
community.community = '';
community.clear_community();
};
a.options = function(name, value) {
if(!name) { throw new Error("name is required. what option are you looking for?"); }
if(typeof value !== "undefined") {
a.save("options",name,value);
return false;
} else {
return db.options[name];
}
};
return a;
};
var Story = function(optionsin) {
var a = {};
var options = optionsin || {};
if(!options.instance) { throw new Error("instance of this is required"); }
if(!options.namespace) { throw new Error("namespace is required"); }
var _this = ffn$(options.instance);
a["default"] = {
template: function() {
var template = '
(\n| |[ ])*
/gi,"
");
break;
case 40:
textcontent = document.getElementById("content");
textcontent.outerHTML = textcontent.outerHTML.replace(/
/gi,"
\n
");
ffn$("#undo_typograf").click();
break;
}
}
if (!e.ctrlKey && !e.altKey && e.shiftKey) {
switch (e.keyCode){
case 38:
ffn$("#do_typograf").click();
break;
case 40:
ffn$("#undo_typograf").click();
break;
}
}
}, false);
}
}
// in lists
if (patharr[1]==="find" ||
(patharr[1]==="popular") ||
(patharr[1]==="collections") ||
(patharr[1]==="fanfiction") ||
(patharr[3]==="profile" && patharr[4]==="works") || // in author profile / works
(patharr[1] === "home" && ["favourites","collections"].indexOf(patharr[2])!=-1) ){ // indexOf => checks if patharr[2] is in [] array
//ffn$(".description").each(function() {
ffn$("article.block > div.description").each(function() {
var story = new Story({
namespace: app.namespace,
instance: this
});
app.collection.push(story);
});
}
// button for quickly reading/unreading story
if (patharr[1]==="find" ||
(patharr[1]==="popular") ||
(patharr[1]==="authors" && patharr[3]==="profile" && patharr[4]==="works")) {
ffn$("article.block > div.description").each(function() {
_this = ffn$(this);
ficId = _this.find('a[href^="/readfic"]:first').attr('href').split('/')[2].replace(/(\d+).*?$/,"$1");
if (_this.find(".read-notification").length===0) {
_this.append('');
} else {
_this.append('');
}
});
}
// add hotkeys
if (app.options("enable_list_hotkeys")) {
document.addEventListener('keydown', function(e){
if (!e.ctrlKey && !e.altKey && !e.shiftKey) {
switch (e.keyCode){
case 37:
var Prev = ffn$("a[aria-label='Предыдущая']");
if (Prev.length>0) Prev[0].click();
break;
case 39:
var Next = ffn$("a[aria-label='Следующая']");
if (Next.length>0) Next[0].click();
break;
}
}
}, false);
}
// hide/show options
ffn$('section.content-section').after(
' '
);
break;
case "tbooklist.org":
// in feed
if (patharr[1]==="feed"){
ffn$("div.content-block > div.regular").each(function() {
var story = new Story({
namespace: app.namespace,
instance: this
});
app.collection.push(story);
});
}
// book page
else if (patharr[1]==="books"){
ffn$("div.cmedia-divided__child__top").each(function() {
var story = new Story({
namespace: app.namespace,
instance: this
});
app.collection.push(story);
});
}
// hide/show options
ffn$('div.content-block').after(
' '
);
break;
}
// OPTIONS
// - show/hide global options
//
if(app.options("hide_likes")){
ffn$('.liker_script_options').append('Show Likes');
ffn$('.liker_script_options .show_likes').click(function(){ show_likes(); });
} else {
ffn$('.liker_script_options').append('Hide Likes');
ffn$('.liker_script_options .hide_likes').click(function(){ hide_likes(); });
}
ffn$('.liker_script_options').append('| ');
if(app.options("hide_dislikes")){
ffn$('.liker_script_options').append('Show Dislikes');
ffn$('.liker_script_options .show_dislikes').click(function(){ show_dislikes(); });
} else {
ffn$('.liker_script_options').append('Hide Dislikes');
ffn$('.liker_script_options .hide_dislikes').click(function(){ hide_dislikes(); });
}
ffn$('.liker_script_options').append('| ');
if(app.options("hide_marked")){
ffn$('.liker_script_options').append('Show Marked');
ffn$('.liker_script_options .show_marked').click(function(){ show_marked(); });
} else {
ffn$('.liker_script_options').append('Hide Marked');
ffn$('.liker_script_options .hide_marked').click(function(){ hide_marked(); });
}
ffn$('.liker_script_options').append('| ');
if(app.options("hide_inlibrary")){
ffn$('.liker_script_options').append('Show InLibrary');
ffn$('.liker_script_options .show_inlibrary').click(function(){ show_inlibrary(); });
} else {
ffn$('.liker_script_options').append('Hide InLibrary');
ffn$('.liker_script_options .hide_inlibrary').click(function(){ hide_inlibrary(); });
}
// specific links for sites
// dislike all authors, currently on ffnet only (enter some slash community and blacklist everyone there, saves time when browsing HP fanfics later)
if (window.location.hostname === "www.fanfiction.net") {
ffn$('.liker_script_options').append('| ');
ffn$('.liker_script_options').append('Dislike All Authors');
ffn$('.liker_script_options .dislike_all').click(function(e){ e.preventDefault(); dislike_all();});
}
if (window.location.hostname === "ficbook.net") {
switch(patharr[1]){
case "find":
// revert read status for all visible fics (circumvent ficbook bug where alreade read fics still appear in search)
ffn$('.liker_script_options').append('| Actions: ');
ffn$('.liker_script_options').append('Invert Read status');
ffn$('.liker_script_options .ffn_ficbook_invert_read').click(function(e){ e.preventDefault(); ficbook_invertread(); return false; });
break
case "readfic":
// specific request - download fb2 and like fic in one click
ffn$('.new_like_actions').append('| Actions: FB2 and Like');
ffn$('.new_like_actions .ffn_ficbook_fb2_and_like').click(function(e){ e.preventDefault(); ficbook_fb2andlike(); return false; });
break
}
//
}
function ficbook_fb2andlike(){
var a= ffn$('a[href^="/fanfic_download/fb2/"]');
ffn$('.like_story').click();
document.location = a.attr("href");
return false;
}
ffn$('.liker_script_options').append('| FFN Options');
ffn$('.liker_script_options').after(
"