Tumblr Savior

Saves you from ever having to see another post or notes about certain things ever again (heavily modified by Vindicar).

当前为 2015-04-16 提交的版本,查看 最新版本

// ==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&notes', 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或关注我们的公众号极客氢云获取最新地址