Place to store the code and config used for the next-Iterations live event. https://iterations.space/live/
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
 
 
 
 

342 lines
11 KiB

var AutoComplete = Backbone.View.extend({
events: {
'click .autocomplete-item': 'onItemClick',
'mousemove .autocomplete-item': 'onItemMouseMove',
'mousedown' : 'onMouseDown',
'click .autocomplete-item .action': 'onActionClick'
},
initialize: function() {
this.$list = $('<ul class="autocomplete-list"></ul>');
this.$list.appendTo(this.$el);
this.reset();
this.open = false;
this._show_ui = true;
this.filter_list = false;
},
render: function() {
return this;
},
showUi: function(show_ui) {
this._show_ui = show_ui;
},
// Set the list of words to be searching through
setWords: function(word_list, filter_list) {
var new_list = [];
var template_str_default = '<li class="autocomplete-item"><span class="word"><%= word %></span><span class="matches"><%= match_list %></span><span class="description"><%= description %></span></li>';
var template_str_nicks = '<li class="autocomplete-item autocomplete-nick" data-nick="<%= word %>"><span class="word"><%= match_list %></span><span class="actions"><a class="action" data-event="message">Message</a><a class="action" data-event="more">More...</a></span></li>';
var template = {};
this.reset();
this.filter_list = !!filter_list;
_.each(word_list, function(word) {
var template_str, $el, $word;
if (this._show_ui) {
if (typeof word === 'string') {
template.match_list = '';
template.word = word;
template.description = '';
} else {
// Only show the alternative matches for this word if there is more than 1
// Eg. for matching /part show the alternatives ['/part', '/leave']
template.match_list = template.word = (word.match.length > 1 ? word.match.join(', ') : '');
template.description = word.description || '';
}
template_str = (word.type === 'nick') ? template_str_nicks : template_str_default;
$el = $(_.template(template_str)(template)).hide();
$word = $el.find('.word');
} else {
template_str = '';
$el = null;
$word = null;
}
var list_entry = {
match: (typeof word === 'string') ? [word] : word.match,
type: (word.type === 'nick') ? 'nick' : 'default',
$el: $el,
$word: $word
};
new_list.push(list_entry);
$el && $el.data('word', list_entry);
$el && $el.appendTo(this.$list);
}, this);
this.list = new_list;
},
setTitle: function(type) {
var texts = {
nicks: 'People or channels',
command: 'Commands'
};
this.$('.autocomplete-header-label').text(texts[type] || texts['nicks']);
},
// Update the list with a word to search for
update: function(word) {
var first_match = null;
// No need to update the list if it's the same search
if (this.matching_against_word !== null && word.toLowerCase() === this.matching_against_word.toLowerCase()) {
return false;
}
// Filter our available auto complete list down to ones that match
this.matches = _.filter(this.list, function(item) {
var matched_word = _.find(item.match, function(match_word) {
if (match_word.toLowerCase().indexOf(word.toLowerCase()) === 0) {
return match_word;
}
});
if (matched_word) {
item.matched_word = matched_word;
if (this._show_ui) {
item.$word.text(matched_word);
item.$el.show();
}
if (!first_match) {
first_match = item;
}
} else {
item.matched_word = null;
if (this._show_ui) {
item.$el.hide();
}
}
return matched_word;
}, this);
this.matching_against_word = word;
this.$('.selected').removeClass('selected');
// Reset the selected match to the first
this.selected_idx = 0;
if (first_match) {
this.selectEl(this.matches[0].$el);
this.trigger('selected', first_match.matched_word, first_match);
} else {
this.trigger('selected', null);
}
},
show: function() {
this.open = true;
if (this._show_ui) {
this.$el.css('max-height', (_kiwi.app.view.$el.height() / 2) + 'px').show();
this.$list.css('max-height', (_kiwi.app.view.$el.height() / 2)-32 + 'px').show();
}
},
close: function() {
this.open = false;
this._show_ui && this.$el.hide();
this.reset();
this.trigger('close');
},
reset: function() {
this.matching_against_word = null;
this._show_ui && this.$list.empty();
this.list = [];
this.matches = [];
this.selected_idx = 0;
},
onMouseDown: function(event) {
// This stops the control input box from loosing focus when clicking here
event.preventDefault();
// IE doesn't prevent moving focus even with event.preventDefault()
// so we set a flag to know when we should ignore the blur event
this.cancel_blur = true;
_.defer(_.bind(function() {
delete this.cancel_blur;
}, this));
},
onItemClick: function(event) {
var el_data = $(event.currentTarget).data('word');
if (!el_data) return;
this.trigger('match', el_data.matched_word, el_data);
},
onItemMouseMove: function(event) {
$this = $(event.currentTarget);
// No need to re-add the class if it already has it
if ($this.hasClass('selected')) {
return;
}
var idx = null;
_.each(this.matches, function(match, match_idx) {
if (match.$el[0] === $this[0]) {
idx = match_idx;
return false;
}
});
if (idx !== null) {
this.selected_idx = idx;
this.selectEl($this);
}
},
onActionClick: function(event) {
event.stopPropagation();
var $this = $(event.currentTarget),
event_name = $this.data('event'),
$item = $this.parents('.autocomplete-item'),
el_data = $item.data('word');
this.trigger('action-'+event_name, el_data.matched_word, el_data);
},
previous: function() {
this.selected_idx = this.matches[this.selected_idx-1] ? this.selected_idx-1 : this.matches.length-1;
if (this.matches[this.selected_idx]) {
this.selectEl(this.matches[this.selected_idx].$el, true);
this.trigger('selected', this.matches[this.selected_idx].matched_word, this.matches[this.selected_idx]);
}
},
next: function() {
this.selected_idx = this.matches[this.selected_idx+1] ? this.selected_idx+1 : 0;
if (this.matches[this.selected_idx]) {
this.selectEl(this.matches[this.selected_idx].$el, true);
this.trigger('selected', this.matches[this.selected_idx].matched_word, this.matches[this.selected_idx]);
}
},
cancel: function(reason) {
this.trigger('cancel', reason);
},
selectEl: function($el, scroll_in_view) {
var el, this_height;
if (!this._show_ui) return;
this.$('.selected').removeClass('selected');
if ($el) {
$el.addClass('selected');
}
if ($el && scroll_in_view) {
el = this.$el[0];
this_height = this.$el.height();
$el[0].scrollIntoView();
if($el.position().top + $el.outerHeight() > this_height / 2){
el.scrollTop = el.scrollHeight;
} else {
el.scrollTop -= (this_height / 2);
}
}
},
currentMatch: function() {
return this.matches[this.selected_idx] ?
this.matches[this.selected_idx].matched_word :
null;
},
onKeyDown: function(event) {
if (!this.open) return;
var $inp = $(event.currentTarget);
var dont_process_other_input_keys = false;
// Handling input box caret positioning
var caret_pos = $inp[0].selectionStart,
new_position = 0,
text_range;
if (event.keyCode === 38 || (event.keyCode === 9 && event.shiftKey)) { // up or tab+shift
this.previous();
event.preventDefault();
dont_process_other_input_keys = true;
}
else if (event.keyCode === 40 || event.keyCode === 9) { // down or tab
this.next();
event.preventDefault();
dont_process_other_input_keys = true;
}
else if (caret_pos === 1 && (event.keyCode === 37 || event.keyCode === 8)) { // Caret about to move to the beginning of the box
event.preventDefault();
this.cancel('caret_moved');
}
else if (0 && event.keyCode === 37) { // left
// If the caret is moved before the current word, stop autocompleting
if (caret_pos > 0 && $inp.val().toUpperCase()[caret_pos-1] === ' ') {
event.preventDefault();
this.cancel('caret_moved');
}
}
else if (event.keyCode === 13) { // return
if (this.matches[this.selected_idx]) {
this.trigger('match', this.currentMatch(), this.matches[this.selected_idx]);
event.preventDefault();
// If the UI is not open, let the return key keep processing as normal.
// If we did not let this happen, since there is no visual UI it would look
// weird to the user if they had to press return twice for something to happen.
dont_process_other_input_keys = this._show_ui ? true : false;
}
}
else if (event.keyCode === 27) { // escape
this.cancel();
}
else if (event.keyCode === 32) { // space
this.cancel('typing');
}
else if (event.keyCode === 16) { // shift
// Shift is used to tab+shift
dont_process_other_input_keys = true;
}
else if (!this.filter_list) {
// If we have started typing again, cancel the autocomplete
this.cancel('typing');
}
return dont_process_other_input_keys;
}
});