mlmb

Media Lens Message Board - add-on to improve the usability of the board

You will need to install an extension such as Tampermonkey, Greasemonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install an extension such as Tampermonkey or Violentmonkey to install this script.

You will need to install an extension such as Tampermonkey or Userscripts to install this script.

You will need to install an extension such as Tampermonkey to install this script.

You will need to install a user script manager extension to install this script.

(I already have a user script manager, let me install it!)

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install an extension such as Stylus to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

You will need to install a user style manager extension to install this style.

(I already have a user style manager, let me install it!)

// ==UserScript==
// @name        mlmb
// @namespace   http://thinkingoutquiet.wordpress.com/
// @description Media Lens Message Board - add-on to improve the usability of the board 
// @include     http://members5.boardhost.com/medialens/
// @include     http://members5.boardhost.com/medialens/index*
// @include     http://members5.boardhost.com/medialens/thread*
// @include     http://members5.boardhost.com/medialens/msg*
// @grant       GM_getValue
// @grant       GM_setValue
// @grant       GM_deleteValue
// @grant       GM_listValues
// @version     4.1
// @require     http://code.jquery.com/jquery-1.7.2.min.js
// ==/UserScript==


// Global variables

var url = window.location.href;
var indexPage = (url == "http://members5.boardhost.com/medialens/" || url.substr(0,45) == "http://members5.boardhost.com/medialens/index");
var threadPage = (url.substr(0,46) == "http://members5.boardhost.com/medialens/thread");
var msgPage = (url.substr(0,43) == "http://members5.boardhost.com/medialens/msg");

var threadName = '';
var threadSubList = '';
var threadList = '';
var filter = GM_getValue("var-filter","all");
var option_filter_enabled = GM_getValue("var-option-filter-enabled",true);
GM_setValue("var-option-filter-enabled",option_filter_enabled);
var option_markers_enabled = GM_getValue("var-option-markers-enabled",true);
GM_setValue("var-option-markers-enabled",option_markers_enabled);
var option_hide_quoted_text = GM_getValue("var-option-hide-quoted-text",true);
GM_setValue("var-option-hide-quoted-text",option_hide_quoted_text);
var option_collapse_threads = GM_getValue("var-option-collapse-threads",true);
GM_setValue("var-option-collapse-threads",option_collapse_threads);
var option_collapse_sub_threads = GM_getValue("var-option-collapse-sub-threads",true);
GM_setValue("var-option-collapse-sub-threads",option_collapse_sub_threads);
var option_collapse_read_messages = GM_getValue("var-option-collapse-read-messages",true);
GM_setValue("var-option-collapse-read-messages",option_collapse_read_messages);
var option_highlight_all_read = GM_getValue("var-option-highlight-all-read",true);
GM_setValue("var-option-highlight-all-read",option_highlight_all_read);
var option_view_thread_link = GM_getValue("var-option-view-thread-link",true);
GM_setValue("var-option-view-thread-link",option_view_thread_link);

// Styles

var styles = '<style>' +
'div#mlmb_options {display:none; position:fixed; left:50%; width:450px; margin-left:-225px; top:100px; border:2px solid #000; background-color:#cef; padding:5px; font-family:sans-serif;}' +
'div#mlmb_options a.close {padding:2px 4px; float:right; text-decoration:none; border:1px solid #000; font-weight:bold;}' +
'div#mlmb_options p {margin:0 0 4px 0;}' +
'div#mlmb_options p.sub {margin-left:16px;}' +
'div#mlmb_options a.reload {font-weight:bold;}' +
'div#mlmb_options a:visited.reload {color:#00f;}';

if (indexPage) {
  styles += 'body {background-color:#eee; margin-left:30px; }' +
    'table#linkstable table td {vertical-align:top;}' +
    'span.collapseButton {font-size:12px; margin-right:8px; padding: 0 5px; background-color:#f88; border:1px solid #000; cursor:pointer;}' +
    'span.expandButton {font-size:12px; margin-right:8px; padding: 0 5px; background-color:#8f8; border:1px solid #000; cursor:pointer;}' +
    'form.threadFilter {margin-right:8px;}' +
    'div.threadButtons {position:absolute; left:-74px; width:62px;}' +
    'div.threadButtons span {float:right; margin-right:4px; padding:0 5px;}' +
    'span.toggleButton {background-color:#8f8; border:1px solid #000; cursor:pointer; font-weight:bold;}' +
    'span.markRead {color:#fff;}' +
    'span.closed {background-color:#f88;}' +
    'span.viewThreadLink {padding-left:20px; display:none;}' +
    'span.starButton {color:#999; width:10px; text-align:center; background-color:#fff; border:1px solid #777; cursor:pointer;}' +
    'span.starred {background-color:#07f; color:#fff; border-color:#000;}' +
    'span.ignored {background-color:#999; color:#fff;}' +
    'table#linkstable + ul > table > tbody > tr > td > font > li {position:relative; list-style: none; background-color:#fff; margin-bottom:8px; padding:6px; border-left:8px solid #fff; border-right:8px solid #fff;}' +
    'table#linkstable + ul > table > tbody > tr > td > font > li.posted {border-left-color: #f00; border-right-color: #f00;}' +
    'table#linkstable + ul > table > tbody > tr > td > font > li.starred {border-left-color: #07f;}' +
    'table#linkstable + ul > table > tbody > tr > td > font > li.ignored {border-left-color:#eee; border-right-color:#eee; background-color:#eee;}';

}

styles += 'span.subCollapse {background-color:#8f8;padding-left:4px;padding-right:4px;cursor:pointer;}' +
'span.subClosed {background-color:#f88;}'; 

if (threadPage || msgPage) {
  styles += 'div.toggleQuotedButton {font-size: 11px; cursor:pointer; background-color:#f88; display:inline-block; border:1px solid #000; padding: 2px 4px; margin: 4px 0 8px 0;}' +
    '.quoteBlock {display:none;}';
}

if (threadPage) {
  styles += 'div#fullThreadControls {font-family:sans-serif; position:fixed; top:10px; right: 10px;}' +
    'div.collapseButton {font-size:14px; margin-bottom:6px; padding: 0 5px; background-color:#f88; border:1px solid #000; cursor:pointer;}' +
    'div.expandButton {font-size:14px; margin-bottom:6px; padding: 0 5px; background-color:#8f8; border:1px solid #000; cursor:pointer;}';
}

styles += '</style>';


// Functions

function getThreadInfo(t) { // t is a button span
  threadName = t.parent().next('a').attr('name');
  threadSubList = t.parent().parent().children("ul");
  threadList = t.parent().parent();
}

function toggleThread(t) {
  getThreadInfo(t);
  threadSubList.toggle();  
  t.toggleClass("closed");
  t.parent().parent().find("span.viewThreadLink").toggle();
  GM_setValue(threadName, !t.hasClass("closed"));
}

function restoreMarkers(t) { 
  getThreadInfo(t);

  var star = GM_getValue(threadName + "-star", "");

  switch (star) { // restores starred & ignored threads
    case "*":
      t.addClass("starred");
      t.html("*");
      threadList.addClass('starred');
      break;
    case "i":
      t.addClass("ignored");
      t.html("i");
      threadList.addClass('ignored');
      break;
  }
}

function restoreThread(t) { 
  getThreadInfo(t);
  if (!GM_getValue(threadName, true)) { // assumes thread is open, and (potentially) closes it
    threadSubList.toggle();  
    t.toggleClass("closed");
    t.parent().parent().find("span.viewThreadLink").toggle();
  };
}

function collapseThread(t) {
  getThreadInfo(t);
  threadSubList.hide();  
  t.addClass("closed");
  t.parent().parent().find("span.viewThreadLink").show();
  GM_setValue(threadName, false);
}

function expandThread(t) {
  getThreadInfo(t);
  threadSubList.show();  
  t.removeClass("closed");
  t.parent().parent().find("span.viewThreadLink").hide();
  GM_setValue(threadName, true);
}

function cycleStarred(t) {
  getThreadInfo(t);
  if (t.hasClass("starred")) { // cycle to ignored
    threadList.addClass('ignored').removeClass('starred');
    t.addClass('ignored').removeClass('starred');
    t.html("i");
    GM_setValue(threadName + "-star", "i");
  }
  else if (t.hasClass("ignored")) { // cycle to neutral
    threadList.removeClass('ignored');
    t.removeClass('ignored');
    t.html("&nbsp;");
    GM_deleteValue(threadName + "-star");
  }
  else { // cycle to starred
    threadList.addClass('starred');
    t.addClass('starred');
    t.html("*");
    GM_setValue(threadName + "-star", "*");
  }
}

function changeFilter(t) {
  filter = t.attr("value");
  GM_setValue("var-filter", filter);
  $("span.starButton").each(function() {applyFilter($(this))});
  $("form.threadFilter select").blur();
}

function applyFilter(t) {
  var list = t.parent().parent();
  switch (filter) {
    case "all":
      list.show();
      break;
    case "hide_ignored":
      if (list.hasClass("ignored")) {
        list.hide();
      }
      else {
        list.show();
      }
      break;
    case "starred":
      if (list.hasClass("starred")) {
        list.show();
      }
      else {
        list.hide();
      }
      break;
    case "contributed":
      if (list.hasClass("posted")) {
        list.show();
      }
      else {
        list.hide();
      }
      break;
  } 
}


// Main code

$(document).ready(function() { 

  // Add global styles
  $("head").append(styles);

  // Options
  $("body center font[size=2]").html($("body center font[size=2]").html().replace(']','| <a href="" id="mlmb_options_link">add-on options</a> ]'));
  $("a#mlmb_options_link").click(function() {$("#mlmb_options").toggle(); return false;});
  var options = '<div id="mlmb_options"><form><a href="" class="close">X</a>' +
  '<p><b>mlmb script v4.1 options</b> | <a href="http://thinkingoutquiet.wordpress.com/mlmb-add-on/" target="_blank">see website for details</a></p><hr>' +
  '<p><input type="checkbox" name="collapse-threads"> Enable thread collapsing</p>' +
  '<p class="sub"><input type="checkbox" name="collapse-sub-threads"> Enable sub-thread collapsing</p>' +
  '<p class="sub"><input type="checkbox" name="view-thread-link"> Add \'View thread\' link when collapsed</p>' +
  '<p class="sub"><input type="checkbox" name="highlight-all-read"> Highlight when thread is fully read</p><hr>' +
  '<p><input type="checkbox" name="markers-enabled"> Enable thread markers (star/ignore/author)</p>' +
  '<p class="sub"><input type="checkbox" name="filter-enabled"> Enable thread filtering</p><hr>' +
  '<p><input type="checkbox" name="hide-quoted-text"> Hide quoted text</p><hr>' +
  '<p><input type="checkbox" name="collapse-read-messages"> Collapse read messages in thread view</p><hr>' +
  '<p><a href="" class="reload">Reload page to see changes</a></p>' +
  '</form></div>';

$("body").append(options);

$("#mlmb_options input").change(function() {
  setting = ($(this).attr("checked") == "checked");
  GM_setValue("var-option-" + $(this).attr("name"), setting);

  if ($(this).attr("name") == "collapse-threads" && setting == false && $('input[name="collapse-sub-threads"]').attr("checked") == "checked") {
    $('input[name="collapse-sub-threads"]').trigger("click");
  }
  if ($(this).attr("name") == "collapse-sub-threads" && setting == true && $('input[name="collapse-threads"]').attr("checked") != "checked") {
    $('input[name="collapse-threads"]').trigger("click");
  }

  if ($(this).attr("name") == "collapse-threads" && setting == false && $('input[name="view-thread-link"]').attr("checked") == "checked") {
    $('input[name="view-thread-link"]').trigger("click");
  }
  if ($(this).attr("name") == "view-thread-link" && setting == true && $('input[name="collapse-threads"]').attr("checked") != "checked") {
    $('input[name="collapse-threads"]').trigger("click");
  }

  if ($(this).attr("name") == "collapse-threads" && setting == false && $('input[name="highlight-all-read"]').attr("checked") == "checked") {
    $('input[name="highlight-all-read"]').trigger("click");
  }
  if ($(this).attr("name") == "highlight-all-read" && setting == true && $('input[name="collapse-threads"]').attr("checked") != "checked") {
    $('input[name="collapse-threads"]').trigger("click");
  }

  if ($(this).attr("name") == "markers-enabled" && setting == false && $('input[name="filter-enabled"]').attr("checked") == "checked") {
    $('input[name="filter-enabled"]').trigger("click");
  }
  if ($(this).attr("name") == "filter-enabled" && setting == true && $('input[name="markers-enabled"]').attr("checked") != "checked") {
    $('input[name="markers-enabled"]').trigger("click");
  }
});

$("#mlmb_options input").each(function() {
  if (GM_getValue("var-option-" + $(this).attr("name"),false) == true) {$(this).attr("checked","checked");}
});
$("#mlmb_options a.close").click(function() {$("#mlmb_options").hide(); return false;});
$("#mlmb_options a.reload").click(function() {location.reload(true);});


// Delete old saved thread states, to avoid filling up Firefox store
var numKeysToSave = 3000;
var keys = GM_listValues();
keys.sort();
for (var i = 0; i < (keys.length - numKeysToSave); i++) {
  GM_deleteValue(keys[i]);
}

if (indexPage) {

  // Insert thread toggle buttons & star buttons

//  $("table#linkstable + ul > li").each(function() {
  $("table#linkstable + ul > table > tbody > tr > td > font > li").each(function() {
    var starButton = '';
    var toggleButton = '';
    var length;
    var markRead = '';
    length = $(this).find('li').length + 1;
    if (option_collapse_threads) {
      if (option_highlight_all_read) {
        if ($(this).find('a[style="color: #999999;"]').length == length) {
          markRead = " markRead";
        } 
      }
      toggleButton = '<span class="toggleButton' + markRead + '">' + length + '</span>';
      if (option_view_thread_link) {
        viewThread = '<span class="viewThreadLink"><font size="1"><a style="text-decoration: none;" href="thread/' + $(this).find('a').first().attr('name') + '.html"><font color="#000080">View thread</font></a> <font color="999999">»</font></font></span>';
        $(this).find('i').first().after(viewThread);
      }
    }
    if (option_markers_enabled) {
      starButton = '<span class="starButton">&nbsp;</span>';
    }
    $(this).prepend('<div class="threadButtons">' + toggleButton + starButton + '</div>');
  });

  if (option_collapse_threads) {
    // Insert 'collapse all' & 'expand all' buttons
    $("table#linkstable table tr").prepend('<td><span class="expandButton">Expand all</span></td>');
    $("table#linkstable table tr").prepend('<td><span class="collapseButton">Collapse all</span></td>');

    // Enable 'collapse all' and 'expand all' buttons click events
    $("span.collapseButton").click(function() {
      $("span.toggleButton").each(function() {collapseThread($(this))})
    });
    $("span.expandButton").click(function() {
      $("span.toggleButton").each(function() {expandThread($(this))})
    });

    // Enable toggle button click event
    $("span.toggleButton").click(function() {toggleThread($(this))});

    // Restore saved thread collapse states
    $("span.toggleButton").each(function() {restoreThread($(this))});
  }

  // Star, etc markers

  if (option_markers_enabled) {
    // Enable starred button click event
    $("span.starButton").click(function() {cycleStarred($(this))});
    // Flag threads with contributions from user
    var username1 = $("table#logininfo div#cookiegreeting b").html();
    var username2 = $("table#logininfo a[target=user_info] font").html();
    $("li b:contains('" + username1 + "')").closest('table#linkstable + ul > li').addClass('posted');
    $("li b:contains('" + username2 + "')").closest('table#linkstable + ul > li').addClass('posted');
    // Restore saved thread marker states
    $("span.starButton").each(function() {restoreMarkers($(this))});
  }


  // Filter

  if (option_filter_enabled) {
    // Insert filter drop-down
    var filterOptions = '<option value="all">Show All Threads</option>' +
      '<option value="hide_ignored">All Except Ignored</option>' +
      '<option value="starred">Only Starred *</option>' +
      '<option value="contributed">Only Contributed</option>';
    $("table#linkstable table tr").prepend('<td><form class="threadFilter"><select>' + filterOptions + '</select></form></td>');
    // Enable filter drop-down change event
    $("form.threadFilter select").change(function() {changeFilter($(this))});
    // Apply filter
    $("form.threadFilter select").attr("value",filter);
    $("span.starButton").each(function() {applyFilter($(this))});
  }

} // end of if indexPage


// Collapse sub-threads

if (indexPage && option_collapse_sub_threads) { // Ideally want this on msg page, but click event not working... ??
  if (indexPage) {selector = $("table#linkstable + ul > li li > ul > li");}
  if (msgPage) {selector = $("ul ul").first().find("li > ul > li");}
  selector.parent().parent().each(function() {
    var subThreadId = $(this).children("a").first().attr('href').replace(/[^0-9]/g,'');
    if (GM_getValue(subThreadId, true)) {
      $closed = '">-';
    }
    else {
      $closed = ' subClosed">+';  
    $(this).find("ul").first().hide();
    }
  $(this).prepend('<span name="' + subThreadId +'" class="subCollapse' + $closed + '</span>&nbsp;');
  $(this).css('list-style-type','none');
  $(this).css('margin-left','-18px');
  });
  $("span.subCollapse").click(function() {
    $(this).parent().find("ul").first().toggle();
    $(this).toggleClass("subClosed");
    if ($(this).hasClass("subClosed")) {
      $(this).html('+');
      GM_setValue($(this).attr('name'), false);
    }
    else {
      $(this).html('-');
      GM_setValue($(this).attr('name'), true);
    }
  });
} // End of Collapse Sub Threads


// Hide Quoted Text

if ((threadPage || msgPage) && (option_hide_quoted_text)) {
  $("td > font").each(function() {
    var a = $(this).html();
    if (!a.match(/<object/)) { // don't process text with embedded video
      a = a.replace('<p></p>','<br>'); // Make line-break before signature in br
      var htmlArray = a.split('<br>');
      var inQuoteBlock = false;
      var quoteCount = 0;
      var finishOnQuote = false;
      var inSignature = false;

      for (var i = 0; i < htmlArray.length; i++) {
        if (htmlArray[i].match(/<hr/)) { inSignature = true; }
        if (htmlArray[i].charAt(0) == ":") {
          if (!inQuoteBlock) { // First line of a quote block
            quoteCount += 1;
            htmlArray[i] = '<span class="dummyQuoteBlock' + quoteCount + '">' + htmlArray[i];
            inQuoteBlock = true;
            finishOnQuote = true;
            inSignature = false;
          }
        } 
        else {
          if (inQuoteBlock) { // End of a quote block
            if ((htmlArray[i].length > 2) && (inSignature == false)) {
              finishOnQuote = false;
            }
            htmlArray[i] = '</span>' + htmlArray[i];
            inQuoteBlock = false;
          }
          else {
            if (inSignature == false) { finishOnQuote = false; }
            inQuoteBlock = false;
          }
        }
      }
      var htmlDone;
      if (finishOnQuote) { // Hide final quote
        htmlDone = htmlArray.join('<br>');
        htmlDone = htmlDone.replace('<span class="dummyQuoteBlock' + quoteCount + '">', '<div class="toggleQuotedButton">: Show Quoted Text</div><span class="quoteBlock"><br>');
      }
      else { // Don't hide any quotes
        htmlDone = a;
      }
      $(this).html(htmlDone);
    }
  });

  // Enable Show/Hide Quote buttons
  $("div.toggleQuotedButton").click(function() {
    var block = $(this).next();
    block.toggleClass("quoteBlock");
    if (block.hasClass("quoteBlock")) {
      $(this).html(": Show Quoted Text");
      $(this).css("background-color","#f88");
    }
    else {
      $(this).html(": Hide Quoted Text");
      $(this).css("background-color","#8f8");
    }
  });
} // End of Hide Quoted Text


// Message Collapsing in Full Thread View
if (threadPage && option_collapse_read_messages) {
  $("body").append('<div id="fullThreadControls"><div class="collapseButton">Collapse all</div><div class="expandButton">Expand all</div><div style="font-size:14px;"><i>Click titles to<br>toggle individual<br>messages</i></div></div>');
  $("body").css("background-color","#eee");
  $("table p font[size=4]").parent().parent().css("background-color","#fff");
  $("table p font[size=4]").parent().parent().css("padding","5px");

  $("table p font[size=4][color=#999999]").parent().parent().find("p:eq(1)").nextAll().hide();

  $("table p font[size=4]").css("cursor","pointer");
  $("table p font[size=4]").click(function() {
    $(this).parent().parent().find("p:eq(1)").nextAll().toggle();
  });

  // Enable 'collapse all' and 'expand all' buttons click events
  $("div.collapseButton").click(function() { $("table p font[size=4]").parent().parent().find("p:eq(1)").nextAll().hide(); });
  $("div.expandButton").click(function() { $("table p font[size=4]").parent().parent().find("p:eq(1)").nextAll().show(); });

} // End of Message Collapsing in Full Thread View

});