Tumblr Savior

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

目前為 2014-07-12 提交的版本,檢視 最新版本

// ==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.6.0
// @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==
(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);
//hide posts from recommended blogs
var rcmd = GM_getValue('norecommended', /*default goes here >>>*/false);
// ===================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.length == 0))
		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(';'));
			location.reload();
			}
		});
	}
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));
		location.reload();
		});	
	}

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);
menuCheckbox('norecommended', 'Hide recommended posts', rcmd);

var rules = {
	UNAFFECTED		: -1,
	WHITELISTED		: 0,
	GRAYLISTED		: 1,
	BLACKLISTED		: 2,
	BLOCKEDOTHER	: 3,
	notes			: nts,
	inhtml			: html,
	nosponsored		: spns,
	norecommended	: rcmd,
	blackList		: parseList(blk),
	grayList		: parseList(gry),
	whiteList		: parseList(wht),
	};
unsafeWindow.tumblrsaviour_rules = cloneInto(rules, unsafeWindow);

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();
			}

		for (var i=0;i<rules.whiteList.length;i++) {
			if(theStr.indexOf(rules.whiteList[i])>=0) {
				//console.log('White:'+this.whiteList[i]);
				return {status:rules.WHITELISTED, array:[rules.whiteList[i]]};
				}
			}
		for(var i=0;i<rules.blackList.length;i++) {
			if(theStr.indexOf(rules.blackList[i])>=0) {
				//console.log('Black:'+this.blackList[i]);
				return {status:rules.BLACKLISTED, array:[rules.blackList[i]]};
				}
			}
		var graylisted = new Array();
		for(var i=0;i<rules.grayList.length;i++) {
			if(theStr.indexOf(rules.grayList[i])>=0) {
				//console.log('Gray:'+this.grayList[i]);
				graylisted.push(rules.grayList[i]);
				}
			}
		if (graylisted.length>0) 
			return {status:rules.GRAYLISTED, array:graylisted};
		else
			return {status:rules.UNAFFECTED, array:[]};
		}

	function spoilerPost($el, reason) {	
		var id = $el.attr('id').substring(5);
		//console.log('covering post',id,'because of',reason);
		//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, reason) {
		//console.log('hiding post',$el.attr('id').substring(5),'because of',reason);
		//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);
			if ( (tumblrsaviour_rules.nosponsored && ($el.hasClass('sponsored_post') > 0)) ||
				 (tumblrsaviour_rules.norecommended && ($el.hasClass('is_recommended') > 0)) ) {
				hidePost($el);
				$el.attr('data-saviour-status', tumblrsaviour_rules.BLOCKEDOTHER);
			} 
			else {
				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);
				$el.attr('data-saviour-status', savedfrom.status);
				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, savedfrom.array.join(';')); 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 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();
				}
			});
		}

	//console.log ('White list: "'+tumblrsaviour_rules.whiteList.join('";"')+'"');
	//console.log ('Black list: "'+tumblrsaviour_rules.blackList.join('";"')+'"');
	//console.log ('Gray list: "'+tumblrsaviour_rules.grayList.join('";"')+'"');

	// 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(); 
		}

	//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.
	(function () {
		var render = Tumblr.PostForms.RenderPost.prototype;
		var _render_posts = render.render_posts; //this method is called when new post is ready and upcoming posts should be added to the dashboard
		render.render_posts = function (URL, insertPos, htmlAndDomHandler, domHandler) {
			return _render_posts.call(this, 
				URL, 
				insertPos, 
				function(){ //this function is called after the posts are added
					if (tumblrsaviour_rules.notes) 
						processNotifications();
					processPosts();
					if (typeof htmlAndDomHandler == 'function')
						return htmlAndDomHandler.apply(this,arguments);
					},
				domHandler);
			};
		})();
	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或关注我们的公众号极客氢云获取最新地址