Click to install this script

TWoP: Add Reply and Multi Quote

  • Added: October 27, 2007
  • Last Updated: October 27, 2007

File Name: twop_addreplyandmultiqt.user.js

Click to hide sectionDescription

This script adds the "toggle multi-quote addition" and "reply" buttons to each post on an Invision Power (IP) Board. However, certain lines in this script are tailored specifically to TWoP. Actually, about a week ago the whole dang thing was very specifically tailored to TWoP, but when I realized the forums were IPS, I rewrote a huge chunk (note to self: before writing scripts, investigate uderlying site to see if things can be done more easily). Anyway, instead of doing a "[quote name='Somebody' date='Oct 17 2007, 11:25 AM' post='798']" style when replying, instead I do my own formatting (bolding the name, and having the name link back to the originating post) because I don't like how the TWoP boards are currently configured to handle those QUOTE attributes. However, the "toggle multi-quote-addition" still uses the traditional style. I've commented the script pretty well; if you want to edit it for use on another IP board, modifying it should be relatively easy.

This script:
  • 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 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.
Screenshot of "Quote" and "Reply" buttons added by script Screenshot of "Hide post" link added by script Screenshot of "Ignore User" link added by script

Click to hide sectionScript Source

// 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);