// ==UserScript== // @name FanFictionNavigator // @name:ru FanFictionNavigator // @license MIT // @version 76 // @namespace window // @description 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 http://www.archiveofourown.org/* // @include https://www.archiveofourown.org/* // @include https://author.today/* // @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 // @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": 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 = '
'+ 'Story: Like | '+ 'Dislike | '+ 'Mark | '+ 'InLibrary | '+ 'Clear'+ ' Author: Like | '+ 'Dislike | '+ 'Clear'+ '
'; return template; }, addActions: function() { var instance = this; _this.append(this.template()); _this.find('.new_like_actions .like_author').click(function(){ app.author.like(instance.authorId()); return false; }); _this.find('.new_like_actions .dislike_author').click(function(){ app.author.dislike(instance.authorId()); return false; }); _this.find('.new_like_actions .clear_author').click(function(){ app.author.clear(instance.authorId()); return false; }); _this.find('.new_like_actions .like_story').click(function(){ app.fic.like(instance.ficId()); return false; }); _this.find('.new_like_actions .dislike_story').click(function(){ app.fic.dislike(instance.ficId()); return false; }); _this.find('.new_like_actions .mark_story').click(function(){ app.fic.mark(instance.ficId()); return false; }); _this.find('.new_like_actions .inlibrary_story').click(function(){ app.fic.inlibrary(instance.ficId()); return false; }); _this.find('.new_like_actions .clear_story').click(function(){ app.fic.clear(instance.ficId()); return false; }); }, hide: function() { _this.hide(); }, set_story: function(){ switch(this.fic){ case FIC_LIKED: // _this.css('background-color',COLOR_LIKED); // this.$fic.css('font-weight','900') _this.addClass("ffn_like_fic"); this.$fic.addClass("ffn_like_fic"); break; case FIC_DISLIKED: _this.addClass("ffn_dislike_fic"); this.$fic.addClass("ffn_dislike_fic"); break; case FIC_MARKED: _this.addClass("ffn_mark"); this.$fic.addClass("ffn_mark"); break; case FIC_INLIBRARY: _this.addClass("ffn_inlibrary"); this.$fic.addClass("ffn_inlibrary"); break; } }, set_author: function() { if(this.author === AUTHOR_LIKED) { this.$author.addClass("ffn_like_author"); _this.addClass("ffn_like_author"); } if(this.author === AUTHOR_DISLIKED) { this.$author.addClass("ffn_dislike_author"); _this.addClass("ffn_dislike_author"); } }, like_story: function(){ this.clear_story(); _this.addClass("ffn_like_fic"); this.$fic.addClass("ffn_like_fic"); }, dislike_story: function(){ this.clear_story(); _this.addClass("ffn_dislike_fic"); this.$fic.addClass("ffn_dislike_fic"); }, mark_story: function(){ this.clear_story(); _this.addClass("ffn_mark"); this.$fic.addClass("ffn_mark"); }, inlibrary_story: function(){ this.clear_story(); _this.addClass("ffn_inlibrary"); this.$fic.addClass("ffn_inlibrary"); }, clear_story: function(){ _this.removeClass("ffn_like_fic ffn_dislike_fic ffn_mark ffn_inlibrary"); this.$fic.removeClass("ffn_like_fic ffn_dislike_fic ffn_mark ffn_inlibrary"); }, like_author: function(){ this.clear_author(); this.$author.addClass("ffn_like_author"); _this.addClass("ffn_like_author"); }, dislike_author: function(){ this.clear_author(); this.$author.addClass("ffn_dislike_author"); _this.addClass("ffn_dislike_author"); }, clear_author: function(){ _this.removeClass("ffn_like_author ffn_dislike_author"); this.$author.removeClass("ffn_like_author ffn_dislike_author"); } }; // Specific sites overrides a["www.fanfiction.net"] = { $author: _this.find('a[href^="/u"]:first'), $fic: _this.find('a[href^="/s"]:first'), authorId: function() { if (typeof this.$author.attr('href') === "undefined") { return patharr[2]; } else { return this.$author.attr('href').split('/')[2]; } }, ficId: function() { if (this.$fic.length === 0) { return patharr[2]; } else { return this.$fic.attr('href').split('/')[2]; } }, hide: function() { // do not hide story header on reading page and story block on author page if (!patharr[1].match("^s$|^u$")) _this.hide(); // do not hide fic on author pages (to clearly see how many fics you like and dislike) and on reading pages } }; // a["archiveofourown.org"] = { $author: _this.find('a[href^="/users/"]:first'), $fic: _this.find('a[href^="/works/"]:first'), authorId: function() { // old: return this.$author.attr('href').split('/')[2]; // Contributed by Vannis: if (this.$author.length === 0) { return 0; } else { return this.$author.attr('href').split('/')[2]; } }, ficId: function() { if (this.$fic.length === 0) { return patharr[2]; } else { return this.$fic.attr('href').split('/')[2]; } }, hide:function(){ if (patharr[1] !== "users" && // do not hide fic on author pages (to clearly see how many fics you like and dislike) !/(collections\/[^\/]+\/)?works\/\d+/.test(window.location.pathname)) { // do not hide fic header on reading pages) _this.hide(); } } }; // a["ficbook.net"] = { $author: _this.find('a[href^="/authors"]:first'), $fic: _this.find('a[href^="/readfic"]:first'), authorId: function() { return this.$author.attr('href').split('/')[2]; }, ficId: function() { if (this.$fic.length === 0) { return patharr[2].replace(/(\d+).*?$/,"$1"); } else { return this.$fic.attr('href').split('/')[2].replace(/(\d+).*?$/,"$1"); } }, hide:function(){ if (patharr[1]==="popular"){ _this.parent().hide(); return; } if (patharr[1]!=="readfic" && patharr[2]!=="favourites") { _this.hide(); return; } } }; // a["author.today"] = { $author: _this.find('a[href^="/u"]:first'), $fic: _this.find('a[href^="/work/"]:first'), authorId: function() { if (typeof this.$author.attr('href') === "undefined") { return patharr[2]; } else { return this.$author.attr('href').split('/')[2]; } }, ficId: function() { //console.log(this.$fic.attr('href').split('/')[2]) return this.$fic.attr('href').split('/')[2]; }, hide: function() { // do not hide story header on reading page and story block on author page if (!patharr[1].match("^work$|^u$")) _this.hide(); } }; var b = ffn$.extend({}, a["default"], a[options.namespace]); b.fic = app.fic.get(b.ficId()); b.author = app.author.get(b.authorId()); // do not show liker links if ficid or authorid are undefined (tweak for tbooklist.org) if (b.ficId() !== 0 && b.authorId() !== 0) { b.addActions(); } b.set_story(); b.set_author(); //hide if((app.options("hide_dislikes") === true && (b.fic === FIC_DISLIKED || b.author === AUTHOR_DISLIKED)) || (app.options("hide_likes") === true && (b.fic === FIC_LIKED || b.author === AUTHOR_LIKED)) || (app.options("hide_marked") === true && b.fic === FIC_MARKED) || (app.options("hide_inlibrary") === true && b.fic === FIC_INLIBRARY)){ // if(b.fic !== true && b.author) { // for liked fics of disliked authors b.hide(); // } } return b; }; // Community var Community = function(optionsin) { var c = {}; 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); c["default"] = { template: function() { var template = '
'+ 'Community: Like | '+ 'Dislike | '+ 'Clear'+ '
'; return template; }, addActions: function() { var instance = this; _this.append(this.template()); _this.find('.new_like_actions .like_community').click(function(){ app.community.like(instance.communityId()); return false; }); _this.find('.new_like_actions .dislike_community').click(function(){ app.community.dislike(instance.communityId()); return false; }); _this.find('.new_like_actions .clear_community').click(function(){ app.community.clear(instance.communityId()); return false; }); }, hide: function() { _this.hide(); }, set_community: function(){ switch(this.community){ case COMMUNITY_LIKED: _this.addClass("ffn_like_community"); this.$community.addClass("ffn_like_community"); break; case COMMUNITY_DISLIKED: _this.addClass("ffn_dislike_community"); this.$community.addClass("ffn_dislike_community"); break; } }, like_community: function(){ this.clear_community(); _this.addClass("ffn_like_community"); this.$community.addClass("ffn_like_community"); }, dislike_community: function(){ this.clear_community(); _this.addClass("ffn_dislike_community"); this.$community.addClass("ffn_dislike_community"); }, clear_community: function(){ _this.removeClass("ffn_like_community ffn_dislike_community"); this.$community.removeClass("ffn_like_community ffn_dislike_community"); } }; // Specific sites overrides c["www.fanfiction.net"] = { $community: _this.find('a[href^="/community"]:first'), communityId: function() { if (typeof this.$community.attr('href') === "undefined") { return patharr[3]; } else { return this.$community.attr('href').split('/')[3]; } }, hide: function() { // needrework for community // do not hide story header on reading page and story block on author page if (!patharr[1].match("^s$|^u$")) _this.hide(); // do not hide fic on author pages (to clearly see how many fics you like and dislike) and on reading pages } }; var d = ffn$.extend({}, c["default"], c[options.namespace]); d.community = app.community.get(d.communityId()); // do not show liker links if communityId is undefined if (d.communityId() !== 0) { d.addActions(); } d.set_community(); //hide if((app.options("hide_dislikes") === true && d.community === COMMUNITY_DISLIKED) || (app.options("hide_likes") === true && d.community === COMMUNITY_LIKED)){ d.hide(); // } } return d; }; var app = new Application({namespace:document.location.host, db: db}); // Adding action links and navigation shortcuts to pages switch(window.location.hostname){ case "www.fanfiction.net": // small tweak to allow text selection GM_addStyle("* {user-select:text !important;}"); // adding hotkeys // added toggle option, suggested by Vannius 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:contains('« Prev')"); if (typeof(Prev[0])!=='undefined') {Prev[0].click();} break; case 39: var Next = ffn$("a:contains('Next »')"); if (typeof(Next[0])!=='undefined') {Next[0].click();} break; } } }, false); } if (app.options("enable_read_hotkeys")) { document.addEventListener('keydown', function(e){ if (!e.ctrlKey && !e.altKey && !e.shiftKey) { switch (e.keyCode){ case 37: var Prev = ffn$("button:contains('< Prev')"); if (typeof(Prev[0])!=='undefined') {Prev.click();} break; case 39: var Next = ffn$("button:contains('Next >')"); if (typeof(Next[0])!=='undefined') {Next.click();} break; } } }, false); } // links in list if (patharr[1] == "communities") { ffn$(".z-list").each(function(){ var community = new Community({ namespace: app.namespace, instance: this }); app.collection.push(community); }); } else { ffn$(".z-list").each(function(){ var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); } // links on reading page ffn$("div#profile_top").each(function(){ var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); // hide/show options ffn$('div#content_wrapper_inner').after( '
'+ 'Liker Options: '+ '
' ); break; case "archiveofourown.org": // adding 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:contains('← Previous')"); if (typeof(Prev[0])!=='undefined') {Prev[0].click();} break; case 39: var Next = ffn$("a:contains('Next →')"); if (typeof(Next[0])!=='undefined') {Next[0].click();} break; } } }, false); } if (app.options("enable_read_hotkeys")) { document.addEventListener('keydown', function(e){ if (!e.ctrlKey && !e.altKey && !e.shiftKey) { switch (e.keyCode){ case 37: var Prev = ffn$("a:contains('←Previous Chapter')"); if (typeof(Prev[0])!=='undefined') {Prev[0].click();} break; case 39: var Next = ffn$("a:contains('Next Chapter →')"); if (typeof(Next[0])!=='undefined') {Next[0].click();} break; } } }, false); } // in lists // old: ffn$("ol.work.index.group > li").each(function(){ // contribution by Vannius from greasyfork site ffn$(".blurb").each(function(){ var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); // on reading page ffn$("div.preface.group").each(function(){ var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); // hide/show options ffn$('div.navigation.actions.module, div.primary.header.module').after( '
'+ 'Liker Options: '+ '
' ); break; case "ficbook.net": // on reading page if (patharr[1]==="readfic"){ //ffn$("div.row.hat-row > ul.list-unstyled").each(function() { //ffn$("section.fanfiction-hat.container-fluid").each(function() { ffn$("div.fanfic-hat-body").each(function() { // console.log(this); var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); if (app.options("enable_read_hotkeys")) { document.addEventListener('keydown', function(e){ var textcontent; if (!e.ctrlKey && !e.altKey && !e.shiftKey) { switch (e.keyCode){ case 37: var Prev = ffn$("a.btn-back"); if (Prev.length>0) window.location.href = Prev[0]; break; case 39: var Next = ffn$("a.btn-next"); if (Next.length>0) window.location.href = Next[0]; break; } } // Ctrl+ up/dn hotkeys if (e.ctrlKey && !e.altKey && !e.shiftKey) { switch (e.keyCode){ case 38: console.log("Ctrl+up") textcontent = document.getElementById("content"); var txt = textcontent.outerHTML; txt = txt.replace(/ /g," "); txt = txt.replace(/(]*id=\"content\"[^>]*>)[ ]*/m,"$1 "); txt = txt.replace(/[\r\n][\ \r\n\t]+/gm,"\r\n"); txt = txt.replace(/^((<[^>]+>)*)[ \t]+/gm,"$1"); txt = txt.replace(/\n[ \t]*/gm,"\n "); txt = txt.replace("- ","— "); textcontent.outerHTML = txt; break; /* removed since we don't have a way to completely restore formatting after deleting so much junk 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$("article.fanfic-inline").each(function() { //ffn$("div.js-toggle-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[2]==="favourites") || (patharr[1]==="authors" && patharr[3]==="profile" && patharr[4]==="works")) { ffn$("article.fanfic-inline").each(function() { // need more wide block to detect if the fic is read/unread to construct correct button var _this = ffn$(this); console.log(_this); var ficId = _this.find('a[href^="/readfic"]:first').attr('href').split('/')[2].replace(/(\d+).*?$/,"$1"); var readButton; console.log(_this.find(".read-notification").length); if (_this.find(".read-notification").length!==0 && _this.find(".new-content").length===0) { // button should be 'read' if there's a notification and there's no indication of new chapters (for consistency with readfic pages) _this.find(".new_like_actions").append(''); readButton = _this.find("button.btn-success"); } else { _this.find(".new_like_actions").append(''); readButton = _this.find("button.btn-default"); }; readButton.click(function(e){ e.preventDefault(); if (readButton.hasClass("notread")){ fanfiction_net_read(ficId); readButton.removeClass("notread btn-default "); readButton.addClass("read btn-success"); readButton.html(' Прочитано '); } else { fanfiction_net_unread(ficId); readButton.removeClass("read btn-success"); readButton.addClass("notread btn-default "); readButton.html(' Прочитано '); } }); }); } // */ // 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( '
'+ 'Liker Options: '+ '
' ); break; case "author.today": // small tweak to allow text selection GM_addStyle("* {user-select:text !important;}"); // adding hotkeys // added toggle option, suggested by Vannius 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:contains('« Prev')"); if (typeof(Prev[0])!=='undefined') {Prev[0].click();} break; case 39: var Next = ffn$("a:contains('Next »')"); if (typeof(Next[0])!=='undefined') {Next[0].click();} break; } } }, false); } if (app.options("enable_read_hotkeys")) { document.addEventListener('keydown', function(e){ if (!e.ctrlKey && !e.altKey && !e.shiftKey) { switch (e.keyCode){ case 37: var Prev = ffn$("button:contains('< Prev')"); if (typeof(Prev[0])!=='undefined') {Prev.click();} break; case 39: var Next = ffn$("button:contains('Next >')"); if (typeof(Next[0])!=='undefined') {Next.click();} break; } } }, false); } // links on fic page if (patharr[1] === "work") { ffn$("div.book-row-content").each(function(){ var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); } // links on author page if (patharr[1] === "u" && patharr[3] === "works") { ffn$("div.book-row-content").each(function(){ var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); }); } // Options panel ffn$('div#search-results').after( '
'+ 'Liker Options: '+ '
' ); ffn$('div#profile-work-search-results').after( '
'+ 'Liker Options: '+ '
' ); 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 (patharr[1] === "s") { ffn$('.new_like_actions').append('| Actions: Open all chapters'); ffn$('#ffn_open_all_chapters').click(function(e){ e.preventDefault(); fanfiction_net_open_all();}); } } if (window.location.hostname === "ficbook.net") { switch(patharr[1]){ case "find": // revert read status for all visible fics (circumvent ficbook bug where alreadн 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+Like+Read'); ffn$('.new_like_actions .ffn_ficbook_fb2_and_like').click(function(e){ e.preventDefault(); ficbook_fb2andlike(); return false; }); ffn$('.new_like_actions').append(' Open all chapters'); ffn$('#ficbook_open_all_chapters').click(function(e){ e.preventDefault(); ficbooknet_open_all();}); break } // } function ficbook_fb2andlike(){ var a= ffn$('a[href^="/fanfic_download/fb2/"]'); ffn$('.like_story').click(); document.location = a.attr("href"); ffn$('.js-mark-readed').not('.btn-success').click(); return false; } ffn$('.liker_script_options').append('| FFN Options'); ffn$('.liker_script_options').after( "" ); ffn$('#ffn_import_file_button').click(function(){ var selectedFile = document.getElementById('ffn_import_file_box').files.item(0); if (selectedFile === null) return; fileToText(selectedFile, (text) => { var new_db; try { new_db = JSON.parse(text); } catch(err) { alert("JSON data in file is invalid"); return; } localStorage.setItem("FFLikerAA", JSON.stringify(new_db)); document.location = document.location; }); }); function fileToText(file, callback) { const reader = new FileReader(); reader.readAsText(file); reader.onload = () => { callback(reader.result); }; } ffn$('#ffn_OptionsToggle').click(function(){ ffn$("#ffn_options_block").toggle(); ffn$("#ffn_checkbox_hide_likes").prop("checked",app.options("hide_likes")); ffn$("#ffn_checkbox_hide_dislikes").prop("checked",app.options("hide_dislikes")); ffn$("#ffn_checkbox_hide_marked").prop("checked",app.options("hide_marked")); ffn$("#ffn_checkbox_hide_inlibrary").prop("checked",app.options("hide_inlibrary")); ffn$("#ffn_checkbox_enable_list_hotkeys").prop("checked",app.options("enable_list_hotkeys")); ffn$("#ffn_checkbox_enable_read_hotkeys").prop("checked",app.options("enable_read_hotkeys")); return false; }); ffn$('#ffn_options_button_save').click(function(){ app.options("hide_likes", ffn$("#ffn_checkbox_hide_likes").prop("checked")); app.options("hide_dislikes", ffn$("#ffn_checkbox_hide_dislikes").prop("checked")); app.options("hide_marked", ffn$("#ffn_checkbox_hide_marked").prop("checked")); app.options("hide_inlibrary", ffn$("#ffn_checkbox_hide_inlibrary").prop("checked")); app.options("enable_list_hotkeys", ffn$("#ffn_checkbox_enable_list_hotkeys").prop("checked")); app.options("enable_read_hotkeys", ffn$("#ffn_checkbox_enable_read_hotkeys").prop("checked")); location.reload(); return false; }); // import/export db data /* old import/export, with textboxes ffn$('.backup_text .backup_data textarea').on("click",function(){ ffn$(this).select(); }); ffn$('.backup_text .save_new_data button').on("click",function(){ var v = ffn$('.backup_text .save_new_data textarea').val(); var new_db; try { new_db = JSON.parse(v); } catch(err) { alert("that data is not valid"); return; } localStorage.setItem("FFLikerAA", JSON.stringify(new_db)); document.location = document.location; }); ffn$('.backupToggle').click(function(){ ffn$(".backup_text").toggle(); ffn$(".backup_text .backup_data textarea").html(JSON.stringify(db)); return false; }); */ //ffn$('.liker_script_options').append('| test' + ''); ffn$('#ffn_export_to_file').click(function(){ var curdate = new Date(); var year = curdate.getFullYear(); var month = curdate.getMonth() + 1; month = month<10? "0"+month : month; var day = curdate.getDate(); day = day<10? "0"+day : day; export_file(JSON.stringify(db,null," "),"FFN_" + window.location.host + "_" + year + "-" + month + "-" + day + ".txt", "text/plain"); return false; }); function export_file(content, fileName, mime) { const blob = new Blob([content], { type: mime }); const url = URL.createObjectURL(blob); const a = document.createElement("a"); a.style = "display: none"; a.href = url; a.download = fileName; document.body.appendChild(a); a.click(); setTimeout(function(){ document.body.removeChild(a); window.URL.revokeObjectURL(url); }, 100); } function show_dislikes(){ app.options("hide_dislikes",false); return false; } function hide_dislikes(){ app.options("hide_dislikes",true); return false; } function show_likes(){ app.options("hide_likes",false); return false; } function hide_likes(){ app.options("hide_likes",true); return false; } function show_marked(){ app.options("hide_marked",false); return false; } function hide_marked(){ app.options("hide_marked",true); return false; } function show_inlibrary(){ app.options("hide_inlibrary",false); return false; } function hide_inlibrary(){ app.options("hide_inlibrary",true); return false; } function dislike_all(){ ffn$("div.z-list:visible").each(function() { var story = new Story({ namespace: app.namespace, instance: this }); app.collection.push(story); app.author.dislike(story.authorId()); }); } function ficbook_invertread(){ ffn$("button.btn.btn-primary.btn-success.js-mark-readed:visible").each(function() { this.click(); }) ffn$("button.btn.btn-primary.btn-default.js-mark-readed:visible").each(function() { this.click(); }) } function ficbooknet_open_all(){ // get fic id var ficid = patharr[2]; // get links to chapters var chapters = ffn$.find("ul.list-of-fanfic-parts > li > a"); var chapnum = chapters.length - 1; if (confirm("Are you sure? This will open " + chapnum + " tabs\n(Delay of 2 seconds for each tab)") === true) { for (let i=0; i