Script Details
File Name: twop_advancedfeatures.user.js
Description
This script displays all options and features that were hidden/commented out in the "member" pop up menu. As of October 2007, these features include:
- An "online" status indicator
- Add as Friend
- Send Message
- Send Email (if the member has a public email)
- The member's avatar (which is not-so-useful, since very few people have specified a picture)
This script also does everything the "TWoP: Add Reply and Multi-Quote" script does:
- Adds the "Reply" and "Toggle Multi-quote Addition" buttons to each post. These buttons appears under the poster/member name. The "Reply" button will add the post text, including the member name and a link back to the post being "replied", to the "fast reply" text area. This button will not replace text in the "fast reply" area; it will just add to it.
- Adds the ability to Hide/Show individual posts. This is done via the new "Hide" (which collapses the post) and "Show" (which expands the post) link that is placed on the end of the "Edit", "Report", "Post #" line above the post.
- Adds the ability to Ignore (and un-ignore) posts from a particular member. The member's posts are still on the page, but they will default to hidden/ collapsed (see #2 above). You can still "Show/expand" these posts. A new "Ignore Member's Posts" (and Un-ignore) menu item has been added to the popup menu that appears when you click on/hover over a member's name when reading a topic.
WARNING: Does everything (but more!) that "twop_addreplyandmultiqt.user.js" does, so do not run both. Well, you can run both, but things will not look good.
Script Source
// TWoP: Turn on Hidden Features
// Copyright (c) 2006-2007 Orbona
// Version 1.1
// Release Date: Never - this script was not publicly released
//
// See also: http://www.orbona.com/greasemonkey/
//
// Original file name: twop_advancedfeatures.user.js
// Please reference the original file name when contacting me regarding this
// script.
//
// This software is licensed under the CC-GNU GPL:
// http://creativecommons.org/license/cc-gpl
//
//---------------------------------------------------------------------------
// DESCRIPTION
//
// This script displays all options and features that were hidden/commented
// out in the "member" pop up menu. As of October 2007, these features
// include:
//
// (1) An "online" status indicator
// (2) Add as Friend
// (3) Send Message
// (4) Send Email (if the member has a public email)
// (5) The member's avatar (which is not-so-useful, since very few people
// have specified a picture)
//
// This script also does everything the "TWoP: Add Reply and Multi-Quote"
// script does:
// (1) Adds the "Reply" and "Toggle Multi-quote Addition" buttons to each post.
// These buttons appears under the poster/member name. The "Reply" button
// will add the post text, including the member name and a link back to
// the post being "replied", to the "fast reply" text area. This button
// will not replace text in the "fast reply" area; it will just add to it.
//
// (2) Adds the ability to Hide/Show individual posts. This is done via the new
// "Hide" (which collapses the post) and "Show" (which expands the post)
// link that is placed on the end of the "Edit", "Report", "Post #" line
// above the post.
//
// (3) Adds the ability to Ignore (and un-ignore) posts from a particular
// member. The member's posts are still on the page, but they will
// default to hidden/ collapsed (see #2 above). You can still
// "Show/expand" these posts. A new "Ignore Member's Posts" (and Un-ignore)
// menu item has been added to the popup menu that appears when you click
// on/hover over a member's name when reading a topic.
//
// WARNING:
// Does everything (but more!) that "twop_addreplyandmultiqt.user.js" does,
// so do not run both. Well, you can run both, but things will not look good.
// -----------------------------------------------------------------------------
//
// VERSION HISTORY
//
// 1.0 Initial release
// 1.1 Fixed some problems, caused by a change on TWoP's site, with
// restoring "commented out sections"
//
// ==UserScript==
// @name TWoP: Turn on Hidden Features
// @description Displays options/features that the TWoP admins hid
// @namespace http://www.orbona.com/greasemonkey/
// @include http://forums.televisionwithoutpity.com/index.php?showtopic*
// ==/UserScript==
var postsByMember = [];
//---------------------------------------------
// Hides (hidden=true) or shows (hidden=false) the
// specified post.
//---------------------------------------------
function doHidePost (hidden, postid, elem) {
var display = hidden ? 'none' : 'block';
var nextElem;
elem = elem || document.getElementById('post-' + postid);
if (elem) elem.style.display = display;
elem = document.getElementById('hidesinglepost_' + postid);
if (elem) elem.innerHTML = hidden ? 'Show' : 'Hide';
elem = document.getElementById('post-member-' + postid + '_menu');
nextElem = elem.nextSibling;
while (nextElem) {
if (nextElem.nodeType==1 && nextElem.tagName!='SCRIPT' && nextElem.tagName!='IMG') nextElem.style.display= display;
nextElem = nextElem.nextSibling;
}
}
//---------------------------------------------
// Handler for the ignore/un-ignore member's
// posts functionality. "postid" specifies the
// post from which this activity was initiated
// and is needed so we can hide the popup menu,
// which ditn't always disappear properly.
//---------------------------------------------
function toggleMembersPosts (memberid, postid) {
var currentpostid, element, menu;
var isHidden=!GM_getValue(memberid, false);
var currentPosts = postsByMember[memberid];
var menuString = isHidden ? "Un-ignore Member's Posts" : "Ignore Member's Posts" ;
GM_setValue(memberid, isHidden);
for (var i=0; i < currentPosts.length; i++) {
currentpostid=currentPosts[i];
doHidePost(isHidden, currentpostid);
element = document.getElementById('hideposts_' + currentpostid);
if (element) element.innerHTML =menuString;
}
menu = document.getElementById('post-member-' + postid + '_menu');
if (menu) menu.style.display='none';
}
//---------------------------------------------
// Handler for the hide/un-hide an individual
// post. "postid" specifies the post to hide
// or show.
//---------------------------------------------
function toggleHidePost (postid) {
var elem = document.getElementById('post-' + postid);
if (elem)
doHidePost(elem.style.display != 'none', postid, elem);
}
// ------------------------------------------------------------------
// Looks for and returns the previous sibling with innerHTML code.
// Necessary because any spaces or line-breaks in the source code
// will be interpreted as DOM nodes by Mozilla browsers.
// ------------------------------------------------------------------
function previousSibling(element) {
if (!element) return null;
var prevelem = element.previousSibling;
while(prevelem && prevelem.nodeType != 1)
prevelem = prevelem.previousSibling;
return prevelem;
}
// ------------------------------------------------------------------
// Looks for and returns the next sibling with innerHTML code.
// Necessary because any spaces or line-breaks in the source code
// will be interpreted as DOM nodes by Mozilla browsers.
// ------------------------------------------------------------------
function nextSibling(element) {
if (!element) return null;
var nextelem = element.nextSibling;
while(nextelem && nextelem.nodeType != 1)
nextelem = nextelem.nextSibling;
return nextelem;
}
// ------------------------------------------------------------------
// Returns a string that can be used to represent an HTML entity in
// a TextNode.
// ------------------------------------------------------------------
function entity(str, mode) {
str = str ? str : "";
mode = mode || "string";
var e = document.createElement("div");
e.innerHTML = str;
if (mode == "numeric")
return "&#" + e.innerHTML.charCodeAt(0) + ";";
else if (mode == "utf16") {
var un = e.innerHTML.charCodeAt(0).toString(16);
while (un.length < 4) un = "0" + un;
return "\\u" + un;
}
else return e.innerHTML;
}
//-----------------------------------------------------------------------------
// Stops the default event action from occuring.
//-----------------------------------------------------------------------------
function stopDefault(event) {
event.preventDefault();
event.stopPropogation();
}
// ----------------------------------------------------------------------------
// Main function that does everything described in the description area above.
// ----------------------------------------------------------------------------
function doIt() {
var script = document.createElement('script');
var hiddenPosts = [];
var allusers, postid=0,member, membername, memberid=0, currentListener, nextelem, prevelem, i, addedSibling, online, extraReplyOptions, popupMenuItem, popupMenuLastItems, len, isHidden, links,currentShowLink, onClickAtt, textNode, newLink, ignoreUserMenuItem, ignoreUserMenuItemLink, allUsers, currentUser, userLen;
var regexMember =/<a href=['"]http:\/\/forums.televisionwithoutpity\.com\/index.php\?showuser=(\d+)[^<>]*>([.\s\S]*?)<\/a>/im;
var makeEventListener = function(theMember, thePost) {
return function(){ toggleMembersPosts(theMember, thePost); return false; };
}
var script = document.createElement('script');
//get the "href" or "onclick" action for the "add reply" button and use in the script
links=document.evaluate("//A[contains(@href,'reply_post')]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
if (links.snapshotLength > 0) {
currentLink = links.snapshotItem(i);
onclickAtt = currentLink.getAttribute('href');
if (onclickAtt!='#') {
if (onclickAtt && onclickAtt.match(/^javascript:/i))
onclickAtt = onclickAtt.replace(/^javascript:/gi, '');
else {
if (onclickAtt.indexOf('&qpid')<0 && onclickAtt.indexOf('&qpid')<0 )
onclickAtt += '&qpid=" + postid';
else
oncclickAtt += '"';
onclickAtt = 'window.location="' + onclickAtt + ';';
}
}
else
onclickAtt = currentLink.getAttribute('onclick');
}
script.type = 'text/javascript'; //http://forums.televisionwithoutpity.com/index.php?act=post&do=reply_post&f=529&t=3159925
scriptText="function doFastReply(membername, postid) {\n var itm = my_getbyid('qr_open');\n if (itm && itm.style.display == 'none'){\n ShowHide('qr_open','qr_closed');\n\n document.getElementById('fastreplyarea').value += '[QUOTE][B][URL=' + ipb_var_base_url + \"showtopic=\" + ipb_input_t + \"&view=findpost&p=\" + postid + ']' + membername + ' said:[/URL][/B] ' + document.getElementById('post-' + postid).innerHTML.replace(/<\\/div>/gim, '\\n').replace(/<br[^>]*>/gim, '\\n').replace(/<li[^<>]*>/gim, '#').replace(/<\\/li[^<>]*>/gim, '\\n').replace(/<span class=.edit.[^<>]*>([.\\s\\S]*?)<\\/span>/gim, '').replace(/(<([^>]+)>)/ig,'').replace(/(<([^>]+)>)/ig,'').replace(/[\\t\\f\\r]/igm,'').replace(/^[\\s]*$/gm,'') + '[/QUOTE]';\n\n document.getElementById('fastreplyarea').focus();\n }\n";
if (onclickAtt) scriptText += " else {\n " + onclickAtt + "\n }\n";
scriptText += "}\n";
script.innerHTML = scriptText;
document.body.appendChild(script);
allUsers = document.evaluate("//*[@class='postdetails']", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
userLen = allUsers.snapshotLength;
for (i = 0; i < userLen; i++) {
//unhide the cool features that twop hid in the user pop up menu
currentUser = allUsers.snapshotItem(i);
nextelem=nextSibling(currentUser.parentNode);
if (nextelem && nextelem.id) {
postid = nextelem.id.substring(10);
//////////////////////////////////////
prevelem = previousSibling(previousSibling(currentUser));
if (prevelem) {
//by putting back in what was commented out
prevelem.innerHTML = prevelem.innerHTML.replace(/<!--<div/gi, "<div").replace(/<\/div>-->/gi, "</div>");
prevelem = previousSibling(prevelem);
if (member = regexMember.exec( prevelem.innerHTML ) ) {
membername = member[2];
memberid=member[1];
}
//add an "online" indicator
addedSibling=false;
if (prevelem.innerHTML.match(/online\.png/i)) {
online = document.createElement("div");
online.innerHTML = '<div><b><i>online</i></b></div>';
currentUser.parentNode.insertBefore(online, currentUser.nextSibling);
addedSibling=true;
}
}
//////////////////////////////////////
// the usual format of quoting attribution is:
// [quote name='Lauurent' date='Oct 17 2007, 11:25 AM' post='100798']
// However, I don't really like the current CSS formatting on the TWoP
// site for this, and other people have expressed a desire to not have
// the jump-to-post link. So, I'm doing my own thing below, which
// places the member name (bolded) between the quote tags, and not as
// an attribute of the quote element.
extraReplyOptions = document.createElement("div");
extraReplyOptions.innerHTML = '<div><a href="#" onclick="multiquote_add(' + postid + '); return false;" title="Toggle multiquote addition"><img src="style_images/1/p_mq_add.gif" name="mad_' + postid + '" alt="+" /></a><a href="#" onclick="doFastReply(\'' + membername + '\', ' + postid + '); return false;" title="Fast Reply to this Post"><img src="style_images/1/p_quote.gif" name="customreply_' + postid + '" alt="Quote Post" /></a></div>';
if (!addedSibling)
currentUser.parentNode.insertBefore(extraReplyOptions, currentUser.nextSibling);
else
currentUser.parentNode.insertBefore(extraReplyOptions, nextSibling(nextSibling(currentUser)));
// add the hide/view member's posts menu items
if (memberid) {
if (!postsByMember[memberid]) postsByMember[memberid] = [];
postsByMember[memberid].push(postid);
popupMenuItem = document.getElementById('post-member-' + postid + '_menu');
if (popupMenuItem) {
popupMenuLastItems = document.evaluate(".//div[@class='popupmenu-item-last']", popupMenuItem, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
len = popupMenuLastItems.snapshotLength;
if (len > 0) {
popupMenuItem = popupMenuLastItems.snapshotItem(len-1);
popupMenuItem.className='popupmenu-item';
ignoreUserMenuItem = document.createElement('div');
ignoreUserMenuItem.className='popupmenu-item-last';
ignoreUserMenuItemLink = document.createElement('a');
ignoreUserMenuItemLink.href='#';
currentListener = makeEventListener(memberid, postid);
ignoreUserMenuItemLink.addEventListener('click', currentListener, true);
ignoreUserMenuItemLink.addEventListener('click', function(event) {stopDefault(event);}, false);
ignoreUserMenuItemLink.id = 'hideposts_' + postid;
isHidden= GM_getValue(memberid, false);
hiddenPosts[postid]=isHidden;
doHidePost (isHidden, postid);
ignoreUserMenuItemLink.innerHTML = isHidden ? "Un-ignore this member's posts" : "Ignore this member's posts" ;
ignoreUserMenuItem.appendChild(ignoreUserMenuItemLink);
popupMenuItem.parentNode.appendChild(ignoreUserMenuItem);
}
}
}
} //have an element id
} //all users
makeEventListener = function(thePost) {
return function(){ toggleHidePost(thePost); return false; };
}
links=document.evaluate("//A[contains(@onclick,'link_to_post')]", document, null, XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE, null);
for (i = 0; i < links.snapshotLength; i++) {
currentShowLink = links.snapshotItem(i);
onClickAtt = currentShowLink.getAttribute('onclick');
postid =onClickAtt.substr(13,onClickAtt.length-29);
textNode = document.createTextNode(' ' + entity('·') + ' ');
newLink = document.createElement('a');
newLink.href="#";
newLink.id = 'hidesinglepost_' + postid;
newLink.innerHTML = hiddenPosts[postid] ? 'Show' : 'Hide';
currentListener = makeEventListener(postid);
newLink.addEventListener('click', currentListener, true);
newLink.addEventListener('click', function(event) {stopDefault(event);}, false);
currentShowLink.parentNode.insertBefore(textNode, currentShowLink.nextSibling);
currentShowLink.parentNode.insertBefore(newLink, currentShowLink.nextSibling.nextSibling);
}
}
// ------------------END OF FUNCTIONS -----------------------------
window.addEventListener("load", function() { doIt(); }, false);