User talk:Void Singer/SL Forum Ignore
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 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)
- 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 =|
- 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)
- 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
<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.1.2.4 // ==/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:0 0 0 16px;", " 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;}", ].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); 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); } else { //--VS +highlight Linden Names... yes it's sloppy $X("./a", linkwrapper).setAttribute( 'style', 'color: red;' ); } }); }
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.document || node.ownerDocument || node)) || document; return doc.evaluate(_xpath, node || doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null).snapshotItem(0); }
function $Y(_xpath, node){ var doc = (node && (node.document || node.ownerDocument || node)) || document; return doc.evaluate(_xpath, node || doc, null, XPathResult.ORDERED_NODE_SNAPSHOT_TYPE, null); }
function $Z(_xpath, func, node){ var doc = (node && (node.document || node.ownerDocument || node)) || document; var res = doc.evaluate(_xpath, node || doc, 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>
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>