/*
* !!! Please read this before modifying !!!
*
* One of the public benefits of this extension is that everyone's quotes look the
* same -- Fark threads are more visually consistant and look less messy. Please think
* twice before customizing this code because you want a different format (or color, or
* whatever). The current output is based on the defacto-standard that was being widely
* used before I even wrote Farkit.
*/
window.addEventListener("load", function() { FARKIT.init(); }, false);
// Extension code stored in a single object. This elimiates problems with namespace collisions.
var FARKIT = {
_logService : null,
_prefs : null,
_menuitem1 : null,
_menuitem2 : null,
_key1 : null,
_key2 : null,
_onFark : false,
_onFarkStart : null,
//
// log()
//
// Log debug messages to the Javascript console.
// Note: javascript.options.showInConsole must be enabled
//
log : function (message) {
if (!FARKIT._prefs.getBoolPref("debug")) { return; }
if (this._logService == null) {
this._logService = Components.classes['@mozilla.org/consoleservice;1'].getService();
this._logService.QueryInterface(Components.interfaces.nsIConsoleService);
}
this._logService.logStringMessage("Farkit: " + message);
},
//
// _locationListener
//
// Receive updates whenever the location bar changes. This happens when a tab
// is switched or the user navigates to another page. It does NOT fire for
// tabs in the background.
//
_locationListener : {
QueryInterface : function (aIID) {
if (aIID.equals(Components.interfaces.nsIWebProgressListener) ||
aIID.equals(Components.interfaces.nsISupportsWeakReference) ||
aIID.equals(Components.interfaces.nsISupports))
return this;
throw Components.results.NS_NOINTERFACE;
},
onLocationChange : function (aProgress, aRequest, aURI) {
// try/catch needed when URI has no host (eg about:blank). Even a
// "typeof(aURI.host) == undefined" check throws an error. [XXX - OK in FF2]
var onFark = false, onFarkForum = false;
try {
if (aURI.host) {
switch (aURI.host) {
case "forums.fark.com":
onFarkForum = true;
/* FALLTHRU */
case "fark.com":
case "totalfark.com":
case "www.fark.com":
case "www.totalfark.com":
onFark = true;
break;
case "cgi.fark.com":
// alternate view
if (/^[^\?]*\/comments.pl/.test(aURI.path)) {
onFarkForum = true;
}
onFark = true;
break;
default:
break;
}
}
} catch (e) {}
FARKIT.updateMenuForLocation(onFarkForum);
// Compute time spent on Fark
function savetime() {
// Increment the counter
var farkTime = Math.round(new Date().getTime() / 1000) - FARKIT._onFarkStart;
var farkTimeTotal = FARKIT._prefs.getIntPref("farkTime");
farkTimeTotal += farkTime;
FARKIT._prefs.setIntPref("farkTime", farkTimeTotal);
}
if (onFark && !FARKIT._onFark) {
// We have changed state to on-fark
FARKIT._onFark = true;
FARKIT._onFarkStart = Math.round(new Date().getTime() / 1000);
} else if (!onFark && FARKIT._onFark) {
// We have changed state to off-fark
savetime();
FARKIT._onFark = false;
FARKIT._onFarkStart = null;
} else if (onFark && FARKIT._onFark) {
// We are still on a Fark page. Save the time anyway.
savetime();
FARKIT._onFarkStart = Math.round(new Date().getTime() / 1000);
}
return 0;
},
// unused stubs
onStateChange: function() { return 0; },
onProgressChange: function() { return 0; },
onStatusChange: function() { return 0; },
onSecurityChange: function() { return 0; },
onLinkIconAvailable: function() { return 0; }
},
//
// init()
//
// Called once, when browser window opens.
//
init: function() {
FARKIT._prefs = Components.classes["@mozilla.org/preferences-service;1"].getService(
Components.interfaces.nsIPrefService).getBranch("extensions.farkit.");
// Open JS Console when debugging. [This function is what the FF menu calls]
if (FARKIT._prefs.getBoolPref("debug")) { toJavaScriptConsole(); }
FARKIT.log("Initializing...");
// Locate XUL menu entry
this._menuitem1 = document.getElementById("farkit-menu-entry1");
this._menuitem2 = document.getElementById("farkit-menu-entry2");
// Locate XUL key shortcuts
this._key1 = document.getElementById("farkit-key1");
this._key2 = document.getElementById("farkit-key2");
// Attach a lister for location changes
gBrowser.addProgressListener(this._locationListener,
Components.interfaces.nsIWebProgress.NOTIFY_STATE_DOCUMENT);
// Attach a listener to the right-click context menu
var menu = document.getElementById("contentAreaContextMenu");
menu.addEventListener("popupshowing", this.updateMenuForPopup, false);
FARKIT.log("Initialization done.");
},
//
// updateMenuForLocation() and updateMenuForPopup()
//
// Together, these two callbacks manage the context (right-click) popup menu
// and keyboard shortcuts. One is called when the page location changes, so that
// the entries are only visible on Fark forum pages. The other is called when
// the context menu is displayed, to enable/disable entries based on the current
// state of things.
//
updateMenuForLocation : function (enabled) {
FARKIT._menuitem1.hidden = !enabled;
FARKIT._menuitem2.hidden = !enabled;
FARKIT._key1.setAttribute("disabled", !enabled);
FARKIT._key2.setAttribute("disabled", !enabled);
},
updateMenuForPopup : function (event) {
// We'll assume that if the Farkit menuentry1 isn't visible, we're not on Fark.
if (!FARKIT._menuitem1.hidden) {
// Disable quoting unless some text is selected. [Always false if in comment box.]
var noSelection = !gContextMenu.isTextSelected
FARKIT._menuitem1.setAttribute("disabled", noSelection);
// Hide pasting unless we're in the comment box. Disable it if there's no URL.
var HTMLnode = document.popupNode;
var wrongPlace = (HTMLnode.nodeName != "TEXTAREA" || HTMLnode.name != "comment");
FARKIT._menuitem2.hidden = wrongPlace;
FARKIT._menuitem2.setAttribute("disabled", (FARKIT.getClipboardURL() == null));
}
},
//
// doQuoteorPaste()
//
// Called by the key handler. If we're currently inside a text area, do
// a Farkit Paste. Otherwise do the normal user quoting.
//
doQuoteOrPaste : function (trigger, event) {
var ele = document.commandDispatcher.focusedElement;
if (ele && ele.nodeName == 'TEXTAREA') {
FARKIT.doPaste(trigger, event);
} else {
FARKIT.doQuote(trigger, event);
}
},
///////////////////////////////////////////////////////////////////////////////
//
// doQuote()
//
// Primary Farkit function: quote the selected text into the comment box.
//
doQuote : function (trigger, event) {
var ok;
var commentbox;
var quote;
var lastUsername, username;
var prefix = "";
var scroll = true;
FARKIT.log("Quoting activated from " + trigger + ", Shift-key? " + event.shiftKey);
// Check to see if we're quoting in no-scroll mode
switch (trigger) {
case "menu" :
// can't use .altKey; modifier prevents menu from popping up, or dismisses it.
if (event.shiftKey == false) { break; }
/* fallthru */
case "key-alt" :
scroll = false;
break;
case "key" :
default :
/* fallthru */
}
// First, grab the selected text (formatted).
quote = FARKIT.getSelectedText();
if (quote == null) { return; } // errors alerted in call
// Find the comment box for this thread (and make sure HTML is enabled).
commentbox = FARKIT.getCommentBox();
if (commentbox == null) { return; } // errors alerted in call
// Figure out who is being quoted
username = FARKIT.getSelectionUsername();
if (username == null) { return; } // errors alerted in call
// Recall who (if anyone) we last quoted for this comment.
// This is an easy way to store semi-persistant that's unique per tab and destroyed
// when the page changes (or is reloaded, or revisited with the Back button).
lastUsername = commentbox.getAttribute("x-FarkIt-lastUsername");
// Determine if the quote should be prefixed with an attribution.
// We don't handle edits by the user, but we can check for a completely empty box.
if (lastUsername != username || commentbox.value.length == 0) {
commentbox.setAttribute("x-FarkIt-lastUsername", username);
prefix = "" + username + ": ";
}
if (commentbox.value.length != 0) {
// If this is the first quote, notify the user there is already text in the comment.
// Firefox saves the previous comment when clicking "Back" (after submitting it), this
// helps ensure old comments don't get accidently posted again with the new comment.
if (lastUsername == null) {
ok = confirm("Farkit: There is already text in your comment box. Append the quote?");
if (!ok) { return; }
}
// Ensure there is a blank line following the existing comment text.
prefix = FARKIT.getCommentSpacing(commentbox) + prefix;
}
// Put the prefix (if any) and the quote into the comment box.
commentbox.value = commentbox.value + prefix + quote + "\n\n";
if (scroll) {
// scroll to commentbox, also deselects the quoted text
commentbox.focus();
// scroll a bit more so submit button is visible
// Could find the button and scroll to it directly, but this is good enough.
content.window.scrollByLines(2);
} else {
// Toss in an XXX marker so it's obvious where to type a reply.
commentbox.value += "XXX";
// Remove the selection hilighting to indicate success.
content.window.getSelection().removeAllRanges();
}
// Scroll commentbox to bottom (list "page") so the new quote is visible.
commentbox.scrollTop = commentbox.scrollHeight - commentbox.clientHeight;
// Increment the quote counter
var numQuotes = FARKIT._prefs.getIntPref("numQuotes");
numQuotes++;
FARKIT._prefs.setIntPref("numQuotes", numQuotes);
},
///////////////////////////////////////////////////////////////////////////////
//
// doPaste()
//
// Secondary Farkit function: paste formatted URL from clipboard into comment box
//
doPaste : function(trigger, event) {
FARKIT.log("Pasting activated from " + trigger);
var url = FARKIT.getClipboardURL();
if (!url) {
alert("Farkit: No URL in clipboard to paste.");
return;
}
var isImage = /\.(gif|jpg|jpeg|jpe|png)$/i.test(url);
// Find the comment box for this thread (and make sure HTML is enabled).
var commentbox = FARKIT.getCommentBox();
if (commentbox == null) { return; } // errors alerted in call
// Extract the selected text (if any).
var selStart = commentbox.selectionStart;
var selEnd = commentbox.selectionEnd;
var body = commentbox.value.substring(selStart, selEnd);
// If it's an image, but text is selected, treat is as a link instead.
if (isImage && body.length > 0) {
isImage = false;
}
// Format the tag(s) we're inserting
var header, footer;
if (isImage) {
header = '
';
body = '';
footer = '';
} else {
header = '';
footer = ' (pops)';
}
// Toss the tag text into the comment box.
// Using a command is efficient, and adds this edit to the undo stack.
try {
var command = "cmd_insertText";
var controller = document.commandDispatcher.getControllerForCommand(command);
if (!controller) { throw("can't get controller"); }
if (!controller.isCommandEnabled(command)) { throw("controller is disabled!"); }
controller = controller.QueryInterface(Components.interfaces.nsICommandController);
if (!controller) { throw("controller QI failed"); }
var params = Components.classes["@mozilla.org/embedcomp/command-params;1"];
if (!params) { throw("can't get params component"); }
params = params.createInstance(Components.interfaces.nsICommandParams);
if (!params) { throw("can't create params instance"); }
params.setStringValue("state_data", header + body + footer);
controller.doCommandWithParams(command, params);
} catch (e) {
alert("Farkit: Internal error pasting into comment box: " + e);
}
// Increment the paste counter
var numPastes = FARKIT._prefs.getIntPref("numPastes");
numPastes++;
FARKIT._prefs.setIntPref("numPastes", numPastes);
return;
},
//
// getSelectedText()
//
// Returns the cleaned and formatted text which was selected by the user.
//
getSelectedText : function() {
var selection = new String(content.window.getSelection());
// ??? try to fix ghostly newline from "foo\nfoo"
selection = selection.replace(/\r\n/g, "\n");
// strip leading and trailing whitespace
selection = selection.replace(/^\s*/, '');
selection = selection.replace(/\s*$/, '');
// strip extra whitespace between paragraphs.
selection = selection.replace(/\s*\n\s*\n\s*/g, "\n\n");
// strip multiple blank lines
selection = selection.replace(/\n{3,}/g, "\n\n");
// (at this point, text should only have mix of "item\nitem" and "para\n\npara")
// Check for empty selection
if (selection.length == 0) {
alert("Farkit: You need to select some text to quote!");
return null;
}
// Mumble hummm mumble...
if (FARKIT._prefs.getBoolPref("loremipsum")) {
selection = FARKIT.loremize(selection);
}
// Check for huge quotes in case of accidental select-all
// XXX - might want to have a newline-count check too.
if (selection.length > 1700) {
ok = confirm("Farkit: You are quoting a large amount of text. Are you sure you need to?");
if (!ok) { return null; }
}
// Make sure any plain text isn't accidently intrepreted as HTML by Fark
selection = selection.replace(/&/g, "&");
selection = selection.replace(//g, ">");
// Put quote in italics. We do this to each paragraph, so that it's
// easier to reply to each one (without manually entering HTML).
selection = selection.replace(/(\n+)/g, "$1");
selection = "" + selection + "";
return selection;
},
//
// getClipboardURL()
//
// Scans for an URL in the system clipboard and returns it.
//
getClipboardURL : function() {
// Prepare to grab text from the clipboard
var clipboard = Components.classes["@mozilla.org/widget/clipboard;1"].
createInstance(Components.interfaces.nsIClipboard);
var clipboardXfer = Components.classes["@mozilla.org/widget/transferable;1"].
createInstance(Components.interfaces.nsITransferable);
if (!clipboard || !clipboardXfer) {
FARKIT.log("Error: can't get system clipboard or transfer component(s)");
return null;
}
clipboardXfer.addDataFlavor("text/unicode");
clipboard.getData(clipboardXfer, clipboard.kGlobalClipboard);
// Grab the text from the clipboard
var clipboardText = "";
try {
var uniData = new Object();
var uniDataLen = new Object();
clipboardXfer.getTransferData("text/unicode", uniData, uniDataLen);
if (!uniData) { throw("can't get transfer data"); }
uniData = uniData.value.QueryInterface(Components.interfaces.nsISupportsString);
if (!uniData) { throw("QI on uniData failed"); }
clipboardText = uniData.data.substring(0, uniDataLen.value / 2);
} catch (e) {
// Normal: if clipboard is empty, getTransferData() will throw.
// alert("Farkit: Internal error getting text from clipboard: " + e);
FARKIT.log("no text found in clipboard (throw from getTransferData is normal): " + e);
return null;
}
// Did we get anything?
if (clipboardText.length <= 0) {
// This case is probably always handled in the catch() above.
FARKIT.log("can't paste, there is nothing in clipboard.");
return null;
}
// Check if text is completely an URL [Verified that Fark supports HTTP, HTTPS, and FTP links.]
var matches = /^((?:http:|https:|ftp:)\/\/\S+)$/i.exec(clipboardText);
if (!matches) {
// Look for an URL embedded in the text.
// XXX - what if there are 2 or more matches?
// XXX - may need allow additional ending characters, but we're trying to strip trailing punctuation.
matches = /((?:http:|https:|ftp:)\/\/\S+[\w\/\\\-])/i.exec(clipboardText);
if (!matches) {
FARKIT.log("Can't paste, no URLs found in clipboard.");
return null;
}
}
return matches[1];
},
//
// getCommentBox()
//
// Finds the Fark comment box in the page's HTML, and checks to see that
// the user's HTML privledges are enabled.
//
getCommentBox : function() {
var nodes = content.document.getElementsByName("comment");
if (nodes.length != 1) {
alert("Farkit: Can't quote, no comment box found for this thread.");
return null;
}
var commentbox = nodes[0];
nodes = content.document.getElementsByName("use_html");
if (nodes.length != 1) {
alert("Farkit: 'HTML Enabled' not available for thread, or your HTML privledges were revoked.");
return null;
}
var htmlCheckbox = nodes[0];
if (!htmlCheckbox.checked) {
alert("Farkit: You must check the 'HTML Enabled' option below the comment box to quote a user.");
return null;
}
return commentbox;
},
//
// getSelectionUsername()
//
// Figure out what username to associate with the selected text. Also performs checks for
// illegal selections (non-comment, non-headline, multiple comments, etc.).
//
getSelectionUsername : function() {
// Note that getSelection() actually returns a Selection object, not text (but .toString() is there).
//
// Selections can traverse multiple nodes: .anchorNode is related to where the click+drag began,
// and .focusNode is related to where it ended. We'll work with the Range object returned by
// selection.getRangeAt(0), because it won't flip the start/end depending on the direction
// of the selection.
//
// If an endpoint was in a #text node, the offset for the endpoint is within the text length. But if
// the endpoint is at element node (eg BR/IMG), then the endpoint node is the *parent*, and the
// offset is to which child.
//
// EG: "
foo bar baz
// Select from "bar baz" to the line break below it. The startNode will be the #text node with an
// offset of 4 ("foo "), while the endNode is the DIV node, with an offset of 2 (because the BR which
// got selected is the 3rd child of the DIV).
//
// On Fark, an endpoint node is usually a #text node within the DIV/ctext (if an image is selected, the
// node may actually be the DIV itself). So we'll first move up the tree, looking for the DIV. It's
// also possible the user selected the BR's offsetting the comment text, in which case we should look
// sideways (nextNode/prevNode) for the DIV. [This doesn't need to loop, because Fark's current HTML
// has all the valid selection nodes sequential.
//
// range.commonAncestorContainer might be a useful shortcut [we should either get the DIV (or child),
// or the middle column TD]. The TD isn't readily identifiable, though.
var username;
var selRange = content.window.getSelection().getRangeAt(0);
var selStart = selRange.startContainer;
var selEnd = selRange.endContainer;
FARKIT.log("startNode: " + selStart.nodeName + " endNode: " + selEnd.nodeName);
FARKIT.log("startOffset: " + selRange.startOffset + " endOffset: " + selRange.endOffset);
var headlineStart = FARKIT.isHeadline(selStart);
var headlineEnd = FARKIT.isHeadline(selEnd);
// If either end is in the headline, process it as a headline selection.
if (headlineStart || headlineEnd) {
if (headlineStart && headlineEnd) {
username = "submitter";
} else {
alert("Farkit error: Your headline selection includes non-headline text.");
return null;
}
} else {
// Find the nearest comment section (DIV).
selStart = FARKIT.getNearestComment(selStart, selRange.startOffset, true);
selEnd = FARKIT.getNearestComment(selEnd, selRange.endOffset, false);
if (selStart == null || selEnd == null) {
alert("Farkit Error: Your selected text includes part of something that isn't a comment or headline.");
return null;
}
if (selStart != selEnd) {
alert("Farkit Error: You have selected text from two or more users. One at a time, please.");
return null;
}
username = FARKIT.getUsernameByNode(selStart);
if (username == null) {
alert("Farkit Error: Can't determine user being quoted. Fark's HTML may have changed.");
return null;
}
}
return username;
},
//
// isHeadline()
//
// Checks to see if the given node is in the headline.
//
isHeadline : function (node) {
var result = false;
while (node) {
if (node.nodeName == "TD" && node.className == "nilink") { result = true; break; }
node = node.parentNode;
}
return result;
},
//
// getNearestComment()
//
// Finds the nearest comment (DIV) to the given node. We will search up the DOM tree until we find
// it (or fall off). We also look a little bit sideways in some cases. Note that the search will
// delibrately fail if the selection isn't in an expected area. We don't want any DIV in that case.
//
// Normally, the comment DIVs are all children of a TD holding the entire middle of the page. But
// if the thread has voting enabled, the DIVs are all children of a FORM.
//
getNearestComment : function (node, offset, start) {
if (node.nodeName == "TD" || (node.nodeName == "FORM" && node.action == "http://cgi.fark.com/cgi/fark/comments-vote.pl")) {
// This test shouldn't be needed, as it should always be true. But selecting text in a
// headline into the whitespace by the Fark tag was causing an offset of 1 in a single-child
// TD node. I don't quote understand why.
//
if (offset >= node.childNodes.length) { offset = node.childNodes.length - 1; }
node = node.childNodes[offset];
// If we're not already there, walk through siblings until we find a comment.
if (node.nodeName != "DIV" || node.className != "ctext") {
if (start) { node = node.nextSibling; } else { node = node.previousSibling; }
// Check the sibling
if (node == null || node.nodeName != "DIV" || node.className != "ctext") { node = null; }
}
} else {
while (node) {
// Have we walked up to a comment DIV?
if (node.nodeName == "DIV" && node.className == "ctext") { break; }
// Check for TABLE/ctable here if we ever want to handle selecting the header.
node = node.parentNode;
}
}
return node;
},
//
// getCommentSpacing()
//
// Determine how many newlines should be added to the comment box to ensure proper spacing
// between quotes (at least one blank line). Returns 0, 1, or 2 newlines.
//
getCommentSpacing : function (commentbox) {
if (/\n\n$/.test(commentbox.value)) { return ""; }
if (/\n$/.test(commentbox.value)) { return "\n"; }
return "\n\n";
},
loremdata :
[
["a", "e", "u"],
["ac", "ad", "at", "et", "eu", "ex", "id", "in", "mi", "no", "te", "ut"],
["cum", "dis", "dui", "est", "hac", "leo", "mus", "nam", "nec", "non", "per", "sed", "sem", "sit", "vel"],
["amet", "ante", "arcu", "cras", "diam", "duis", "eget", "elit", "enim", "erat", "eros", "nibh", "nisi",
"nisl", "nunc", "odio", "orci", "pede", "quam", "quis", "urna"],
["augue", "class", "curae", "dolor", "donec", "etiam", "fames", "felis", "fusce", "ipsum", "justo", "lacus",
"lorem", "magna", "massa", "metus", "morbi", "neque", "netus", "nulla", "porta", "proin", "purus",
"risus", "velit", "vitae"],
["aenean", "aptent", "auctor", "congue", "cursus", "dictum", "lectus", "libero", "ligula", "litora", "luctus",
"magnis", "mattis", "mauris", "mollis", "montes", "nostra", "nullam", "ornare", "platea", "primis",
"rutrum", "sapien", "semper", "sociis", "taciti", "tellus", "tempor", "tempus", "tortor", "turpis", "varius"],
["aliquam", "aliquet", "blandit", "commodo", "conubia", "cubilia", "dapibus", "egestas", "euismod", "feugiat",
"gravida", "iaculis", "integer", "lacinia", "laoreet", "natoque", "nonummy", "posuere", "potenti",
"pretium", "quisque", "rhoncus", "sodales", "vivamus", "viverra"],
["accumsan", "bibendum", "dictumst", "eleifend", "facilisi", "faucibus", "habitant", "inceptos", "interdum",
"lobortis", "maecenas", "molestie", "nascetur", "pharetra", "placerat", "praesent", "pulvinar",
"sagittis", "senectus", "sociosqu", "suscipit", "torquent", "ultrices", "vehicula", "volutpat"],
["consequat", "convallis", "curabitur", "dignissim", "elementum", "facilisis", "fermentum", "fringilla",
"habitasse", "hendrerit", "hymenaeos", "imperdiet", "malesuada", "penatibus", "phasellus", "porttitor",
"ridiculus", "tincidunt", "tristique", "ultricies", "venenatis", "vulputate"],
["adipiscing", "consetetur", "parturient", "sadipscing", "vestibulum"],
["condimentum", "scelerisque", "suspendisse", "ullamcorper"],
["consectetuer", "pellentesque", "sollicitudin"],
],
//
// loremize()
//
// Lorem ipsum dolor sit amet, consectetur adipisicing elit, sed do eiusmod tempor incididunt
// ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco
// laboris nisi ut aliquip ex ea commodo consequat.
//
loremize : function (text) {
var words, newword, oldword, newtext = "";
// Grab any non-word stuff at the beginning.
var results = /^([^A-Za-z]+)/.exec(text);
if (results) { newtext += results[1]; }
// Each loop, grab a word and the non-word characters trailing it.
do {
results = /([a-z]+)(?:'[a-z]{1,2}|\-)?([^a-z]*)/ig.exec(text);
if (!results) { continue; }
// convert the word
oldword = results[1];
words = FARKIT.loremdata[oldword.length - 1]
if (words) {
// select a random word
newword = words[Math.floor(words.length * Math.random())];
// capitalize any characters that were capitalized in the old word
for (var i = 0; i < newword.length; i++) {
if (oldword.charAt(i) == oldword.charAt(i).toUpperCase()) {
newtext += newword.charAt(i).toUpperCase();
} else {
newtext += newword.charAt(i);
}
}
} else {
newtext += oldword;
}
// append any non-word characters we found.
newtext += results[2];
} while (results);
return newtext;
},
//
// getUsernameByNode()
//
// Given a comment node (or a child thereof), return who wrote the comment.
//
getUsernameByNode : function (node) {
var nodes, username = null;
// Because node is a result from a previous getNearestComment call, we know
// that it's already the class="ctext" DIV. No need to step thru parent nodes.
// The comment header should be a previous sibling.
while ((node = node.previousSibling) != null) {
if (node.nodeName == "TABLE" && node.className && (node.className == "ctableTF" || node.className == "ctable")) { break; }
}
if (node == null) { return null; }
// alternate view
try {
if (node.rows[0].cells[0].className == 'main') {
node = node.previousSibling.previousSibling;
}
} catch (e) { }
// (Farky code to handle different HTML in voting results thread. Not needed for Farkit, as there's no commentbox.)
//
//if (node.previousSibling && node.previousSibling.previousSibling) {
// var otherHeader = node.previousSibling.previousSibling;
// if (otherHeader.className && otherHeader.className == "ctable") { node = otherHeader; }
//}
// node is now the TABLE/class=ctable (comment header). Grab the cells in the first row.
nodes = node.rows[0].cells;
// Find the TD/class=clogin cell
for (var i = 0; i < nodes.length; i++) {
if (nodes[i].className == "clogin") {
// Grab the TD -> A -> text directly
//username = new String(nodes[i].firstChild.firstChild.nodeValue);
username = nodes[i].getElementsByTagName('a')[0].innerHTML;
break;
}
}
return username;
}
};