Difference between revisions of "User talk:Void Singer/SL Forum Ignore"

From Second Life Wiki
Jump to navigation Jump to search
m (→‎Last Update?: forgot one tweak)
Line 356: Line 356:


: I had to convert the username attributes in most cases to class additions, to avoid searching for multiple classes on quickhide/show. and then I cheated by using those instead in the ignore button box, to grab it for class find since I don't see a simple solution to 'ancestor' support for that. having to make two calls for the original button inserts (because there is no "OR" support in that function) is a bit ugly... but it's damn fast. stuff got renamed/reformatted/collapsed/inlined to fit my personal style for consistency (hope you don't mind) but the structure is basically yours. ''with your permission (and assuming you don't find any obvious bugs) I'd like to release it as shown below.'' <br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 01:27, 19 February 2010 (UTC)
: I had to convert the username attributes in most cases to class additions, to avoid searching for multiple classes on quickhide/show. and then I cheated by using those instead in the ignore button box, to grab it for class find since I don't see a simple solution to 'ancestor' support for that. having to make two calls for the original button inserts (because there is no "OR" support in that function) is a bit ugly... but it's damn fast. stuff got renamed/reformatted/collapsed/inlined to fit my personal style for consistency (hope you don't mind) but the structure is basically yours. ''with your permission (and assuming you don't find any obvious bugs) I'd like to release it as shown below.'' <br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 01:27, 19 February 2010 (UTC)
<javascript>
<javascript>// ==UserScript==
// ==UserScript==
// @name          SL Forum Ignore
// @name          SL Forum Ignore
// @namespace      Private
// @namespace      Private
Line 366: Line 365:
// @match          https://blogs.secondlife.com/message/*
// @match          https://blogs.secondlife.com/message/*
// @version        1.3
// @version        1.3
// @copyright      Void Singer 2009 [ https://wiki.secondlife.com/wiki/User:Void_Singer ] & BlindWanderer [ http://userscripts.org/users/4223 ]
// @license        CC-BY [ http://creativecommons.org/licenses/by/3.0 ]
// @licesne.ext    Free to copy, use, modify, distribute, or sell, with attribution.
// ==/UserScript==
// ==/UserScript==


Line 371: Line 373:
/*//-- IE7pro unsupported: lacks CSS inheritance & getElementsByClassName      --//*/
/*//-- IE7pro unsupported: lacks CSS inheritance & getElementsByClassName      --//*/


  //-- compatibility replacements for Chrome4+, Opera10.5+, Safari4+
  //-- compatibility replacements for Chrome4+, Opera10.5+, Safari4+, I hope
if (typeof GM_addStyle  == 'undefined'){
if (typeof GM_addStyle  == 'undefined'){
function GM_addStyle( vCss ){
function GM_addStyle( vCss ){
Line 380: Line 382:
} //-- "it's possible that *this* GM_ function is supported in chromium, but the others are not. can't find docs"
} //-- "it's possible that *this* GM_ function is supported in chromium, but the others are not. can't find docs"


  //-- compatibility replacements for Chrome4+, Opera10.5+, Safari4+
  //-- compatibility replacements for Chrome4+, Opera10.5+, Safari4+, I hope
if (typeof GM_setValue  == 'undefined'){
if (typeof GM_setValue  == 'undefined'){
//-- native JSON and localStorage for GM_*Value replacement
//-- native JSON and localStorage for GM_*Value replacement
Line 397: Line 399:
}
}


  //-- function contributed by Blind Wanderer
  //-- function contributed by BlindWanderer
function uHide( vPostNode ){
function uHide( vPostNode ){
vPostNode.className += ' ignored-user';
vPostNode.className += ' ignored-user';
}
}


  //-- function contributed by Blind Wanderer
  //-- function contributed by BlindWanderer
function uShow( vPostNode ){
function uShow( vPostNode ){
vPostNode.className = vPostNode.className.split( ' ' ).filter( function( v ){ return v != 'ignored-user';} ).join( ' ' );
vPostNode.className = vPostNode.className.split( ' ' ).filter( function( v ){ return v != 'ignored-user';} ).join( ' ' );
}
}


  //-- function contributed by Blind Wanderer, modifed by Void Singer
  //-- function contributed by BlindWanderer, modifed by Void Singer
function uToggleIgnore(){
function uToggleIgnore(){
//-- no ancestor support outside of xpath?
//-- no ancestor support outside of xpath?
Line 426: Line 428:
}
}


//--VS +jive version change protection
//-- adds styles, ignore toggles, and marks post containers
function uTweakPage(){
function uTweakPage(){
//-- use style to reduce load, contributed by Blind Wanderer, modifed by Void Singer
//-- use style to reduce load, contributed by BlindWanderer, modifed by Void Singer
GM_addStyle( [
GM_addStyle( [
'.GM-Fix-Thread-Edit {',
'.GM-Fix-Thread-Edit {',
Line 475: Line 477:
}
}
//-- create generic toggle for ignore buttons, contributed by Blind Wanderer
//-- create generic toggle for ignore buttons, contributed by BlindWanderer
var vToggleBox = document.createElement( 'div' );
var vToggleBox = document.createElement( 'div' );
vToggleBox.className = 'GM-ignore-link-wrapper';
vToggleBox.className = 'GM-ignore-link-wrapper';
Line 491: Line 493:
}
}
function uInsertToggles( vPostNodes ){ //-- contributed by Blind Wanderer, modifed by Void Singer
function uInsertToggles( vPostNodes ){ //-- contributed by BlindWanderer, modifed by Void Singer
var vCount = 0;
var vCount = 0;
//-- loop through posts
//-- loop through posts
Line 520: Line 522:
//-- insert Toggle in replies
//-- insert Toggle in replies
uInsertToggles( document.getElementsByClassName( 'jive-thread-reply-body' ) );
uInsertToggles( document.getElementsByClassName( 'jive-thread-reply-body' ) );
//-- not happy with the separate inserts for OP and reply, but it wasn't worth a custom function to grab both
}
}


//-- ?is there really any savings to caching instead of a direct call to GM_getValue?
//-- ?is there really any savings to caching instead of a direct call to GM_getValue?
var vIgnoreCache = {}; //-- contributed by Blind Wanderer
var vIgnoreCache = {}; //-- contributed by BlindWanderer


//-- jive version checking
//-- jive version checking
if (document.getElementsByClassName( 'jiveVersion' ).item( 0 ).textContent.indexOf( '80211' ) != -1){
if (document.getElementsByClassName( 'jiveVersion' ).item( 0 ).textContent.indexOf( '80211' ) != -1){
uTweakPage();
uTweakPage();
}
}</javascript>
</javascript>
EDIT: forgot to tweak the linden name css selector when I switch from username to class addition <br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 01:34, 19 February 2010 (UTC)
EDIT: forgot to tweak the linden name css selector when I switch from username to class addition <br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 01:34, 19 February 2010 (UTC)
EDIT: Tweaked comments/header<br/>-- '''[[User:Void_Singer|Void]]''' <sup><small>([[User_talk:Void_Singer|talk]]|[[Special:Contributions/Void_Singer|contribs]])</small></sup> 08:55, 21 February 2010 (UTC)

Revision as of 00:55, 21 February 2010

Design

I'm curious why you are injecting code, it seems to complicate things. You could put everything in the uPersist function (I'd rename the function). I'm a pretty experienced GM script writer and I wouldn't mind making the changes. If you haven't already, consider posting it on UserScripts.org (it's got version tracking and makes script installation easy, oh and it's free). -- Strife (talk|contribs) 18:51, 13 February 2010 (UTC)

P.S. The onclick attribute has fallen out of favor. addEventListener is the approved method of registering events.

originally I had been injecting the whole script, but didn't know about the changes to GM_*Value context or some of the newer methods (haven't played with javascript for awhile since I quite doing web work)... took me awhile to dig up a useful example for grabbing variables back to the sandbox context... current version is only beta while I make sure the features I have, work, then I'll rewrite for max efficiency...
There is a more recent version on userscripts [1] that adds jive version testing and wrappers the the GM_*Value functions for safe degrading to localStorage w/ JSON for Chrome/Opera/Safari/IE8 (although I'm not sure all the other methods will pass... I can't get a good idea what level of XPATH support they have, for instance). My understanding was that FF didn't have support for GM_* before v3 though? But that was just a matter of quick reading out of date resources...
Oh and I didn't know you could "or" the classes like that... nice, I was reading the MSDN for XPATH and must've missed it. (I also didn't realize we have javascript code wrappers on the wiki). I'd more than welcome input/suggestion/assistance on the script, I've got a few planned feature tweaks to make it nicer (listed on the new forum under beta testers), but some of them run in different directories on the new forum, so I haven't decided whether to make them separate scripts or not to cut down on page parsing...
The page layout on the Blogorums is... messy. and the wysiwyg editor loves to overuse &nbsp; and there's lots of garbage spacing every... plus I forgot to convert childNodes to getElementByTagName since in both cases there's only 1 html element in the container, and it's an anchor. anyways, I'm rambling so I'll stop here
-- Void (talk|contribs) 00:33, 14 February 2010 (UTC)

I tried to tweak it to be better... but it turned out to be easier to rewrite it from scratch. I redid just about everything with fuzzy xpath (I love "ancestor"). This final version is a lot similar than my previous version (which instead of mod'ing the post classes on the fly, it generated custom style elements on the fly; it did not scale). It looks like there is more CSS than code.

As to debugging JS in firefox, I recommend:

  • FireXPath - XPath tab in Firebug, easier to use than XPather.
  • XPather - Has an excellent concise XPath help window.

<javascript> // ==UserScript== // @name SL Forum Ignore // @namespace http://home.comcast.net/~mailerdaemon // @description Adds Ignore Links to User Posts on SL Forum Discussions/Questions (And Make Linden Names RED) // @include https://blogs.secondlife.com/thread/* // @include https://blogs.secondlife.com/message/* // @version 1.1 // ==/UserScript==

var names = {};

GM_addStyle([

       ".GM-ignore-link-wrapper { cursor:pointer; }",
       "",
       ".GM-ignore-link-wrapper .ignore { display:block;}",
       ".GM-ignore-link-wrapper .unignore { display:none;}",
       ".ignored-user .GM-ignore-link-wrapper .ignore {display:none;}",
       ".ignored-user .GM-ignore-link-wrapper .unignore {display:block;}",
       "",
       ".ignored-user .jive-author { padding-top:0; padding-bottom:0; }",
       ".ignored-user .jive-author-avatar-container { display:none!important; }",
       ".ignored-user .jive-author-avatar-container { background-image: none !important; }",
       ".ignored-user .jive-author > em { display:none; }",
       "",
       ".ignored-user .jive-thread-post-body-container { min-height:0; background-image: none !important; padding-bottom:8px; }",
       ".ignored-user .jive-thread-post-subject { float: left; width:auto; }",
       ".ignored-user .jive-thread-post-subject h2 { display:none; }",
       ".ignored-user .jive-thread-post-message { display:none; }",
       ".ignored-user .jive-thread-post-details { background-image: none !important; padding-top:0; margin-top:0; width:auto;}",
       ".ignored-user .jive-thread-post-subject-content { background-image: none !important; padding-bottom:0; margin-bottom:0; }",
       ".ignored-user .jive-thread-post-subject-content .jive-thread-post-reply { display:none; }",
       "",
       ".ignored-user .jive-thread-reply-body-container { min-height:0; padding-bottom:6px; }",
       ".ignored-user .jive-thread-reply-subject { float: left; width:auto; padding-bottom:0!important;}",
       ".ignored-user .jive-thread-reply-subject strong { display:none!important; }",
       ".ignored-user .jive-thread-reply-message { display:none; }",
       ".ignored-user .jive-content-controls { text-align:right; width:auto; padding-top:0; padding-bottom:0;}",
   ].join("\n"));

var box;

$Z("//div[@class='jive-thread-post-body' or @class='jive-thread-reply-body']", function(r,i){

       var linkwrapper = $X("./div[@class='jive-author']/div[@class='jive-username-link-wrapper']", r);
       var fullname = $X("./a", linkwrapper).href.replace(/https?:\/\/blogs\.secondlife\.com\/people\/([^;\/?]+).*/, "$1");
       var name = fullname.split(".");
       if(name.slice(-1)[0] != "Linden")
       {//this all could be made part of the original selector but... that would be ugly to say the least.
           var hidden = names[fullname] = Boolean(GM_getValue(fullname, false));
           r.parentNode.setAttribute("username", fullname);//didn't want to pollute the body css
           if(!box)
           {
               box = document.createElement("div");
               box.className = "GM-ignore-link-wrapper";
               {
                   var span = document.createElement("span");
                   span.appendChild(document.createTextNode("Ignore"));
                   span.className="ignore";
                   box.appendChild(span);
               }
               {
                   var span = document.createElement("span");
                   span.appendChild(document.createTextNode("Unignore"));
                   span.className="unignore";
                   box.appendChild(span);
               }
           }
           if(hidden)
               QuickHide(r.parentNode);
           
           var boxy = box.cloneNode(true);
           insertAfter(boxy, linkwrapper);
           boxy.addEventListener("click", toggleIgnore, false);
       }
   });

function QuickHide(r){ r.className = r.className + " ignored-user"; } function QuickShow(r){ r.className = r.className.split(" ").filter(function(v){ return v != "ignored-user";}).join(" "); }

function toggleIgnore(event){

   var base = $X("./ancestor::div[@username]", event.currentTarget);
   var fullname = base.getAttribute("username");
   var hidden = names[fullname] = !names[fullname];
   if(hidden)
       GM_setValue(fullname, true);
   else
       GM_deleteValue(fullname);
   $Z("//div[@username='"+fullname+"']", hidden?QuickHide:QuickShow, base);

}

function insertAfter(insert, after){return after.parentNode.insertBefore(insert, after.nextSibling);} function insertBefore(insert, before){return before.parentNode.insertBefore(insert, before);}

function $X(_xpath, node){//to search in a frame, you must traverse the .contentDocument or .contentWindow attribute.

   var doc = (node)?(node.ownerDocument || node):(node = document);
   return doc.evaluate(_xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);

} function $Y(_xpath, node){

   var doc = (node)?(node.ownerDocument || node):(node = document);
   return doc.evaluate(_xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

} function $Z(_xpath, func, node){

   var doc = (node)?(node.ownerDocument || node):(node = document);
   var res = doc.evaluate(_xpath, node, null,	XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
   var args = Array.prototype.slice.call(arguments, 3);
   var i = 0;
   for (; i < res.snapshotLength; ++i)
       func.apply(func, [res.snapshotItem(i), i].concat(args));
   return i;

} </javascript>

-- Strife (talk|contribs) 07:47, 14 February 2010 (UTC)

lol, I should know better than to ask you for suggestions... you anticipated most of the niceties that I was going to add (kill every but the the name and unignore buttons)... and then did them all =P
I did notice one slight bug in these three lines var fullname = $X("./a", linkwrapper).textContent; var name = fullname.split(" "); if(name[1] != "Linden"). won't work 100% because we've got at least one linden with a space in their first name (Kona linden Linden) and also, any user that sets the preference "hide last name" in their profile (as I have), will have a text name that matches their system name... including Lindens (Making that Linden ignorable... the system format is "first.last", with other spaces collapsed)... hence the reason why I was parsing their system name from the .href, rather than from the .textContent which could lead to a user taking themselves off ignore by changing that setting (at least until they were added again under the new name as well, and also means it's harder to take them off ignore)... I haven't found any Lindens that have used this setting, but people with this setting on are near impossible to find via people search on the site, and google stubbornly refuses to follow a strict match on punctuation marks =/
I'm assuming names is being used as a backup GM_*Value... and only good per session? assuming GM_ functions fail, won't GM_addStyle fail too?
I'm not going to pretend to automatically understand all the references at a glance, but the $Z function is a quite confusing... func.apply(func, [res.snapshotItem(i), i].concat(args), your essentially appending _xpath, func, and node (when it's called) to array of things to apply to, which would seem to be an infinitely expanding loop of calls to quickHide or quickShow.... (including calls of those functions to those functions) I'm not seeing how it's breaking out, or what the "3" argument to .call in "args" is doing....
ps thanks for the link to FireXPath, it's funny but I was actually considering using getElementsByClass in the rewrite if it turned out faster... believe it or not I only recently installed FireBug, as I've had no want to do web work in a while... it's quite the fun tool though and I've been wandering around the SL site crying at the markup it uses
pps I am NOT suggesting YOU make those changes... although at this point your script is so drastically different and improved from my own, I wouldn't want to take any credit on it's creation
-- Void (talk|contribs) 03:33, 15 February 2010 (UTC)
I didn't know that about the names and thats a really silly oversight with Kona's name (has it been reported as a bug?). I understand now why you had it reading the href. GM_[sg]etValue's persist across sessions and between tabs; they are stored in the users preferences (but are only accessible to the GM script that stored them).
I'm proud of the design of $Z, but not entirely happy with the implementation.

<javascript> function $Z(_xpath, func, node){

   //When working with frames we cannot assume the node we are parsing is in the script's document.
   //So we try to find the document that owns 'node', which is conveniently in the 'ownerDocument' property.
   //While Document objects do have an 'ownerDocument' property, it's always null.
   //If 'node' is null, than we set 'node' and 'doc' to the scripts 'document' ('node' should not be null).
   var doc = (node)?(node.ownerDocument || node):(node = document);
   //Using 'ownerDocument' this is brittle.
   //I spent a sizable chunk of time trying to find a better way, i didn't find one; 
   //"instanceof" is unusable on XPCNativeWrapper'ed objects (unwrapping does not help).
   //Next we evalute the xpath in our node, in it's document
   var res = doc.evaluate(_xpath, node, null,	XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
   //Later we will want an array of any extra arguments that were passed this function.
   //'arguments' may look like an array but it is not; it lacks array function methods.
   //But that is irrelevant. We can get at the function another way.
   var args = Array.prototype.slice.call(arguments, 3);
   //'call' btw is a default method of all functions (functions are objects too).
   //The 'this' value in the function is set equal to the first parameter, any following parameters are passed along to the function.
   //'call' can be defined as:
   //function call(targetThis){ this.apply(targetThis, Array.prototype.slice.call(arguments, 1)); }
   //We now loop through the xpath results.
   var i = 0;
   for (; i < res.snapshotLength; ++i) {
       //We want to call func, passing along the found node, the index of the node, and the any extra arguments.
       //So we build the partial argument array and append the extra arguments.
       func.apply(func, [res.snapshotItem(i), i].concat(args));
   }
   //Because we can, lets return the number of nodes found.
   return i;

} </javascript>

You may notice I edited the script, I had partial Window support but it was incomplete (hadn't realized at the time of posting); full support wasn't worth the extra code, and only marginally useful.
Jive uses jQuery and loads it into it's pages, if you wanted, you could port the script to jQuery and free it from using XPath; jQuery supports more browsers than those that implement XPath. -- Strife (talk|contribs) 17:53, 15 February 2010 (UTC)
understood on the persistence of GM_*Value, was more curious about if the name array was supposed to back it up for browsers that didn't support it, and if so, does name persist across sessions (wouldn't seem like it, but greasemonkey and related environments are new toys for me). If it is backup support for those 3 functions (on other Grease* platforms), it would seem there needs to be a backup for GM_addStyle too as I'm pretty sure none of the other Grease* products implement them separately (chrome, opera, safari). although I couldn't attest 100% to that. it's why I wrote conditional JSON alternatives for the GM_*Value functions in the update of mine the other day on userscripts (all recent browsers are supposed to have native JSON support, though I haven't had any one test them yet)... I'll look into a conditional alternative for GM_addStyle when I have a moment, for supporting other platforms
PS no clue if it's been reported, I found it accidentally while looking for Lindens that may have set "hide last name". AFAIK it's the only instance of a first name that has a space in it ANYWHERE in SL, and was obviously a case of bad data entry checking on LL's end... shouldn't have been possible =|
-- Void (talk|contribs) 03:00, 16 February 2010 (UTC)
GM_addStyle does not persist (I knew there was a question I forgot to answer), but neither does the script state. A new instance of the script is created for each page that matches the rule, for each page the script is executed from scratch. If you want something a little more intelligent you may want to look at Jetpack (which IMHO is too immature to be used; I'm giving them feedback so they can the design right for the Reboot). -- Strife (talk|contribs) 08:09, 16 February 2010 (UTC)
Added some tweaks to yours (was faster than rebuilding mine), linden names in red, OP thread edit button on post, conditional replacements for GM_* for other browsers (untested, should work), namespace back to private (for compatibility with previous saves of mine) and jive version checking. still intend to do a less artful version myself, but I think you should release yours... I don't feel comfortable releasing it under my account unless you want me too (w/ credit, 'natch)... anyways, here 'tis below
-- Void (talk|contribs) 07:37, 17 February 2010 (UTC)

removed intermediate version


hmmm <javascript> // ==UserScript== // @name SL Forum Ignore // @namespace Private // @description +Ignore Links on User Posts on SL Forum Discussions/Questions, +Make Linden Names RED, +OP Edit fix // @include https://blogs.secondlife.com/thread/* // @include https://blogs.secondlife.com/message/* // @version 1.2 // ==/UserScript==

var names = {};

//--VS +compatibility replacements for Chrome4+, Opera10.5+, Safari4+ if (typeof GM_addStyle == 'undefined'){ function GM_addStyle( vCss ){ var vStyle = document.createElement( 'style' ); style.textContent = vCss; document.getElementsByTagName( 'head' )[0].appendChild( vStyle ); }

//-- native JSON and localStorage for GM_*Value replacement GM_getValue = function( vKey, vDefault ){ return (JSON.parse( window.localStorage.getItem( vKey ) ) || vDefault); }

GM_setValue = function( vKey, vValue ){ window.localStorage.setItem( vKey, JSON.stringify( vValue ) ); }

GM_deleteValue = function( vKey ){ window.localStorage.removeItem( vKey ); } }

//--VS +jive version change protection if ($X( "//div[@class='jiveVersion']", document ).textContent.indexOf( '80211' ) != -1 ){ //--VS +Thread Edit Link Fix var vGetEdit = document.getElementsByClassName( 'jive-link-edit' ).item( 0 ); if (vGetEdit != null){ vCloneEdit = vGetEdit.cloneNode( false ); document.getElementsByClassName( 'jive-thread-post-details' ).item( 0 ).appendChild( vCloneEdit ); vCloneEdit.className= "GM-Fix-Thread-Edit"; }

//--VS +.GM-fix-thread-edit GM_addStyle([ ".GM-Fix-Thread-Edit {", " background: url( '../images/jive-icon-edit-16x16.gif' ) no-repeat scroll left center transparent;", " font-weight: bold;", " padding-left: 17px;", " margin-right: 0.5em;", " float: right;}", "", ".GM-ignore-link-wrapper { cursor:pointer; }", "", ".GM-ignore-link-wrapper .ignore { display:block;}", ".GM-ignore-link-wrapper .unignore { display:none;}", ".ignored-user .GM-ignore-link-wrapper .ignore {display:none;}", ".ignored-user .GM-ignore-link-wrapper .unignore {display:block;}", "", ".ignored-user .jive-author { padding-top:0; padding-bottom:0; }", ".ignored-user .jive-author-avatar-container { display:none!important; }", ".ignored-user .jive-author-avatar-container { background-image: none !important; }", ".ignored-user .jive-author > em { display:none; }", "", ".ignored-user .jive-thread-post-body-container { min-height:0; background-image: none !important; padding-bottom:8px; }", ".ignored-user .jive-thread-post-subject { float: left; width:auto; }", ".ignored-user .jive-thread-post-subject h2 { display:none; }", ".ignored-user .jive-thread-post-message { display:none; }", ".ignored-user .jive-thread-post-details { background-image: none !important; padding-top:0; margin-top:0; width:auto;}", ".ignored-user .jive-thread-post-subject-content { background-image: none !important; padding-bottom:0; margin-bottom:0; }", ".ignored-user .jive-thread-post-subject-content .jive-thread-post-reply { display:none; }", "", ".ignored-user .jive-thread-reply-body-container { min-height:0; padding-bottom:6px; }", ".ignored-user .jive-thread-reply-subject { float: left; width:auto; padding-bottom:0!important;}", ".ignored-user .jive-thread-reply-subject strong { display:none!important; }", ".ignored-user .jive-thread-reply-message { display:none; }", ".ignored-user .jive-content-controls { text-align:right; width:auto; padding-top:0; padding-bottom:0;}", "", "div[username$='.Linden'] div.jive-author div.jive-username-link-wrapper > a { color:red; }", ].join("\n"));

var box;

$Z("//div[@class='jive-thread-post-body' or @class='jive-thread-reply-body']", function(r,i){ var linkwrapper = $X("./div[@class='jive-author']/div[@class='jive-username-link-wrapper']", r); var fullname = $X("./a", linkwrapper).href.replace(/https?:\/\/blogs\.secondlife\.com\/people\/([^;\/?]+).*/, "$1"); var name = fullname.split("."); r.parentNode.setAttribute("username", fullname); if(name.slice(-1)[0] != "Linden") {//this all could be made part of the original selector but... that would be ugly to say the least. var hidden = names[fullname] = Boolean(GM_getValue(fullname, false)); if(!box) { box = document.createElement("div"); box.className = "GM-ignore-link-wrapper"; { var span = document.createElement("span"); span.appendChild(document.createTextNode("Ignore")); span.className="ignore"; box.appendChild(span); } { var span = document.createElement("span"); span.appendChild(document.createTextNode("Unignore")); span.className="unignore"; box.appendChild(span); } } if(hidden) QuickHide(r.parentNode);

var boxy = box.cloneNode(true); insertAfter(boxy, linkwrapper); boxy.addEventListener("click", toggleIgnore, false); } }); }

function QuickHide(r){ r.className = r.className + " ignored-user"; } function QuickShow(r){ r.className = r.className.split(" ").filter(function(v){ return v != "ignored-user";}).join(" "); }

function toggleIgnore(event){ var base = $X("./ancestor::div[@username]", event.currentTarget); var fullname = base.getAttribute("username"); var hidden = names[fullname] = !names[fullname]; if(hidden) GM_setValue(fullname, true); else GM_deleteValue(fullname); $Z("//div[@username='"+fullname+"']", hidden?QuickHide:QuickShow, base); }

function insertAfter(insert, after){return after.parentNode.insertBefore(insert, after.nextSibling);} function insertBefore(insert, before){return before.parentNode.insertBefore(insert, before);}

function $X(_xpath, node){//to search in a frame, you must traverse the .contentDocument or .contentWindow attribute.

   var doc = (node)?(node.ownerDocument || node):(node = document);
   return doc.evaluate(_xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0);

} function $Y(_xpath, node){

   var doc = (node)?(node.ownerDocument || node):(node = document);
   return doc.evaluate(_xpath, node, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);

} function $Z(_xpath, func, node){

   var doc = (node)?(node.ownerDocument || node):(node = document);
   var res = doc.evaluate(_xpath, node, null,	XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null);
   var args = Array.prototype.slice.call(arguments, 3);
   var i = 0;
   for (; i < res.snapshotLength; ++i)
       func.apply(func, [res.snapshotItem(i), i].concat(args));
   return i;

} </javascript>

Last Update?

removed the intermediate version, this is my non-xpath rewrite using getElementsByClassName (wanted to see if it's really as much faster as claimed). fixed a bit of bugginess that I introduced int the replacement functions, and a logic error in the handling of OP edit buttons (actions bar retains an edit link on following pages in flat view)
I had to convert the username attributes in most cases to class additions, to avoid searching for multiple classes on quickhide/show. and then I cheated by using those instead in the ignore button box, to grab it for class find since I don't see a simple solution to 'ancestor' support for that. having to make two calls for the original button inserts (because there is no "OR" support in that function) is a bit ugly... but it's damn fast. stuff got renamed/reformatted/collapsed/inlined to fit my personal style for consistency (hope you don't mind) but the structure is basically yours. with your permission (and assuming you don't find any obvious bugs) I'd like to release it as shown below.
-- Void (talk|contribs) 01:27, 19 February 2010 (UTC)

<javascript>// ==UserScript== // @name SL Forum Ignore // @namespace Private // @description +Ignore Links on User Posts on SL Forum Discussions/Questions, +Make Linden Names RED, +OP Edit fix // @include https://blogs.secondlife.com/thread/* // @include https://blogs.secondlife.com/message/* // @match https://blogs.secondlife.com/thread/* // @match https://blogs.secondlife.com/message/* // @version 1.3 // @copyright Void Singer 2009 [ https://wiki.secondlife.com/wiki/User:Void_Singer ] & BlindWanderer [ http://userscripts.org/users/4223 ] // @license CC-BY [ http://creativecommons.org/licenses/by/3.0 ] // @licesne.ext Free to copy, use, modify, distribute, or sell, with attribution. // ==/UserScript==

/*//-- added match statements for compatibility with chromium 'content scripts' --//*/ /*//-- IE7pro unsupported: lacks CSS inheritance & getElementsByClassName --//*/

//-- compatibility replacements for Chrome4+, Opera10.5+, Safari4+, I hope

if (typeof GM_addStyle == 'undefined'){ function GM_addStyle( vCss ){ var vStyle = document.createElement( 'style' ); vStyle.textContent = vCss; document.getElementsByTagName( 'head' )[0].appendChild( vStyle ); } } //-- "it's possible that *this* GM_ function is supported in chromium, but the others are not. can't find docs"

//-- compatibility replacements for Chrome4+, Opera10.5+, Safari4+, I hope

if (typeof GM_setValue == 'undefined'){ //-- native JSON and localStorage for GM_*Value replacement GM_getValue = function( vKey, vDefault ){ var vReturn = JSON.parse( window.localStorage.getItem( vKey ) ); return ((vReturn == null)? vDefault: vReturn); }

GM_setValue = function( vKey, vValue ){ window.localStorage.setItem( vKey, JSON.stringify( vValue ) ); }

GM_deleteValue = function( vKey ){ window.localStorage.removeItem( vKey ); } }

//-- function contributed by BlindWanderer

function uHide( vPostNode ){ vPostNode.className += ' ignored-user'; }

//-- function contributed by BlindWanderer

function uShow( vPostNode ){ vPostNode.className = vPostNode.className.split( ' ' ).filter( function( v ){ return v != 'ignored-user';} ).join( ' ' ); }

//-- function contributed by BlindWanderer, modifed by Void Singer

function uToggleIgnore(){ //-- no ancestor support outside of xpath? var vName = this.getAttribute( 'username' ); //-- ?is there really any savings to caching instead of a direct call to GM_getValue? var vAction = (vIgnoreCache[vName] = !vIgnoreCache[vName])? uHide : uShow; if (vIgnoreCache[vName]){ GM_setValue( vName, true ); } else{ GM_deleteValue( vName ); } var vSwaps = document.getElementsByClassName( vName ); var vCount = 0; for (vCount; vCount < vSwaps.length; ++vCount){ vAction.apply( vAction, [vSwaps.item( vCount )] ); } }

//-- adds styles, ignore toggles, and marks post containers function uTweakPage(){ //-- use style to reduce load, contributed by BlindWanderer, modifed by Void Singer GM_addStyle( [ '.GM-Fix-Thread-Edit {', ' background: url( "../images/jive-icon-edit-16x16.gif" ) no-repeat scroll left center transparent;', ' font-weight: bold;', ' padding-left: 17px;', ' margin-right: 0.5em;', ' float: right;}', , '.GM-ignore-link-wrapper { cursor:pointer; }', , '.GM-ignore-link-wrapper .ignore { display:block;}', '.GM-ignore-link-wrapper .unignore { display:none;}', '.ignored-user .GM-ignore-link-wrapper .ignore {display:none;}', '.ignored-user .GM-ignore-link-wrapper .unignore {display:block;}', , '.ignored-user .jive-author { padding-top:0; padding-bottom:0; }', '.ignored-user .jive-author-avatar-container { display:none!important; }', '.ignored-user .jive-author-avatar-container { background-image: none !important; }', '.ignored-user .jive-author > em { display:none; }', , '.ignored-user .jive-thread-post-body-container { min-height:0; background-image: none !important; padding-bottom:8px; }', '.ignored-user .jive-thread-post-subject { float: left; width:auto; }', '.ignored-user .jive-thread-post-subject h2 { display:none; }', '.ignored-user .jive-thread-post-message { display:none; }', '.ignored-user .jive-thread-post-details { background-image: none !important; padding-top:0; margin-top:0; width:auto;}', '.ignored-user .jive-thread-post-subject-content { background-image: none !important; padding-bottom:0; margin-bottom:0; }', '.ignored-user .jive-thread-post-subject-content .jive-thread-post-reply { display:none; }', , '.ignored-user .jive-thread-reply-body-container { min-height:0; padding-bottom:6px; }', '.ignored-user .jive-thread-reply-subject { float: left; width:auto; padding-bottom:0!important;}', '.ignored-user .jive-thread-reply-subject strong { display:none!important; }', '.ignored-user .jive-thread-reply-message { display:none; }', '.ignored-user .jive-content-controls { text-align:right; width:auto; padding-top:0; padding-bottom:0;}', , 'div[class$=".Linden"] div.jive-author div.jive-username-link-wrapper > a { color:#BB0000; }', ].join( '\n' ) );

//-- clone edit thread link to OP var vGetOP = document.getElementsByClassName( 'jive-thread-post-details' ).item( 0 ); var vGetEdit = document.getElementsByClassName( 'jive-link-edit' ).item( 0 ); if ((null != vGetEdit) && (null != vGetOP)){ //-- need to text both for flat paged views (edit still shows on follow-up pages) var vCloneEdit = vGetEdit.cloneNode( true ); vGetOP.appendChild( vCloneEdit ); vCloneEdit.className = 'GM-Fix-Thread-Edit'; }

//-- create generic toggle for ignore buttons, contributed by BlindWanderer var vToggleBox = document.createElement( 'div' ); vToggleBox.className = 'GM-ignore-link-wrapper'; { var span = document.createElement( 'span' ); span.appendChild( document.createTextNode( 'Ignore' ) ); span.className = 'ignore'; vToggleBox.appendChild(span); } { var span = document.createElement( 'span' ); span.appendChild( document.createTextNode( 'Unignore' ) ); span.className = 'unignore'; vToggleBox.appendChild(span); }

function uInsertToggles( vPostNodes ){ //-- contributed by BlindWanderer, modifed by Void Singer var vCount = 0; //-- loop through posts for (vCount; vCount < vPostNodes.length; ++vCount){ //-- get user profile link for position, and grab their system name var vUserLink = vPostNodes.item( vCount ).getElementsByClassName( 'jive-username-link' ).item( 0 ); var vName = vUserLink.href.substring( vUserLink.href.lastIndexOf( '/' ) + 1 ); //-- tag post for ignore handling vPostNodes.item( vCount ).parentNode.className += ' ' + vName; //-- changed to class vs custom attribute if (vName.split( '.' ).slice( -1 )[0] != 'Linden'){ //-- check and ignore post if user already on ignore //-- ?is there really any savings to caching instead of a direct call to GM_getValue? var vIsIgnored = vIgnoreCache[vName] = GM_getValue( vName, false ); if (vIsIgnored){ uHide( vPostNodes.item( vCount ).parentNode ); } //-- clone ignore toggle into user info box, and tie it the switch function var vIgnoreToggle = vToggleBox.cloneNode( true ); //-- ?no ancestor support outside of xpath? vIgnoreToggle.setAttribute( 'username', vName ); vUserLink.parentNode.insertBefore( vIgnoreToggle, vUserLink.nextSibling ); //--contributed by Blind Wanderer vIgnoreToggle.addEventListener( 'click', uToggleIgnore, false ); } } } //-- insert Toggle in OP uInsertToggles( document.getElementsByClassName( 'jive-thread-post-body' ) ); //-- insert Toggle in replies uInsertToggles( document.getElementsByClassName( 'jive-thread-reply-body' ) ); //-- not happy with the separate inserts for OP and reply, but it wasn't worth a custom function to grab both }

//-- ?is there really any savings to caching instead of a direct call to GM_getValue? var vIgnoreCache = {}; //-- contributed by BlindWanderer

//-- jive version checking if (document.getElementsByClassName( 'jiveVersion' ).item( 0 ).textContent.indexOf( '80211' ) != -1){ uTweakPage(); }</javascript> EDIT: forgot to tweak the linden name css selector when I switch from username to class addition
-- Void (talk|contribs) 01:34, 19 February 2010 (UTC)

EDIT: Tweaked comments/header
-- Void (talk|contribs) 08:55, 21 February 2010 (UTC)