Tumblr Tamer

Restyles Tumblr's new reblogging template, and makes it look like before. Plus adds some bonus features!

// ==UserScript==
// @name         Tumblr Tamer
// @namespace    fortytwo
// @homepageURL	 https://greasyfork.org/en/users/14247-fortytwo
// @supportURL	 http://awesomefortytwo.tumblr.com/tagged/tumblr-tamer
// @version      5.3
// @description  Restyles Tumblr's new reblogging template, and makes it look like before. Plus adds some bonus features!
// @author       fortytwo
// @match        https://www.tumblr.com/*
// @noframes
// @require		 https://ajax.googleapis.com/ajax/libs/jquery/2.1.4/jquery.min.js
// @grant		 GM_getValue
// @grant		 GM_setValue
// @compatible	 firefox
// @compatible	 chrome
// @compatible	 opera
// ==/UserScript==

/*
todo

* enlarge images







*/

/***
	NOTICE: YOU ARE AGREEING THAT ANY USE OF THE FOLLOWING SCRIPT IS AT
	YOUR OWN RISK. I DO NOT MAKE ANY GUARANTEES THE SCRIPT WILL WORK, NOR 
	WILL I HOLD MYSELF ACCOUNTABLE FOR DAMAGE TO YOUR DEVICE.
	
	WHILE THE SCRIPT IS UNLIKELY TO CAUSE ANY HARM, AS WITH ALL TECHNICAL
	COMPONENTS, BUGS AND GLITCHES CAN HAPPEN.

	IF THE SCRIPT ISN'T WORKING FOR YOU, FEEL FREE TO SEND ME A MESSAGE: http://awesomefortytwo.tumblr.com/
***/
(function(){
	function inIframe(){
		try {
			return window.self !== window.top;
		} catch (e) {
			return true;
		}
	}
	//Catch in case @noframes doesn't work: Don't run in frames
	if(inIframe()){
		return;
	}

	var theme			= GM_getValue('theme', 'tumblrOriginal'),
		colours			= GM_getValue('colours', 'D3D3D3'),
		barwidth		= GM_getValue('barwidth', '4'),
		padding			= GM_getValue('padding', '18'),
		scrollthreshold = GM_getValue('scrollthreshold', '2000'),
		coloursArr		= colours.split(" "),
		peepchange		= null;

	var obs_change = new MutationObserver(function(mutations){
		var peeper = $('.peepr-drawer-container');
		for(var i = 0; i < mutations.length; ++i){
			var mutation = mutations[i];
			for(var j = 0; j < mutation.addedNodes.length; ++j){
				addPostObserver(peeper.find('.posts'), 'peeper');
			}
		}
	});

	/* Add things used on all pages */
	//Allows us to reverse results
	jQuery.fn.reverse = [].reverse;
	createStylesheet();
	observePeeper();

	/* FUNCTIONS */
	function createStylesheet(){
		var stylesheet = document.createElement('style');
		var styles = [
			".tumblrtamer { }",
			//".tumblrtamer .reblog-content > * {margin:0px 0px 5px 0px}",
			".tumblrtamer .reblog-list-item{ border-bottom: none !important; border-left:", barwidth, "px solid #fff; padding:0px ", padding, "px; background:#fff; margin:0px; border-bottom:none; min-width:80px}",
			".tumblrtamer .reblog-header{margin:0px 0px 5px 0px;}",
			".tumblrtamer-container{margin:5px 0px 0px 0px; padding: 0px 0px 0px 20px; background:#fff; overflow: auto; ", (scrollthreshold > 0 ? ("max-height:"+scrollthreshold+"px;") : ''), "}",
			".tumblrtamer .tmblr-full{ margin-left: 0px !important; margin-right: 0px !important;}",
			".tumblrtamer .contributed-content{border:0px none !important; padding:0px ", padding, "px }",
			".tumblrtamer .inactive {font-weight:bold;}",
			".tumblrtamer .inactive .reblog-tumblelog-deactivated-status{display:none; font-weight: none; color:#a2a2a2; }",
			".tumblrtamer .inactive .reblog-tumblelog-deactivated-status:before{content: ' ';}",
			".tumblrtamer .inactive:hover > .reblog-tumblelog-deactivated-status{display:inline;}"
		];
		
		//conditional styling
		if(theme == 'tumblrTamer' || theme == "tumblrTamerNoIcons"){
			styles.push(".tumblrtamer .reblog-header{margin-top: 5px}");
		}
		
		if(theme == 'tumblrTamerNoIcons' || theme == 'tumblrOriginal'){
			styles.push('.tumblrtamer .reblog-header{padding-left:0px !important;}');
		}
		if(theme == 'tumblrOriginal'){
			styles.push('.tumblrtamer .reblog-avatar-image-thumb{ width:25px !important; height: 25px !important }');
			styles.push('.tumblrtamer a.reblog-tumblelog-name.post_info_link {font-weight:normal !important; font-size:15px !important; border-bottom: 1px solid #444; color: #444 !important; text-decoration: none !important; background-size: 1em 2px; padding-bottom: 0.15em;}');
			styles.push('.tumblrtamer a.reblog-tumblelog-name.post_info_link:after{content: ":"}');
		}

		stylesheet.textContent = styles.join('');
		document.body.appendChild(stylesheet);
	};

	//Fetches the next colour index
	function fetchColour(curIndex){
		return (coloursArr[curIndex + 1] == undefined ? 0 : curIndex + 1);
	};

	function loadExternalImage(){
		var div = $(this);
		div.html(div.attr('data-loading-text'));
		div.replaceWith($("<p><img src='"+div.attr('data-src')+"' /></p>"));
	};

	//Function to fix the post
	function fixPost(post){
		var container		= $('<div class="tumblrtamer-container"></div>'),
			contr_container = $('<div class="tumblrtamer-contributed"></div>'),
			chain			= container,
			contributed		= post.find('.contributed-content'),
			content			= post.find(".post_content"),
			colourIndex		= 0;

		//so we can use our styling
		post.addClass('tumblrtamer');

		//Fix the contributed content by adding it outside of our new reblogs
		contributed.find('.reblog-header').remove();
		contributed.appendTo(contr_container);

		//Fix external images
		post.find('.external-image-wrapper').on('click', loadExternalImage);

		//Fetch a list of reblogs and add them inside of each other
		var reblogs	= post.find('.reblog-list-item').reverse();
		
		//Remove icon if we have to
		if(theme === 'tumblrTamerNoIcons' || theme === 'tumblrOriginal'){
			post.find('.reblog-avatar').remove();
		}

		for(var i = 0; i < reblogs.length; i++){
			var li = $(reblogs[i]);
			li.css('border-left-color', "#"+coloursArr[colourIndex]);
			li.prependTo(chain);

			if(theme === 'tumblrOriginal'){
				//need to put the header before the content
				li.find('.reblog-header').prependTo(chain);
			}
			chain = li;
			colourIndex = fetchColour(colourIndex);
		}
		//done, remove the original blog list
		post.find(".reblog-list").remove();

		//Put ours in
		container.insertAfter(content);
		contr_container.insertAfter(container);
	};

	//Observes blog posts
	function addPostObserver($for, type){
		//Fix initial posts
		var clsName = (type === 'peeper' ? 'post' : 'post_container');
		$for.find('.'+clsName).each(function(index, value){
			fixPost($(value));
		});

		//When an ajax call loads more posts
		var observer = new MutationObserver(function(mutations) {
			for(var i = 0; i < mutations.length; ++i){
				var mutation = mutations[i];
				for(var j = 0; j < mutation.addedNodes.length; ++j){
					var element = $(mutation.addedNodes[j]);
					if(element.hasClass(clsName)){
						fixPost(element);
					}
				}
			}
		});

		//Observe the posts list
		observer.observe($for[0], { childList: true });
		
		//For peeper, we want to cache the observer so we can remove it
		//during swaps to avoid memory leaks.
		if(type == 'peeper'){
			peepchange = observer;
		}
	};

	function observePeeper(){
		var obs_peep = new MutationObserver(function(mutations){
			for(var i = 0; i < mutations.length; ++i){
				var mutation = mutations[i];
				for(var j = 0; j < mutation.addedNodes.length; ++j){
					var element = $(mutation.addedNodes[j]);
					//Peeper was added
					if(element.hasClass('peepr-drawer-container')){
						//Clean up in case we've changed blog inline
						cleanUpPeeperObserver();
						
						//Observe the new content
						addPostObserver(element.find('.posts'), 'peeper');
						obs_change.observe(element.find('.peepr-drawer')[0], { childList: true });
						return;
					}
				}
				
				//Peeper is being closed. Clean up.
				for(var j = 0; j < mutation.removedNodes.length; ++j){
					var element = $(mutation.removedNodes[j]);
					//Peeper was added
					if(element.hasClass('peepr-drawer-container')){
						cleanUpPeeperObserver();
						return;
					}
				}
			}
		});
		obs_peep.observe(document.body, { childList: true });
	};

	function cleanUpPeeperObserver(){
		if(peepchange !== null){
			peepchange.disconnect();
			peepchange = null;
		}
	};

	//for testing purposes
	//window.setInterval(function(){ console.log(peepchange); }, 5000);

	/* PAGES AFFECTED */
	//Make sure to accomodate for users with infinite dashboard off
	if(
		window.location.href.indexOf("www.tumblr.com/dashboard") > 0
		|| window.location.href.indexOf("www.tumblr.com/blog/") > 0
		|| window.location.href.indexOf("www.tumblr.com/liked/by/") > 0
		|| window.location.href.indexOf("www.tumblr.com/likes") > 0){

		if(document.getElementById('posts')){
			addPostObserver($('#posts'), 'regular');
		}
	}
	//Search page
	else if(window.location.href.indexOf("www.tumblr.com/search/") > 0){
		if(document.getElementById('search_posts')){
			addPostObserver($('#search_posts'), 'regular');
		}
	}
	//Settings page
	else if(window.location.pathname === "/settings/account"){
		//Function to add options to the themer select
		var makeThemer = function(select){
			var themes =[
				{ value: 'tumblrTamer', name: 'Tumblr Tamer' },
				{ value: 'tumblrOriginal', name: 'Tumblr Original' },
				{ value: 'tumblrTamerNoIcons', name: 'Tumblr Tamer (no icons)'}
			];

			for(var i = 0; i < themes.length; ++i){
				var item = themes[i];
				select.append('<option value="'+item.value+'"'+(theme == item.value ? ' selected="selected"' : '')+'>'+item.name+'</option>');
			}
		};

		//add our new settings
		var html = [
			'<div class="settings_group v_center">',
				'<div class="group_label">',
					'<h3 class="settings_subheading">Tumblr Tamer</h3>',
				'</div>',
				'<div class="group_content">',
					'<div class="select setting">',
						'Display: <select id="tumblrtamer-theme"></select>',
						'<div style="border-top:1px solid lightgrey; margin-top:5px">',
							'<input type="number" min="10" max="50" id="tumblrtamer-padding" value="', padding, '" /><label for="tumblrtamer-padding">Padding (px)</label><br />',
							'<input type="number" min="1" max="10" id="tumblrtamer-barwidth"  value="', barwidth, '" /><label for="tumblrtamer-barwidth">Bar width (px)</label><br />',
							'<input type="number" min="0" id="tumblrtamer-scrollthresh"  value="', scrollthreshold, '" /><label for="tumblrtamer-scrollthresh">Reblog list height threshold (px)</label><br />Put as 0 if you don\'t want any scrollbar',
							'<input type="text" id="tumblrtamer-colour" style="width:100%"  value="', colours, '" /><label for="tumblrtamer-colour">Colour (hex)</label><br />Seperate with a space for alternating colours e.g: D3D3D3 C0FFEE FF0000',
							"<div id='tumblrtamer-errlog' style='color: red; font-weight:bold;'></div>",
						'</div>',
					'</div>',
				'</div>',
			'</div>'
			].join('');
		$(html).insertBefore($('.settings_footer'));
		var select = $('#tumblrtamer-theme');
		makeThemer(select);

		//Set any new settings
		select.on('change', function(){GM_setValue('theme', $.trim(this.options[this.selectedIndex].value)); });
		$('#tumblrtamer-padding').on('change', function(){ GM_setValue('padding', $.trim($(this).val())); });
		$('#tumblrtamer-barwidth').on('change', function(){ GM_setValue('barwidth', $.trim($(this).val())); });
		$('#tumblrtamer-scrollthresh').on('change', function(){ GM_setValue('scrollthreshold', $.trim($(this).val())); });
		$('#tumblrtamer-colour').on('change', function(){
			var errlog = $('#tumblrtamer-errlog'),
				errors	= [],
				regex	= /^[a-z0-9]+$/i,
				colours = $.trim($(this).val()).split(" ");

			//verify
			for(var i = 0; i < colours.length; ++i){
				var colour = colours[i];
				if(!regex.test(colour)){
					errors.push(colour + " (must be alphanumeric)");
				}
				if(colour.length != 6){
					errors.push(colour + " (must be 6 characters)");
				}
			}

			//No problems found
			if(!errors.length){
				GM_setValue('colours', $.trim($(this).val()));
			}
			//Tell user to fix things
			else{
				var html = "Some errors were found. Please look at the following colours: <ul>";
				for(var i = 0; i < errors.length; ++i){
					html += "<li>"+errors[i]+"</li>";
				}
				html += "</ul>";
				errlog.html(html);
			}
		});
	}
})();