// 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('&amp;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('&middot;') + ' ');
      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);



