// ==UserScript== // @name SE Chat Modifications // @description A collection of modifications for SE chat rooms // @match *://chat.meta.stackexchange.com/rooms/* // @match *://chat.stackoverflow.com/rooms/* // @match *://chat.stackexchange.com/rooms/* // @author @rchern // ==/UserScript== /* * Injects functions into the page so they can freely interact with existing code */ function inject() { for (var i = 0; i < arguments.length; ++i) { if (typeof(arguments[i]) == 'function') { var script = document.createElement('script'); script.type = 'text/javascript'; script.textContent = 'if (window.jQuery) (' + arguments[i].toString() + ')(window.jQuery)'; document.body.appendChild(script); } } } // Inject the support plugins, followed by the main userscript function inject(livequery, bindas, expressions, function ($) { // Setup the selector shortcuts var Selectors = { 'getMessage': function getMessage(id) { if (id) validate('number'); return id ? '#message-' + id : '.user-container.mine:last .message:last'; }, 'getSignature': function getSignature(match) { validate('string'); return ".signature:contains('" + match + "') ~ .messages"; }, 'getRoom': function getRoom(match) { validate('string'); return "#my-rooms > li > a[href^='/rooms']:contains('" + match + "')"; } }; // Setup the highlight and clipping objects var Highlights = new Storage(), Clippings = new Storage('chatClips'); // The list of command states that can be returned from a command function, or one of the command utility functions var CommandState = { // The command wasn't found 'NotFound': -1, // The command failed validation or couldn't execute properly 'Failed': 0, // The command succeeded, and the input should be cleared (where applicable) 'SucceedDoClear': 1, // The command succeeded, and the input should not be cleared (where applicable) 'SucceedNoClear': 2 }; // Setup the command and onebox mappings var Commands = {}, Oneboxes = {}; // Create the navigation var Navigation = new Navigation(); // Setup the main chat extension function, responsible for handling command processing (client-exposed) var ChatExtension = window.ChatExtension = new function () { /* * Defines new chat extension commands, allowing outside functions to plug into the existing userscript infrastructure */ this.define = function (name, fn, help) { name = name.toLowerCase(); if (typeof fn !== 'function') throw new Error("The function assigned to " + name + " is not a function"); if (Commands[name]) throw new Error("The command " + name + " is already defined"); if (help && typeof help === 'string') fn.helptext = help; Commands[name] = fn; }; /* * Associates domains with functions that produce pseudo-oneboxes */ this.associate = function (domain, fn) { if (typeof domain === 'string') { var assignment = domain.match(/^(?:https?:\/\/)?(?:www\.)?([^\/]+).*/i); if (!assignment) throw new Error("The domain " + domain + " does not look valid"); domain = assignment[1].toLowerCase(); if (Oneboxes[domain]) throw new Error("The domain " + domain + " is already onebox-associated"); Oneboxes[domain] = fn; } else if (domain instanceof RegExp) { if (!Oneboxes['_regex']) Oneboxes['_regex'] = []; Oneboxes['_regex'].push({ 'pattern': domain, 'handler': fn }); } else { throw new Error("The provided domain is not an acceptable type"); } } /* * Executes commands and automatically displays errors in the case of failed function validation */ this.execute = function (name, args) { var result = CommandState.NotFound; // Check if the command is defined if (Commands[name]) { try { // Attempt to run the command and get the result result = Commands[name].apply(this, args); } catch (ex) { if (ex.message) ChatExtension.notify(ex.message); result = CommandState.Failed; } } return result; }; /* * Displays a (usually error) notification dialog to the user for the specified period of time, or until a keyboard or mouse press event occurs */ this.notify = function (message, delay) { if (!delay) delay = 3000; $('#inputerror').html(message) .clearQueue() .fadeIn("slow") .delay(delay) .fadeOut("slow") .hover( function () { $(this).clearQueue(); }, function () { $(this).delay(delay).fadeOut("slow"); } ) .css({ 'max-height': ($(window).height() - 90) + 'px', 'max-width': '60%' }); }; /* * Adds styles to the userscript's