c3d2-web/content/static/candy/plugins/namecomplete/candy.js

261 lines
6.6 KiB
JavaScript

/** File: candy.js
* Candy - Chats are not dead yet.
*
* Authors:
* - Troy McCabe <troy.mccabe@geeksquad.com>
* - Ben Klang <bklang@mojolingo.com>
*
* Copyright:
* (c) 2012 Geek Squad. All rights reserved.
* (c) 2014 Power Home Remodeling Group. All rights reserved.
*/
/* global document, Candy, jQuery */
var CandyShop = (function(self) { return self; }(CandyShop || {}));
/** Class: CandyShop.NameComplete
* Allows for completion of a name in the roster
*/
CandyShop.NameComplete = (function(self, Candy, $) {
/** Object: _options
* Options:
* (String) nameIdentifier - Prefix to append to a name to look for. '@' now looks for '@NICK', '' looks for 'NICK', etc. Defaults to '@'
* (Integer) completeKeyCode - Which key to use to complete
*/
var _options = {
nameIdentifier: '@',
completeKeyCode: 9
};
/** Array: _nicks
* An array of nicks to complete from
* Populated after 'candy:core.presence'
*/
var _nicks = [];
/** String: _selector
* The selector for the visible message box
*/
var _selector = 'input[name="message"]:visible';
/** Boolean:_autocompleteStarted
* Keeps track of whether we're in the middle of autocompleting a name
*/
var _autocompleteStarted = false;
/** Function: init
* Initialize the NameComplete plugin
* Show options for auto completion of names
*
* Parameters:
* (Object) options - Options to apply to this plugin
*/
self.init = function(options) {
// apply the supplied options to the defaults specified
$.extend(true, _options, options);
// listen for keydown when autocomplete options exist
$(document).on('keypress', _selector, function(e) {
if (e.which === _options.nameIdentifier.charCodeAt()) {
_autocompleteStarted = true;
}
if (_autocompleteStarted) {
// update the list of nicks to grab
self.populateNicks();
// set up the vars for this method
// break it on spaces, and get the last word in the string
var field = $(this);
var msgParts = field.val().split(' ');
var lastWord = new RegExp( "^" + msgParts[msgParts.length - 1] + String.fromCharCode(e.which), "i");
var matches = [];
// go through each of the nicks and compare it
$(_nicks).each(function(index, item) {
// if we have results
if (item.match(lastWord) !== null) {
matches.push(item);
}
});
// if we only have one match, no need to show the picker, just replace it
// else show the picker of the name matches
if (matches.length === 1) {
self.replaceName(matches[0]);
// Since the name will be autocompleted, throw away the last character
e.preventDefault();
} else if (matches.length > 1) {
self.showPicker(matches, field);
}
}
});
};
/** Function: keyDown
* The listener for keydown in the menu
*/
self.keyDown = function(e) {
// get the menu and the content element
var menu = $('#context-menu');
var content = menu.find('ul');
var selected = content.find('li.selected');
if(menu.css('display') === 'none') {
$(document).unbind('keydown', self.keyDown);
return;
}
// switch the key code
switch (e.which) {
// up arrow
case 38:
// down arrow
case 40:
var newEl;
if (e.which === 38) {
// move the selected thing up
newEl = selected.prev();
} else {
// move the selected thing down
newEl = selected.next();
}
// Prevent going off either end of the list
if ($(newEl).length > 0) {
selected.removeClass('selected');
newEl.addClass('selected');
}
// don't perform any key actions
e.preventDefault();
break;
// esc key
case 27:
// delete Key
case 8:
case 46:
self.endAutocomplete();
break;
// the key code for completion
case _options.completeKeyCode:
case 13:
// get the text of the selected item
var val = content.find('li.selected').text();
// replace the last item with the selected item
self.replaceName(val);
// don't perform any key actions
e.preventDefault();
break;
}
};
/** Function: endAutocomplete
* Disables autocomplete mode, hiding the context menu
*/
self.endAutocomplete = function() {
_autocompleteStarted = false;
$(_selector).unbind('keydown', self.keyDown);
$('#context-menu').hide();
};
/** Function: selectOnClick
* The listener for click on decision in the menu
*
* Parameters:
* (Event) e - The click event
*/
self.selectOnClick = function(e) {
self.replaceName($(e.currentTarget).text());
$(_selector).focus();
e.preventDefault();
};
/** Function: populateNicks
* Populate the collection of nicks to autocomplete from
*/
self.populateNicks = function() {
// clear the nick collection
_nicks = [];
// grab the roster in the current room
var room = Candy.Core.getRoom(Candy.View.getCurrent().roomJid);
if (room !== null) {
var roster = room.getRoster().getAll();
// iterate and add the nicks to the collection
$.each(roster, function(index, item) {
_nicks.push(_options.nameIdentifier + item.getNick());
});
}
};
/** Function: replaceName
*
*/
self.replaceName = function(replaceText) {
// get the parts of the message
var $msgBox = $(_selector);
var msgParts = $msgBox.val().split(' ');
// If the name is the first word, add a colon to the end
if (msgParts.length === 1) {
replaceText += ": ";
} else {
replaceText += " ";
}
// replace the last part with the item
msgParts[msgParts.length - 1] = replaceText;
// put the string back together on spaces
$msgBox.val(msgParts.join(' '));
self.endAutocomplete();
};
/** Function: showPicker
* Show the picker for the list of names that match
*/
self.showPicker = function(matches, elem) {
// get the element
elem = $(elem);
// get the necessary items
var pos = elem.offset(),
menu = $('#context-menu'),
content = $('ul', menu),
i;
// clear the content if needed
content.empty();
// add the matches to the list
for(i = 0; i < matches.length; i++) {
content.append('<li class="candy-namecomplete-option">' + matches[i] + '</li>');
}
// select the first item
$(content.find('li')[0]).addClass('selected');
content.find('li').click(self.selectOnClick);
// bind the keydown to move around the menu
$(_selector).bind('keydown', self.keyDown);
var posLeft = elem.val().length * 7,
posTop = Candy.Util.getPosTopAccordingToWindowBounds(menu, pos.top);
// show it
menu.css({'left': posLeft, 'top': posTop.px, backgroundPosition: posLeft.backgroundPositionAlignment + ' ' + posTop.backgroundPositionAlignment});
menu.fadeIn('fast');
return true;
};
return self;
}(CandyShop.NameComplete || {}, Candy, jQuery));