Last active
December 25, 2015 20:39
-
-
Save joshrobb/7036399 to your computer and use it in GitHub Desktop.
Autocomplete user script for Hipchat - drop this file into an open Chrome extensions window and it will enable autocomplete for hipchat emoticons.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name HipchatEmotiAutocomplete | |
// @description Autocomplete for emoticons in hipchat | |
// @include https://*.hipchat.com/chat* | |
// @version 1.0 | |
// ==/UserScript== | |
var code = function() { | |
window.emoti_autocomplete = { | |
input: null, | |
position_node: null, | |
autocomplete: null, | |
search_str: '', | |
startingchars: {}, | |
choose_selected: function() { | |
var tag = this.autocomplete.find('div.selected:first').attr('shortcut'); | |
if (tag) { | |
var selection = this.input.getSelection(); | |
var replacement = (tag.indexOf(' ') == -1 ? tag : (tag + ' ')); | |
var replaced_message = this.input.val().substr(0, selection.end).replace(/\(\w*$/, replacement); | |
this.input.val(replaced_message + this.input.val().substr(selection.end)); | |
this.input.setSelection(replaced_message.length, replaced_message.length); | |
} | |
this.autocomplete.hide(); | |
}, | |
display_autocomplete: function() { | |
// Initial showing of autocomplete, populate it first | |
this.populate(); | |
this.autocomplete.show(). | |
scrollTop(0). | |
width(this.position_node.outerWidth() - 2); // subtract 2 for borders | |
}, | |
// Filter the autocomplete list based on the current search string | |
// Also sort the users based on match strength | |
filter: function(str) { | |
str = str.toLowerCase(); | |
this.search_str = str; | |
$("div", this.autocomplete).sort(this.sort).appendTo(this.autocomplete); | |
this.autocomplete.find('div.member').each(function(idx, elem) { | |
var elem = $(elem); | |
elem.removeClass('selected'); | |
// Everything matches at 0 | |
if (str.length == 0) { | |
elem.show(); | |
return; | |
} | |
var matches = false; | |
var highlighted_string = null; | |
var shortcut = elem.attr("shortcut") | |
matches = shortcut.indexOf(str) > -1 || shortcut.indexOf(str.substr(1)) > -1; | |
if (matches) { | |
elem.show(); | |
} else { | |
elem.hide(); | |
} | |
}); | |
this.autocomplete.find('div.member:visible:first').addClass('selected'); | |
}, | |
handle_input_keydown: function(event) { | |
if (!this.autocomplete.is(':visible')) { | |
mention_autocomplete.handle_input_keydown(event); | |
return; | |
} | |
if (event.keyCode == 13 || event.keyCode == 9) { // enter and tab | |
if (this.autocomplete.is(':visible')) { | |
event.preventDefault(); | |
event.stopImmediatePropagation(); | |
} | |
} else if (event.keyCode == 38) { // up arrow | |
if (this.autocomplete.is(':visible')) { | |
event.preventDefault(); | |
event.stopImmediatePropagation(); | |
this.select_previous(); | |
} | |
} else if (event.keyCode == 40) { // down arrow | |
if (this.autocomplete.is(':visible')) { | |
event.preventDefault(); | |
event.stopImmediatePropagation(); | |
this.select_next(); | |
} | |
} | |
}, | |
handle_input_keyup: function(event) { | |
var is_cursor_move = false; | |
if (event.keyCode == 37 || event.keyCode == 38 || // up,down,left,right arrows | |
event.keyCode == 39 || event.keyCode == 40) { | |
is_cursor_move = true; | |
} | |
var cur_text = this.input.val(); | |
var pos = this.input.getSelection().start; | |
//var sregex = "[\\"+Object.keys(this.startingchars).join("\\")+"](\w*)$"; | |
//var test = new RegExp("(\\(\w*)$"); | |
var test = /([\(]\w*)$/; | |
var matches = cur_text.substr(0, pos).match(test); | |
var colonemoti = pos > 0 && (cur_text[pos - 1] == ":" || cur_text[pos - 2] == ":") | |
if (!matches || matches.length == 0 || colonemoti) { | |
this.autocomplete.hide(); | |
mention_autocomplete.handle_input_keyup(event); | |
return; | |
} | |
// Don't show autocomplete on cursor movement, only hide it | |
if (is_cursor_move) { | |
return; | |
} | |
// Handle enter/tab on key up so we don't end up completing before we've | |
// filtered and selected the proper user (e.g. if you type @g<tab> really | |
// fast) | |
if (event.keyCode == 13 || event.keyCode == 9) { // enter and tab | |
if (this.autocomplete.is(':visible')) { | |
this.choose_selected(); | |
return; | |
} | |
} else if (event.keyCode == 27) { // esc | |
// Hide autocomplete on esc | |
if (this.autocomplete.is(':visible')) { | |
this.autocomplete.hide(); | |
return; | |
} | |
} | |
if (!this.autocomplete.is(':visible')) { | |
this.display_autocomplete(); | |
} | |
this.filter(matches[1]); | |
// Check to see that we actually have results | |
if (this.autocomplete.find('div:visible').length == 0) { | |
this.autocomplete.hide(); | |
return; | |
} | |
var self = this; | |
setTimeout(function() { | |
var coords = self.position_node.offset(); | |
self.autocomplete.offset({ | |
top: (coords.top - self.autocomplete.outerHeight()), | |
left: coords.left | |
}); | |
}, 0); | |
}, | |
handle_mousedown: function(event) { | |
var target = $(event.target); | |
var member = (target.hasClass('member') ? target : target.closest('.member')); | |
if (member.length > 0) { | |
this.autocomplete.find('div.member').removeClass('selected'); | |
member.addClass('selected'); | |
this.choose_selected(); | |
} | |
}, | |
init: function(input, position_node) { | |
this.input = $(input); | |
this.position_node = $(position_node); | |
this.startingchars = {} | |
for (i in config.emoticons) { | |
e = config.emoticons[i]; | |
this.startingchars[e.shortcut[0]] = 1 | |
}; | |
var autocomplete_node = $(document.createElement('div')); | |
autocomplete_node.attr('id', 'emojiautocomplete'); | |
autocomplete_node.hide(); | |
var css = ["body.chat #emojiautocomplete { background-color: #fefefe; border: 1px solid #ccc; position: absolute; max-height: 130px; overflow: auto;}", | |
"body.chat #emojiautocomplete div.member { padding: 5px; cursor: pointer; color: #333; -moz-user-select: none; -webkit-user-select: none; white-space: nowrap; }", | |
"body.chat #emojiautocomplete div.member:hover { background-color: #f6f6f6; } ", | |
"body.chat #emojiautocomplete div.selected, body.chat #emojiautocomplete div.selected:hover { background-color: #669acc; color: #fff; }" | |
] | |
var sheet = document.styleSheets[0]; | |
for (var i in css) { | |
sheet.insertRule(css[i], sheet.rules.length) | |
} | |
$('body').append(autocomplete_node); | |
this.autocomplete = autocomplete_node; | |
this.autocomplete.mousedown($.proxy(this, 'handle_mousedown')); | |
this.input.off("keyup"); | |
this.input.off("keydown"); | |
this.input.keyup($.proxy(this, 'handle_input_keyup')) | |
.keydown($.proxy(this, 'handle_input_keydown')); | |
//setup the app listeners again | |
mention_autocomplete.init(input, position_node); | |
this.input.keydown($.proxy(chat, 'handle_message_keydown')); | |
this.input.keyup($.proxy(chat, 'handle_message_keyup')); | |
//next tab | |
this.input.bind('keydown', 'Ctrl+]', $.proxy(chat, 'show_next_tab')); | |
this.input.bind('keydown', 'Ctrl+Shift+]', $.proxy(chat, 'show_next_tab')); | |
// previous tab | |
this.input.bind('keydown', 'Ctrl+[', $.proxy(chat, 'show_prev_tab')); | |
this.input.bind('keydown', 'Ctrl+Shift+[', $.proxy(chat, 'show_prev_tab')); | |
// invite | |
this.input.bind('keydown', 'Ctrl+i', $.proxy(chat, 'show_room_invite_popup')); | |
//chat.setup_listeners(); | |
}, | |
// Generate the full autcomplete list | |
populate: function() { | |
this.autocomplete.html(''); | |
// Go through each room member and create a label and tag for them | |
// tag = string to display when autocompleting for that user | |
// nick = non-default chosen mention name (null if not different from default mention name) | |
var html_strings = []; | |
for (var i = 0; i < config.emoticons.length; i++) { | |
var emoti = config.emoticons[i]; | |
var class_name = (i == 0 ? ' selected' : ''); | |
var label = emoti.shortcut; | |
var imgpath = emoticons.path_prefix + '/' + emoti.file; | |
var img = ' <img height="' + emoti.height + '" width="' + emoti.width + '" src="' + imgpath + '" />'; | |
html_strings.push('<div class="member' + class_name + '" shortcut="' + label + '">' + img + label + '</div>'); | |
} | |
this.autocomplete.append(html_strings.join('')); | |
}, | |
select_next: function() { | |
var selected = this.autocomplete.find('.selected'); | |
var next_item = selected.nextAll(':visible:first'); | |
if (next_item.length > 0) { | |
selected.removeClass('selected'); | |
next_item.addClass('selected'); | |
if (next_item.position().top + next_item.outerHeight() > this.autocomplete.height()) { | |
this.autocomplete.scrollTop(this.autocomplete.scrollTop() + next_item.outerHeight()); | |
} | |
} | |
}, | |
select_previous: function() { | |
var selected = this.autocomplete.find('.selected'); | |
var prev_item = selected.prevAll(':visible:first'); | |
if (prev_item.length > 0) { | |
selected.removeClass('selected'); | |
prev_item.addClass('selected'); | |
if (prev_item.position().top < 0) { | |
this.autocomplete.scrollTop(this.autocomplete.scrollTop() - prev_item.outerHeight()); | |
} | |
} | |
}, | |
}; | |
message_input = $('#message_input'); | |
window.emoti_autocomplete.init(message_input, message_input.parent()); | |
}; | |
var execed = false; | |
function exec(fn) { | |
if (execed) return; | |
execed = true; | |
var script = document.createElement('script'); | |
script.setAttribute("type", "application/javascript"); | |
script.textContent = '(' + fn + ')();'; | |
document.body.appendChild(script); // run the script | |
document.body.removeChild(script); // clean up | |
} | |
window.addEventListener("load", function() { | |
// script injection | |
exec(code); | |
}, false); | |
setTimeout(exec(code), 1000); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment