// TWoP: Add Reply and Multi-Quote
// Copyright (c) 2006-2007 Orbona
// Version 1.0
// Release Date: 2007-10-22
//
// See also: http://www.orbona.com/greasemonkey/
//
// Original file name: twop_addreplyandmultiqt.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:
//
// (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.
// -----------------------------------------------------------------------------

// ==UserScript==
// @name            TWoP: Add Reply and Multi-Quote
// @description     Adds "reply" and "toggle multi-quote addition" to each post
// @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) {
   var e = document.createElement("div");
   e.innerHTML = str;
   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() {
try {
   var hiddenPosts = [];
   var regexMember =/<a href=['"]http:\/\/forums.televisionwithoutpity\.com\/index.php\?showuser=(\d+)[^<>]*>([.\s\S]*?)<\/a>/im;

   var postid=0, memberid=0;
   var i, member, membername, currentListener, isHidden, nextelem, prevelem, extraReplyOptions, popupMenuItem, popupMenuLastItems, len, ignoreUserMenuItem, ignoreUserMenuItemLink, links, currentLink, onclickAtt, textNode, newLink, allUsers, currentUser, userLen, scripText;

   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++) {
      currentUser = allUsers.snapshotItem(i);
      nextelem=nextSibling(currentUser.parentNode);
      if (nextelem && nextelem.id) {
         postid = nextelem.id.substring(10);

         prevelem = previousSibling(previousSibling(previousSibling(currentUser)));

         if (prevelem) {
            if (member = regexMember.exec( prevelem.innerHTML ) ) {
               membername = member[2];
               memberid=member[1];
            }
         }

         // 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 formatting on the TWoP
         // site for these elements, 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>';

         currentUser.parentNode.insertBefore(extraReplyOptions, currentUser.nextSibling);

         // 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);
               } //popup menu items
            } //popup menu item
         } //member 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++) {
      currentLink = links.snapshotItem(i);
      onclickAtt = currentLink.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);

      currentLink.parentNode.insertBefore(textNode, currentLink.nextSibling);
      currentLink.parentNode.insertBefore(newLink, currentLink.nextSibling.nextSibling);
   }

} catch(e) { alert(e)}
}
// ------------------END OF FUNCTIONS -----------------------------

window.addEventListener("load", function() { doIt(); }, false);
