- // ==UserScript==
- // @name Robin Enhancement Script
- // @namespace https://www.reddit.com/
- // @version 3.3.2
- // @description Highlight mentions, make links clickable, add tabbed channels & automatically remove spam
- // @author Bag, netnerd01
- // @match https://www.reddit.com/robin*
- // @grant none
- // @grant GM_setValue
- // @grant GM_getValue
- // ==/UserScript==
- (function() {
-
- // Grab users username + play nice with RES
- var robin_user = $("#header-bottom-right .user a").first().text().toLowerCase();
- var ignored_users = {};
-
- // for spam counter - very important i know :P
- var blocked_spam_el = null;
- var blocked_spam = 0;
-
- // via RobinEggs
- var messageHistory = [];
- var messageHistoryIndex = -1;
- var _robin_grow_detected = false;
-
- var colors = [
- 'rgba(255,0,0,0.1)',
- 'rgba(0,255,0,0.1)',
- 'rgba(0,0,255,0.1)',
- 'rgba(0,255,255,0.1)',
- 'rgba(255,0,255,0.1)',
- 'rgba(255,255,0,0.1)',
- 'rgba(211,211,211, .1)',
- 'rgba(0,100,0, .1)',
- 'rgba(255,20,147, .1)',
- 'rgba(184,134,11, .1)',
- ];
-
-
- // Play nice with Greasemonkey
- if(typeof GM_getValue === "undefined") GM_getValue = function(){return false;};
- if(typeof GM_setValue === "undefined") GM_setValue = function(){return false;};
-
- /**
- * Pull tabber out in to semi-stand alone module
- * Big thanks to netnerd01 for his pre-work on this
- *
- * Basic usage - tabbedChannels.init( dom_node_to_add_tabs_to );
- * and hook up tabbedChannels.proccessLine(lower_case_text, jquery_of_line_container); to each line detected by the system
- */
- var tabbedChannels = new function(){
- var _self = this;
-
- // Default options
- this.channels = ["~","*",".","%","$","#",";","^","<3",":gov","#rpg","@"];
- this.mode = 'single';
-
- // internals
- this.unread_counts = {};
- this.$el = null;
- this.$opt = null;
- this.defaultRoomClasses = '';
- this.channelMatchingCache = [];
-
- //channels user is in currently
- this.currentRooms = 0;
-
- // When channel is clicked, toggle it on or off
- this.toggle_channel = function(e){
- var channel = $(e.target).data("filter");
- if(channel===null)return; // no a channel
-
- if(!$("#robinChatWindow").hasClass("robin-filter-" + channel)){
- _self.enable_channel(channel);
- $(e.target).addClass("selected");
- // clear unread counter
- $(e.target).find("span").text(0);
- _self.unread_counts[channel] = 0;
- }else{
- _self.disable_channel(channel);
- $(e.target).removeClass("selected");
- }
-
- // scroll everything correctly
- _scroll_to_bottom();
- };
-
- // Enable a channel
- this.enable_channel = function(channel_id){
-
- // if using room type "single", deslect other rooms on change
- if(this.mode == "single"){
- this.disable_all_channels();
- }
-
- $("#robinChatWindow").addClass("robin-filter robin-filter-" + channel_id);
- $("#robinChatWindow").attr("data-channel-key", this.channels[channel_id]);
- this.currentRooms++;
- // unselect show all
- _self.$el.find("span.all").removeClass("selected");
- };
-
- // disable a channel
- this.disable_channel = function(channel_id){
- $("#robinChatWindow").removeClass("robin-filter-" + channel_id);
- this.currentRooms--;
-
- // no rooms selcted, run "show all"
- if(this.currentRooms == 0){
- this.disable_all_channels();
- }else{
- // Grab next channel name if u leave a room in multi mode
- $("#robinChatWindow").attr("data-channel-key", $(".robin-filters span.selected").first().data("filter-name"));
- }
- };
-
- // turn all channels off
- this.disable_all_channels = function(e){
- $("#robinChatWindow").attr("class", _self.defaultRoomClasses).attr("data-channel-key","");
- _self.$el.find(".robin-filters > span").removeClass("selected");
- this.currentRooms = 0;
-
- _self.$el.find("span.all").addClass("selected");
- _scroll_to_bottom();
- };
-
- // render tabs
- this.drawTabs = function(){
- html = '';
- for(var i in this.channels){
- if(typeof this.channels[i] === 'undefined') continue;
- html += '<span data-filter="' + i + '" data-filter-name="'+ this.channels[i] +'">' + this.channels[i] + ' (<span>0</span>)</span> ';
- }
- this.$el.find(".robin-filters").html(html);
- };
-
- // After creation of a new channel, go find if any content (not matched by a channel already) is relevant
- this.reScanChannels = function(new_channel){
- $("#robinChatWindow").find("div.robin-message").each(function(idx,item){
- var line = $(item).find(".robin-message--message").text().toLowerCase();
- tabbedChannels.proccessLine(line, $(item), true);
- });
- }
-
- // Add new channel
- this.addChannel = function(new_channel){
- if(this.channels.indexOf(new_channel) === -1){
- this.channels.push(new_channel);
- this.unread_counts[this.channels.length-1] = 0;
- this.updateChannelMatchCache();
- this.saveChannelList();
- this.drawTabs();
-
- // Populate content for channel
- this.reScanChannels();
-
- // refresh everything after redraw
- this.disable_all_channels();
- }
- };
-
- // remove existing channel
- this.removeChannel = function(channel){
- if(confirm("are you sure you wish to remove the " + channel + " channel?")){
- var idx = this.channels.indexOf(channel);
- delete this.channels[idx];
- this.updateChannelMatchCache();
- this.saveChannelList();
- this.drawTabs();
-
- // sub channels, will fall back to existing channels
- this.reScanChannels();
-
- // refresh everything after redraw
- this.disable_all_channels();
- }
- };
-
-
- // save channel list
- this.saveChannelList = function(){
- // clean array before save
- var channels = this.channels.filter(function (item) { return item != undefined });
- GM_setValue("robin-enhance-channels", channels);
- };
-
- // Change chat mode
- this.changeChannelMode = function(e){
- _self.mode = $(this).data("type");
-
- // swicth bolding
- $(this).parent().find("span").css("font-weight","normal");
- $(this).css("font-weight","bold");
- _self.disable_all_channels();
-
- // Update mode setting
- GM_setValue("robin-enhance-mode", _self.mode);
- };
-
- this.updateChannelMatchCache = function(){
- var order = this.channels.slice(0);
- order.sort(function(a, b){
- return b.length - a.length; // ASC -> a - b; DESC -> b - a
- });
- for(var i in order){
- order[i] = this.channels.indexOf(order[i]);
- }
- // sorted array of channel name indexs
-
- this.channelMatchingCache = order;
- }
-
- // Procces each chat line to create text
- this.proccessLine = function(text, $element, rescan){
- var i, idx, channel;
-
- // If rescanning, clear any existing "channel" classes
- if(typeof rescan !== 'undefined' && rescan === true){
- $element.removeClass("in-channel");
-
- for(i=0; i <= this.channels.length; i++){
- $element.removeClass("robin-filter-" + i);
- }
- }
-
- // Scann for channel identifiers
- for(i=0; i< this.channelMatchingCache.length; i++){ // sorted so longer get picked out before shorter ones (sub channel matching)
- idx = this.channelMatchingCache[i];
- channel = this.channels[idx];
-
- if(typeof channel === 'undefined') continue;
-
- if(text.indexOf(channel) === 0){
- $element.addClass("robin-filter-" + idx +" in-channel");
- this.unread_counts[idx]++;
- return;
- }
- }
- };
-
- // If in one channel, auto add channel keys
- this.submit_helper = function(){
- if($("#robinChatWindow").hasClass("robin-filter")){
- // auto add channel key
- var channel_key = $("#robinChatWindow").attr("data-channel-key");
-
- if($(".text-counter-input").val().indexOf("/me") === 0){
- $(".text-counter-input").val("/me " + channel_key + " " + $(".text-counter-input").val().substr(3));
- }else if($(".text-counter-input").val().indexOf("/") !== 0){
- // if its not a "/" command, add channel
- $(".text-counter-input").val(channel_key + " " + $(".text-counter-input").val());
- }
- }
- };
-
- // Update everuything
- this.tick = function(){
- _self.$el.find(".robin-filters span").each(function(){
- if($(this).hasClass("selected")) return;
- $(this).find("span").text(_self.unread_counts[$(this).data("filter")]);
- });
- };
-
- // Init tab zone
- this.init = function($el){
- // Load channels
- if(GM_getValue("robin-enhance-channels")){
- this.channels = GM_getValue("robin-enhance-channels");
- }
- if(GM_getValue("robin-enhance-mode")){
- this.mode = GM_getValue("robin-enhance-mode");
- }
-
- // init counters
- for(var i in this.channels){
- this.unread_counts[i] = 0;
- }
-
- // update channel cache
- this.updateChannelMatchCache();
-
- // set up el
- this.$el = $el;
-
- // Create inital markup
- this.$el.html("<span class='all selected'>Everything</span><span><div class='robin-filters'></div></span><span class='more'>[Options]</span>");
- this.$opt = $("<div class='robin-channel-add' style='display:none'><input name='add-channel'><button>Add channel</button> <span class='channel-mode'>Channel Mode: <span title='View one channel at a time' data-type='single'>Single</span> | <span title='View many channels at once' data-type='multi'>Multi</span></span></div>").insertAfter(this.$el);
-
- // Attach events
- this.$el.find(".robin-filters").click(this.toggle_channel);
- this.$el.find("span.all").click(this.disable_all_channels);
- this.$el.find("span.more").click(function(){ $(".robin-channel-add").slideToggle(); });
- this.$el.find(".robin-filters").bind("contextmenu", function(e){
- e.preventDefault();
- e.stopPropagation();
- var chan_id = $(e.target).data("filter");
- if(chan_id===null)return; // no a channel
- _self.removeChannel(_self.channels[chan_id]);
- });
- // Form events
- this.$opt.find(".channel-mode span").click(this.changeChannelMode);
- this.$opt.find("button").click(function(){
- var new_chan = _self.$opt.find("input[name='add-channel']").val();
- if(new_chan != '') _self.addChannel(new_chan);
- _self.$opt.find("input[name='add-channel']").val('');
- });
-
-
- $("#robinSendMessage").submit(this.submit_helper);
-
- // store default room class
- this.defaultRoomClasses = $("#robinChatWindow").attr("class");
-
- // redraw tabs
- this.drawTabs();
-
- // start ticker
- setInterval(this.tick, 1000);
- }
- };
-
- /**
- * Check if a message is "spam"
- */
- var is_spam = function(line){
- return (
- // Hide auto vote messages
- (/^voted to (grow|stay|abandon)/.test(line)) ||
- // random unicode?
- (/[\u0080-\uFFFF]/.test(line)) ||
- // hide any auto voter messages
- (/\[.*autovoter.*\]/.test(line)) ||
- // Common bots
- (/^(\[binbot\]|\[robin-grow\])/.test(line)) ||
- // repeating chars in line (more than 5). e.g. aaaaaaa !!!!!!!!
- (/(.)\1{5,}/.test(line)) ||
- // Some common messages
- (/(voting will end in approximately|\[i spam the most used phrase\]|\[message from creator\]|\[.*bot.*\])/.test(line)) ||
- // no spaces = spam if its longer than 25 chars (dont filter links)
- (line.indexOf(" ") === -1 && line.length > 25 && line.indexOf("http") === -1) ||
- // repeating same word
- /(\b\S+\b)\s+\b\1\b/i.test(line)
- );
- };
-
- /**
- * Check if a message is from an ignored user
- *
- */
- var is_ignored = function($usr, $ele){
- // no user name, go looking for when said it
- if($usr.length === 0){
- while($usr.length === 0){
- $ele = $ele.prev();
- $usr = $ele.find(".robin--username");
- }
- }
- // are they ignored?
- return (ignored_users[$usr.text()]);
- };
-
- /**
- * Make links clickable
- *
- */
- var auto_link = function($msg){
- var text = $msg.html(); // read as html so stuff stays escaped
- // normal links
- text = text.replace(/\b(?:https?|ftp):\/\/[a-z0-9-+&@#\/%?=~_|!:,.;]*[a-z0-9-+&@#\/%=~_|]/gim, '<a target="blank" href="$&">$&</a>');
-
- // reddit subreddit links
- text = text.replace(/\s+\/r\/(\w+)\/?/gi, ' <a target="blank" href="https://reddit.com/r/$1">/r/$1</a>');
- text = text.replace(/\s+\/u\/(\w+)\/?/gi, ' <a target="blank" href="https://reddit.com/u/$1">/r/$1</a>');
-
- // update text
- $msg.html(text);
- };
-
- /**
- * Mute a user
- */
- var _mute_user = function(usr){
- // Add to ignore list
- ignored_users[usr] = true;
- _render_muted_list();
- };
-
- /**
- * un-mute a user
- */
- var _unmute_user = function(usr){
- // Add to ignore list
- delete ignored_users[usr];
- _render_muted_list();
- };
-
- // Render list of ignored users
- var _render_muted_list = function(){
- var html = "<strong>Ignored users</strong><br>";
- for(var u in ignored_users){
- html += "<div data-usr='"+ u + "'>" + u + " - [unmute]</div>";
- }
- $("#muted_users").html(html);
- };
-
- // Scroll chat back to bottom
- var _scroll_to_bottom = function(){
- $("#robinChatWindow").scrollTop($("#robinChatMessageList").height());
- };
-
- // create persistant option
- function createOption(name, click_action, default_state){
- var checked_markup;
- var key = "robin-enhance-" + name.replace(/\W/g, '');
- console.log(key);
- var state = (typeof default_state !== "undefined") ? default_state : false;
-
- // try and state if setting is defined
- if(GM_getValue(key)){
- console.log("state loaded");
- state = (GM_getValue(key) === 'true') ? true : false;
- }
- // markup for state
- checked_markup = (state === true) ? "checked='checked'" : "";
- // render option
- var $option = $("<label><input type='checkbox' "+checked_markup+">"+name+"</label>").click(function(){
- var checked = $(this).find("input").is(':checked');
-
- // persist state
- if(checked != state){
- console.log("state saved", checked);
- GM_setValue(key, checked ? 'true' : 'false'); // true/false stored as strings, to avoid unset matching
- state = checked;
- }
-
- click_action(checked, $(this));
- });
- // add to dom
- $("#robinDesktopNotifier").append($option);
- // init
- click_action(default_state, $option)
- };
-
- // update spam count
- var update_spam_count = function(){
- blocked_spam++;
- blocked_spam_el.innerHTML = blocked_spam;
- };
-
- // when name is clicked, fill it into the chat box
- var fill_name = function(e){
- e.preventDefault();
- e.stopPropagation();
-
- // if text area blank, prefill name. if not, stick it on the end
- if($(".text-counter-input").val() === ''){
- $(".text-counter-input").val($(this).text() + ' ').focus();
- }else{
- $(".text-counter-input").val($(".text-counter-input").val() + ' ' + $(this).text()).focus();
- }
- };
- // remove channel key from message
- var remove_channel_key_from_message = function(message){
- if($("#robinChatWindow").attr("data-channel-key")){
- var offset = $("#robinChatWindow").attr("data-channel-key").length;
- return (offset === 0) ? message : message.slice(offset+1);
- }
- return message;
- }
-
- /**
- * Parse a link and apply changes
- */
- var parse_line = function($ele){
- var $msg = $ele.find(".robin-message--message");
- var $usr = $ele.find(".robin--username");
- var line = $msg.text().toLowerCase();
-
- // dont parse system messages
- if($ele.hasClass("robin--user-class--system")){
- if(line.indexOf("ratelimit | you are doing that too much") !== -1){
- $(".text-counter-input").val(messageHistory[messageHistoryIndex-1]);
- }
- return;
- }
-
- // If user is ignored or message looks like "Spam". hide it
- if (is_ignored($usr, $ele) || is_spam(line)) {
- $ele.addClass("spam-hidden");
- update_spam_count();
- }
-
- // Highlight mentions
- if(line.indexOf(robin_user) !== -1){
- $ele.addClass("user-mention");
- }
-
- // Make links clickable
- if(!_robin_grow_detected && (line.indexOf("http") !== -1 || line.indexOf("/r/") !== -1 || line.indexOf("/u/") !== -1)){
- auto_link($msg);
- }
-
- // Add mute button to users
- if(!$ele.hasClass("robin--user-class--system") && $usr.text() != robin_user){
- $("<span style='font-size:.8em;cursor:pointer'> [mute] </span>").insertBefore($usr).click(function(){
- _mute_user($usr.text());
- });
- }
-
- // Track channels
- tabbedChannels.proccessLine(line, $ele);
-
- // bind click to use (override other click events if we can)
- $usr.bindFirst("click", fill_name);
- };
-
-
- // Detect changes, are parse the new message
- $("#robinChatWindow").on('DOMNodeInserted', function(e) {
- if ($(e.target).is('div.robin-message')) {
- // Apply changes to line
- parse_line($(e.target));
- }
- });
-
- // When everything is ready
- $(document).ready(function(){
-
- // Set default spam filter type
- $("#robinChatWindow").addClass("hide-spam");
-
- createOption("Hide spam completely (<span id='spamcount'>0</span> removed)", function(checked, ele){
- if(checked){
- $("#robinChat").removeClass("mute-spam").addClass("hide-spam");
- }else{
- $("#robinChat").removeClass("hide-spam").addClass("mute-spam");
- }
- // correct scroll after spam filter change
- _scroll_to_bottom();
- },true);
-
- createOption("Use channel colors", function(checked, ele){
- if(checked){
- $("#robinChat").addClass("show-colors");
- }else{
- $("#robinChat").removeClass("show-colors");
- }
- // correct scroll after spam filter change
- _scroll_to_bottom();
- },false);
-
-
-
- blocked_spam_el = $("#spamcount")[0];
-
- // Add Muted list & hook up unmute logic
- $('<div id="muted_users" class="robin-chat--sidebar-widget robin-chat--notification-widget"><strong>Ignored users</strong></div>').insertAfter($("#robinDesktopNotifier"));
- $('#muted_users').click(function(e){
- var user = $(e.target).data("usr");
- if(user) _unmute_user(user);
- });
-
- // Init tabbed channels
- tabbedChannels.init($('<div id="filter_tabs"></div>').insertAfter("#robinChatWindow"));
-
- // store i copy of last message, in case somthing goes wrong (rate limit)
- $("#robinSendMessage").submit(function(){
- var user_last_message = $(".text-counter-input").val();
-
- // if message history is to long, clear it out
- if(messageHistory.length === 25){
- messageHistory = messageHistory.shift();
- }
- messageHistory.push(remove_channel_key_from_message(user_last_message));
- messageHistoryIndex = messageHistory.length;
- });
-
- // up for last message send, down for prev (if moving between em)
- $('input.text-counter-input').on('keydown', function(e) {
- if(e.keyCode == 38) {
- e.preventDefault();
- messageHistoryIndex--;
- if(messageHistoryIndex > -1){
- $(this).val(messageHistory[messageHistoryIndex]);
- }
- }else if(e.keyCode == 40){
- e.preventDefault();
- if(messageHistoryIndex <= messageHistory.length){
- messageHistoryIndex++;
- $(this).val(messageHistory[messageHistoryIndex]);
- }else{
- $(this).val('');
- }
- }
- });
- });
-
- // fix by netnerd01
- var stylesheet = document.createElement('style');
- document.head.appendChild(stylesheet);
- stylesheet = stylesheet.sheet;
-
- // filter for channel
- stylesheet.insertRule("#robinChatWindow.robin-filter div.robin-message { display:none; }", 0);
- stylesheet.insertRule("#robinChatWindow.robin-filter div.robin-message.robin--user-class--system { display:block; }", 0);
- var color;
- for(var c=0;c<35;c++){
- color = colors[(c % (colors.length))];
-
- stylesheet.insertRule("#robinChat.show-colors #robinChatWindow div.robin-message.robin-filter-"+c+" { background: "+color+";}", 0);
- stylesheet.insertRule("#robinChatWindow.robin-filter.robin-filter-"+c+" div.robin-message.robin-filter-"+c+" { display:block;}", 0);
- }
-
- // Styles for filter tabs
- stylesheet.insertRule("#filter_tabs {width:100%; display: table; table-layout: fixed; background:#d7d7d2; border-bottom:1px solid #efefed;}",0);
- stylesheet.insertRule("#filter_tabs > span {width:90%; display: table-cell;}",0);
- stylesheet.insertRule("#filter_tabs > span.all, #filter_tabs > span.more {width:60px; text-align:center; vertical-align:middle; cursor:pointer;}",0);
- stylesheet.insertRule("#filter_tabs > span.all.selected, #filter_tabs > span.all.selected:hover {background: #fff;}", 0);
- stylesheet.insertRule("#filter_tabs .robin-filters { display: table; width:100%;table-layout: fixed; '}", 0);
- stylesheet.insertRule("#filter_tabs .robin-filters > span { padding: 5px 2px;text-align: center; display: table-cell; cursor: pointer;width:2%; vertical-align: middle; font-size: 1.1em;}", 0);
- stylesheet.insertRule("#filter_tabs .robin-filters > span.selected, #filter_tabs .robin-filters > span:hover { background: #fff;}", 0);
- stylesheet.insertRule("#filter_tabs .robin-filters > span > span {pointer-events: none;}", 0);
-
- stylesheet.insertRule(".robin-channel-add {padding:5px; display:none;}", 0);
- stylesheet.insertRule(".robin-channel-add input {padding: 2.5px; }", 0);
- stylesheet.insertRule(".robin-channel-add .channel-mode {float:right; font-size:1.2em;padding:5px;}", 0);
- stylesheet.insertRule(".robin-channel-add .channel-mode span {cursor:pointer}", 0);
- //mentions should show even in filter view
- stylesheet.insertRule("#robinChat #robinChatWindow div.robin-message.user-mention { display:block; font-weight:bold; }", 0);
-
- // Add initial styles for "spam" messages
- stylesheet.insertRule("#robinChat.hide-spam #robinChatWindow div.robin-message.spam-hidden { display:none; }", 0);
- stylesheet.insertRule("#robinChat.mute-spam #robinChatWindow div.robin-message.spam-hidden { opacity:0.3; font-size:1.2em; }", 0);
- stylesheet.insertRule("#robinChat.show-colors #robinChatWindow div.robin-message.spam-hidden { opacity:0.3; font-size:1.2em; }", 0);
- // muted user box
- stylesheet.insertRule("#muted_users { font-size:1.2em; }", 0);
- stylesheet.insertRule("#muted_users div { padding: 2px 0; }", 0);
- stylesheet.insertRule("#muted_users strong { font-weight:bold; }", 0);
-
- // FIX RES nightmode (ish) [ by Kei ]
- stylesheet.insertRule(".res-nightmode #robinChatWindow div.robin-message { color: #ccc; }", 0);
- stylesheet.insertRule(".res-nightmode .robin-chat--sidebar-widget { background: #222; color: #ccc;}", 0);
- stylesheet.insertRule(".res-nightmode .robin-room-participant { background: #222; color: #999;}", 0);
- stylesheet.insertRule(".res-nightmode #filter_tabs {background: rgb(51, 51, 51);}", 0);
- stylesheet.insertRule(".res-nightmode #filter_tabs .robin-filters > span.selected,.res-nightmode #filter_tabs .robin-filters > span:hover,.res-nightmode #filter_tabs > span.all.selected,.res-nightmode #filter_tabs > span.all:hover {background: rgb(34, 34, 34)}", 0);
- stylesheet.insertRule(".res-nightmode .robin-chat--input { background: #222 }", 0);
- stylesheet.insertRule(".res-nightmode .robin--presence-class--away .robin--username {color: #999;}", 0);
- stylesheet.insertRule(".res-nightmode .robin--presence-class--present .robin--username {color: #ccc;}", 0);
- stylesheet.insertRule(".res-nightmode #robinChat .robin--user-class--self .robin--username { color: #999; }", 0);
- stylesheet.insertRule(".res-nightmode .robin-chat--vote { background: #777; color: #ccc;}", 0);
- stylesheet.insertRule(".res-nightmode .robin-chat--buttons button.robin-chat--vote.robin--active { background: #ccc; color:#999; }", 0);
-
- $(document).ready(function(){
- setTimeout(function(){
- // Play nice with robin grow (makes room for tab bar we insert)
- if($(".usercount.robin-chat--vote").length !== 0){
- _robin_grow_detected = true;
- stylesheet.insertRule("#robinChat.robin-chat .robin-chat--body { height: calc(100vh - 150px); }", 0);
- }
- },500);
- });
-
- // Allow me to sneek functions in front of other libaries - used when working with robin grow >.< sorry guys
- //http://stackoverflow.com/questions/2360655/jquery-event-handlers-always-execute-in-order-they-were-bound-any-way-around-t
- $.fn.bindFirst = function(name, fn) {
- // bind as you normally would
- // don't want to miss out on any jQuery magic
- this.on(name, fn);
-
- // Thanks to a comment by @Martin, adding support for
- // namespaced events too.
- this.each(function() {
- var handlers = $._data(this, 'events')[name.split('.')[0]];
- // take out the handler we just inserted from the end
- var handler = handlers.pop();
- // move it at the beginning
- handlers.splice(0, 0, handler);
- });
- };
-
- })();