Skip to content
Browse files

Caught up with latest changes, including addition of Chrome extension…

…s for UIL (works) and TIL (needs updated)
  • Loading branch information...
1 parent b5dcf98 commit c055bf7010530bf016cb945e46e3396b552a1f84 @insin committed Feb 5, 2011
View
BIN SOTM-stats-arrow.gif
Deleted file not rendered
View
7 TIL/TIL.js
@@ -0,0 +1,7 @@
+// Request settings from the extension's localStorage and kick things off
+var folderNames = TIL.Config.getFolderNamesFromCurrentPage();
+chrome.extension.sendRequest({type: "getprefs", folderNames: folderNames}, function(response)
+{
+ cachedSettings = response;
+ TIL.init();
+});
View
588 TIL/TIL.lib.js
@@ -0,0 +1,588 @@
+// ==UserScript==
+// @name Rllmuk Topic Ignore List
+// @namespace http://www.jonathanbuchanan.plus.com/repos/greasemonkey/
+// @description Implements a topic ignore list, sending selected topics to an unobtrusive Ignored Topics section at the foot of topic listing pages. For users who primarily browse the forum using View New Posts, topics may also be ignored on search result pages based on the folder they belong to.
+// @include http://www.rllmukforum.com/*
+// @include http://rllmukforum.com/*
+// ==/UserScript==
+
+/* Changelog
+ * ---------
+ * 2010-06-17 Now usable as a library in a Chrome extension.
+ * 2010-06-16 Refactored into main logic, config and UI modules.
+ * 2009-07-14 Fixed error saving ignored folders in latest version.
+ * 2008-09-13 Fixed preferences display issue in Firefox 3.
+ * 2007-03-05 Forum software was updated, which broke the script.
+ * 2007-02-20 Minor style update to remove multiple scrollbars when the window
+ * is smaller than the preferences dialogue.
+ * 2007-02-19 No longer using User Script Commands menu - Script controls are
+ * now integrated into pages.
+ * 2007-01-25 Added extranoise.co.uk domain.
+ * 2006-11-02 Added GUI for configuration.
+ * Removed positionInArray function, now using JS 1.6's Array.indexOf
+ * 2006-04-07 Added ignoring of specified folders on search pages and a menu
+ * item to edit the ignored folder list.
+ * 2006-03-13 Removed setting which toggled usage of the Ignored Topics section
+ * and did a general code tidy.
+ * 2006-03-12 Changed method for getting topic's row to avoid dumping the entire
+ * post table in the Ignored Topics section.
+ * 2006-03-10 Updated to work with latest version of Greasemonkey and removed
+ * all use of cookies in favour of GM's own storage mechanism.
+ * 2005-06-12 Reduced MAX_COOKIE_SIZE to 4000, as setting cookies when close to
+ * the old 4096 limit seems to silently fail.
+ * 2005-05-26 Functionally complete version finished, tidied up and commented.
+ * -------------------------------------------------------------------------- */
+
+var isGM = !(typeof GM_getValue == "undefined" || GM_getValue("a", "b") == undefined);
+
+/**
+ * If we're running on a content page, this variable will point at an object
+ * containing settings retrieved from the extension's localStorage, otherwise
+ * we're running in the extension's context and want to access localStorage
+ * directly.
+ *
+ * This allows us to include this script for use as a library in extension
+ * contexts, such as in the preferences dialogue.
+ */
+var cachedSettings = null;
+
+/**
+ * Cached folder names for use in the preferences dialogue. For the chrome
+ * extension, the initialising script will send these names to the background
+ * page, where the preferences dialogue can retrieve them to populate this
+ * variable.
+ */
+var folderNames = null;
+
+if (!isGM)
+{
+ GM_getValue = function(name, defaultValue)
+ {
+ var value = (cachedSettings == null ? localStorage.getItem(name) : cachedSettings[name]);
+ if (!value)
+ {
+ return defaultValue;
+ }
+ var type = value[0];
+ value = value.substring(1);
+ switch (type)
+ {
+ case "b":
+ return (value == "true");
+ case "n":
+ return Number(value);
+ default:
+ return value;
+ }
+ }
+
+ GM_setValue = function(name, value)
+ {
+ value = (typeof value)[0] + value;
+ if (cachedSettings == null)
+ {
+ localStorage.setItem(name, value);
+ }
+ else
+ {
+ cachedSettings[name] = value;
+ chrome.extension.sendRequest({type: "setpref", name: name, value: value});
+ }
+ }
+
+ if (typeof(unsafeWindow) == "undefined")
+ {
+ unsafeWindow = window;
+ }
+}
+
+Function.prototype.bind = function(object)
+{
+ var __method = this;
+ return function()
+ {
+ __method.apply(object, arguments);
+ }
+};
+
+/**
+ * Processing of the current page.
+ */
+var TIL =
+{
+ topicIdRegExp: /who_posted\(([0-9]+)\)/,
+
+ crossIcon: '<img src="data:image/gif;base64,R0lGODlhCAAIAKECAIyMjKqqqp%2B' +
+ 'fn5%2BfnyH5BAEKAAIALAAAAAAIAAgAAAIQFIRmcXvAYFss0SmlQ3qqAgA7">&nbsp;&nbsp;&nbsp;',
+
+ plusIcon: '<img src="data:image/gif;base64,R0lGODlhCAAIAKECAIuLi6qqqp%2B' +
+ 'fn5%2BfnyH5BAEKAAIALAAAAAAIAAgAAAIQlBGmgntpgpwSWHRVc3v1AgA7"> ',
+
+ init: function()
+ {
+ var pageType = this.determineCurrentPageType();
+ if (pageType != null)
+ {
+ this.processPage(pageType);
+ }
+ this.registerControls(pageType);
+ },
+
+ determineCurrentPageType: function()
+ {
+ var pageType = null;
+ if (window.location.href.indexOf("searchid=") != -1)
+ {
+ pageType = "search";
+ }
+ else if (window.location.href.indexOf("showforum=") != -1 ||
+ window.location.href.indexOf("act=SF") != -1)
+ {
+ pageType = "topicListing";
+ }
+ return pageType;
+ },
+
+ processPage: function(pageType)
+ {
+ var topicLinkXPath = (pageType == "search"
+ ? "//div[@class='borderwrap']/table/tbody/tr/td[6]/a"
+ : "//div[@class='borderwrap']/table[@class='ipbtable']/tbody/tr/td[4]/a");
+
+ var removedTopics = [];
+ var ignoredTopicIds = TIL.Config.getIgnoredTopicIds();
+ var topicLinkNodes =
+ document.evaluate(
+ topicLinkXPath,
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ for (var i = 0; i < topicLinkNodes.snapshotLength; i++)
+ {
+ var topicLinkNode = topicLinkNodes.snapshotItem(i);
+ var topicId = this.topicIdRegExp.exec(topicLinkNode.href)[1];
+ var ignoredTopicIndex = ignoredTopicIds.indexOf(topicId);
+ var beingIgnored = (ignoredTopicIndex > -1);
+
+ if (beingIgnored)
+ {
+ // Remove this topic's id from the current ignore list and place it
+ // at the front of the removed topics list.
+ removedTopics.splice(0, 0, ignoredTopicIds.splice(ignoredTopicIndex, 1));
+ }
+
+ // Create control for topic management
+ var control = document.createElement("span");
+ if (beingIgnored)
+ {
+ control.innerHTML = this.plusIcon;
+ }
+ else
+ {
+ control.innerHTML = this.crossIcon;
+ }
+ control = control.firstChild;
+ control.style.margin = "6px";
+ control.style.cursor = "pointer";
+ if (beingIgnored)
+ {
+ control.alt = "Unignore";
+ control.title = "Click to stop ignoring this topic";
+ }
+ else
+ {
+ control.alt = "Ignore";
+ control.title = "Click to ignore this topic";
+ }
+ control.addEventListener("click", this.createIgnoreHandler(topicId), false);
+
+ // Find the table cell which will contain the ignore control
+ var cell = topicLinkNode.parentNode.parentNode.getElementsByTagName("td")[1];
+
+ // Remove existing child nodes
+ while (cell.childNodes.length > 0)
+ {
+ cell.removeChild(cell.firstChild);
+ }
+
+ // Insert the control
+ cell.appendChild(control);
+
+ // Insert the Ignored Topics section on the first loop iteration
+ if (i === 0)
+ {
+ var postTable = cell.parentNode.parentNode.parentNode.parentNode;
+ this.insertIgnoredTopicsSection(postTable, pageType);
+ }
+
+ // If this topic is being ignored, move its row to the Ignored Topics
+ // section.
+ if (beingIgnored)
+ {
+ document.getElementById("TILInsertTarget").appendChild(cell.parentNode);
+ }
+ }
+
+ // Place any active ignored topics on the front of the ignored topic list
+ // and store it.
+ if (removedTopics.length > 0)
+ {
+ TIL.Config.setIgnoredTopicIds(
+ removedTopics.concat(ignoredTopicIds));
+ }
+
+ // Remove topics from ignored folders if we're on a search page
+ if (pageType == "search")
+ {
+ var ignoredFolderNames = TIL.Config.getIgnoredFolderNames();
+ if (ignoredFolderNames.length > 0)
+ {
+ var folderLinkNodes =
+ document.evaluate(
+ "//div[@class='borderwrap']/table/tbody/tr/td[4]/span/a",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+ for (var i = 0; i < folderLinkNodes.snapshotLength; i++)
+ {
+ var folderLinkNode = folderLinkNodes.snapshotItem(i);
+ if (ignoredFolderNames.indexOf(folderLinkNode.innerHTML) != -1)
+ {
+ var row = folderLinkNode.parentNode.parentNode.parentNode;
+ document.getElementById("TILInsertTarget").appendChild(row);
+ }
+ }
+ }
+ }
+ },
+
+ /**
+ * Inserts an Ignored Topics section into the current page to store table
+ * rows which contain ignored topic details.
+ *
+ * @param postTable the DOM Node for the table which holds topic listings,
+ * to be used as a reference point for insertion of the new
+ * section.
+ */
+ insertIgnoredTopicsSection: function(postTable, pageType)
+ {
+ // The following HTML is a direct lift from the toggleable topic folder
+ // sections of the forum - it uses the forum's own Javascript functions
+ // to toggle its display.
+ var toggleableSectionHTML =
+'<div class="borderwrap" style="margin-bottom: 10px;" id="fc_99">\
+<div class="maintitlecollapse">\
+<p class="expand"><a href="javascript:togglecategory(99, 0);"><img src="style_images/1/exp_plus.gif" alt="Expand" border="0"></a></p>\
+<p><img src="style_images/1/nav_m.gif" alt="&gt;" border="0" height="8" width="8">&nbsp;<a>Ignored Topics</a></p>\
+</div>\
+</div>\
+<div class="borderwrap" style="display: none; margin-bottom: 10px;" id="fo_99">\
+<div class="maintitle">\
+<p class="expand"><a href="javascript:togglecategory(99, 1);"><img src="style_images/1/exp_minus.gif" alt="Collapse" border="0"></a></p>\
+<p><img src="style_images/1/nav_m.gif" alt="&gt;" border="0" height="8" width="8">&nbsp;<a>Ignored Topics</a></p>\
+</div>\
+<table cellspacing="1" width="100%">\
+<tbody id="TILInsertTarget">\
+<tr> \
+<th align="center">&nbsp;</th>\
+<th align="center">&nbsp;</th>\
+<th nowrap="nowrap">Topic Title</th>';
+// Search page topic lists have an extra column
+if (pageType == "search")
+{
+ toggleableSectionHTML += '<th align="center" nowrap="nowrap">Forum</th>';
+}
+toggleableSectionHTML +=
+' <th align="center" nowrap="nowrap">Replies</th>\
+<th align="center" nowrap="nowrap">Topic Starter</th>\
+<th align="center" nowrap="nowrap">Views</th>\
+<th nowrap="nowrap" width="22%">Last Action</th>\
+</tr>\
+</tbody>\
+</table>\
+</div>';
+
+ // Move one element past where we want to insert the toggleable section
+ for (var i = 0; i < 2; i++)
+ {
+ postTable = postTable.nextSibling;
+ while (postTable.nodeType != 1)
+ {
+ postTable = postTable.nextSibling;
+ }
+ }
+ // Search pages contain two extra <br> elements
+ if (pageType == "search")
+ {
+ for (var i = 0; i < 2; i++)
+ {
+ postTable = postTable.nextSibling;
+ while (postTable.nodeType != 1)
+ {
+ postTable = postTable.nextSibling;
+ }
+ }
+ }
+
+ area = document.createElement("div");
+ area.innerHTML = toggleableSectionHTML;
+ postTable.parentNode.insertBefore(area, postTable);
+ },
+
+ /**
+ * Creates an event handling Function for ignoring a topic.
+ *
+ * @param topicId the id of the topic to be ignored.
+ *
+ * @return a Function which, when executed, will toggle the ignored state of
+ * the topic with the given id.
+ */
+ createIgnoreHandler: function(topicId)
+ {
+ return function(e)
+ {
+ // Toggle this topic out of the list if it's already there
+ var newlyIgnoredTopic = true;
+ var ignoredTopicIds = TIL.Config.getIgnoredTopicIds();
+ var topicIdIndex = ignoredTopicIds.indexOf(topicId);
+ if (topicIdIndex > -1)
+ {
+ ignoredTopicIds.splice(topicIdIndex, 1);
+ newlyIgnoredTopic = false;
+ }
+
+ var ignoreControl = e.target.parentNode;
+ if (newlyIgnoredTopic)
+ {
+ // Add this topic's id to the front of the ignore list
+ ignoredTopicIds.splice(0, 0, topicId);
+
+ // Move the table row Node which contains this topic's details
+ // to the Ignored Topics section.
+ var row = ignoreControl;
+ do
+ {
+ row = row.parentNode;
+ } while (row.nodeName.toLowerCase() != "tr")
+ document.getElementById("TILInsertTarget").appendChild(row);
+
+ // Update the topic ignoring control appropriately
+ ignoreControl.innerHTML = TIL.plusIcon;
+ ignoreControl.title = "Click to stop ignoring this topic";
+ }
+ else
+ {
+ // Show that this topic won't be ignored on next page load
+ ignoreControl.innerHTML = TIL.crossIcon;
+ ignoreControl.title = "Click to re-ignore this topic";
+ }
+
+ // Store the updated ignored topic list
+ TIL.Config.setIgnoredTopicIds(ignoredTopicIds);
+ };
+ },
+
+ registerControls: function(pageType)
+ {
+ var controls =
+ document.getElementById("userlinks");
+ // Only insert this link for GM - Chrome will use a page action
+ if (isGM && controls)
+ {
+ controls = controls.getElementsByTagName("p")[1];
+ controls.insertBefore(document.createTextNode(" · "), controls.firstChild);
+ controls.insertBefore(this.createLinkControl("Topic Ignore List",
+ TIL.UI.show.bind(TIL.UI)),
+ controls.firstChild);
+ }
+ },
+
+ createLinkControl: function(name, handler)
+ {
+ var a = document.createElement("a");
+ a.href = "#";
+ a.appendChild(document.createTextNode(name));
+ a.addEventListener("click", handler, false);
+ return a;
+ }
+};
+
+/**
+ * Configuration.
+ */
+TIL.Config =
+{
+ getIgnoredTopicIds: function()
+ {
+ var topicIds = GM_getValue("ignoredTopics");
+ return (topicIds ? topicIds.split(",") : []);
+ },
+
+ setIgnoredTopicIds: function(ignoredTopics)
+ {
+ GM_setValue("ignoredTopics", ignoredTopics.join(","));
+ },
+
+ getIgnoredFolderNames: function()
+ {
+ var topicIds = GM_getValue("ignoredFolders");
+ return (topicIds ? topicIds.split(",") : []);
+ },
+
+ setIgnoredFolderNames: function(ignoredFolderNames)
+ {
+ GM_setValue("ignoredFolders", ignoredFolderNames.join(","));
+ },
+
+ /**
+ * Retrieves a list of available folders from the forum jump menu, if
+ * available, otherwise returns and empty Array.
+ */
+ getFolderNamesFromCurrentPage: function()
+ {
+ var folders = [];
+ var jumpForm = document.forms.namedItem("jumpmenu");
+ if (jumpForm)
+ {
+ var folderNameRegex = /-- (.+)$/;
+ var folderSelect = jumpForm.elements.namedItem("f");
+ for (var i = 0; i < folderSelect.options.length; i++)
+ {
+ var matches = folderSelect.options[i].text.match(folderNameRegex);
+ if (matches != null)
+ {
+ folders.push(matches[1]);
+ }
+ }
+ }
+ folders.sort();
+ return folders;
+ }
+};
+
+/**
+ * Preferences User Interface (UI).
+ */
+TIL.UI =
+{
+ PREFS_HTML: "data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQvc3RyaWN0LmR0ZCI%2BDQo8aHRtbCBsYW5nPSJlbiI%2BDQo8aGVhZD4NCiAgPHRpdGxlPlVzZXJzY3JpcHQgUHJlZmVyZW5jZXM8L3RpdGxlPg0KICA8bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD1VVEYtOCI%2BDQogIDxtZXRhIG5hbWU9IkF1dGhvciIgY29udGVudD0iSm9uYXRoYW4gQnVjaGFuYW4iPg0KICA8bWV0YSBuYW1lPSJDb3B5cmlnaHQiIGNvbnRlbnQ9IiZjb3B5OyAyMDA2LCBKb25hdGhhbiBCdWNoYW5hbiI%2BDQogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI%2BDQogIGJvZHkgeyBtYXJnaW46MDsgcGFkZGluZzowOyBmb250LXNpemU6MTJweDsgZm9udC1mYW1pbHk6Ikx1Y2lkYSBHcmFuZGUiLCJCaXRzdHJlYW0gVmVyYSBTYW5zIixWZXJkYW5hLEFyaWFsLHNhbnMtc2VyaWY7IGNvbG9yOiMzMzM7IHdpZHRoOiA3MjBweDsgbWFyZ2luOiAwIGF1dG87fQ0KICAubW9kdWxlIHsgYm9yZGVyOiAxcHggc29saWQgI2NjYzsgbWFyZ2luLWJvdHRvbTogNXB4OyBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmOyB9DQogIC5tb2R1bGUgaDIsIC5tb2R1bGUgY2FwdGlvbiB7IG1hcmdpbjogMDsgcGFkZGluZzogMnB4IDVweCAzcHggNXB4OyBmb250LXNpemU6IDExcHg7IHRleHQtYWxpZ246IGxlZnQ7IGZvbnQtd2VpZ2h0OiBib2xkOyBiYWNrZ3JvdW5kOiAjN0NBMEM3IHVybCgiZGF0YTppbWFnZS9naWY7YmFzZTY0LFIwbEdPRGxoRVFBZkFPWUFBTHpTNmJESjQ0JTJCdzBZR2x5cnZSNkxUTTVZaXF6bjZpeUlPbnk0Q2t5YUclMkIyNDJ1MEtLJTJCMjdqUDUzdWd4b2lxellhb3pKeTYySnE0MTN1Z3g0cXN6NSUyQjgyYVRBM0s3SDRvU255NSUyQjgycmJONXJuUTZLVEEzYWZDM3JMTDVKUzAxS25FMzdiTzVuJTJCa3laeTUySHloeUs3STRiTEs1TGpQNXJuUTU1VzAxS25EMzVlMjFYNmp5SzdINFpLeTA2dkc0WHlpeUl5dDBKU3oxTEhKNDR1c3pwZTExbnloeDdyUTU1ZTIxcEd4MDZmQjNvcXN6bjJpeDVLeDBwZTExYVMlMkYzSUtseXF6RzRLYkMzcHEzMXJ2UjZhekY0TFhNNVl5dTBLdkY0YlBLNDVtNDFxZkMzYnZTNkgyaHg2ekc0WUtteXJQSzVLekY0Wld6MUl5dXo1cTQxb2FwellDanlaS3kwcXJFMzdmTjVxckQ0SiUyQjcycEN3MFpHeTBvR215byUyQnYwYlRONWJYTjVabTQxNTI2Mkp5NjJZV296SVdwektyRDM0YW96YnJSNkklMkJ2MHJqTzVyak81NmZCM2FuRTRJU215N1BMNUxmTzVwJTJCNzJiYk41WWFwekh1aHhyZlA1bnlneDdmUDU2SzkyNCUyQncwckhKNHFYQTNZdXJ6MzZqeVl1c3p5SDVCQUFBQUFBQUxBQUFBQUFSQUI4QUFBZiUyRmdBQ0NnNFNGZ2dRRVRFUk1pSWhFQkdtUGpBUWJHemMzS0NpV21aWTNsUnNvYXcyamRpY25EYWVvcUhoc0RSb2hyN0FoV1ZseGN4cTRJYkJnUmdXJTJCWWIxaEJVWmd2c1ltSGxBZVNjaklIczhtSm5CUWNBRUJNOWJZMW52VzJkZ1hMUzBsTFJjbDVlUG00QmNYUlVFdlRraFI3ZTh2UVZGSUwwVklLbTVZSUNBcUFBclVnaVdnbGpOQ2RDenAwS1pEaDRVNk91Z1FJa1RpRWc0Y2ZuQ3dZQ0dqQlQ0Y05YTGtvSUFCZzVJb1RTcFllVElQZ3dvWk1teXBBRk5tQnBvVjVNU3NNR2JFaUFnUmZnSWRROFluR2FBUkpDaFZNb1FLRmFaS3hZaVJvRVRDa0JvclZ0VHdzUUlIRGg4JTJCY0dEOXFqWEZCeGt5VXBpVmN2YkQyaFF5cVQ2NDZKRWp4NVVlYzd1NHNKc2pyd3NCQXJnQUR2eEZRR0UxQXZRTWpoRmpnV1BITVk0Y2VkeDR5aEVLbUduUTJFRkJNNFVkZlNqOCUyQmJ6amdRSFRCbEklMkZXSzJhdFlFeWFDQ1lnVURiREIwSVZjeFVLVU1iQWdJTUNJTCUyRmZoTWNPSWJqdjU4QUdlQmxPZk1uQTZJN2o1NUFoSWpxMWE5YlNaQmdPM1llTEZnY0dIOUFQUG55NGYzQXNNR2VSQk1TSkhqQWFHSkRmaE1lSk80NG1MRGZRUjMlMkJFd1RZbndNT0JBSUFPdyUzRCUzRCIpIHRvcCBsZWZ0IHJlcGVhdC14OyBjb2xvcjogI2ZmZjsgYm9yZGVyLWJvdHRvbTogMDsgfQ0KDQogIC5mb3JtLXJvdyB7IG92ZXJmbG93OiBoaWRkZW47IHBhZGRpbmc6IDhweCAxMnB4OyBmb250LXNpemU6IDExcHg7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZWVlOyBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjZWVlOyB9DQogIC5mb3JtLXJvdyBpbWcsIC5mb3JtLXJvdyBpbnB1dCB7IHZlcnRpY2FsLWFsaWduOiBtaWRkbGU7IH0NCiAgLmZvcm0tZmllbGQgeyBmbG9hdDogbGVmdDsgfQ0KICAuYWxpZ25lZCBsYWJlbCB7IHBhZGRpbmc6IDAgMWVtIDNweCAwOyBmbG9hdDogbGVmdDsgd2lkdGg6IDhlbTsgfQ0KICAuY2hlY2tib3gtcm93IGxhYmVsIHsgcGFkZGluZzogMDsgZmxvYXQ6IG5vbmU7IHdpZHRoOiBhdXRvOyB9DQogIC5zdWJtaXQtcm93IHsgcGFkZGluZzogOHB4IDEycHg7IHRleHQtYWxpZ246IHJpZ2h0OyBib3JkZXItYm90dG9tOiAxcHggc29saWQgI2VlZTsgYm9yZGVyLXJpZ2h0OiAxcHggc29saWQgI2VlZTsgfQ0KDQogIHVsIHsgbWFyZ2luOiAwOyBwYWRkaW5nOiAwIDAgMCAxLjVlbTsgbGluZS1oZWlnaHQ6IDEuNWVtOyB9DQogIGxpIHsgbWFyZ2luOiAwOyBwYWRkaW5nOiAwOyB9DQoNCiAgc3Bhbi5jb250cm9sIHsgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7IGN1cnNvcjogcG9pbnRlcjsgY29sb3I6ICMwMGY7IH0NCg0KICAuc2VsZWN0b3IgeyB3aWR0aDo1ODBweDsgZmxvYXQ6bGVmdDsgfQ0KICAuc2VsZWN0b3Igc2VsZWN0IHsgd2lkdGg6MjcwcHg7IGhlaWdodDoxNy4yZW07IH0NCiAgLnNlbGVjdG9yLWF2YWlsYWJsZSwgLnNlbGVjdG9yLWNob3NlbiB7IGZsb2F0OmxlZnQ7IHdpZHRoOjI3MHB4OyB0ZXh0LWFsaWduOmNlbnRlcjsgbWFyZ2luLWJvdHRvbTo1cHg7IH0NCiAgLnNlbGVjdG9yLWF2YWlsYWJsZSBoMiwgLnNlbGVjdG9yLWNob3NlbiBoMiB7IGJvcmRlcjoxcHggc29saWQgI2NjYzsgfQ0KICAuc2VsZWN0b3IgLnNlbGVjdG9yLWF2YWlsYWJsZSBoMiB7IGJhY2tncm91bmQ6d2hpdGUgdXJsKCJkYXRhOmltYWdlL2dpZjtiYXNlNjQsUjBsR09EbGhFUUFmQU1RQUFQZjM5JTJCM3Q3ZWZuNSUyQnJxNnYzOSUyRmZyNiUyQnZEdzhQUHo4JTJGUHo4dVRrNVBIeDhlSGg0ZTd1N3Z2NyUyQiUyRmo0JTJCT2pvNlBYMTllTGk0dno4JTJGT3pzN1BuNSUyQmZiMjl1Ym01djclMkIlMkZ2JTJGJTJGJTJGd0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUNINUJBQUFBQUFBTEFBQUFBQVJBQjhBQUFXT0lDYU9aR21lYUtxdWJPdTZWeXpQZEczWFJLN3ZmQzc5d0tEdzF5Z2FqOGhpWWNsc09wZVVxSFJLalRxdTJLejJDdWg2diUyQkJ1WlV3dW04ZVF0SHJOVGglMkZlOExqOGdGRFk3JTJGaTgzY0R2JTJCJTJGOThESUtEaElXQ0FZaUppb3VJRTQ2UGtKR09BNVNWbHBlVUQ1cWJuSjJhQXFDaG9xT2dGcWFucUttbUNheXRycSUyQnNFYkt6dExXeUM3aTV1cnU0SVFBNyIpIGJvdHRvbSBsZWZ0IHJlcGVhdC14OyBjb2xvcjojNjY2OyB9DQogIC5zZWxlY3RvciAuc2VsZWN0b3ItYXZhaWxhYmxlIGlucHV0IHsgd2lkdGg6MjMwcHg7IH0NCiAgLnNlbGVjdG9yIHVsLnNlbGVjdG9yLWNob29zZXIgeyBmbG9hdDpsZWZ0OyB3aWR0aDoyMnB4OyBoZWlnaHQ6NTBweDsgYmFja2dyb3VuZDp1cmwoImRhdGE6aW1hZ2UvZ2lmO2Jhc2U2NCxSMGxHT0RsaEZnQXZBTE1BQVA3JTJCJTJGdSUyRnY3JTJGSHg4ZjM5JTJGZmYzOSUyRlB6OCUyRmo0JTJCUEx5OHZEdzhQbjUlMkJmJTJGJTJGJTJGJTJCN3U3Z0FBQUFBQUFBQUFBQUFBQUNINUJBQUFBQUFBTEFBQUFBQVdBQzhBQUFSMFVNa0pUQkZyaVdMQSUyRkJRUlpDUVpFQjZvREVmcGtzY0FEc2hyTDRnOHRiZDlUSVJlajZBQWpJUzJBQ0NCN0ZtYXR3dlVoamhPcjlpc2RzdnRlciUyRmdzSGhNTHB2UDZMUjZiYlptQTVqdGhsdGdianZ1cVZJUnpCSWxjVk1DS1NzMVVEa3pQRUl4S2tVaU55ZUVqUlZTR2h5U0VoRUFPdyUzRCUzRCIpIHRvcCBjZW50ZXIgbm8tcmVwZWF0OyBtYXJnaW46OGVtIDNweCAwIDNweDsgcGFkZGluZzowOyB9DQogIC5zZWxlY3Rvci1jaG9vc2VyIGxpIHsgbWFyZ2luOjA7IHBhZGRpbmc6M3B4OyBsaXN0LXN0eWxlLXR5cGU6bm9uZTsgfQ0KICAuc2VsZWN0b3Igc2VsZWN0IHsgbWFyZ2luLWJvdHRvbTo1cHg7IG1hcmdpbi10b3A6MDsgfQ0KICAuc2VsZWN0b3ItYWRkLCAuc2VsZWN0b3ItcmVtb3ZlIHsgd2lkdGg6MTZweDsgaGVpZ2h0OjE2cHg7IGRpc3BsYXk6YmxvY2s7IHRleHQtaW5kZW50Oi0zMDAwcHg7IH0NCiAgLnNlbGVjdG9yLWFkZCB7IGN1cnNvcjogcG9pbnRlcjsgYmFja2dyb3VuZDp1cmwoImRhdGE6aW1hZ2UvZ2lmO2Jhc2U2NCxSMGxHT0RsaEVBQVFBT1paQUlhcHpZbXN6NCUyQnYwZHJrNyUyQmZ1OVphMDFhTzkxM3VneG9Xb3kzNml4N1RKMyUyQnJ3OSUyQkRwOHJqUDVvT215WnE1MTRDa3lmMyUyQiUyRnU3eiUyQkxQSTNvaXF6WTZ0enBPeTB1JTJGMCUyQktyQzJuJTJCanliN1E1WU9ueTVtMzFZQ2p5SSUyQnYwSDJpeCUyQkhxODVhMTFMbk40cU85MmFPJTJCMnAyNTFueWh4NEtseXBtNDE2VyUyRjI3Yk41SjY2MW4lMkJreWJMSzQlMkZQMiUyQm9LbXluMmh4NXE0MXZIMSUyQmJiSzMzNmp5Skd4MG82dTBPdnc5cmJONWFTJTJGMjRTb3pNTFU1OXptOFlxc3pwaTQxbnloeG8lMkJ3MFlDa3lwZTExSWVwek96eTk1JTJCODJPbnY5cUslMkIyNTY3MTdUTTVKV3oxTWZZNmFYQjNKS3gwMyUyQmp5TEhIM2JQTDQ1ZTIxWjI2Mkt6RzRKT3owNGVxemFYQTNJNnYwZlg0JTJCJTJGJTJGJTJGJTJGd0FBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFDSDVCQUVBQUZrQUxBQUFBQUFRQUJBQUFBZTdnRm1DV1F3WURobEJIUVlEZzRNUkV3aEhTUTBOT0V3VVR4ZU5UeFl0VUolMkJnVUJ3JTJGRW9JS0hsT3BxU3FxVXlGUFdVUVFPVmExdFVaTHRsWUlBek0yVXNEQkJGZzd3UllHRlUwRkJTa0x6aTVZV0JyTFNnY0pBdGdqMGR2Ukl0Z3dDUUhpU056YlJlSW1GVG9BQUVJRTd6TFJKZXdBQndvdlZmbjZJRmdyJTJCaHNHYm55NFFyQWdEeElGcjdCZ3BBQUJsWWNQWTBDa2dtblFreUZSTW1yTTJPTkFLVUdQSUFEeDhlQUJpaG9uTWpVU1ZNZ0JEU2NKRmpVS0JBQTciKSB0b3AgY2VudGVyIG5vLXJlcGVhdDsgbWFyZ2luLWJvdHRvbToycHg7IH0NCiAgLnNlbGVjdG9yLXJlbW92ZSB7IGN1cnNvcjogcG9pbnRlcjsgYmFja2dyb3VuZDp1cmwoImRhdGE6aW1hZ2UvZ2lmO2Jhc2U2NCxSMGxHT0RsaEVBQVFBTlUlMkJBTUhCd2J5OHZQUHo4N1MwdExHeHNjN1czYTIxdmJDeHNzWEZ4YnU3dSUyQkRnNFBEdzhMSzd4Y3pNekxtNXVmejklMkZheXd0Ykt5c3VicTc5WGM0OFBEdzc2JTJCdnN2THk4ckt5dm41JTJCYks3eEs2d3NxJTJCM3Y5M2QzZWpzOE1MQ3d0cmEydDdlM3V2cjY4JTJGUHo3T3pzN084eHJhMnRzZkh4JTJGajQlMkJPM3Q3ZVRwN2ZIeDhjREF3SyUyQnhzdmIyOXRMUzB1TG02OWpZMk0zTnpiRzZ3N2k0dU52YjI4bkp5YzdPenJXMXRkWFYxY2pJeUxlM3Q4VEV4TCUyQiUyRnYlMkZYNCUyQiUyRiUyRiUyRiUyRndBQUFDSDVCQUVBQUQ0QUxBQUFBQUFRQUJBQUFBYXJRSiUyRlFOOWxvU2pxV29UQWNQaklPRVVlaEFMa1NzazVUUnFGNXYxNFRRU0prOEhCbzlDZU4yOGw4cVZ2TVJyY3BCSFg2ckVDcTVQNDVIRDBxZ0g4QUJoQThBSXN3UFQwbkFwRVhBQ3NFQndHWU9JNDlHSnM1bUJFSE02TUltNTJPQ0tNUkVBTTNyaFNPTFF1elBLNEVEQU02dWpxbEtMdTZOd1l2SXp6RnhSWWh4c1VEVEF3T085RFFPZEU3V0VNeUNUWGEyOW9WWTA0Wk54NFhEUTBXT3pwWlRVSkZHcTBIUzAxQkFEcyUzRCIpIHRvcCBjZW50ZXIgbm8tcmVwZWF0OyB9DQogIHNwYW4uc2VsZWN0b3ItY2xlYXJhbGwgeyBjdXJzb3I6IHBvaW50ZXI7IGRpc3BsYXk6YmxvY2s7IHdpZHRoOjZlbTsgdGV4dC1hbGlnbjpsZWZ0OyBtYXJnaW4tbGVmdDphdXRvOyBtYXJnaW4tcmlnaHQ6YXV0bzsgZm9udC13ZWlnaHQ6Ym9sZDsgY29sb3I6IzY2NjsgIHBhZGRpbmc6M3B4IDAgM3B4IDE4cHg7IH0NCiAgc3Bhbi5zZWxlY3Rvci1jbGVhcmFsbDpob3ZlciB7IGNvbG9yOiMwMzY7IH0NCiAgc3Bhbi5zZWxlY3Rvci1jbGVhcmFsbCB7IGJhY2tncm91bmQ6dXJsKCJkYXRhOmltYWdlL2dpZjtiYXNlNjQsUjBsR09EbGhFQUFRQU5Vc0FOWFYxY3ZMeTclMkIlMkZ2NnVycTZ5c3JMeTh2Tm5aMmRiVzF0N2UzcWFtcHJLeXNzVEV4S2lvcU1uSnlhcXFxckd4c2VUazVLNnVycm01dWM3T3pxbXBxYVdscGJPenM5SFIwZDNkM2UlMkZ2NyUyRmYzOSUyQmZuNTlyYTJ0alkyTSUyRlB6NzYlMkJ2dExTMHZiMjl0ZlgxOVRVMUxxNnV2THk4cnU3dTdhMnR1UGo0NmVucDhmSHglMkZqNCUyQlAlMkYlMkYlMkZ3QUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQUFBQ0g1QkFFQUFDd0FMQUFBQUFBUUFCQUFBQWFBUUpad1NDd2FqMGdXcEVDaEZDRElsU3JTT0J3YUVkWEtxSklBdmlJRVFLSXFvZ2FCTktDRVNnOVFSTk5KSU9Cb1ZoajZ5VVJNUFVnYks0SWRGaFlQS1gwT0h4bUNLeU1EQXc2SVF5WVZLUVFJZ2lBcEtSVjhReWdKSjZNWElRYWpDWEJFS2dvcXJoNEdyR1ZGVWdRTEV4TUxCRnBJS0NZTURDYXFTY1RGUlVFQU93JTNEJTNEIikgbGVmdCBjZW50ZXIgbm8tcmVwZWF0OyB9DQogIDwvc3R5bGU%2BDQogIDxzY3JpcHQgdHlwZT0idGV4dC9qYXZhc2NyaXB0Ij4NCiAgdmFyIFNlbGVjdHMgPQ0KICB7DQogICAgICBtb3ZlU2VsZWN0ZWRPcHRpb25zOiBmdW5jdGlvbihmcm9tLCB0bykNCiAgICAgIHsNCiAgICAgICAgICB2YXIgZnJvbSA9IGRvY3VtZW50LmdldEVsZW1lbnRCeUlkKGZyb20pDQogICAgICAgICAgdmFyIHRvID0gZG9jdW1lbnQuZ2V0RWxlbWVudEJ5SWQodG8pOw0KICAgICAgICAgIHZhciBvcHRpb247DQoNCiAgICAgICAgICBmb3IgKHZhciBpID0gZnJvbS5vcHRpb25zLmxlbmd0aCAtIDE7IGkgPj0gMDsgaS0tKQ0KICAgICAgICAgIHsNCiAgICAgICAgICAgICAgb3B0aW9uID0gZnJvbS5vcHRpb25zW2ldOw0KICAgICAgICAgICAgICBpZiAob3B0aW9uLnNlbGVjdGVkID09IHRydWUpDQogICAgICAgICAgICAgIHsNCiAgICAgICAgICAgICAgICAgIHRvLmFwcGVuZENoaWxkKG9wdGlvbik7DQogICAgICAgICAgICAgIH0NCiAgICAgICAgICB9DQogICAgICB9LA0KDQogICAgICBtb3ZlQWxsT3B0aW9uczogZnVuY3Rpb24oZnJvbSwgdG8pDQogICAgICB7DQogICAgICAgICAgdmFyIGZyb20gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZChmcm9tKTsNCiAgICAgICAgICB2YXIgdG8gPSBkb2N1bWVudC5nZXRFbGVtZW50QnlJZCh0byk7DQogICAgICAgICAgdmFyIG9wdGlvbjsNCg0KICAgICAgICAgIGZvciAodmFyIGkgPSBmcm9tLm9wdGlvbnMubGVuZ3RoIC0gMTsgaSA%2BPSAwOyBpLS0pDQogICAgICAgICAgew0KICAgICAgICAgICAgICBvcHRpb24gPSBmcm9tLm9wdGlvbnNbaV07DQogICAgICAgICAgICAgIHRvLmFwcGVuZENoaWxkKG9wdGlvbik7DQogICAgICAgICAgfQ0KICAgICAgfQ0KICB9Ow0KICA8L3NjcmlwdD4NCjwvaGVhZD4NCjxib2R5Pg0KDQo8Zm9ybSBuYW1lPSJwcmVmZXJlbmNlcyIgaWQ9InByZWZlcmVuY2VzIiBjbGFzcz0iYWxpZ25lZCI%2BDQogIDxkaXYgY2xhc3M9Im1vZHVsZSI%2BDQogICAgPGgyPlRvcGljIElnbm9yZSBMaXN0IFByZWZlcmVuY2VzPC9oMj4NCiAgICA8ZGl2IGNsYXNzPSJmb3JtLXJvdyI%2BDQogICAgICA8bGFiZWw%2BRm9sZGVyczo8L2xhYmVsPg0KICAgICAgPGRpdiBjbGFzcz0ic2VsZWN0b3IiPg0KICAgICAgICA8ZGl2IGNsYXNzPSJzZWxlY3Rvci1hdmFpbGFibGUiPg0KICAgICAgICAgIDxoMj5BdmFpbGFibGUgRm9sZGVyczwvaDI%2BDQogICAgICAgICAgPHNlbGVjdCBpZD0iYWxsX2ZvbGRlcnMiIGNsYXNzPSJmaWx0ZXJlZCIgbmFtZT0iYWxsX2ZvbGRlcnMiIHNpemU9IjE1IiBtdWx0aXBsZT0ibXVsdGlwbGUiPg0KICAgICAgICAgIDwvc2VsZWN0Pg0KICAgICAgICA8L2Rpdj4NCg0KICAgICAgICA8dWwgY2xhc3M9InNlbGVjdG9yLWNob29zZXIiPg0KICAgICAgICAgIDxsaT48c3BhbiBjbGFzcz0ic2VsZWN0b3ItYWRkIiBvbmNsaWNrPSdTZWxlY3RzLm1vdmVTZWxlY3RlZE9wdGlvbnMoImFsbF9mb2xkZXJzIiwiaWdub3JlZF9mb2xkZXJzIik7Jz5BZGQ8L3NwYW4%2BPC9saT4NCiAgICAgICAgICA8bGk%2BPHNwYW4gY2xhc3M9InNlbGVjdG9yLXJlbW92ZSIgb25jbGljaz0nU2VsZWN0cy5tb3ZlU2VsZWN0ZWRPcHRpb25zKCJpZ25vcmVkX2ZvbGRlcnMiLCJhbGxfZm9sZGVycyIpOyc%2BUmVtb3ZlPC9zcGFuPjwvbGk%2BDQogICAgICAgIDwvdWw%2BDQoNCiAgICAgICAgPGRpdiBjbGFzcz0ic2VsZWN0b3ItY2hvc2VuIj4NCiAgICAgICAgICA8aDI%2BSWdub3JlZCBGb2xkZXJzPC9oMj4NCiAgICAgICAgICA8c2VsZWN0IGNsYXNzPSJmaWx0ZXJlZCIgbmFtZT0iaWdub3JlZF9mb2xkZXJzIiBzaXplPSIxNSIgbXVsdGlwbGU9Im11bHRpcGxlIiBpZD0iaWdub3JlZF9mb2xkZXJzIj4NCiAgICAgICAgICA8L3NlbGVjdD4NCiAgICAgICAgICA8c3BhbiBjbGFzcz0ic2VsZWN0b3ItY2xlYXJhbGwiIG9uY2xpY2s9J1NlbGVjdHMubW92ZUFsbE9wdGlvbnMoImlnbm9yZWRfZm9sZGVycyIsICJhbGxfZm9sZGVycyIpOyc%2BQ2xlYXIgYWxsPC9zcGFuPg0KICAgICAgICA8L2Rpdj4NCiAgICAgIDwvZGl2Pg0KICAgIDwvZGl2Pg0KICA8L2Rpdj4NCg0KICA8ZGl2IGNsYXNzPSJtb2R1bGUiPg0KICAgIDxkaXYgY2xhc3M9InN1Ym1pdC1yb3ciPg0KICAgICAgPGlucHV0IHR5cGU9ImJ1dHRvbiIgdmFsdWU9IkNsb3NlIiBuYW1lPSJjbG9zZV9idXR0b24iIGlkPSJjbG9zZV9idXR0b24iPg0KICAgICAgPGlucHV0IHR5cGU9ImJ1dHRvbiIgdmFsdWU9IlNhdmUgUHJlZmVyZW5jZXMiIG5hbWU9InNhdmVfYnV0dG9uIiBpZD0ic2F2ZV9idXR0b24iPg0KICAgIDwvZGl2Pg0KICA8L2Rpdj4NCjwvZm9ybT4NCg0KPC9ib2R5Pg0KPC9odG1sPg%3D%3D",
+
+ // This will only be called when running on GreaseMonkey, as the Chrome
+ // extension will use a page action to display the preferences dialogue.
+ show: function(e)
+ {
+ if (e)
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ var blocker = document.createElement("div");
+ this.blocker = blocker;
+ blocker.id = "til_blocker";
+ blocker.style.position = "fixed";
+ blocker.style.top = "0px";
+ blocker.style.right = "0px";
+ blocker.style.bottom = "0px";
+ blocker.style.left = "0px";
+ blocker.style.backgroundColor = "#000";
+ blocker.style.opacity = "0.5";
+ document.body.appendChild(blocker);
+
+ var prefs = document.createElement("iframe");
+ prefs.addEventListener("load", this.preferenceDocumentLoadHandler.bind(this), false);
+ this.prefs = prefs;
+
+ document.body.appendChild(prefs);
+
+ prefs.id = "til_preferences";
+ prefs.name = "til_preferences";
+ prefs.style.position = "fixed";
+ prefs.style.top = "1em";
+ prefs.style.left = "0px";
+ prefs.style.right = "0px";
+ prefs.style.border = "none";
+ prefs.style.height = "100%";
+ prefs.style.width = "100%";
+ prefs.style.overflow = "hidden";
+ prefs.src = this.PREFS_HTML;
+ },
+
+ hide: function(e)
+ {
+ if (isGM)
+ {
+ document.body.removeChild(this.prefs);
+ document.body.removeChild(this.blocker);
+ this.prefs = null;
+ this.blocker = null;
+ }
+ else
+ {
+ window.close();
+ }
+ },
+
+ getDocument: function()
+ {
+ return (isGM ? this.prefs.contentDocument : document);
+ },
+
+ preferenceDocumentLoadHandler: function()
+ {
+ var form = this.getDocument().forms.namedItem("preferences");
+
+ // Set up form state
+ if (folderNames == null)
+ {
+ folderNames = TIL.Config.getFolderNamesFromCurrentPage();
+ }
+ var ignoredFolders = TIL.Config.getIgnoredFolderNames();
+ this.populateFolderSelects(folderNames, ignoredFolders);
+
+ // Set up event handlers
+ form.elements.namedItem("close_button").addEventListener("click", this.hide.bind(this), false);
+ form.elements.namedItem("save_button").addEventListener("click", this.saveConfigurationHandler.bind(this), false);
+ },
+
+ populateFolderSelects: function(folders, ignoredFolders)
+ {
+ var document = this.getDocument();
+ var form = document.forms.namedItem("preferences");
+ folders.forEach(function(folderName)
+ {
+ var option = document.createElement("option");
+ option.text = folderName;
+ if (ignoredFolders.indexOf(folderName) > -1)
+ {
+ form.elements.namedItem("ignored_folders").appendChild(option);
+ }
+ else
+ {
+ form.elements.namedItem("all_folders").appendChild(option);
+ }
+ });
+ },
+
+ saveConfigurationHandler: function()
+ {
+ var ignoredFolders = [];
+ var form = this.getDocument().forms.namedItem("preferences");
+ var select = form.elements.namedItem("ignored_folders");
+ for (var i = 0; i < select.options.length; i++)
+ {
+ ignoredFolders.push(select.options[i].text);
+ }
+ TIL.Config.setIgnoredFolderNames(ignoredFolders);
+ this.hide();
+ }
+};
+
+// Chrome will use another content script to initialise the script.
+if (isGM)
+{
+ TIL.init();
+}
View
37 TIL/background.html
@@ -0,0 +1,37 @@
+<html>
+ <script>
+var folderNames = [];
+
+chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
+{
+ if (request.type == "getprefs")
+ {
+ // Initialise the list of folder names for use on the preferences
+ // dialogue if not yet initialised and they are available.
+ if (folderNames.length == 0 &&
+ request.folderNames.length > 0)
+ {
+ folderNames = request.folderNames;
+ }
+
+ sendResponse({
+ ignoredTopics: localStorage.ignoredTopics,
+ ignoredFolders: localStorage.ignoredFolders
+ });
+ }
+ else if (request.type == "setpref")
+ {
+ localStorage.setItem(request.name, request.value);
+ }
+});
+
+chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
+{
+ if (tab.url.indexOf("http://rllmukforum.com") == 0 ||
+ tab.url.indexOf("http://www.rllmukforum.com") == 0)
+ {
+ chrome.pageAction.show(tabId);
+ }
+});
+ </script>
+</html>
View
BIN TIL/icon128.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN TIL/icon16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN TIL/icon32.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN TIL/icon48.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
24 TIL/manifest.json
@@ -0,0 +1,24 @@
+{
+ "name": "Rllmuk Topic Ignore List",
+ "version": "1.0",
+ "description": "Moves selected topics or topics in certain folders to an unobtrusive Ignored Topics section at the foot of topic listing pages.",
+ "icons": {
+ "16": "icon16.png",
+ "48": "icon48.png",
+ "128": "icon128.png"
+ },
+ "background_page": "background.html",
+ "page_action": {
+ "default_icon": "table_row_delete.png",
+ "default_title": "Topic Ignore List",
+ "default_popup": "prefs.html"
+ },
+ "content_scripts": [
+ {
+ "matches": ["http://rllmukforum.com/*", "http://www.rllmukforum.com/*"],
+ "js": ["TIL.lib.js", "TIL.js"],
+ "run_at": "document_end"
+ }
+ ],
+ "permissions": ["tabs"]
+}
View
127 TIL/prefs.html
@@ -0,0 +1,127 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en">
+<head>
+ <title>Userscript Preferences</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="Author" content="Jonathan Buchanan">
+ <meta name="Copyright" content="&copy; 2006, Jonathan Buchanan">
+ <style type="text/css">
+ body { margin:0; padding:0; font-size:12px; font-family:"Lucida Grande","Bitstream Vera Sans",Verdana,Arial,sans-serif; color:#333; width: 720px; margin: 0 auto; }
+ .module { background-color: #fff; }
+ .module h2, .module caption { margin: 0; padding: 2px 5px 3px 5px; font-size: 11px; text-align: left; font-weight: bold; background: #7CA0C7 url("data:image/gif;base64,R0lGODlhEQAfAOYAALzS6bDJ44%2Bw0YGlyrvR6LTM5Yiqzn6iyIOny4CkyaG%2B242u0KK%2B27jP53ugxoiqzYaozJy62Jq413ugx4qsz5%2B82aTA3K7H4oSny5%2B82rbN5rnQ6KTA3afC3rLL5JS01KnE37bO5n%2BkyZy52HyhyK7I4bLK5LjP5rnQ55W01KnD35e21X6jyK7H4ZKy06vG4XyiyIyt0JSz1LHJ44uszpe11nyhx7rQ55e21pGx06fB3oqszn2ix5Kx0pe11aS%2F3IKlyqzG4KbC3pq31rvR6azF4LXM5Yyu0KvF4bPK45m41qfC3bvS6H2hx6zG4YKmyrPK5KzF4ZWz1Iyuz5q41oapzYCjyZKy0qrE37fN5qrD4J%2B72pCw0ZGy0oGmyo%2Bv0bTN5bXN5Zm415262Jy62YWozIWpzKrD34aozbrR6I%2Bv0rjO5rjO56fB3anE4ISmy7PL5LfO5p%2B72bbN5YapzHuhxrfP5nygx7fP56K924%2Bw0rHJ4qXA3Yurz36jyYuszyH5BAAAAAAALAAAAAARAB8AAAf%2FgACCg4SFggQETERMiIhEBGmPjAQbGzc3KCiWmZY3lRsoaw2jdicnDaeoqHhsDRohr7AhWVlxcxq4IbBgRgW%2BYb1hBUZgvsYmHlAeScjIHs8mJnBQcAEBM9bY1nvW2dgXLS0lLRcl5ePm4BcXRUEvTkhR7e8vQVFIL0VIKm5YICAqAArUgiWgljNCdCzp0KZDh4U6OugQIkTiEg4cfnCwYCGjBT4cNXLkoIABg5IoTSpYeTIPgwoZMmypAFNmBpoV5MSsMGbEiAgRfgIdQ8YnGaARJChVMoQKFaZKxYiRoETCkBorVtTwsQIHDh8%2BcGD9qjXFBxkyUpiVcvbD2hQyqT646JEjx5Uec7u4sJsjrwsBArgADvxFQGE1AvQMjhFjgWPHMY4cedx4yhEKmGnQ2EFBM4UdfSj8%2BbzjgQHTBlI%2FWK2atYEyaCCYgUDbDB0IVcxUKUMbAgIMCIL%2FfhMcOIbjv58AGeBlOfMnA6I7j55AhIjq1a9bSZBgO3YeLFgcGH9APPny4f3AsMGeRBMSJHjAaGJDfhMeJO44mLDfQR3%2BEwTYnwMOBAIAOw%3D%3D") top left repeat-x; color: #fff; border-bottom: 0; }
+
+ .form-row { overflow: hidden; padding: 8px 12px; font-size: 11px; }
+ .form-row img, .form-row input { vertical-align: middle; }
+ .form-field { float: left; }
+ .aligned label { padding: 0 1em 3px 0; float: left; width: 8em; }
+ .checkbox-row label { padding: 0; float: none; width: auto; }
+ .submit-row { padding: 0 12px 8px 12px; text-align: right; }
+
+ ul { margin: 0; padding: 0 0 0 1.5em; line-height: 1.5em; }
+ li { margin: 0; padding: 0; }
+
+ span.control { text-decoration: underline; cursor: pointer; color: #00f; }
+
+ .selector { width:580px; float:left; }
+ .selector select { width:270px; height:17.2em; }
+ .selector-available, .selector-chosen { float:left; width:270px; text-align:center; margin-bottom:5px; }
+ .selector-available h2, .selector-chosen h2 { border:1px solid #ccc; }
+ .selector .selector-available h2 { background:white url("data:image/gif;base64,R0lGODlhEQAfAMQAAPf39%2B3t7efn5%2Brq6v39%2Ffr6%2BvDw8PPz8%2FPz8uTk5PHx8eHh4e7u7vv7%2B%2Fj4%2BOjo6PX19eLi4vz8%2FOzs7Pn5%2Bfb29ubm5v7%2B%2Fv%2F%2F%2FwAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAARAB8AAAWOICaOZGmeaKqubOu6VyzPdG3XRK7vfC79wKDw1ygaj8hiYclsOpeUqHRKjTqu2Kz2Cuh6v%2BBuZUwum8eQtHrNTh%2Fe8Lj8gFDY7%2Fi83cDv%2B%2F98DIKDhIWCAYiJiouIE46PkJGOA5SVlpeUD5qbnJ2aAqChoqOgFqanqKmmCaytrq%2BsEbKztLWyC7i5uru4IQA7") bottom left repeat-x; color:#666; }
+ .selector .selector-available input { width:230px; }
+ .selector ul.selector-chooser { float:left; width:22px; height:50px; background:url("data:image/gif;base64,R0lGODlhFgAvALMAAP7%2B%2Fu%2Fv7%2FHx8f39%2Fff39%2FPz8%2Fj4%2BPLy8vDw8Pn5%2Bf%2F%2F%2F%2B7u7gAAAAAAAAAAAAAAACH5BAAAAAAALAAAAAAWAC8AAAR0UMkJTBFriWLA%2FBQRZCQZEB6oDEfpkscADshrL4g8tbd9TIRej6AAjIS2ACCB7FmatwvUhjhOr9isdsvter%2FgsHhMLpvP6LR6bbZmA5jthltgbjvuqVIRzBIlcVMCKSs1UDkzPEIxKkUiNyeEjRVSGhySEhEAOw%3D%3D") top center no-repeat; margin:8em 3px 0 3px; padding:0; }
+ .selector-chooser li { margin:0; padding:3px; list-style-type:none; }
+ .selector select { margin-bottom:5px; margin-top:0; }
+ .selector-add, .selector-remove { width:16px; height:16px; display:block; text-indent:-3000px; }
+ .selector-add { cursor: pointer; background:url("data:image/gif;base64,R0lGODlhEAAQAOZZAIapzYmsz4%2Bv0drk7%2Bfu9Za01aO913ugxoWoy36ix7TJ3%2Brw9%2BDp8rjP5oOmyZq514Ckyf3%2B%2Fu7z%2BLPI3oiqzY6tzpOy0u%2F0%2BKrC2n%2Bjyb7Q5YOny5m31YCjyI%2Bv0H2ix%2BHq85a11LnN4qO92aO%2B2p251nyhx4Klypm416W%2F27bN5J661n%2BkybLK4%2FP2%2BoKmyn2hx5q41vH1%2BbbK336jyJGx0o6u0Ovw9rbN5aS%2F24SozMLU59zm8Yqszpi41nyhxo%2Bw0YCkype11IepzOzy95%2B82Onv9qK%2B256717TM5JWz1MfY6aXB3JKx03%2BjyLHH3bPL45e21Z262KzG4JOz04eqzaXA3I6v0fX4%2B%2F%2F%2F%2FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAAFkALAAAAAAQABAAAAe7gFmCWQwYDhlBHQYDg4MREwhHSQ0NOEwUTxeNTxYtUJ%2BgUBw%2FEoIKHlOpqSqqUyFPWUQQOVa1tUZLtlYIAzM2UsDBBFg7wRYGFU0FBSkLzi5YWBrLSgcJAtgj0dvRItgwCQHiSNzbReImFToAAEIE7zLRJewABwovVfn6IFgr%2BhsGbny4QrAgDxIFr7BgpAABlYcPY0CkgmnQkyFRMmrM2ONAKUGPIADx8eABihonMjUSVMgBDScJFjUKBAA7") top center no-repeat; margin-bottom:2px; }
+ .selector-remove { cursor: pointer; background:url("data:image/gif;base64,R0lGODlhEAAQANU%2BAMHBwby8vPPz87S0tLGxsc7W3a21vbCxssXFxbu7u%2BDg4PDw8LK7xczMzLm5ufz9%2FaywtbKysubq79Xc48PDw76%2BvsvLy8rKyvn5%2BbK7xK6wsq%2B3v93d3ejs8MLCwtra2t7e3uvr68%2FPz7Ozs7O8xra2tsfHx%2Fj4%2BO3t7eTp7fHx8cDAwK%2Bxsvb29tLS0uLm69jY2M3NzbG6w7i4uNvb28nJyc7OzrW1tdXV1cjIyLe3t8TExL%2B%2Fv%2FX4%2B%2F%2F%2F%2FwAAACH5BAEAAD4ALAAAAAAQABAAAAarQJ%2FQN9loSjqWoTAcPjIOEUehALkSsk5TRqF5v14TQSJk8HBo9CeN28l8qVvMRrcpBHX6rECq5P45HD0qgH8ABhA8AIswPT0nApEXACsEBwGYOI49GJs5mBEHM6MIm52OCKMREAM3rhSOLQuzPK4EDAM6ujqlKLu6NwYvIzzFxRYhxsUDTAwOO9DQOdE7WEMyCTXa29oVY04ZNx4XDQ0WOzpZTUJFGq0HS01BADs%3D") top center no-repeat; }
+ span.selector-clearall { cursor: pointer; display:block; width:6em; text-align:left; margin-left:auto; margin-right:auto; font-weight:bold; color:#666; padding:3px 0 3px 18px; }
+ span.selector-clearall:hover { color:#036; }
+ span.selector-clearall { background:url("data:image/gif;base64,R0lGODlhEAAQANUsANXV1cvLy7%2B%2Fv6urq6ysrLy8vNnZ2dbW1t7e3qamprKyssTExKioqMnJyaqqqrGxseTk5K6urrm5uc7OzqmpqaWlpbOzs9HR0d3d3e%2Fv7%2Ff39%2Bfn59ra2tjY2M%2FPz76%2BvtLS0vb29tfX19TU1Lq6uvLy8ru7u7a2tuPj46enp8fHx%2Fj4%2BP%2F%2F%2FwAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAACH5BAEAACwALAAAAAAQABAAAAaAQJZwSCwaj0gWpEChFCDIlSrSOBwaEdXKqJIAviIEQKIqogaBNKCESg9QRNNJIOBoVhj6yURMPUgbK4IdFhYPKX0OHxmCKyMDAw6IQyYVKQQIgiApKRV8QygJJ6MXIQajCXBEKgoqrh4GrGVFUgQLExMLBFpIKCYMDCaqScTFRUEAOw%3D%3D") left center no-repeat; }
+ </style>
+ <script type="text/javascript">
+ var Selects =
+ {
+ moveSelectedOptions: function(from, to)
+ {
+ var from = document.getElementById(from)
+ var to = document.getElementById(to);
+ var option;
+
+ for (var i = from.options.length - 1; i >= 0; i--)
+ {
+ option = from.options[i];
+ if (option.selected == true)
+ {
+ to.appendChild(option);
+ }
+ }
+ },
+
+ moveAllOptions: function(from, to)
+ {
+ var from = document.getElementById(from);
+ var to = document.getElementById(to);
+ var option;
+
+ for (var i = from.options.length - 1; i >= 0; i--)
+ {
+ option = from.options[i];
+ to.appendChild(option);
+ }
+ }
+ };
+ </script>
+ <script type="text/javascript" src="TIL.lib.js"></script>
+ <script type="text/javascript">
+ window.addEventListener("load", function()
+ {
+ folderNames = chrome.extension.getBackgroundPage().folderNames;
+ var noFolderNamesMessage = document.getElementById("noFolderNames");
+ noFolderNamesMessage.style.display = (folderNames.length > 0 ? "none" : "");
+ TIL.UI.preferenceDocumentLoadHandler();
+ }, false);
+ </script>
+</head>
+<body>
+
+<form name="preferences" id="preferences" class="aligned">
+ <div class="module">
+ <h2>Topic Ignore List Preferences</h2>
+ <div class="form-row" id="noFolderNames">
+ <p>It looks like you haven't viewed any forum pages which have the folder jump menu on them yet in this session.</p>
+ <p>After you've viewed a topic listing page or a topic, the cofiguration options below will be populated.</p>
+ </div>
+ <div class="form-row">
+ <label>Folders:</label>
+ <div class="selector">
+ <div class="selector-available">
+ <h2>Available Folders</h2>
+ <select id="all_folders" class="filtered" name="all_folders" size="15" multiple="multiple">
+ </select>
+ </div>
+
+ <ul class="selector-chooser">
+ <li><span class="selector-add" onclick='Selects.moveSelectedOptions("all_folders","ignored_folders");'>Add</span></li>
+ <li><span class="selector-remove" onclick='Selects.moveSelectedOptions("ignored_folders","all_folders");'>Remove</span></li>
+ </ul>
+
+ <div class="selector-chosen">
+ <h2>Ignored Folders</h2>
+ <select class="filtered" name="ignored_folders" size="15" multiple="multiple" id="ignored_folders">
+ </select>
+ <span class="selector-clearall" onclick='Selects.moveAllOptions("ignored_folders", "all_folders");'>Clear all</span>
+ </div>
+ </div>
+ </div>
+ </div>
+
+ <div class="module">
+ <div class="submit-row">
+ <input type="button" value="Close" name="close_button" id="close_button">
+ <input type="button" value="Save Preferences" name="save_button" id="save_button">
+ </div>
+ </div>
+</form>
+
+</body>
+</html>
View
BIN TIL/table_row_delete.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
6 UIL/UIL.js
@@ -0,0 +1,6 @@
+// Request settings from the extension's localStorage and kick things off
+chrome.extension.sendRequest({type: "getprefs"}, function(response)
+{
+ cachedSettings = response;
+ UIL.init();
+});
View
935 UIL/UIL.lib.js
@@ -0,0 +1,935 @@
+// ==UserScript==
+// @name Rllmuk User Ignore List
+// @namespace http://www.jonathanbuchanan.plus.com/repos/greasemonkey/
+// @description Implements a user ignore list which removes all traces of the users on the list and optionally removes topics created by ignored users and posts which quote ignored users. The ignore list can be synchronised with your Manage Ignored Users settings when viewing that page.
+// @include http://www.rllmukforum.com/*
+// @include http://rllmukforum.com/*
+// ==/UserScript==
+
+/* Changelog
+ * ---------
+ * 2010-12-14 Minor tweaks to make the script work in Chrome with IPB3.
+ * 2010-08-05 Restyled to fit in with default forum theme.
+ * 2010-07-27 Tweaks after testing on updated forum.
+ * 2010-07-07 Updated script for impending move to IPB3.
+ * 2010-06-16 Now usable as a library in a Chrome extension.
+ * 2010-05-11 Notification of post/topic removal now defaults to off.
+ * 2008-10-18 Fixed preferences display issue in Firefox 3.
+ * 2008-04-27 Implemented removal of posts by users with truncated usernames.
+ * Fixed removal of posts by ignored users on reply pages.
+ * 2008-03-20 Fixed error when viewing locked topics.
+ * 2007-10-30 A markup change broke removal of quoted posts.
+ * 2007-03-05 Forum software was updated, which broke the script in certain
+ * places.
+ * Fixed a bug in removal of posts which quote ignored users in
+ * post/edit/preview pages.
+ * 2007-02-20 Minor style update to remove multiple scrollbars when the window
+ * is smaller than the preferences dialogue.
+ * 2007-02-19 No longer using User Script Commands menu - Script controls are
+ * now integrated into pages.
+ * 2007-01-25 Added extranoise.co.uk domain.
+ * 2007-01-24 "Complex" configuration data is now stored as JSON - added the
+ * JSON library to the script for this purpose.
+ * Added ignoring of specific users only in specific topics.
+ * Refactored the script, placing everything under a "UIL" object.
+ * 2006-11-02 Added GUI for configuration instead of using clunky menu items.
+ * Added removal of topics created by ignored users.
+ * 2006-09-09 Fixed bug where the number of posts reported as being removed was
+ * incorrect when an ignored user was quoted repeatedly.
+ * Removed isInArray function: now using JS 1.6's Array.indexOf
+ * URL substring checks are now performed on a lowercase version of
+ * the URL - this fixes removal of posts on the Reply page.
+ * 2006-03-10 Updated to work with latest version of Greasemonkey and to remove
+ * posts containing quotes from ignored users.
+ * 2005-05-26 Functionally complete version finished, tidied up and commented.
+ * -------------------------------------------------------------------------- */
+
+var isGM = !(typeof GM_getValue == "undefined" || GM_getValue("a", "b") == undefined);
+
+/**
+ * If we're running on a content page, this variable will point at an object
+ * containing settings retrieved from the extension's localStorage, otherwise
+ * we're running in the extension's context and want to access localStorage
+ * directly.
+ *
+ * This allows us to include this script for use as a library in extension
+ * contexts, such as in the preferences dialogue.
+ */
+var cachedSettings = null;
+
+if (!isGM)
+{
+ GM_getValue = function(name, defaultValue)
+ {
+ var value = (cachedSettings == null ? localStorage.getItem(name) : cachedSettings[name]);
+ if (!value)
+ {
+ return defaultValue;
+ }
+ var type = value[0];
+ value = value.substring(1);
+ switch (type)
+ {
+ case "b":
+ return (value == "true");
+ case "n":
+ return Number(value);
+ default:
+ return value;
+ }
+ }
+
+ GM_setValue = function(name, value)
+ {
+ value = (typeof value)[0] + value;
+ if (cachedSettings == null)
+ {
+ localStorage.setItem(name, value);
+ }
+ else
+ {
+ cachedSettings[name] = value;
+ chrome.extension.sendRequest({type: "setpref", name: name, value: value});
+ }
+ }
+
+ if (typeof(unsafeWindow) == "undefined")
+ {
+ unsafeWindow = window;
+ }
+}
+
+String.prototype.endsWith = function(s)
+{
+ lastIndex = this.lastIndexOf(s);
+ return (lastIndex != -1 && lastIndex == (this.length - s.length));
+};
+
+Function.prototype.bind = function(object)
+{
+ var __method = this;
+ return function()
+ {
+ __method.apply(object, arguments);
+ }
+};
+
+/**
+ * Processing of the current page.
+ */
+var UIL =
+{
+ init: function()
+ {
+ var pageType = this.determineCurrentPageType();
+ this.processPage(pageType);
+ this.registerControls(pageType);
+ },
+
+ determineCurrentPageType: function()
+ {
+ var pageType = null;
+ if (window.location.href.indexOf("/index.php?showtopic=") != -1)
+ {
+ pageType = "topic";
+ }
+ else if ((window.location.href.indexOf("module=post") != -1 ||
+ window.location.href.endsWith("/index.php?")) &&
+ document.getElementById("postingform") &&
+ document.getElementById("postingform").elements.namedItem("t") !== null)
+ {
+ pageType = "postEditPreview";
+ }
+ else if (window.location.href.indexOf("/index.php?showforum=") != -1 ||
+ window.location.href.indexOf("module=search") != -1)
+ {
+ pageType = "topicListing";
+ }
+ else if (window.location.href.indexOf("area=ignoredusers") != -1)
+ {
+ pageType = "ignoredUsers";
+ }
+ return pageType;
+ },
+
+ processPage: function(pageType)
+ {
+ if (pageType !== null)
+ {
+ var pageProcessor = pageType + "PageProcessor";
+ if (typeof(this[pageProcessor]) == "function")
+ {
+ var removedItemCount = this[pageType + "PageProcessor"]();
+ if (UIL.Config.getNotification() === true && removedItemCount > 0)
+ {
+ this.notifyOfItemRemoval(removedItemCount);
+ }
+ }
+ }
+ },
+
+ topicPageProcessor: function()
+ {
+ var itemsRemoved = 0;
+ var topicId = this.getTopicIdFromCurrentPage();
+ var ignoredUsers =
+ UIL.Config.getGloballyIgnoredUsers()
+ .concat(UIL.Config.getIgnoredUsersForTopic(topicId));
+
+ // Get a list of username links
+ var nodes =
+ document.evaluate(
+ "//a[@class='url fn']",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ // Remove posts made by ignored usernames
+ for (var i = 0; i < nodes.snapshotLength; i++)
+ {
+ var node = nodes.snapshotItem(i);
+ if (ignoredUsers.indexOf(node.innerHTML) != -1)
+ {
+ node.parentNode.parentNode.parentNode.parentNode.style.display = "none";
+ itemsRemoved++;
+ }
+ }
+
+/*
+ // Now try truncated usernames
+ var nodes =
+ document.evaluate(
+ "//div[@class='popupmenu-item']/strong",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ // Remove posts made by ignored usernames
+ for (var i = 0; i < nodes.snapshotLength; i++)
+ {
+ var node = nodes.snapshotItem(i);
+ if (ignoredUsers.indexOf(node.innerHTML) != -1)
+ {
+ node.parentNode.parentNode.parentNode.parentNode.style.display = "none";
+ itemsRemoved++;
+ }
+ }
+*/
+
+ if (UIL.Config.getKillQuotes())
+ {
+ // Get a list of quote headers
+ var nodes =
+ document.evaluate(
+ "//p[@class='citation']",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ // Remove posts containing quotes from ignored usernames
+ for (var i = 0; i < nodes.snapshotLength; i++)
+ {
+ var node = nodes.snapshotItem(i);
+ for (var j = 0; j < ignoredUsers.length; j++)
+ {
+ if (node.textContent.indexOf(ignoredUsers[j] + ", on") === 0)
+ {
+ var postNode = node.parentNode.parentNode.parentNode.parentNode;
+ // May have already been hidden due to ignored quoter or
+ // another ignored quotee in the same post.
+ if (postNode.style.display != "none")
+ {
+ postNode.style.display = "none";
+ itemsRemoved++;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return itemsRemoved;
+ },
+
+ getTopicIdFromCurrentPage: function()
+ {
+ var input = document.evaluate(
+ "//input[@name='t']",
+ document,
+ null,
+ XPathResult.FIRST_ORDERED_NODE_TYPE,
+ null).singleNodeValue;
+ return (input != null ? input.value : null);
+ },
+
+ postEditPreviewPageProcessor: function()
+ {
+ var itemsRemoved = 0;
+ var topicId = this.getTopicIdFromCurrentPage();
+ var ignoredUsers =
+ UIL.Config.getGloballyIgnoredUsers()
+ .concat(UIL.Config.getIgnoredUsersForTopic(topicId));
+
+ // Get a list of username links
+ var nodes =
+ document.evaluate(
+ "//div[@id='topic_summary']/div/h3/a[1]",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ // Remove posts made by ignored usernames
+ for (var i = 0; i < nodes.snapshotLength; i++)
+ {
+ var node = nodes.snapshotItem(i);
+
+ if (ignoredUsers.indexOf(node.innerHTML) != -1)
+ {
+ node.parentNode.parentNode.style.display = "none";
+ itemsRemoved++;
+ }
+ }
+
+ if (UIL.Config.getKillQuotes())
+ {
+ // Get a list of quote headers
+ var nodes =
+ document.evaluate(
+ "//p[@class='citation']",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ // Remove posts containing quotes from ignored usernames
+ for (var i = 0; i < nodes.snapshotLength; i++)
+ {
+ var node = nodes.snapshotItem(i);
+ for (var j = 0; j < ignoredUsers.length; j++)
+ {
+ if (node.textContent.indexOf(ignoredUsers[j] + ", on") === 0)
+ {
+ var postNode = node.parentNode.parentNode.parentNode;
+ // May have already been hidden due to ignored quoter or
+ // another ignored quotee in the same post.
+ if (postNode.style.display != "none")
+ {
+ postNode.style.display = "none";
+ itemsRemoved++;
+ }
+ break;
+ }
+ }
+ }
+ }
+
+ return itemsRemoved;
+ },
+
+ topicListingPageProcessor: function()
+ {
+ if (!UIL.Config.getKillTopics())
+ {
+ return;
+ }
+
+ var itemsRemoved = 0;
+ var ignoredUsers = UIL.Config.getGloballyIgnoredUsers();
+
+ if (window.location.href.indexOf("module=search") != -1)
+ {
+ var topicStarterXPathQuery =
+ "//table[@id='forum_table']/tbody/tr/td[4]/a[1]";
+ }
+ else
+ {
+ var topicStarterXPathQuery =
+ "//table[@id='forum_table']/tbody/tr/td[3]/a[1]";
+ }
+
+ var topicStarterLinkNodes =
+ document.evaluate(
+ topicStarterXPathQuery,
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ for (var i = 0; i < topicStarterLinkNodes.snapshotLength; i++)
+ {
+ var topicStarterLinkNode = topicStarterLinkNodes.snapshotItem(i);
+ if (ignoredUsers.indexOf(topicStarterLinkNode.innerHTML) != -1)
+ {
+ var row = topicStarterLinkNode.parentNode.parentNode;
+ row.parentNode.removeChild(row);
+ itemsRemoved++;
+ }
+ }
+
+ return itemsRemoved;
+ },
+
+ notifyOfItemRemoval: function(removedItemCount)
+ {
+ var message = removedItemCount + " item";
+ if (removedItemCount > 1)
+ {
+ message += "s";
+ }
+ message += " removed";
+ this.showNotification(message);
+ },
+
+ showNotification: function(message)
+ {
+ // Show the notification
+ var s = document.createElement("DIV");
+ s.id = "UIL-notification";
+ s.style.position = "fixed";
+ s.style.top = "0px";
+ s.style.right = "0px";
+ if (isGM)
+ {
+ s.style.MozBorderRadiusBottomleft = "1em";
+ }
+ else
+ {
+ s.style.webkitBorderBottomLeftRadius = "1em";
+ }
+ s.style.backgroundColor = "red";
+ s.style.color = "white";
+ s.style.padding = "3px 6px 5px 8px";
+ s.style.fontWeight = "bold";
+ s.style.zIndex = "10002";
+ s.appendChild(document.createTextNode(message));
+ document.body.appendChild(s);
+
+ // Remove the notification later
+ window.setTimeout(function()
+ {
+ var el = document.getElementById("UIL-notification");
+ el.parentNode.removeChild(el);
+ }, 3000);
+ },
+
+ addIgnoredUserForCurrentTopic: function()
+ {
+ var userName = prompt("User to be ignored in this topic:");
+ if (userName)
+ {
+ if (UIL.Config.getGloballyIgnoredUsers().indexOf(userName) != -1)
+ {
+ alert("You're already ignoring " + userName + " globally");
+ return;
+ }
+
+ var topicId = this.getTopicIdFromCurrentPage();
+ var added = UIL.Config.addIgnoredUserForTopic(topicId, userName);
+ if (!added)
+ {
+ alert("You're already ignoring " + username + " in this topic");
+ }
+ }
+ /*
+ unsafeWindow.menu_action_close();
+ */
+ },
+
+ registerControls: function(pageType)
+ {
+ var controls =
+ document.getElementById("section_links");
+ // Only insert this link for GM - Chrome will use a page action
+ if (isGM && controls)
+ {
+ controls.insertBefore(this.createLinkControl("User Ignore List",
+ UIL.UI.show.bind(UIL.UI)),
+ controls.firstChild);
+ }
+
+ /*
+ if (pageType == "topic")
+ {
+ var menu = document.getElementById("topicmenu-options_menu");
+ var insertPoint = menu.getElementsByTagName("div")[5];
+
+ var item = document.createElement("div");
+ item.className = "popupmenu-item";
+
+ var img = document.createElement("img");
+ img.border = 0;
+ img.style.verticalAlign = "middle";
+ img.alt = "V";
+ img.src = "style_images/1/menu_item.gif";
+
+ item.appendChild(img);
+ item.appendChild(document.createTextNode(" "));
+ item.appendChild(this.createLinkControl(
+ "Ignore a user in this topic",
+ this.addIgnoredUserForCurrentTopic.bind(this)));
+
+ menu.insertBefore(item, insertPoint);
+ }
+ */
+
+ if (pageType == "ignoredUsers")
+ {
+ var saveButton =
+ document.evaluate(
+ "//input[@value='Save Changes']",
+ document.getElementById("usercp_content"),
+ null,
+ XPathResult.FIRST_ORDERED_NODE_TYPE,
+ null).singleNodeValue;
+
+ var input = document.createElement("input");
+ input.type = "button";
+ input.className = "input_submit alt";
+ input.value = "Synchronise User Ignore List";
+ input.addEventListener(
+ "click",
+ UIL.Config.importGloballyIgnoredUserList.bind(UIL.Config),
+ false);
+
+ saveButton.parentNode.insertBefore(input, saveButton.nextSibling);
+ saveButton.parentNode.insertBefore(document.createTextNode(" "), saveButton.nextSibling);
+ }
+ },
+
+ createLinkControl: function(name, handler)
+ {
+ var li = document.createElement("li");
+ var a = document.createElement("a");
+ a.href = "#";
+ a.appendChild(document.createTextNode(name));
+ a.addEventListener("click", handler, false);
+ li.appendChild(a);
+ return li;
+ }
+};
+
+/**
+ * Configuration.
+ */
+UIL.Config =
+{
+ getGloballyIgnoredUsers: function()
+ {
+ var ignoredUsers = [];
+ var config = GM_getValue("globallyIgnoredUsers");
+ if (config !== undefined && /^\[.*\]$/.test(config))
+ {
+ ignoredUsers = JSON.parse(config);
+ }
+ else
+ {
+ // Set up initial array, or reset if the config is invalid
+ GM_setValue("globallyIgnoredUsers", "[]");
+ }
+ return ignoredUsers;
+ },
+
+ setGloballyIgnoredUsers: function(userNames)
+ {
+ GM_setValue("globallyIgnoredUsers", JSON.stringify(userNames));
+ },
+
+ addGloballyIgnoredUser: function(userName)
+ {
+ var added = false;
+ var ignoredUsers = this.getGloballyIgnoredUsers();
+ if (ignoredUsers.indexOf(userName) == -1)
+ {
+ ignoredUsers.push(userName);
+ ignoredUsers.sort();
+ this.setGloballyIgnoredUsers(ignoredUsers);
+ added = true;
+ }
+ return added;
+ },
+
+ removeGloballyIgnoredUser: function(userName)
+ {
+ var ignoredUsers = this.getGloballyIgnoredUsers();
+ ignoredUsers.splice(ignoredUsers.indexOf(userName), 1);
+ this.setGloballyIgnoredUsers(ignoredUsers);
+ },
+
+ importGloballyIgnoredUserList: function()
+ {
+ // Get a list of username links
+ var nodes =
+ document.evaluate(
+ "//table[@summary='Ignored Users']/tbody/tr/td[1]/strong/a[1]",
+ document,
+ null,
+ XPathResult.UNORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+
+ var ignoredUsers = [];
+ for (var i = 0; i < nodes.snapshotLength; i++)
+ {
+ ignoredUsers.push(nodes.snapshotItem(i).innerHTML);
+ }
+
+ this.setGloballyIgnoredUsers(ignoredUsers);
+ UIL.showNotification("Synchronised to Globally Ignored Users");
+ },
+
+ getPerTopicIgnoredUsers: function()
+ {
+ var topics = {};
+ var config = GM_getValue("perTopicIgnoredUsers");
+ if (config !== undefined && /^\{.*\}$/.test(config))
+ {
+ var topics = JSON.parse(config);
+ }
+ else
+ {
+ // Set up initial object, or reset if the config is invalid
+ GM_setValue("perTopicIgnoredUsers", "{}");
+ }
+ return topics;
+ },
+
+ setPerTopicIgnoredUsers: function(topics)
+ {
+ GM_setValue("perTopicIgnoredUsers", JSON.stringify(topics));
+ },
+
+ addIgnoredUserForTopic: function(topicId, userName)
+ {
+ var added = false;
+ var topics = this.getPerTopicIgnoredUsers();
+
+ if (typeof(topics[topicId]) == "undefined")
+ {
+ topics[topicId] = [];
+ }
+
+ if (topics[topicId].indexOf(userName) == -1)
+ {
+ topics[topicId].push(userName);
+ topics[topicId].sort();
+ this.setPerTopicIgnoredUsers(topics);
+ added = true;
+ }
+ return added;
+ },
+
+ getIgnoredUsersForTopic: function(topicId)
+ {
+ var ignoredUsers = [];
+ var topics = this.getPerTopicIgnoredUsers();
+ if (typeof(topics[topicId]) != "undefined")
+ {
+ ignoredUsers = topics[topicId];
+ }
+ return ignoredUsers;
+ },
+
+ removeIgnoredUserForTopic: function(topicId, userName)
+ {
+ var topics = this.getPerTopicIgnoredUsers();
+ topics[topicId].splice(topics[topicId].indexOf(userName), 1);
+ if (topics[topicId].length == 0)
+ {
+ delete(topics[topicId]);
+ }
+ this.setPerTopicIgnoredUsers(topics);
+ },
+
+ getNotification: function()
+ {
+ return this._getBooleanConfig("notification", false);
+ },
+
+ setNotification: function(notification)
+ {
+ GM_setValue("notification", notification);
+ },
+
+ getKillQuotes: function()
+ {
+ return this._getBooleanConfig("killQuotes", true);
+ },
+
+ setKillQuotes: function(killQuotes)
+ {
+ GM_setValue("killQuotes", killQuotes);
+ },
+
+ getKillTopics: function()
+ {
+ return this._getBooleanConfig("killTopics", false);
+ },
+
+ setKillTopics: function(killTopics)
+ {
+ GM_setValue("killTopics", killTopics);
+ },
+
+ _getBooleanConfig: function(configName, defaultValue)
+ {
+ var config = GM_getValue(configName);
+ if (config === undefined)
+ {
+ GM_setValue(configName, defaultValue);
+ config = defaultValue;
+ }
+ return config;
+ }
+};
+
+/**
+ * Preferences User Interface (UI).
+ */
+UIL.UI =
+{
+ PREFS_HTML: "data:text/html;charset=utf-8;base64,PCFET0NUWVBFIGh0bWwgUFVCTElDICItLy9XM0MvL0RURCBIVE1MIDQuMDEvL0VOIiAiaHR0cDovL3d3dy53My5vcmcvVFIvaHRtbDQvc3RyaWN0LmR0ZCI%2BDQo8aHRtbCBsYW5nPSJlbiI%2BDQo8aGVhZD4NCiAgPHRpdGxlPlVzZXJzY3JpcHQgUHJlZmVyZW5jZXM8L3RpdGxlPg0KICA8bWV0YSBodHRwLWVxdWl2PSJDb250ZW50LVR5cGUiIGNvbnRlbnQ9InRleHQvaHRtbDsgY2hhcnNldD1VVEYtOCI%2BDQogIDxtZXRhIG5hbWU9IkF1dGhvciIgY29udGVudD0iSm9uYXRoYW4gQnVjaGFuYW4iPg0KICA8bWV0YSBuYW1lPSJDb3B5cmlnaHQiIGNvbnRlbnQ9IiZjb3B5OyAyMDA2LCBKb25hdGhhbiBCdWNoYW5hbiI%2BDQogIDxzdHlsZSB0eXBlPSJ0ZXh0L2NzcyI%2BDQogIGh0bWwgeyBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudCB9DQogIGJvZHkgeyBtYXJnaW46MDsgcGFkZGluZzowOyBiYWNrZ3JvdW5kLWNvbG9yOiB0cmFuc3BhcmVudDsgY29sb3I6ICMxQzI4Mzc7IGZvbnQ6IDEzcHggYXJpYWwsdmVyZGFuYSx0YWhvbWEsc2Fucy1zZXJpZjsgd2lkdGg6IDcyMHB4OyBtYXJnaW46IDAgYXV0bzsgfQ0KICAubW9kdWxlIHsgbWFyZ2luLWJvdHRvbTogNXB4OyB9DQogIC5tb2R1bGUgaDIsIC5tb2R1bGUgY2FwdGlvbiB7DQogICAgLW1vei1ib3JkZXItcmFkaXVzOjVweCA1cHggMCAwOw0KICAgIGZvbnQtc2l6ZToxNHB4Ow0KICAgIGZvbnQtd2VpZ2h0Om5vcm1hbDsNCiAgICBtYXJnaW46MCAhaW1wb3J0YW50Ow0KICAgIG92ZXJmbG93OmhpZGRlbjsNCiAgICBwYWRkaW5nOjhweCAhaW1wb3J0YW50Ow0KICAgIGJhY2tncm91bmQ6dXJsKCJodHRwOi8vd3d3LnJsbG11a2ZvcnVtLmNvbS9wdWJsaWMvc3R5bGVfaW1hZ2VzL21hc3Rlci9ncmFkaWVudF9iZy5wbmciKSByZXBlYXQteCBzY3JvbGwgbGVmdCA1MCUgIzFEMzY1MjsNCiAgICBjb2xvcjojRkZGRkZGOw0KICB9DQoNCiAgLmZvcm0tcm93IHsgb3ZlcmZsb3c6IGhpZGRlbjsgcGFkZGluZzogOHB4IDEycHg7IGZvbnQtc2l6ZTogMTFweDsgYm9yZGVyLWJvdHRvbTogMXB4IHNvbGlkICNlZWU7IGJvcmRlci1yaWdodDogMXB4IHNvbGlkICNlZWU7IGJhY2tncm91bmQtY29sb3I6ICNmZmY7IH0NCiAgLmZvcm0tcm93IGltZywgLmZvcm0tcm93IGlucHV0IHsgdmVydGljYWwtYWxpZ246IG1pZGRsZTsgfQ0KICAuZm9ybS1maWVsZCB7IGZsb2F0OiBsZWZ0OyB9DQogIC5hbGlnbmVkIGxhYmVsIHsgcGFkZGluZzogMCAxZW0gM3B4IDA7IGZsb2F0OiBsZWZ0OyB3aWR0aDogMTBlbTsgfQ0KICAuY2hlY2tib3gtcm93IGxhYmVsIHsgcGFkZGluZzogMDsgZmxvYXQ6IG5vbmU7IHdpZHRoOiBhdXRvOyB9DQogIHAuaGVscCB7IGNvbG9yOiAjOTk5OyBmb250LXNpemU6IDExcHg7IH0NCiAgLnN1Ym1pdC1yb3cgeyBwYWRkaW5nOiA4cHggMTJweDsgdGV4dC1hbGlnbjogcmlnaHQ7IGJvcmRlci1ib3R0b206IDFweCBzb2xpZCAjZWVlOyBib3JkZXItcmlnaHQ6IDFweCBzb2xpZCAjZWVlOyBiYWNrZ3JvdW5kLWNvbG9yOiAjZmZmOyB9DQoNCiAgdWwgeyBtYXJnaW46IC41ZW0gMCAwIDA7IHBhZGRpbmc6IDAgMCAwIDJlbTsgbGluZS1oZWlnaHQ6IDEuNWVtOyB9DQogIGxpIHsgbWFyZ2luOiAwOyBwYWRkaW5nOiAwOyB9DQoNCiAgc3Bhbi5jb250cm9sIHsgdGV4dC1kZWNvcmF0aW9uOiB1bmRlcmxpbmU7IGN1cnNvcjogcG9pbnRlcjsgY29sb3I6ICMwMGY7IH0NCiAgPC9zdHlsZT4NCjwvaGVhZD4NCjxib2R5Pg0KDQo8Zm9ybSBuYW1lPSJwcmVmZXJlbmNlcyIgaWQ9InByZWZlcmVuY2VzIiBjbGFzcz0iYWxpZ25lZCI%2BDQogIDxkaXYgY2xhc3M9Im1vZHVsZSI%2BDQogICAgPGgyPlVzZXIgSWdub3JlIExpc3QgUHJlZmVyZW5jZXM8L2gyPg0KICAgIDxkaXYgY2xhc3M9ImZvcm0tcm93Ij4NCiAgICAgIDxsYWJlbD5HbG9iYWxseSBJZ25vcmVkIFVzZXJzOjwvbGFiZWw%2BDQogICAgICA8ZGl2IGNsYXNzPSJmb3JtLWZpZWxkIj4NCiAgICAgICAgPGlucHV0IHR5cGU9InRleHQiIG5hbWU9Imlnbm9yZV91c2VyIj4gPGlucHV0IHR5cGU9ImJ1dHRvbiIgdmFsdWU9IkFkZCIgaWQ9Imlnbm9yZV91c2VyX2J1dHRvbiIgbmFtZT0iaWdub3JlX3VzZXJfYnV0dG9uIj4NCiAgICAgICAgPHVsIGlkPSJpZ25vcmVkX3VzZXJzIj4NCiAgICAgICAgPC91bD4NCiAgICAgIDwvZGl2Pg0KICAgIDwvZGl2Pg0KICAgIDxkaXYgY2xhc3M9ImZvcm0tcm93Ij4NCiAgICAgIDxsYWJlbD5QZXItVG9waWMgSWdub3JlZCBVc2Vyczo8L2xhYmVsPg0KICAgICAgPGRpdiBjbGFzcz0iZm9ybS1maWVsZCI%2BDQogICAgICAgIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ0b3BpY19pZ25vcmVfdXNlciI%2BIGluIDxpbnB1dCB0eXBlPSJ0ZXh0IiBuYW1lPSJ0b3BpY19pZ25vcmUiPiA8aW5wdXQgdHlwZT0iYnV0dG9uIiB2YWx1ZT0iQWRkIiBpZD0idG9waWNfaWdub3JlX3VzZXJfYnV0dG9uIiBuYW1lPSJ0b3BpY19pZ25vcmVfdXNlcl9idXR0b24iPg0KICAgICAgICA8cCBjbGFzcz0iaGVscCI%2BVXNlIHRvcGljIGlkcyBmb3IgdGhlICJpbiIgZmllbGQsIGUuZy4gU3RldmUgaW4gMTIzNDU2PC9wPg0KICAgICAgICA8ZGwgaWQ9InRvcGljX2lnbm9yZWRfdXNlcnMiPg0KICAgICAgICA8L2RsPg0KICAgICAgPC9kaXY%2BDQogICAgPC9kaXY%2BDQogICAgPGRpdiBjbGFzcz0iZm9ybS1yb3cgY2hlY2tib3gtcm93Ij4NCiAgICAgIDxsYWJlbCBmb3I9ImtpbGxfcXVvdGVzIj48aW5wdXQgdHlwZT0iY2hlY2tib3giIG5hbWU9ImtpbGxfcXVvdGVzIiBpZD0ia2lsbF9xdW90ZXMiPiBSZW1vdmUgcG9zdHMgd2hpY2ggcXVvdGUgaWdub3JlZCB1c2VyczwvbGFiZWw%2BDQogICAgPC9kaXY%2BDQogICAgPGRpdiBjbGFzcz0iZm9ybS1yb3cgY2hlY2tib3gtcm93Ij4NCiAgICAgIDxsYWJlbCBmb3I9Im5vdGlmeSI%2BPGlucHV0IHR5cGU9ImNoZWNrYm94IiBuYW1lPSJub3RpZnkiIGlkPSJub3RpZnkiPiBEaXNwbGF5IG5vdGlmaWNhdGlvbiB3aGVuIHBvc3RzIG9yIHRvcGljcyBhcmUgcmVtb3ZlZDwvbGFiZWw%2BDQogICAgPC9kaXY%2BDQogICAgPGRpdiBjbGFzcz0iZm9ybS1yb3cgY2hlY2tib3gtcm93Ij4NCiAgICAgIDxsYWJlbCBmb3I9ImtpbGxfdG9waWNzIj48aW5wdXQgdHlwZT0iY2hlY2tib3giIG5hbWU9ImtpbGxfdG9waWNzIiBpZD0ia2lsbF90b3BpY3MiPiBSZW1vdmUgdG9waWNzIGNyZWF0ZWQgYnkgZ2xvYmFsbHkgaWdub3JlZCB1c2VyczwvbGFiZWw%2BDQogICAgPC9kaXY%2BDQogIDwvZGl2Pg0KDQogIDxkaXYgY2xhc3M9Im1vZHVsZSI%2BDQogICAgPGRpdiBjbGFzcz0ic3VibWl0LXJvdyI%2BDQogICAgICA8aW5wdXQgdHlwZT0iYnV0dG9uIiB2YWx1ZT0iQ2xvc2UiIG5hbWU9ImNsb3NlX2J1dHRvbiIgaWQ9ImNsb3NlX2J1dHRvbiI%2BDQogICAgICA8aW5wdXQgdHlwZT0iYnV0dG9uIiB2YWx1ZT0iU2F2ZSBQcmVmZXJlbmNlcyIgbmFtZT0ic2F2ZV9idXR0b24iIGlkPSJzYXZlX2J1dHRvbiI%2BDQogICAgPC9kaXY%2BDQogIDwvZGl2Pg0KPC9mb3JtPg0KDQo8L2JvZHk%2BDQo8L2h0bWw%2B",
+
+ // This will only be called when running on GreaseMonkey, as the Chrome
+ // extension will use a page action to display the preferences dialogue.
+ show: function(e)
+ {
+ if (e)
+ {
+ e.preventDefault();
+ e.stopPropagation();
+ }
+
+ var blocker = document.createElement("div");
+ this.blocker = blocker;
+ blocker.id = "uil_blocker";
+ blocker.style.position = "fixed";
+ blocker.style.top = "0px";
+ blocker.style.right = "0px";
+ blocker.style.bottom = "0px";
+ blocker.style.left = "0px";
+ blocker.style.backgroundColor = "#000";
+ blocker.style.opacity = "0.5";
+ blocker.style.zIndex = "10000";
+ document.body.appendChild(blocker);
+
+ var prefs = document.createElement("iframe");
+ prefs.addEventListener("load", this.preferenceDocumentLoadHandler.bind(this), false);
+ this.prefs = prefs;
+
+ document.body.appendChild(prefs);
+
+ prefs.id = "uil_preferences";
+ prefs.name = "uil_preferences";
+ prefs.style.position = "fixed";
+ prefs.style.top = "1em";
+ prefs.style.left = "0px";
+ prefs.style.right = "0px";
+ prefs.style.border = "none";
+ prefs.style.height = "100%";
+ prefs.style.width = "100%";
+ prefs.style.overflow = "hidden";
+ prefs.style.zIndex = "10000";
+ prefs.src = this.PREFS_HTML;
+ },
+
+ hide: function()
+ {
+ if (isGM)
+ {
+ document.body.removeChild(this.prefs);
+ document.body.removeChild(this.blocker);
+ this.prefs = null;
+ this.blocker = null;
+ }
+ else
+ {
+ window.close();
+ }
+ },
+
+ getDocument: function()
+ {
+ return (isGM ? this.prefs.contentDocument : document);
+ },
+
+ preferenceDocumentLoadHandler: function()
+ {
+ var form = this.getDocument().forms.namedItem("preferences");
+
+ // Set up form state
+ this.populateGloballyIgnoredUserList();
+ this.populatePerTopicIgnoredUserList();
+ // TODO Implement this for Chrome
+ if (isGM)
+ {
+ var currentTopicId = UIL.getTopicIdFromCurrentPage();
+ if (currentTopicId != null)
+ {
+ form.elements.namedItem("topic_ignore").value = currentTopicId;
+ }
+ }
+ form.elements.namedItem("kill_quotes").checked = UIL.Config.getKillQuotes();
+ form.elements.namedItem("notify").checked = UIL.Config.getNotification();
+ form.elements.namedItem("kill_topics").checked = UIL.Config.getKillTopics();
+
+ // Set up event handlers
+ form.elements.namedItem("close_button").addEventListener("click", this.hide.bind(this), false);
+ form.elements.namedItem("save_button").addEventListener("click", this.saveConfigurationHandler.bind(this), false);
+ form.elements.namedItem("ignore_user_button").addEventListener("click", this.addGloballyIgnoredUserHandler.bind(this), false);
+ form.elements.namedItem("topic_ignore_user_button").addEventListener("click", this.addPerTopicIgnoredUserHandler.bind(this), false);
+ },
+
+ saveConfigurationHandler: function()
+ {
+ var form = this.getDocument().forms.namedItem("preferences");
+ UIL.Config.setKillQuotes(form.elements.namedItem("kill_quotes").checked);
+ UIL.Config.setNotification(form.elements.namedItem("notify").checked);
+ UIL.Config.setKillTopics(form.elements.namedItem("kill_topics").checked);
+ this.hide();
+ },
+
+ addGloballyIgnoredUserHandler: function()
+ {
+ var form = this.getDocument().forms.namedItem("preferences");
+ var ignoredUser = form.elements.namedItem("ignore_user");
+ var userName = ignoredUser.value;
+ if (userName.length > 0)
+ {
+ var added = UIL.Config.addGloballyIgnoredUser(userName);
+ if (added)
+ {
+ ignoredUser.value = "";
+ this.populateGloballyIgnoredUserList();
+ }
+ else
+ {
+ alert("You're already ignoring " + userName);
+ }
+ }
+ },
+
+ addPerTopicIgnoredUserHandler: function()
+ {
+ var form = this.getDocument().forms.namedItem("preferences");
+ var ignoredUser = form.elements.namedItem("topic_ignore_user");
+ var ignoredTopic = form.elements.namedItem("topic_ignore");
+ var userName = ignoredUser.value;
+ var topicId = ignoredTopic.value;
+ if (userName.length > 0 && topicId.length > 0)
+ {
+ if (UIL.Config.getGloballyIgnoredUsers().indexOf(userName) != -1)
+ {
+ alert("You're already ignoring " + userName + " globally");
+ return;
+ }
+
+ var added = UIL.Config.addIgnoredUserForTopic(topicId, userName);
+ if (added)
+ {
+ this.populatePerTopicIgnoredUserList();
+ ignoredUser.value = "";
+ ignoredTopic.value = "";
+ }
+ else
+ {
+ alert("You're already ignoring " + userName + " in topic " + topicId);
+ }
+ }
+ },
+
+ createGloballyUnignoreUserHandler: function(userName)
+ {
+ return function()
+ {
+ if (confirm("Are you sure you want to unignore " + userName + "?"))
+ {
+ UIL.Config.removeGloballyIgnoredUser(userName);
+ this.populateGloballyIgnoredUserList();
+ }
+ }.bind(this);
+ },
+
+ createPerTopicUnignoreUserHandler: function(userName, topicId)
+ {
+ return function()
+ {
+ if (confirm("Are you sure you want to unignore " + userName + " in topic " + topicId + "?"))
+ {
+ UIL.Config.removeIgnoredUserForTopic(topicId, userName);
+ this.populatePerTopicIgnoredUserList();
+ }
+ }.bind(this);
+ },
+
+ populateGloballyIgnoredUserList: function()
+ {
+ var document = this.getDocument();
+ var list = document.getElementById("ignored_users");
+ while (list.firstChild)
+ {
+ list.removeChild(list.firstChild);
+ }
+ UIL.Config.getGloballyIgnoredUsers().forEach(function(userName)
+ {
+ var li = document.createElement("li");
+
+ var span = document.createElement("span");
+ span.appendChild(document.createTextNode("Unignore"));
+ span.className = "control";
+ span.addEventListener("click", this.createGloballyUnignoreUserHandler(userName), false);
+
+ li.appendChild(document.createTextNode(userName + " ("));
+ li.appendChild(span);
+ li.appendChild(document.createTextNode(")"));
+ list.appendChild(li);
+ }.bind(this));
+ },
+
+ populatePerTopicIgnoredUserList: function()
+ {
+ var document = this.getDocument();
+ var list = document.getElementById("topic_ignored_users");
+ while (list.firstChild)
+ {
+ list.removeChild(list.firstChild);
+ }
+ var topics = UIL.Config.getPerTopicIgnoredUsers()
+ for (topicId in topics)
+ {
+ if (topics.hasOwnProperty(topicId))
+ {
+ var dt = document.createElement("dt");
+ var a = document.createElement("a");
+ a.href = "http://www.rllmukforum.com/index.php?showtopic=" + topicId;
+ a.target = "_blank";
+ a.appendChild(document.createTextNode(topicId));
+ dt.appendChild(a);
+ list.appendChild(dt);
+
+ topics[topicId].forEach(function(userName)
+ {
+ var dd = document.createElement("dd");
+
+ var span = document.createElement("span");
+ span.appendChild(document.createTextNode("Unignore"));
+ span.className = "control";
+ span.addEventListener("click", this.createPerTopicUnignoreUserHandler(userName, topicId), false);
+
+ dd.appendChild(document.createTextNode(userName + " ("));
+ dd.appendChild(span);
+ dd.appendChild(document.createTextNode(")"));
+ list.appendChild(dd);
+ }.bind(this));
+ }
+ }
+ }
+};
+
+// Chrome will use another content script to initialise the script.
+if (isGM)
+{
+ UIL.init();
+}
View
30 UIL/background.html
@@ -0,0 +1,30 @@
+<html>
+ <script>
+chrome.extension.onRequest.addListener(function(request, sender, sendResponse)
+{
+ if (request.type == "getprefs")
+ {
+ sendResponse({
+ globallyIgnoredUsers: localStorage.globallyIgnoredUsers,
+ perTopicIgnoredUsers: localStorage.perTopicIgnoredUsers,
+ notification: localStorage.notification,
+ killQuotes: localStorage.killQuotes,
+ killTopics: localStorage.killTopics
+ });
+ }
+ else if (request.type == "setpref")
+ {
+ localStorage.setItem(request.name, request.value);
+ }
+});
+
+chrome.tabs.onUpdated.addListener(function(tabId, changeInfo, tab)
+{
+ if (tab.url.indexOf("http://rllmukforum.com") == 0 ||
+ tab.url.indexOf("http://www.rllmukforum.com") == 0)
+ {
+ chrome.pageAction.show(tabId);
+ }
+});
+ </script>
+</html>
View
BIN UIL/icon128.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN UIL/icon16.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
BIN UIL/icon48.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
24 UIL/manifest.json
@@ -0,0 +1,24 @@
+{
+ "name": "Rllmuk User Ignore List",
+ "version": "2.0.0",
+ "description": "Removes all traces of the users on your ignore list, optionally removing posts which quote them and any topics they create.",
+ "icons": {
+ "16": "icon16.png",
+ "48": "icon48.png",
+ "128": "icon128.png"
+ },
+ "background_page": "background.html",
+ "page_action": {
+ "default_icon": "user_delete.png",
+ "default_title": "User Ignore List",
+ "default_popup": "prefs.html"
+ },
+ "content_scripts": [
+ {
+ "matches": ["http://rllmukforum.com/*", "http://www.rllmukforum.com/*"],
+ "js": ["UIL.lib.js", "UIL.js"],
+ "run_at": "document_end"
+ }
+ ],
+ "permissions": ["tabs"]
+}
View
83 UIL/prefs.html
@@ -0,0 +1,83 @@
+<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
+<html lang="en">
+<head>
+ <title>Userscript Preferences</title>
+ <meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
+ <meta name="Author" content="Jonathan Buchanan">
+ <meta name="Copyright" content="&copy; 2006, Jonathan Buchanan">
+ <script type="text/javascript" src="UIL.lib.js"></script>
+ <script type="text/javascript">
+ window.addEventListener("load", UIL.UI.preferenceDocumentLoadHandler.bind(UIL.UI), false);
+ </script>
+ <style type="text/css">
+ html { background-color: transparent }
+ body { margin:0; padding:0; background-color: transparent; color: #1C2837; font: 13px arial,verdana,tahoma,sans-serif; width: 720px; margin: 0 auto; }
+ .module { margin-bottom: 5px; }
+ .module h2, .module caption {
+ -moz-border-radius:5px 5px 0 0;
+ font-size:14px;
+ font-weight:normal;
+ margin:0 !important;
+ overflow:hidden;
+ padding:8px !important;
+ background:url("http://www.rllmukforum.com/public/style_images/master/gradient_bg.png") repeat-x scroll left 50% #1D3652;
+ color:#FFFFFF;
+ }
+
+ .form-row { overflow: hidden; padding: 8px 12px; font-size: 11px; border-bottom: 1px solid #eee; border-right: 1px solid #eee; background-color: #fff; }
+ .form-row img, .form-row input { vertical-align: middle; }
+ .form-field { float: left; }
+ .aligned label { padding: 0 1em 3px 0; float: left; width: 10em; }
+ .checkbox-row label { padding: 0; float: none; width: auto; }
+ p.help { color: #999; font-size: 11px; }
+ .submit-row { padding: 8px 12px; text-align: right; border-bottom: 1px solid #eee; border-right: 1px solid #eee; background-color: #fff; }
+
+ ul { margin: .5em 0 0 0; padding: 0 0 0 2em; line-height: 1.5em; }
+ li { margin: 0; padding: 0; }
+
+ span.control { text-decoration: underline; cursor: pointer; color: #00f; }
+ </style>
+</head>
+<body>
+
+<form name="preferences" id="preferences" class="aligned">
+ <div class="module">
+ <h2>User Ignore List Preferences</h2>
+ <div class="form-row">
+ <label>Globally Ignored Users:</label>
+ <div class="form-field">
+ <input type="text" name="ignore_user"> <input type="button" value="Add" id="ignore_user_button" name="ignore_user_button">
+ <ul id="ignored_users">
+ </ul>
+ </div>
+ </div>
+ <div class="form-row">
+ <label>Per-Topic Ignored Users:</label>
+ <div class="form-field">
+ <input type="text" name="topic_ignore_user"> in <input type="text" name="topic_ignore"> <input type="button" value="Add" id="topic_ignore_user_button" name="topic_ignore_user_button">
+ <p class="help">Use topic ids for the "in" field, e.g. Steve in 123456</p>
+ <dl id="topic_ignored_users">
+ </dl>
+ </div>
+ </div>
+ <div class="form-row checkbox-row">
+ <label for="kill_quotes"><input type="checkbox" name="kill_quotes" id="kill_quotes"> Remove posts which quote ignored users</label>
+ </div>
+ <div class="form-row checkbox-row">
+ <label for="notify"><input type="checkbox" name="notify" id="notify"> Display notification when posts or topics are removed</label>
+ </div>
+ <div class="form-row checkbox-row">
+ <label for="kill_topics"><input type="checkbox" name="kill_topics" id="kill_topics"> Remove topics created by globally ignored users</label>
+ </div>
+ </div>
+
+ <div class="module">
+ <div class="submit-row">
+ <input type="button" value="Close" name="close_button" id="close_button">
+ <input type="button" value="Save Preferences" name="save_button" id="save_button">
+ </div>
+ </div>
+</form>
+
+</body>
+</html>
View
BIN UIL/user_delete.png
Sorry, something went wrong. Reload?
Sorry, we cannot display this file.
Sorry, this file is invalid so it cannot be displayed.
View
91 aretheyrealhiddencommentsorfakehiddencomments.user.js
@@ -0,0 +1,91 @@
+// ==UserScript==
+// @name Are They Real Hidden Comments or Fake Hidden Comments?
+// @namespace http://www.jonathanbuchanan.plus.com/repos/greasemonkey/
+// @description Shows hidden comments when film names which have associated comments are hovered over. Click to view a selectable version of the comment.
+// @include http://realorfake3d.com/
+// ==/UserScript==
+
+var INFO_ID = "tehInfo";
+
+function doSomethingUsefulWithComments(p)
+{
+ for (var i = 0, l = p.childNodes.length; i < l; i++)
+ {
+ var n = p.childNodes[i];
+ if (n.nodeType == 3 &&
+ n.nodeValue.replace(/[\r\n]/g, '') != "" &&
+ l != i+1 &&
+ p.childNodes[i+1].nodeType == 8)
+ {
+ var s = document.createElement("span");
+ s.appendChild(document.createTextNode(n.nodeValue));
+ s.title = p.childNodes[i+1].nodeValue;
+ s.style.cursor = "help";
+ s.addEventListener("click", showInfo, false);
+ p.replaceChild(s, n);
+ i++;
+ }
+ }
+}
+
+function showInfo(e)
+{
+ hideInfo();
+
+ var d = document.createElement("div");
+ d.style.position = "absolute";
+ var pos = objectPosition(this);
+ var left = pos[0] + this.offsetWidth + 4;
+ if (left + 322 >= window.innerWidth)
+ {
+ d.style.right = "0px";
+ }
+ else
+ {
+ d.style.left = left + "px";
+ }
+ d.style.top = (pos[1]) + "px";
+ d.style.width = "300px";
+ d.style.backgroundColor = "#fff";
+ d.style.border = "1px solid #000";
+ d.style.padding = "4px 8px";
+ d.style.MozBorderRadius = "1em";
+ d.id = INFO_ID;
+ d.innerHTML = this.title;
+ document.body.appendChild(d);
+}
+
+function objectPosition(obj)
+{
+ var curleft = 0;
+ var curtop = 0;
+ if (obj.offsetParent)
+ {
+ do
+ {
+ curleft += obj.offsetLeft;
+ curtop += obj.offsetTop;
+ } while (obj = obj.offsetParent);
+ }
+ return [curleft, curtop];
+}
+
+function hideInfo()
+{
+ var d = document.getElementById(INFO_ID);
+ if (d != null)
+ {
+ d.parentNode.removeChild(d);
+ }
+}
+
+var paras = document.evaluate(
+ "//tbody/tr[position()!=1 and position()!=last()]//p",
+ document,
+ null,
+ XPathResult.ORDERED_NODE_SNAPSHOT_TYPE,
+ null);
+for (var i = 0, l = paras.snapshotLength; i < l; i++)
+{
+ doSomethingUsefulWithComments(paras.snapshotItem(i));
+}
View
28 rllmuklinkfixer.user.js
@@ -1,28 +0,0 @@
-// ==UserScript==
-// @name Rllmuk Link Fixer
-// @namespace http://www.jonathanbuchanan.plus.com/repos/greasemonkey/
-// @description Fixes links broken by the forum's post editing bug
-// @include http://www.rllmukforum.com/*
-// @include http://rllmukforum.com/*
-// @include http://www.rpsoft.co.uk/*
-// @include http://rpsoft.co.uk/*
-// @include http://www.extranoise.co.uk/*
-// @include http://extranoise.co.uk/*
-// ==/UserScript==
-for (var i = 0, l = document.links.length; i < l; i++)
-{
- var link = document.links[i];
- if (!link.href || link.href.indexOf("...") == -1)
- {
- continue;
- }
- var correctLink = link.previousSibling;
- if (correctLink &&
- correctLink.nodeType === 1 &&
- correctLink.tagName.toUpperCase() == "A" &&
- correctLink.childNodes.length === 0 &&
- correctLink.href.indexOf(link.href.split("...")[0]) === 0)
- {
- link.href = correctLink.href;
- }
-}
View
70 rllmuklofienhancements.user.js
@@ -1,70 +0,0 @@
-// ==UserScript==
-// @name Rllmuk Lo-fi Enhancements
-// @namespace http://www.jonathanbuchanan.plus.com/repos/greasemonkey/
-// @description Enhances Lo-fi mode with a "Fast Reply" form
-// @include http://www.rllmukforum.com/*
-// @include http://rllmukforum.com/*
-// @include http://www.rpsoft.co.uk/*
-// @include http://rpsoft.co.uk/*
-// @include http://www.extranoise.co.uk/*
-// @include http://extranoise.co.uk/*
-// ==/UserScript==
-if (!/t\d+(?:-\d+)?\.html$/.test(location.href))
-{
- return;
-}
-else if (!GM_getValue("authKey"))
-{
- var authKey = prompt("Please enter your authorisation key (View Source on a normal topic page and search for 'auth_key') - this is required to post new messages.");
- if (!authKey)
- {
- alert("Fine!");
- return;
- }
- GM_setValue("authKey", authKey);
-}
-
-var topicLink = document.evaluate("//div[@id='largetext']/a[1]",
- document,
- null,
- XPathResult.FIRST_ORDERED_NODE_TYPE,
- null).singleNodeValue;
-var topicId = /(\d+)$/.exec(topicLink.href)[1];
-
-var forumLink = document.evaluate("//div[@class='ipbnav']/a[last()]",
- document,
- null,
- XPathResult.FIRST_ORDERED_NODE_TYPE,
- null).singleNodeValue;
-var forumId = /f(\d+)\.html/.exec(forumLink.href)[1];
-
-var authKey = GM_getValue("authKey");
-
-var fastReplyControl = document.createElement("div");
-fastReplyControl.className = "smalltext";
-fastReplyControl.style.textAlign = "right";
-fastReplyControl.innerHTML = '<input type="button" value="Fast Reply" onclick="var f = document.getElementById(\'fastReplyForm\'); f.style.display = f.style.display == \'none\' ? \'\' : \'none\'">';
-
-var fastReply = document.createElement("div");
-fastReply.className = "smalltext";
-fastReply.innerHTML = '<form id="fastReplyForm" style="display: none" method="POST" action="http://www.rllmukforum.com/index.php?">\
- <input type="hidden" name="act" value="Post">\
- <input type="hidden" name="CODE" value="03">\
- <input type="hidden" name="f" value="' + forumId + '">\
- <input type="hidden" name="t" value="' + topicId + '">\
- <input type="hidden" name="st" value="0">\
- <input type="hidden" name="auth_key" value="' + authKey + '">\
- <input type="hidden" name="fast_reply_used" value="1">\
- <textarea rows="15" cols="80" name="Post" style="height: 150px"></textarea>\
- <br>\
- <input type="submit" name="submit" value="Add Reply">\
- <input type="button" value="Close Fast Reply" onclick="this.form.style.display = \'none\'">\
-</form>';
-
-var insertionTarget = document.evaluate("//div[@class='smalltext' and last()]",
- document,
- null,
- XPathResult.FIRST_ORDERED_NODE_TYPE,
- null).singleNodeValue;
-insertionTarget.parentNode.insertBefore(fastReplyControl, insertionTarget);
-insertionTarget.parentNode.insertBefore(fastReply, insertionTarget);
View
26 rllmuktopicignorelist.prefs.html
@@ -6,16 +6,26 @@
<meta name="Author" content="Jonathan Buchanan">
<meta name="Copyright" content="&copy; 2006, Jonathan Buchanan">
<style type="text/css">
- body { margin:0; padding:0; font-size:12px; font-family:"Lucida Grande","Bitstream Vera Sans",Verdana,Arial,sans-serif; color:#333; width: 720px; margin: 0 auto; }
- .module { border: 1px solid #ccc; margin-bottom: 5px; background-color: #fff; }
- .module h2, .module caption { margin: 0; padding: 2px 5px 3px 5px; font-size: 11px; text-align: left; font-weight: bold; background: #7CA0C7 url("data:image/gif;base64,R0lGODlhEQAfAOYAALzS6bDJ44%2Bw0YGlyrvR6LTM5Yiqzn6iyIOny4CkyaG%2B242u0KK%2B27jP53ugxoiqzYaozJy62Jq413ugx4qsz5%2B82aTA3K7H4oSny5%2B82rbN5rnQ6KTA3afC3rLL5JS01KnE37bO5n%2BkyZy52HyhyK7I4bLK5LjP5rnQ55W01KnD35e21X6jyK7H4ZKy06vG4XyiyIyt0JSz1LHJ44uszpe11nyhx7rQ55e21pGx06fB3oqszn2ix5Kx0pe11aS%2F3IKlyqzG4KbC3pq31rvR6azF4LXM5Yyu0KvF4bPK45m41qfC3bvS6H2hx6zG4YKmyrPK5KzF4ZWz1Iyuz5q41oapzYCjyZKy0qrE37fN5qrD4J%2B72pCw0ZGy0oGmyo%2Bv0bTN5bXN5Zm415262Jy62YWozIWpzKrD34aozbrR6I%2Bv0rjO5rjO56fB3anE4ISmy7PL5LfO5p%2B72bbN5YapzHuhxrfP5nygx7fP56K924%2Bw0rHJ4qXA3Yurz36jyYuszyH5BAAAAAAALAAAAAARAB8AAAf%2FgACCg4SFggQETERMiIhEBGmPjAQbGzc3KCiWmZY3lRsoaw2jdicnDaeoqHhsDRohr7AhWVlxcxq4IbBgRgW%2BYb1hBUZgvsYmHlAeScjIHs8mJnBQcAEBM9bY1nvW2dgXLS0lLRcl5ePm4BcXRUEvTkhR7e8vQVFIL0VIKm5YICAqAArUgiWgljNCdCzp0KZDh4U6OugQIkTiEg4cfnCwYCGjBT4cNXLkoIABg5IoTSpYeTIPgwoZMmypAFNmBpoV5MSsMGbEiAgRfgIdQ8YnGaARJChVMoQKFaZKxYiRoETCkBorVtTwsQIHDh8%2BcGD9qjXFBxkyUpiVcvbD2hQyqT646JEjx5Uec7u4sJsjrwsBArgADvxFQGE1AvQMjhFjgWPHMY4cedx4yhEKmGnQ2EFBM4UdfSj8%2BbzjgQHTBlI%2FWK2atYEyaCCYgUDbDB0IVcxUKUMbAgIMCIL%2FfhMcOIbjv58AGeBlOfMnA6I7j55AhIjq1a9bSZBgO3YeLFgcGH9APPny4f3AsMGeRBMSJHjAaGJDfhMeJO44mLDfQR3%2BEwTYnwMOBAIAOw%3D%3D") top left repeat-x; color: #fff; border-bottom: 0; }
+ html { background-color: transparent }
+ body { margin:0; padding:0; background-color: transparent; color: #1C2837; font: 13px arial,verdana,tahoma,sans-serif; width: 720px; margin: 0 auto; }
+ .module { margin-bottom: 5px; }
+ .module h2, .module caption {
+ -moz-border-radius:5px 5px 0 0;
+ font-size:14px;
+ font-weight:normal;
+ margin:0 !important;
+ overflow:hidden;
+ padding:8px !important;
+ background:url("http://www.rllmukforum.com/public/style_images/master/gradient_bg.png") repeat-x scroll left 50% #1D3652;
+ color:#FFFFFF;
+ }
- .form-row { overflow: hidden; padding: 8px 12px; font-size: 11px; border-bottom: 1px solid #eee; border-right: 1px solid #eee; }
+ .form-row { overflow: hidden; padding: 8px 12px; font-size: 11px; border-bottom: 1px solid #eee;