Saves you from ever having to see another post or notes about certain things ever again (heavily modified by Vindicar).
当前为
// ==UserScript==
// @name Tumblr Savior
// @namespace bjornstar
// @description Saves you from ever having to see another post or notes about certain things ever again (heavily modified by Vindicar).
// @version 2.7.3
// @grant unsafeWindow
// @grant GM_getValue
// @grant GM_setValue
// @grant GM_registerMenuCommand
// @include http://www.tumblr.com/*
// @include https://www.tumblr.com/*
// @exclude http://www.tumblr.com/inbox
// @exclude http://www.tumblr.com/help
// @exclude https://www.tumblr.com/inbox
// @exclude https://www.tumblr.com/help
// ==/UserScript==
'use strict';
(function(){
if ( (typeof unsafeWindow.Tumblr === 'undefined') ||
(typeof unsafeWindow.jQuery === 'undefined'))
return;
function RunInPage(func) {
var s = document.createElement("script");
s.textContent = "(" + func + ")();";
document.body.appendChild(s);
setTimeout(function(){document.body.removeChild(s)}, 0);
}
//If we have no access to Greasemonkey methods, we will need dummy replacements
if (typeof GM_getValue === 'undefined')
GM_getValue = function (target, deflt) { return deflt; };
if (typeof GM_setValue === 'undefined')
GM_setValue = function (target, value) { alert('Can not save value of "'+target+'" - GM_setValue not found.'); };
if (typeof GM_registerMenuCommand === 'undefined')
GM_registerMenuCommand = function () {};
// =======================DEFAULT VALUES=======================
//you can edit these if you are using the script without Greasemonkey
//black list
var blk = GM_getValue('blacklist', /*default goes here >>>*/'');
//gray list
var gry = GM_getValue('graylist', /*default goes here >>>*/'');
//white list
var wht = GM_getValue('whitelist', /*default goes here >>>*/'');
//process notes
var nts = GM_getValue('notes', /*default goes here >>>*/false);
//process HTML code, not text
var html = GM_getValue('inhtml', /*default goes here >>>*/true);
//hide sponsored posts
var spns = GM_getValue('nosponsored', /*default goes here >>>*/false);
//process posts from recommended blogs
var rcmdactions = { DONOTHING : '0', WHITELIST : '1', REMOVE : '2', SPOILER : '3'};
var rcmd = GM_getValue('recommended', /*default goes here >>>*/rcmdactions.REMOVE);
// ===================END OF DEFAULT VALUES===================
function parseList(list) {
var lst = list.split(';');
var res = [];
var term;
for (var i=lst.length-1;i>=0;i--) {
term = lst[i];
if (term.trim().length>0)
res.push(term.toLowerCase());
}
res.sort();
return res;
}
function menuList(target, caption, deflt, description) {
if (typeof description === 'undefined')
description = caption;
GM_registerMenuCommand(caption, function() {
var list = GM_getValue(target, deflt);
list = prompt(description, list);
if (list != null) {
var res = parseList(list);
GM_setValue(target, res.join(';'));
}
});
}
function menuCheckbox(target, caption, deflt, mark) {
if (typeof mark === 'undefined')
mark = ['[ ] ','[X] '];
var value = !!GM_getValue(target,deflt);
GM_registerMenuCommand(mark[value?1:0]+caption, function(){
GM_setValue(target, !GM_getValue(target,deflt));
});
}
function menuQuery(target, caption, options, deflt, description) {
if (typeof description === 'undefined')
description = caption;
for (var opt in options)
description += '\n'+opt.toString()+' : '+options[opt].toString();
var value = GM_getValue(target,deflt);
GM_registerMenuCommand(caption, function() {
value = prompt(description, value);
if (value !== null) {
GM_setValue(target, value);
}
});
}
menuList('blacklist', 'Edit blacklist', blk, 'Enter blacklisted words(delimiter is ";"):');
menuList('whitelist', 'Edit whitelist', wht, 'Enter whitelisted words(delimiter is ";"):');
menuList('graylist', 'Edit graylist', gry, 'Enter graylisted words(delimiter is ";"):' );
menuCheckbox('notes', 'Apply blacklist to notifications¬es', nts);
menuCheckbox('inhtml', 'Check HTML code instead of text', html);
menuCheckbox('nosponsored', 'Hide sponsored posts', spns);
var recommendedoptions = {};
recommendedoptions[rcmdactions.DONOTHING] = 'process like any other post';
recommendedoptions[rcmdactions.WHITELIST] = 'whitelist recommended posts';
recommendedoptions[rcmdactions.REMOVE] = 'remove recommended posts';
recommendedoptions[rcmdactions.SPOILER] = 'hide recommended posts under spoiler';
menuQuery('recommended', 'Action for recommended posts', recommendedoptions, rcmd);
var rules = {
UNAFFECTED : -1,
WHITELISTED : 0,
GRAYLISTED : 1,
BLACKLISTED : 2,
BLOCKEDOTHER : 3,
rcmdactions : rcmdactions,
notes : nts,
inhtml : html,
nosponsored : spns,
recommended : rcmd,
blackList : parseList(blk),
grayList : parseList(gry),
whiteList : parseList(wht),
};
// pre Firefox 30/GM2.0 compatibility:
if (typeof cloneInto === 'function')
unsafeWindow.tumblrsaviour_rules = cloneInto(rules, unsafeWindow);
else
unsafeWindow.tumblrsaviour_rules = rules;
RunInPage(function(){
var $ = jQuery;
function check(rules, $item) {
if (rules.inhtml) {
var theStr = '';
$item.each(function () {
theStr += $(this).html().toLowerCase();
});
}
else {
var theStr = $item.text().toLowerCase();
}
var result = {status: rules.UNAFFECTED, array:[]};
//Checking white list - first priority
for (var i = 0; i < rules.whiteList.length; i++) {
if (theStr.indexOf(rules.whiteList[i]) >= 0) {
result.array.push(rules.whiteList[i]);
//break; //uncomment this to speed things up a bit if you have huge white list, but you will only know one reason for whitelisting.
}
}
if (result.array.length > 0) {
result.status = rules.WHITELISTED;
return result;
}
//Checking black list - second priority
for(var i = 0; i < rules.blackList.length; i++) {
if(theStr.indexOf(rules.blackList[i]) >= 0) {
result.array.push(rules.blackList[i]);
//break; //uncomment this to speed things up a bit if you have huge black list, but you will only know one reason for blacklisting.
}
}
if (result.array.length > 0) {
result.status = rules.BLACKLISTED;
return result;
}
//Checking gray list - third priority
for(var i = 0; i < rules.grayList.length; i++) {
if(theStr.indexOf(rules.grayList[i]) >= 0) {
result.array.push(rules.grayList[i]);
//break; //uncomment this to speed things up a bit if you have huge gray list, but you will only know one reason for graylisting.
}
}
if (result.array.length > 0)
result.status = rules.GRAYLISTED;
else
result.status = rules.UNAFFECTED;
return result;
}
function spoilerPost($el, reason) {
var id = $el.attr('id').substring(5);
//content wrapper
var $div_filtered = $('<div style="display:none;"></div>');
//post content
var $post_content = $el.find('.post_content_inner');
// spoiler message
var $div_notice = $('<div class="saviour_placeholder">You have been saved from this post because of: '+reason+'. <i onclick="window.jQuery(this).closest(\'.saviour_placeholder\').hide().next().show();return false;">Click here</i> if you cannot resist the temptation.</div>');
$div_notice.insertBefore($post_content);
$div_filtered.insertBefore($post_content);
$div_filtered.append($post_content);
}
function hidePost($el) {
//look for the next post
var $next = $el.next('li');
var $prev = $el.prev('li');
if (!$el.children('.post').hasClass('same_user_as_last') && $next.children('.post').hasClass('same_user_as_last')) //if current post is the first one in the chain
// if there is next post and it's of the same author
$next.children('.post').removeClass('same_user_as_last'); //we mark next post as being first in the chain
if (($next.hasClass('post_container') && $prev.hasClass('post_container')) &&
($next.find('.post_avatar').data('blog-url')==$prev.find('.post_avatar').data('blog-url')))
$next.children('.post').addClass('same_user_as_last');
//workaround: removing the element causes conflicts with ImgLikeOpera extension.
$el.hide();
//removing class so tumblr keyboard navigation won't recognize it as post.
$el.removeClass('.post');
}
function processPosts() {
var posts = $('#posts').find('.post.not_mine:not([data-saviour-status])');
posts.each(function(){
var $el = $(this);
var $check = $([])
.add($el.find(".post_header"))
.add($el.find(".post_content"))
.add($el.find(".post_tags"));
if (tumblrsaviour_rules.inhtml) {
var $clone = $check.clone();
$clone.find('*[data-tumblelog-popover]').removeAttr('data-tumblelog-popover');
$check = $clone;
}
var savedfrom = check(tumblrsaviour_rules, $check);
// Sponsored posts are hidden, unless specified otherwise
if (tumblrsaviour_rules.nosponsored && $el.hasClass('sponsored_post')) {
$el.attr('data-saviour-status', tumblrsaviour_rules.BLOCKEDOTHER);
$el.attr('data-saviour-reason', 'sponsored post');
hidePost($el);
}
// Recommended posts are processed by the logic below, unless
else if ($el.hasClass('is_recommended') &&
(tumblrsaviour_rules.recommended != tumblrsaviour_rules.rcmdactions.DONOTHING) && // 1) it's specifically said not to do so
(savedfrom.status != tumblrsaviour_rules.BLACKLISTED) && // 2) it's been blacklisted (black list has a priority)
//(savedfrom.status != tumblrsaviour_rules.WHITELISTED) && // 3) it's been whitelisted (feature disabled for now)
true) {
switch (tumblrsaviour_rules.recommended) {
// whitelisting recommended post
case tumblrsaviour_rules.rcmdactions.WHITELIST: {
$el.attr('data-saviour-status', tumblrsaviour_rules.WHITELISTED);
$el.attr('data-saviour-reason', 'recommended post');
}; break;
// blacklisting recommended post
case tumblrsaviour_rules.rcmdactions.REMOVE: {
$el.attr('data-saviour-status', tumblrsaviour_rules.BLOCKEDOTHER);
$el.attr('data-saviour-reason', 'recommended post');
hidePost($el);
}; break;
// graylisting recommended post
case tumblrsaviour_rules.rcmdactions.SPOILER: {
$el.attr('data-saviour-status', tumblrsaviour_rules.GRAYLISTED);
$el.attr('data-saviour-reason', 'recommended post');
spoilerPost($el, 'recommended post');
}; break;
}
}
// we process the post normally
else {
$el.attr('data-saviour-status', savedfrom.status);
$el.attr('data-saviour-reason', savedfrom.array.join(';'));
switch (savedfrom.status) {
case tumblrsaviour_rules.UNAFFECTED: break;
case tumblrsaviour_rules.WHITELISTED: break;
case tumblrsaviour_rules.GRAYLISTED: spoilerPost($el, savedfrom.array.join(';')); break;
case tumblrsaviour_rules.BLACKLISTED: hidePost($el); break;
}
}
});
//Asking Tumblr keyboard control script to regenerate it's positions.
if (typeof Tumblr.KeyCommands !== 'undefined')
Tumblr.KeyCommands.update_post_positions();
}
function processNotifications() {
$('#posts').find('li.notification:not([data-saviour-status])').each(function(){
var $el = $(this);
var savedfrom = check(tumblrsaviour_rules, $el);
$el.attr('data-saviour-status',savedfrom.status);
if (savedfrom.status==tumblrsaviour_rules.BLACKLISTED) {
var $prev = $el.prev('li');
var $next = $el.next('li');
if ($el.hasClass('first_notification') && $next.hasClass('notification')) {
if ($next.hasClass('last_notification'))
$next.addClass('single_notification').removeClass('last_notification');
else
$next.addClass('first_notification');
}
else if ($el.hasClass('last_notification') && $prev.hasClass('notification')) {
if ($prev.hasClass('first_notification'))
$prev.addClass('single_notification').removeClass('first_notification');
else
$prev.addClass('last_notification');
}
$el.hide();
}
});
}
function processSponsoredNotifications() {
//process sponsored notifications
$('#posts').find('li.notification:not([data-saviour-status]:has(.takeover-banner-link))').each(function(){
$(this).hide();
});
}
function processNotes(context) {
var notes = $('li.note', context);
notes.each(function(){
var $el = $(this);
var savedfrom = tumblrsaviour_rules.check($el);
if (savedfrom.status==tumblrsaviour_rules.BLACKLISTED) {
$el.hide();
}
});
}
// if we're going to process notifications, he have to do it first, because post chains are dependent on them
if (tumblrsaviour_rules.notes) {
//filter notifications that are rendered already
processNotifications();
}
//we process sponsored notifications independently from regular notifications option
if (tumblrsaviour_rules.nosponsored)
processSponsoredNotifications();
//process posts that are loaded already
processPosts();
//and ensure we are notified whenever new portion of posts is loaded
AfterAutoPaginationQueue.push(processPosts);
//also posts are loaded whenever new post is made. we should know about it.
if (tumblrsaviour_rules.notes) { //we should process notes as well
//installing filter for the notes
var notes = Tumblr.Notes.prototype;
// we have to replace the function called whenever notes are to be loaded with our own
var _load_notes = notes.load_notes;
notes.load_notes = function(post,options,fn){
//the idea is to allow Tumblr engine to load notes...
_load_notes.call(this,post,options,(function(data){
//...and render those notes...
var res = fn(data);
//...but also to filter them immediately ourselves
processNotes(post);
return res;
}) );
};
}
});
})();
QingJ © 2025
镜像随时可能失效,请加Q群300939539或关注我们的公众号极客氢云获取最新地址