Wednesday, December 7, 2011

RuneScape Forum Improvements


// ==UserScript==
// @name RuneScape Forum Improvements
// @description Improves the forums, and fixes problems.
// @version 3.0.2
// @author Mr Pacman
// @match http://services.runescape.com/m=forum*
// ==/UserScript==
/* -- Please skip to line 725 to read source of my script -- beginning of this is GM_config used to save settings. -- */

/*
Copyright 2009-2010, GM_config Contributors
All rights reserved.

GM_config Contributors:
    Mike Medley <medleymind@gmail.com>
    Joe Simmons
    Izzy Soft
    Marti Martz

GM_config is distributed under the terms of the GNU Lesser General Public License.

    GM_config is free software: you can redistribute it and/or modify
    it under the terms of the GNU Lesser General Public License as published by
    the Free Software Foundation, either version 3 of the License, or
    (at your option) any later version.

    This program is distributed in the hope that it will be useful,
    but WITHOUT ANY WARRANTY; without even the implied warranty of
    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    GNU Lesser General Public License for more details.

    You should have received a copy of the GNU Lesser General Public License
    along with this program.  If not, see <http://www.gnu.org/licenses/>.
*/

// The GM_config constructor
function GM_configStruct() {
  // call init() if settings were passed to constructor
  if (arguments.length)
    GM_configInit(this, arguments);
}

// This is the initializer function
function GM_configInit(config, args) {
  // Initialize instance variables
  if (typeof config.fields == "undefined") {
    config.fields = {};
    config.onInit = function() {};
    config.onOpen = function() {};
    config.onSave = function() {};
    config.onClose = function() {};
    config.onReset = function() {};
    config.isOpen = false;
    config.title = 'User Script Settings';
    config.css = {
      basic: "#GM_config * { font-family: arial,tahoma,myriad pro,sans-serif; }"
             + '\n' + "#GM_config { background: #FFF; }"
             + '\n' + "#GM_config input[type='radio'] { margin-right: 8px; }"
             + '\n' + "#GM_config .indent40 { margin-left: 40%; }"
             + '\n' + "#GM_config .field_label { font-weight: bold; font-size: 12px; margin-right: 6px; }"
             + '\n' + "#GM_config .block { display: block; }"
             + '\n' + "#GM_config .saveclose_buttons { margin: 16px 10px 10px; padding: 2px 12px; }"
             + '\n' + "#GM_config .reset, #GM_config .reset a,"
             + '\n' + "#GM_config_buttons_holder { text-align: right; color: #000; }"
             + '\n' + "#GM_config .config_header { font-size: 20pt; margin: 0; }"
             + '\n' + "#GM_config .config_desc, #GM_config .section_desc, #GM_config .reset { font-size: 9pt; }"
             + '\n' + "#GM_config .center { text-align: center; }"
             + '\n' + "#GM_config .section_header_holder { margin-top: 8px; }"
             + '\n' + "#GM_config .config_var { margin: 0 0 4px; }"
             + '\n' + "#GM_config .section_header { font-size: 13pt; background: #414141; color: #FFF;"
             + '\n' +  "border: 1px solid #000; margin: 0; }"
             + '\n' + "#GM_config .section_desc { font-size: 9pt; background: #EFEFEF; color: #575757;"
             + '\n' + "border: 1px solid #CCC; margin: 0 0 6px; }",
      stylish: ""
    };
  }

  // Set a default id
  if (typeof config.id == "undefined")
    config.id = 'GM_config';

  var settings = null;
  // If the id has changed we must modify the default style
  if (config.id != 'GM_config')
    config.css.basic = config.css.basic.replace(/#GM_config/gm, '#' + config.id);

  // Save the previous initialize callback
  var oldInitCb = config.onInit;

  // loop through GM_config.init() arguments
  for (var i = 0, l = args.length, arg; i < l; ++i) {
    arg = args[i];

    // An element to use as the config window
    if (typeof arg.appendChild == "function") {
      config.frame = arg;
      continue;
    }

    switch (typeof arg) {
      case 'object':
        for (var j in arg) { // could be a callback functions or settings object
          if (typeof arg[j] != "function") { // we are in the settings object
            settings = arg; // store settings object
            break; // leave the loop
          } // otherwise it must be a callback function
          config["on" + j.charAt(0).toUpperCase() + j.slice(1)] = arg[j];
        }
        break;
      case 'function': // passing a bare function is set to open callback
        config.onOpen = arg;
        break;
      case 'string': // could be custom CSS or the title string
        if (arg.indexOf('{') != -1 && arg.indexOf('}') != -1)
          config.css.stylish = arg;
        else
          config.title = arg;
        break;
    }
  }

  if (settings) {
    var stored = config.read(); // read the stored settings

    for (var id in settings) // for each setting create a field object
      config.fields[id] = new GM_configField(settings[id], stored[id], id);
  }

  // Prevent infinite loops
  if (config.onInit === oldInitCb)
    config.onInit = function() {};

  // Call the previous init() callback function
  oldInitCb();
}

GM_configStruct.prototype = {
  // Support old method of initalizing
  init: function() { GM_configInit(this, arguments); },

  // call GM_config.open() from your script to open the menu
  open: function () {
    // Die if the menu is already open on this page
    // You can have multiple instances but they can't be open at the same time
    var match = document.getElementById(this.id);
    if (match && (match.tagName == "IFRAME" || match.childNodes.length > 0)) return;

    // Sometimes "this" gets overwritten so create an alias
    var config = this;

    // Function to build the mighty config window :)
    function buildConfigWin (body, head) {
      var create = config.create,
          fields = config.fields,
          configId = config.id,
          bodyWrapper = create('div', {id: configId + '_wrapper'});

      // Append the style which is our default style plus the user style
      head.appendChild(
        create('style', {
        type: 'text/css',
        textContent: config.css.basic + config.css.stylish
      }));

      // Add header and title
      bodyWrapper.appendChild(create('div', {
        id: configId + '_header',
        className: 'config_header block center',
        innerHTML: config.title
      }));

      // Append elements
      var section = bodyWrapper,
          secNum = 0; // Section count

      // loop through fields
      for (var id in fields) {
        var field = fields[id].settings;

        if (field.section) { // the start of a new section
          section = bodyWrapper.appendChild(create('div', {
              className: 'section_header_holder',
              id: configId + '_section_' + secNum
            }));

          if (typeof field.section[0] == "string")
            section.appendChild(create('div', {
              className: 'section_header center',
              id: configId + '_section_header_' + secNum,
              innerHTML: field.section[0]
            }));

          if (typeof field.section[1] == "string")
            section.appendChild(create('p', {
              className: 'section_desc center',
              id: configId + '_section_desc_' + secNum,
              innerHTML: field.section[1]
            }));
          ++secNum;
        }

        // Create field elements and append to current section
        section.appendChild(fields[id].toNode(configId));
      }

      // Add save and close buttons
      bodyWrapper.appendChild(create('div',
        {id: configId + '_buttons_holder'},

        create('button', {
          id: configId + '_saveBtn',
          textContent: 'Save',
          title: 'Save settings',
          className: 'saveclose_buttons',
          onclick: function () { config.save() }
        }),

        create('button', {
          id: configId + '_closeBtn',
          textContent: 'Close',
          title: 'Close window',
          className: 'saveclose_buttons',
          onclick: function () { config.close() }
        }),

        create('div',
          {className: 'reset_holder block'},

          // Reset link
          create('a', {
            id: configId + '_resetLink',
            textContent: 'Reset to defaults',
            href: '#',
            title: 'Reset fields to default values',
            className: 'reset',
            onclick: function(e) { e.preventDefault(); config.reset() }
          })
      )));

      body.appendChild(bodyWrapper); // Paint everything to window at once
      config.center(); // Show and center iframe
      window.addEventListener('resize', config.center, false); // Center frame on resize

      // Call the open() callback function
      config.onOpen(config.frame.contentDocument || config.frame.ownerDocument,
                    config.frame.contentWindow || window,
                    config.frame);

      // Close frame on window close
      window.addEventListener('beforeunload', function () {
          config.close();
      }, false);

      // Now that everything is loaded, make it visible
      config.frame.style.display = "block";
      config.isOpen = true;
    }

    // Either use the element passed to init() or create an iframe
    var defaultStyle = 'position:fixed; top:0; left:0; opacity:0; display:none; z-index:999;' +
                       'width:75%; height:75%; max-height:95%; max-width:95%;' +
                       'border:1px solid #000000; overflow:auto; bottom: auto;' +
                       'right: auto; margin: 0; padding: 0;';
    if (this.frame) {
      this.frame.id = this.id;
      this.frame.setAttribute('style', defaultStyle);
      buildConfigWin(this.frame, this.frame.ownerDocument.getElementsByTagName('head')[0]);
    } else {
      // Create frame
      document.body.appendChild((this.frame = this.create('iframe', {
        id: this.id,
        style: defaultStyle
      })));

      this.frame.src = 'about:blank'; // In WebKit src can't be set until it is added to the page
      // we wait for the iframe to load before we can modify it
      this.frame.addEventListener('load', function(e) {
          var frame = config.frame;
          var body = frame.contentDocument.getElementsByTagName('body')[0];
          body.id = config.id; // Allows for prefixing styles with "#GM_config"
          buildConfigWin(body, frame.contentDocument.getElementsByTagName('head')[0]);
      }, false);
    }
  },

  save: function () {
    var fields = this.fields;
    for (id in fields)
      if (fields[id].toValue() === null) // invalid value encountered
        return;

    this.write();
    this.onSave(); // Call the save() callback function
  },

  close: function() {
    // If frame is an iframe then remove it
    if (this.frame.contentDocument) {
      this.remove(this.frame);
      this.frame = null;
    } else { // else wipe its content
      this.frame.innerHTML = "";
      this.frame.style.display = "none";
    }

    // Null out all the fields so we don't leak memory
    var fields = this.fields;
    for (var id in fields)
      fields[id].node = null;

    this.onClose(); //  Call the close() callback function
    this.isOpen = false;
  },

  set: function (name, val) {
    this.fields[name].value = val;
  },

  get: function (name) {
    return this.fields[name].value;
  },

  write: function (store, obj) {
    if (!obj) {
      var values = {},
          fields = this.fields;

      for (var id in fields) {
        var field = fields[id];
        if (field.settings.type != "button")
          values[id] = field.value;
      }
    }
    try {
      this.setValue(store || this.id, this.stringify(obj || values));
    } catch(e) {
      this.log("GM_config failed to save settings!");
    }
  },

  read: function (store) {
    try {
      var rval = this.parser(this.getValue(store || this.id, '{}'));
    } catch(e) {
      this.log("GM_config failed to read saved settings!");
      var rval = {};
    }
    return rval;
  },

  reset: function () {
    var fields = this.fields,
        doc = this.frame.contentDocument || this.frame.ownerDocument,
        type;

    for (id in fields) {
      var node = fields[id].node,
          field = fields[id].settings,
          noDefault = typeof field['default'] == "undefined",
          type = field.type;

      switch (type) {
        case 'checkbox':
          node.checked = noDefault ? GM_configDefaultValue(type) : field['default'];
          break;
        case 'select':
          if (field['default']) {
            for (var i = 0, len = node.options.length; i < len; ++i)
              if (node.options[i].value == field['default'])
                node.selectedIndex = i;
          } else
            node.selectedIndex = 0;
          break;
        case 'radio':
          var radios = node.getElementsByTagName('input');
          for (var i = 0, len = radios.length; i < len; ++i)
            if (radios[i].value == field['default'])
              radios[i].checked = true;
          break;
        case 'button' :
          break;
        default:
          node.value = noDefault ? GM_configDefaultValue(type) : field['default'];
          break;
      }
    }

    this.onReset(); // Call the reset() callback function
  },

  create: function () {
    switch(arguments.length) {
      case 1:
        var A = document.createTextNode(arguments[0]);
        break;
      default:
        var A = document.createElement(arguments[0]),
            B = arguments[1];
        for (var b in B) {
          if (b.indexOf("on") == 0)
            A.addEventListener(b.substring(2), B[b], false);
          else if (",style,accesskey,id,name,src,href,which".indexOf("," +
                   b.toLowerCase()) != -1)
            A.setAttribute(b, B[b]);
          else
            A[b] = B[b];
        }
        for (var i = 2, len = arguments.length; i < len; ++i)
          A.appendChild(arguments[i]);
    }
    return A;
  },

  center: function () {
    var node = this.frame,
        style = node.style,
        beforeOpacity = style.opacity;
    if (style.display == 'none') style.opacity = '0';
    style.display = '';
    style.top = Math.floor((window.innerHeight / 2) - (node.offsetHeight / 2)) + 'px';
    style.left = Math.floor((window.innerWidth / 2) - (node.offsetWidth / 2)) + 'px';
    style.opacity = '1';
  },

  remove: function (el) {
    if (el && el.parentNode) el.parentNode.removeChild(el);
  }
};

// Define a bunch of API stuff
(function() {
  var isGM = typeof GM_getValue != 'undefined' &&
             typeof GM_getValue('a', 'b') != 'undefined',
      setValue, getValue, stringify, parser;

  // Define value storing and reading API
  if (!isGM) {
    setValue = function (name, value) {
      return localStorage.setItem(name, value);
    };
    getValue = function(name, def){
      var s = localStorage.getItem(name);
      return s == null ? def : s
    };

    // We only support JSON parser outside GM
    stringify = JSON.stringify;
    parser = JSON.parse;
  } else {
    setValue = GM_setValue;
    getValue = GM_getValue;
    stringify = typeof JSON == "undefined" ?
      function(obj) {
        return obj.toSource();
    } : JSON.stringify;
    parser = typeof JSON == "undefined" ?
      function(jsonData) {
        return (new Function('return ' + jsonData + ';'))();
    } : JSON.parse;
  }

  GM_configStruct.prototype.isGM = isGM;
  GM_configStruct.prototype.setValue = setValue;
  GM_configStruct.prototype.getValue = getValue;
  GM_configStruct.prototype.stringify = stringify;
  GM_configStruct.prototype.parser = parser;
  GM_configStruct.prototype.log = isGM ? GM_log : (window.opera ? opera.postError : console.log);
})();

function GM_configDefaultValue(type) {
  var value;

  if (type.indexOf('unsigned ') == 0)
    type = type.substring(9);

  switch (type) {
    case 'radio': case 'select':
      value = settings.options[0];
      break;
    case 'checkbox':
      value = false;
      break;
    case 'int': case 'integer':
    case 'float': case 'number':
      value = 0;
      break;
    default:
      value = '';
  }

  return value;
}

function GM_configField(settings, stored, id) {
  // Store the field's settings
  this.settings = settings;
  this.id = id;

  // if a setting was passed to init but wasn't stored then
  //      if a default value wasn't passed through init() then
  //      use default value for type
  //      else use the default value passed through init()
  // else use the stored value
  var value = typeof stored == "undefined" ?
                typeof settings['default'] == "undefined" ?
                  GM_configDefaultValue(settings.type)
                : settings['default']
              : stored;

  // Store the field's value
  this.value = value;
}

GM_configField.prototype = {
  create: GM_configStruct.prototype.create,

  node: null,

  toNode: function(configId) {
    var field = this.settings,
        value = this.value,
        options = field.options,
        id = this.id,
        create = this.create;

    var retNode = create('div', { className: 'config_var',
          id: configId + '_' + this.id + '_var',
          title: field.title || '' }),
        firstProp;

    // Retrieve the first prop
    for (var i in field) { firstProp = i; break; }

    var label = create('span', {
      innerHTML: field.label,
      id: configId + '_' + this.id +'_field_label',
      className: 'field_label'
    });

    switch (field.type) {
      case 'textarea':
        retNode.appendChild((this.node = create('textarea', {
          id: configId + '_field_' + this.id,
          innerHTML: value,
          cols: (field.cols ? field.cols : 20),
          rows: (field.rows ? field.rows : 2)
        })));
        break;
      case 'radio':
        var wrap = create('div', {
          id: configId + '_field_' + id
        });
        this.node = wrap;

        for (var i = 0, len = options.length; i < len; ++i) {
          var radLabel = wrap.appendChild(create('span', {
            innerHTML: options[i]
          }));

          var rad = wrap.appendChild(create('input', {
            value: options[i],
            type: 'radio',
            name: id,
            checked: options[i] == value ? true : false
          }));

          if (firstProp == "options")
            wrap.insertBefore(radLabel, rad);
          else
            wrap.appendChild(radLabel);
        }

        retNode.appendChild(wrap);
        break;
      case 'select':
        var wrap = create('select', {
          id: configId + '_field_' + id
        });
        this.node = wrap;

        for (var i in options)
          wrap.appendChild(create('option', {
            innerHTML: options[i],
            value: i,
            selected: options[i] == value ? true : false
          }));

        retNode.appendChild(wrap);
        break;
      case 'checkbox':
        retNode.appendChild((this.node = create('input', {
          id: configId + '_field_' + id,
          type: 'checkbox',
          value: value,
          checked: value
        })));
        break;
      case 'button':
        var btn = create('input', {
          id: configId + '_field_' + id,
          type: 'button',
          value: field.label,
          size: (field.size ? field.size : 25),
          title: field.title || ''
        });
        this.node = btn;

        if (field.script)
          btn.addEventListener('click', function () {
            var scr = field.script;
            typeof scr == 'function' ? setTimeout(scr, 0) : eval(scr);
          }, false);

        retNode.appendChild(btn);
        break;
      case 'hidden':
        retNode.appendChild((this.node = create('input', {
          id: configId + '_field_' + id,
          type: 'hidden',
          value: value
        })));
        break;
      default:
        // type = text, int, or float
        retNode.appendChild((this.node = create('input', {
          id: configId + '_field_' + id,
          type: 'text',
          value: value,
          size: (field.size ? field.size : 25)
        })));
    }

    // If the label is passed first, insert it before the field
    // else insert it after
    if (field.type != "hidden" &&
        field.type != "button" &&
        typeof field.label == "string") {
      if (firstProp == "label")
        retNode.insertBefore(label, retNode.firstChild);
      else
        retNode.appendChild(label);
    }

    return retNode;
  },

  toValue: function() {
    var node = this.node,
        field = this.settings,
        type = field.type,
        unsigned = false,
        rval;

    if (type.indexOf('unsigned ') == 0) {
      type = type.substring(9);
      unsigned = true;
    }

    switch (type) {
      case 'checkbox':
        this.value = node.checked;
        break;
      case 'select':
        this.value = node[node.selectedIndex].value;
        break;
      case 'radio':
        var radios = node.getElementsByTagName('input');
        for (var i = 0, len = radios.length; i < len; ++i)
          if (radios[i].checked)
            this.value = radios[i].value;
        break;
      case 'button':
        break;
      case 'int': case 'integer':
        var num = Number(node.value);
        var warn = 'Field labeled "' + field.label + '" expects a' +
          (unsigned ? ' positive ' : 'n ') + 'integer value';
        if (isNaN(num) || Math.ceil(num) != Math.floor(num) ||
            (unsigned && num < 0)) {
          alert(warn + '.');
          return null;
        }
        if (!this._checkNumberRange(num, warn))
          return null;
        this.value = num;
        break;
      case 'float': case 'number':
        var num = Number(node.value);
        var warn = 'Field labeled "' + field.label + '" expects a ' +
          (unsigned ? 'positive ' : '') + 'number value';
        if (isNaN(num) || (unsigned && num < 0)) {
          alert(warn + '.');
          return null;
        }
        if (!this._checkNumberRange(num, warn))
          return null;
        this.value = num;
        break;
      default:
        this.value = node.value;
        break;
    }

    return this.value; // value read successfully
  },

  _checkNumberRange: function(num, warn) {
    var field = this.settings;
    if (typeof field.min == "number" && num < field.min) {
      alert(warn + ' greater than or equal to ' + field.min + '.');
      return null;
    }

    if (typeof field.max == "number" && num > field.max) {
      alert(warn + ' less than or equal to ' + field.max + '.');
      return null;
    }
    return true;
  }
};

// Create default instance of GM_config
var GM_config = new GM_configStruct();

/* This is start of RuneScape forum improvments. */


/* Section: GLOBAL
   This section is always run.
   Contains settings, variables, and CSS changes. */
   
/* Configuration for Google Chrome browser only.
You will have to edit the settings manually */

/* Configuration:
Look for the RFI Configuration link at the bottom of the page under the support section.
Firefox users should have no need to edit this script */

GM_config.init('RFI Configuration',{
  quickFind      :  { label: 'Add quick find links:', type: 'checkbox', default: true },
  forumReport    :  { label: 'Add forum reporting:', type: 'checkbox', default: true },
  forumHelpQFC   :  { label: 'Forum help quick find code:', type: 'text', cols:50, default: '103-104-5-63426944' },
  myPostsBlue    :  { label: 'Turn your posts blue:', type: 'checkbox', default: true },
  forumSignatures:  { label: 'Add signature to posts:', type: 'checkbox', default: false },
  signature      :  { type: 'textarea', cols:70, rows: 5, default: 'Your signature here.' },
  previewFix     :  { label: '<br><u>Fixes</u><br>Fix post previews:', type: 'checkbox', default: true },
  viewInThreadFix:  { label: 'Add view post in thread:', type: 'checkbox', default: true },
  jumpToPageFix  :  { label: 'Add jump to page box:', type: 'checkbox', default: true },
  textLinks      :  { label: '<br>Change adresses into links.<br>Please only use on allowed forums:', type: 'checkbox', default: false }
});
var slinks = document.evaluate('//*[@id="Footer"]/ul/li[2]/ul', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
var ele_li = document.createElement("li");
var ele_a = document.createElement("a");
ele_a.setAttribute("href", "#");
ele_a.appendChild(document.createTextNode("RFI Configuration"));
ele_li.appendChild(ele_a);
slinks.appendChild(ele_li);
slinks.lastChild.addEventListener("click", config, false);
// Settings
quickFind         =  GM_config.get('quickFind');
forumReport       =  GM_config.get('forumReport');
myPostsBlue       =  GM_config.get('myPostsBlue');
forumHelpQFC      =  GM_config.get('forumHelpQFC').replace(/-/g,',');
previewFix        =  GM_config.get('previewFix');
viewInThreadFix   =  GM_config.get('viewInThreadFix');
jumpToPageFix     =  GM_config.get('jumpToPageFix');
forumSignatures   =  GM_config.get('forumSignatures');
signature         =  GM_config.get('signature');
textLinks         =  GM_config.get('textLinks');

// Fixes for Google Chrome.
try {
    var memory = window.localStorage;
}
catch(e) {
    alert("Security Error: "+e);
}
if (typeof GM_deleteValue == 'undefined') {
GM_getValue = function(name, defaultValue) {
        var value = memory.getItem(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;
        memory.setItem(name, value);
    }
}
// Test for first time running script.
    if (!memory.getItem('runonce')) {
    alert('This is your first time running RuneScape Forum Improvements.\n\nPlease set your desired settings on the following form.\n\nYou can change them later by clicking on the RFI Configuration link\nat the bottom of the page under the support section.\n\nAfter setting what you want, click "Save" then "Close".\nFinally refresh the page so the settings take effect.\n\nThank you for using RuneScape Forum Improvements.\n\nMr Pacman (/\\)');
    GM_config.open();
    memory.setItem('runonce', 'true');
}

function config(e) {
    e.preventDefault();
    GM_config.open();
}

// Try to get current logged in username.
var username = document.evaluate('//*[@id="UsernameDisplay"]/div/div/h5/span', document, null, XPathResult.STRING_TYPE, null ).stringValue;


// Only setup reporting once.
var reportSetup = false;

/* Section: ALL POSTS
   This section will be run on all pages that have posts. */
if (document.getElementById("contentmsg") && !document.URL.match(/users.ws|,add|,edit/)) {
    // Get the number of posts on the page based on the number of div elements minus two for the info and QFC sections.
    numPosts =  document.evaluate( 'count(//*[@id="contentmsg"]/div)', document, null, XPathResult.ANY_TYPE, null ).numberValue - 2;
    // Loop through every post, offset to two, skipping the info div.
    for (p = 2; p <= numPosts+1; p++) {
        // Nodes of current message.
        post = document.evaluate('//*[@id="contentmsg"]/div['+p+']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
        message = document.evaluate('//*[@id="contentmsg"]/div[' + p +']/div[2]/div/div/div/div/div[3]/table/tbody/tr/td', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
        links = document.evaluate('//*[@id="contentmsg"]/div[' + p + ']/div[2]/div/div/div/div/div[2]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
        bubble = document.evaluate('//*[@id="contentmsg"]/div[' + p + ']/div[2]/div', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
        // Try to get posters name, null if post is hidden.
        postersName = document.evaluate('//*[@id="contentmsg"]/div['+ p+ ']/div/div/a', document, null, XPathResult.STRING_TYPE, null ).stringValue;
        // Run on all non hidden posts.
        if (postersName) {
            if (textLinks) AddTextLinks();
            if (quickFind) QuickFindLinks();
            if (forumReport && username && (postersName != username) && !(hasClass(post,"jmod") || hasClass(post,"fmod") || hasClass(post,"moved"))) InstallReporting();
            if (viewInThreadFix && document.URL.match(/showuser/)) viewInThread();
            if (myPostsBlue && (postersName == username)) turnPostBlue();
        }
    }
}

/* Section: FUNCTIONS
   Contains all functions used by the script. */
   
function AddTextLinks() {
    // URI_scheme regexp.
    var urlRE = new RegExp(
        '('
        // leading scheme:// or "www."
        + '\\b([a-z][-a-z0-9+.]+://|www\\.)'
        // everything until non-URL character
        + '[^\\s\'"<>()]+'
        + ')', 'gi');
    // Only run on text nodes.
    for (k = 0; k < message.childNodes.length; k++) {
        // If not a text node skip it.
        if (message.childNodes[k].nodeType != 3) continue;
        // Get all posted links.
        var foundTextLinks = message.childNodes[k].textContent.match(urlRE);
        // If none are found skip it.
        if (!foundTextLinks) continue;
        // Loop through text links.
        for (j = 0; j < foundTextLinks.length; j++) {
            // Generate html link string.
            htmlLink = '<a href="' + foundTextLinks[j] + '">' + foundTextLinks[j] + '</a>';
            // Replace quick find code in post, with forum link.
            message.innerHTML = message.innerHTML.replace(RegExp(foundTextLinks[j],"g" ), htmlLink);
        }
    }
}

function QuickFindLinks() {
    // Get all posted quickfind codes.
    var quickFindCodes = message.innerHTML.match(/(\d{1,3}-){3}\d+/g);
    // If none are found quit.
    if (!quickFindCodes) return;
    // Loop through quick find codes.
    for (j = 0; j < quickFindCodes.length; j++) {
        // Get one quick find code, and replace -'s with ,'s.
        quickFindCode = quickFindCodes[j].replace(/-/g, ",");
        // Generate forum link string.
        forumLink = "<a href=\"forums.ws?" + quickFindCode + "\">" + quickFindCodes[j] + "</a>";
        // Replace quick find code in post, with forum link.
        message.innerHTML = message.innerHTML.replace(RegExp(quickFindCodes[j],"g" ), forumLink);
    }
}

function InstallReporting() {
    // Create the report link.
    var reportLink = document.createElement("a");
    reportLink.setAttribute("href", "#");
    reportLink.setAttribute("class", "CssMO");
    reportLink.appendChild(document.createTextNode("Report"));
    // Add link to post. 
    links.appendChild(reportLink);
    // Add listener.
    links.lastChild.addEventListener("click", reportPost, false);
    
}

function reportPost(e) {
    e.preventDefault();
    // Setup reporting if not already setup.
    if (!reportSetup) setupReporting();
    // Get the reported post's post in thread number.
    var postInThread = parseInt(e.target.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.parentNode.previousElementSibling.getAttribute('name'));
    // Get page of thread, and post number from the post in thread number.
    var pageInThread = Math.ceil((postInThread+1)/10);
    var postNumber = postInThread % 10 + 1;
    // Get quick fick code.
    var QFC = document.URL.split('?')[1].split(',&')[0].split(',g')[0].replace(/,/g,'-');
    // Find the thread title div.
    var mcdivs = document.evaluate( 'count(//*[@id="MainContent"]/div)', document, null, XPathResult.ANY_TYPE, null ).numberValue;
    var title;
    for (title = 1; title <= mcdivs; title++) {
        if (document.evaluate('//*[@id="MainContent"]/div['+title+']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.className == 'BarFullTop headerBar') break;
    }
    // Get thread title.
    var threadTitle = document.evaluate('//*[@id="MainContent"]/div['+title+']/h3/span', document, null, XPathResult.STRING_TYPE, null ).stringValue;
    // Add report template to report form.
    var reportText = document.getElementById("charlimit_text_a").value+'Quick find code: '+QFC+'\nThread Title: '+threadTitle+'\nPage In Thread: '+pageInThread+ '\nPost Number(s): '+postNumber+'\n\n';
    document.getElementById("charlimit_text_a").value = reportText;
    // Jump to report form.
    window.location='#quickReply';
}

function setupReporting() {
    // Clear the quick reply box.
    document.getElementById('charlimit_text_a').value = '';
    // Set the form so it posts to forum help.
    document.getElementById('messageDetails').parentNode.setAttribute('action', 'forums.ws?'+forumHelpQFC+',add');
    // Change submit button to report.
    document.evaluate('//*[@id="add"]/span/span/span/b', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.innerHTML = 'Report';
    // Find the title div.
    var mcdivs = document.evaluate( 'count(//*[@id="MainContent"]/div)', document, null, XPathResult.ANY_TYPE, null ).numberValue;
    var title;
    for (title = 1; title <= mcdivs; title++) {
        if (document.evaluate('//*[@id="MainContent"]/div['+title+']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.className == 'BarFullTop headerBar') break;
    }
    title += 2;
    // Change title of quick reply form.
    for (var s = 1; s < 8; s++) document.evaluate('//*[@id="MainContent"]/div['+title+']/h3/span['+s+']', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.innerHTML = 'Report to Forum Help';
    document.evaluate('//*[@id="MainContent"]/div['+title+']/h3/span[8]/span', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.innerHTML = 'Report to Forum Help';
    document.evaluate('//*[@id="MainContent"]/div['+title+']/h3/span[8]/span[2]/span', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.innerHTML = 'Report to Forum Help';
    document.evaluate('//*[@id="MainContent"]/div['+title+']/h3/span[9]/span', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.innerHTML = 'Report to Forum Help';
    // Set the setup flag after finishing.
    reportSetup = true;
}

function turnPostBlue() {
    bubble.className = 'BubbleContainer Blue';
}

// Adds forum signatures.
if (forumSignatures && document.getElementById("charlimit_text_a") && !document.URL.match(/,edit|,add/)) {
    document.getElementById('charlimit_text_a').value = '\n\n'+signature;
}

function hasClass(ele,cls) {
    return ele.className.match(new RegExp('(\\s|^)'+cls+'(\\s|$)'));
}

/* Section: FIXES
   This section contains fixes for bugs in the forum */
   
// Fixes post preview.
if (previewFix && document.URL.match(/,add|,edit/)) {
    var preview = document.evaluate('//*[@id="contentmsg"]/div/div[2]/div/div/div/div/div[2]', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
    preview.innerHTML = preview.innerHTML.replace(/&lt;/g, "<").replace(/&gt;/g, ">").replace(/&amp;/g, "&");
}

// Adds view in thread option.
function viewInThread() {
    // Get the current message's post in thread number.
    var postInThread = parseInt(post.previousElementSibling.getAttribute('name'));
    // Get page of thread from the post in thread number.
    var pageInThread = Math.ceil((postInThread+1)/10);
    // Get quick find code.
    var QFC = document.URL.split('?')[1].split(',&')[0].split(',g')[0];
    var vitLink = document.createElement("a");
    vitLink.setAttribute('href', 'forums.ws?'+QFC+',goto,'+pageInThread+'#'+postInThread);
    vitLink.setAttribute("class", "CssMO");
    vitLink.appendChild(document.createTextNode("View in thread"));
    links.appendChild(vitLink);
}

// Adds crude jump to page box.
if (jumpToPageFix && document.getElementById("MainContent") && !document.URL.match(/users.ws|,add|,edit/) && !(document.evaluate('//*[@id="MainContent"]/form/div/ul', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue.innerHTML == '\n')) {
// Ignore errors.
try {
    var QFC = document.URL.split('?')[1].split(',&')[0].split(',g')[0].split(',t')[0];
    var form = document.evaluate('//*[@id="MainContent"]/form', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
    var formul = document.evaluate('//*[@id="MainContent"]/form/div/ul', document, null, XPathResult.FIRST_ORDERED_NODE_TYPE , null ).singleNodeValue;
    // Set correct action to form.
    form.setAttribute('action', 'forums.ws?'+QFC);
    var ele_li = document.createElement("li");
    ele_li.innerHTML = '<input type="text" size="2" name="start">';
    formul.appendChild(ele_li);
} catch(err){}    
}

/* The following bit checks for updates once a day, and will alert you if there is one. */
var SUC_script_num = 119517;
try{function updateCheck(forced){if ((forced) || (parseInt(GM_getValue('SUC_last_update', '0')) + 86400000 <= (new Date().getTime()))){try{GM_xmlhttpRequest({method: 'GET',url: 'http://userscripts.org/scripts/source/'+SUC_script_num+'.meta.js?'+new Date().getTime(),headers: {'Cache-Control': 'no-cache'},onload: function(resp){var local_version, remote_version, rt, script_name;rt=resp.responseText;GM_setValue('SUC_last_update', new Date().getTime()+'');remote_version=parseInt(/@uso:version\s*(.*?)\s*$/m.exec(rt)[1]);local_version=parseInt(GM_getValue('SUC_current_version', '-1'));if(local_version!=-1){script_name = (/@name\s*(.*?)\s*$/m.exec(rt))[1];GM_setValue('SUC_target_script_name', script_name);if (remote_version > local_version){if(confirm('There is an update available for the Greasemonkey script "'+script_name+'."\nWould you like to go to the install page now?')){window.open('http://userscripts.org/scripts/show/'+SUC_script_num, '_newtwb');GM_setValue('SUC_current_version', remote_version);}}else if (forced)alert('No update is available for "'+script_name+'."');}else GM_setValue('SUC_current_version', remote_version+'');}});}catch (err){if (forced)alert('An error occurred while checking for updates:\n'+err);}}}updateCheck(false);}catch(err){}

0 comments:

Post a Comment