LJ Thread Unfolder - Unfold All, All layouts

By MrMord Last update Nov 24, 2007 — Installed 2,994 times.
/*

 * LJ Thread Unfolder

 * version 0.4.2

 * 2007-11-24

 * Copyright (c) 2005, Tim Babych, Copyright (c) 2007, MrMord

 * Homepage: http://clear.com.ua/projects/firefox/unfolder

 * Released under the GPL license

 * http://www.gnu.org/copyleft/gpl.html

 *

 * Branched 2006-09-09 by Henrik Nyh <http://henrik.nyh.se>

 * Added "Unfold all".

 *

 * Branched 2007-11-18 by MrMord <http://mrmord.livejournal.com>

 * Removed "Unfold all" and fixed script to work for "Smooth Sailing" layout

 * Will add extra features and cross compatibility if people are interested

 *

 * Update 2007-11-23 by MrMord
 * Fixed layout compatibility and unfolding options, unfold all works again

 * Researching recursive unfold

 *

 * Update 2007-11-24 by MrMord

 * Fixed compatibility with Opera, requires a greasemonkey userjs script found here: http://www.howtocreate.co.uk/operaStuff/userjs/aagmfunctions.js

 * Added insanejournal.com and greatestjournal.com to the included scripts page

 *

 * Update 2007-11-24 by MrMord

 * Made "unfold" link even more generic and able to work on even more layouts

 */



// ==UserScript==

// @name			LJ Thread Unfolder - Unfold All, All layouts 

// @namespace		http://mrmord.livejournal.com/1170.html

// @description		Expands nested discussion threads in place.

// @include			http://*.livejournal.com/*

// @include			http://*.insanejournal.com/*

// @include			http://*.greatestjournal.com/*

// @include			http://*.journalfen.net/*

// ==/UserScript==





/**

 * Insert "Unfold" links

 */
var threadIdRE	= /thread=(\d+)/;


var usernode	= '';

var validnode	= false;
var nodeTrue	= true;

var unfoldNodes	= [];
var allReplies	= $x("//div/a[contains(@href, '?thread')] | //td/a[contains(@href, '?thread')]");




// Loop through all instances that may need an "unfold" link

allReplies.forEach(function( thisReplyLink )

{

	// If there is a sibling node then we're on the right track!

	if ( thisReplyLink.nextSibling )

	{

		// Find the commentors username node

		usernode = thisReplyLink.nextSibling;

		while( !validnode && nodeTrue )

		{

			if ( usernode.nodeType == 1 )

			{

				if ( usernode.getAttribute( 'class' ) == 'ljuser' )

				{

					validnode = usernode;

				}

			}

			usernode = usernode.nextSibling;

				

			if ( usernode == undefined )

			{
				// Set to false to end the while loop gracefully

				nodeTrue = false;

			}

		}
		// Set to true so that the while loop can continue on the next iteration
		nodeTrue = true;

		

		// Display "unfold" link on hidden comments only (wip)

		if ( validnode )

		{

			t = document.createTextNode( ' - ' );

			thisReplyLink.parentNode.insertBefore( t, thisReplyLink.nextSibling );



			var threadId = thisReplyLink.href.match(threadIdRE)[1];

			var replyUrl = thisReplyLink.href.split( "#" )[0];



			a = document.createElement('A');

			with (a)

			{

				innerHTML = "Unfold";

				href = 'javascript:void(0)';

				addEventListener("click", unfold_click, false)

				setAttribute('thread_id', threadId);

				setAttribute('thread_href', replyUrl+'&format=light');

			}



			t.parentNode.insertBefore(a, t.nextSibling);

			unfoldNodes.push(a);

		

			d = document.createElement('div');

			d.style.display = 'none';

			d.id = 'thread_unfolder_temp_div_' + threadId;

			document.body.appendChild(d);

		

		} // End proper display statement

	}
	
	// Make validnode no longer valid (don't want false positives)
	validnode = false;

});





/**

 * Insert "Unfold all" link

 */

if ( unfoldNodes.length )

{

	// Find reply links that contain a <b> tag

	$x("//a[contains(@href, '?mode=reply')]/ancestor::b").forEach(function(b)

	{

		var comma = document.createTextNode(', ');

		b.appendChild(comma);

		

		var newA = document.createElement("A");

		with (newA)

		{

			innerHTML = "Unfold all";

			href = "javascript:void(0)";

			addEventListener("click", unfold_all, false);

		}

	

		b.appendChild( newA );

	});

}





/**

 * Unfolding functions

 */

function unfold_all()

{

	unfoldNodes.forEach( unfold_thread );

}



function unfold_click()

{

	unfold_thread( this );

}

 

function unfold_thread( link )

{

	with ( link )

	{

		className = 'unfold_thread';

		innerHTML = loader;

		blur();

	}

	elem = link;



	(function(elem) {



		GM_xmlhttpRequest({

			method: 'GET',

			url: link.getAttribute('thread_href'),

			onload: function( responseDetails )

			{

				dummy = document.getElementById('thread_unfolder_temp_div_'+elem.getAttribute('thread_id'));

				dummy.innerHTML = responseDetails.responseText.replace( /ljcmt/i, "ljunfold_cmnt" );

				reply = document.getElementById( 'ljunfold_cmnt'+elem.getAttribute( 'thread_id' ) );

				

				try

				{

					// get cell

					td = elem.parentNode;

				

					// Clear place holder

					td.innerHTML = '';



					// Add unfolded comment

					td.appendChild( reply );

				}

				catch(e)

				{

					

				}



				dummy.innerHTML = '';

			}



		}); // End xmlhttpRequest()



	})(elem);

}





/**

 * Loader Image in base64

 */

loader = '<style type="text/css">'+

'a.unfold_thread {color:black; text-decoration:none; cursor:default; }'+

'a.unfold_thread:hover {color:black; text-decoration:none; }'+

'a.unfold_thread img { border:none; }'+

'</style>'+

'<img border=0 src="data:image/gif;base64,R0lGODlhEAAQAMQAAP///+7u7t3d3bu7u6qqqpmZmYi'+

'IiHd3d2ZmZlVVVURERDMzMyIiIhEREQAR'+

'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'+

'fySDhGYQdDWGQyUhADs="> Loading...'





/**

 * Global Functions

 */

if( !Array.prototype.forEach )

{

	Array.prototype.forEach = function(callback,thisObject)

	{

		for(var i=0,len=this.length;i<len;i++)

			callback.call(thisObject,this[i],i,this)

	}

	

	Array.prototype.map = function(callback,thisObject)

	{

		for(var i=0,res=[],len=this.length;i<len;i++)

			res[i] = callback.call(thisObject,this[i],i,this);

		return res

	}

	

	Array.prototype.filter = function(callback,thisObject)

	{

		for(var i=0,res=[],len=this.length;i<len;i++)

			callback.call(thisObject,this[i],i,this) && res.push(this[i]);

		return res

	}

	

	Array.prototype.indexOf = function(searchElement,fromIndex)

	{

		var i = (fromIndex < 0) ? this.length+fromIndex : fromIndex || 0;

		for(;i<this.length;i++)

			if(searchElement === this[i]) return i;

		return -1

	}

	

	Array.prototype.lastIndexOf = function(searchElement,fromIndex)

	{

		var max = this.length-1;

		var i = (fromIndex < 0)   ? Math.max(max+1 + fromIndex,0) :

			(fromIndex > max) ? max :

			max-(fromIndex||0) || max;

		for(;i>=0;i--)

			if(searchElement === this[i]) return i;

		return -1

	}

	

	Array.prototype.every = function(callback,thisObject)

	{

		for(var i=0,len=this.length;i<len;i++)

			if(!callback.call(thisObject,this[i],i,this)) return false;

		return true

	}

	

	Array.prototype.some = function(callback,thisObject)

	{

		for(var i=0,len=this.length;i<len;i++)

			if(callback.call(thisObject,this[i],i,this)) return true;

		return false

	}

} // End Prototype additions



function $( id )

{

	return document.getElementById(id);

}



function $x( path, root )

{

	if (!root) root = document;

	var i, arr = [], xpr = document.evaluate(path, root, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);

	for (i = 0; item = xpr.snapshotItem(i); i++) arr.push(item);

	return arr;

}