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.
461 lines
17 KiB
461 lines
17 KiB
_kiwi.view.ControlBox = Backbone.View.extend({
|
|
events: {
|
|
'keydown .inp': 'inputKeyDown',
|
|
'keyup .inp': 'inputKeyUp',
|
|
'blur .inp': 'inputBlur',
|
|
'click .nick': 'showNickChange'
|
|
},
|
|
|
|
initialize: function () {
|
|
var that = this;
|
|
|
|
this.buffer = []; // Stores previously run commands
|
|
this.buffer_pos = 0; // The current position in the buffer
|
|
|
|
this.preprocessor = new InputPreProcessor();
|
|
this.preprocessor.recursive_depth = 5;
|
|
|
|
this.autocomplete = new AutoComplete({el: this.$('.autocomplete')[0]});
|
|
this.autocomplete_command_list = [];
|
|
this.bindAutocomplete();
|
|
|
|
// Keep the nick view updated with nick changes
|
|
_kiwi.app.connections.on('change:nick', function(connection) {
|
|
// Only update the nick view if it's the active connection
|
|
if (connection !== _kiwi.app.connections.active_connection)
|
|
return;
|
|
|
|
$('.nick', that.$el).text(connection.get('nick'));
|
|
});
|
|
|
|
// Update our nick view as we flick between connections
|
|
_kiwi.app.connections.on('active', function(panel, connection) {
|
|
$('.nick', that.$el).text(connection.get('nick'));
|
|
});
|
|
},
|
|
|
|
render: function() {
|
|
var send_message_text = translateText('client_views_controlbox_message');
|
|
this.$('.inp').attr('placeholder', send_message_text);
|
|
|
|
return this;
|
|
},
|
|
|
|
bindAutocomplete: function() {
|
|
this.listenTo(this.autocomplete, 'match', function(word, matched) {
|
|
// A final word is selected. Either by clicking or hitting enter
|
|
var trailing = '';
|
|
if (matched.type === 'nick' && this.autocomplete_token_idx === 0) {
|
|
trailing = ': ';
|
|
}
|
|
this.autoCompleteFillWord(word + trailing, true);
|
|
this.autocomplete.close();
|
|
});
|
|
|
|
this.listenTo(this.autocomplete, 'selected', function(word, matched) {
|
|
// Words are selected while scrolling through the available options
|
|
var trailing = '';
|
|
if (matched && matched.type === 'nick' && this.autocomplete_token_idx === 0) {
|
|
trailing = ': ';
|
|
}
|
|
|
|
// Only display the match if we're not filtering through the UI
|
|
if (!this.autocomplete.filter_list) {
|
|
this.autoCompleteFillWord(word ? word + trailing : this.autocomplete.matching_against_word, true);
|
|
}
|
|
});
|
|
|
|
|
|
var focus_after_close = true;
|
|
this.listenTo(this.autocomplete, 'cancel', function(reason) {
|
|
var $inp = this.$('.inp'),
|
|
inp = $inp[0],
|
|
inp_val = $inp.val(),
|
|
caret_pos = $inp.selectRange();
|
|
|
|
// If we hit space while typing a word, then take chars 0->caret_pos, remove rest of word, include rest of input value
|
|
if (reason === 'typing' || reason === 'lost_focus') {
|
|
var trailing_start_pos = inp_val.indexOf(' ', caret_pos);
|
|
if (trailing_start_pos === -1 ) trailing_start_pos = inp_val.length;
|
|
$inp.val(inp_val.substr(0, caret_pos) + inp_val.substr(trailing_start_pos+1, inp_val.length));
|
|
} else {
|
|
$inp.val(this.autocomplete_before.value);
|
|
}
|
|
|
|
focus_after_close = (reason === 'lost_focus') ?
|
|
false :
|
|
true;
|
|
|
|
if (focus_after_close) {
|
|
// Move the cursor position back to where it was
|
|
$inp.selectRange(caret_pos);
|
|
}
|
|
|
|
this.autocomplete.close();
|
|
});
|
|
|
|
this.listenTo(this.autocomplete, 'close', function() {
|
|
if (focus_after_close) this.$('.inp').focus();
|
|
});
|
|
|
|
this.listenTo(this.autocomplete, 'action-message', function(nick) {
|
|
_kiwi.app.connections.active_connection.createQuery(nick);
|
|
this.autocomplete.close();
|
|
this.$('.inp').val('');
|
|
});
|
|
|
|
this.listenTo(this.autocomplete, 'action-more', function(nick) {
|
|
var active_panel = _kiwi.app.panels().active,
|
|
members = active_panel.get('members'),
|
|
member = members.getByNick(nick),
|
|
userbox,
|
|
are_we_an_op = !!members.getByNick(_kiwi.app.connections.active_connection.get('nick')).get('is_op');
|
|
|
|
userbox = new _kiwi.view.UserBox();
|
|
userbox.setTargets(member, active_panel);
|
|
userbox.displayOpItems(are_we_an_op);
|
|
|
|
var menu = new _kiwi.view.MenuBox(member.get('nick') || 'User');
|
|
menu.addItem('userbox', userbox.$el);
|
|
menu.showFooter(false);
|
|
|
|
_kiwi.global.events.emit('usermenu:created', {menu: menu, userbox: userbox, user: member})
|
|
.then(_.bind(function() {
|
|
menu.show();
|
|
|
|
var t = _kiwi.app.view.$el.height() - this.autocomplete.$el.outerHeight() - menu.$el.outerHeight();
|
|
var l = _kiwi.app.view.$el.width() - menu.$el.outerWidth();
|
|
|
|
// Set the new positon
|
|
menu.$el.offset({
|
|
left: l,
|
|
top: t
|
|
});
|
|
|
|
}, this))
|
|
.then(null, _.bind(function() {
|
|
userbox = null;
|
|
|
|
menu.dispose();
|
|
menu = null;
|
|
}, this));
|
|
});
|
|
},
|
|
|
|
// Taking a matched word form the auto completion, fill it into the current
|
|
// selected word (where the cursor position is)
|
|
autoCompleteFillWord: function(word, place_cursor_after_word) {
|
|
var $inp = this.$('.inp'),
|
|
inp_val = $inp.val(),
|
|
caret_pos = $inp[0].selectionStart;
|
|
|
|
// If we have the trailing ': ' after nicks, we need to check further back to find
|
|
// the start of the current word
|
|
var trailing_found = inp_val.substr(caret_pos-2, 2) === ': ';
|
|
|
|
var word_start_pos = inp_val.lastIndexOf(' ', caret_pos - (trailing_found ? 2 : 1));
|
|
word_start_pos = (word_start_pos === -1) ? 0 : word_start_pos + 1; // If no space found, start from 0. Otherwise, add 1 to include the space
|
|
var word_end_pos = inp_val.indexOf(' ', word_start_pos);
|
|
if (word_end_pos === -1) word_end_pos = inp_val.length;
|
|
|
|
var start_of_inp = inp_val.substr(0, word_start_pos); // Get text before current selected word
|
|
var rest_of_inp = inp_val.substr(word_end_pos); // Get text after current selected word
|
|
var new_val = start_of_inp + word + rest_of_inp; // Join strings before word, the new word, and after word
|
|
|
|
$inp.val(new_val);
|
|
|
|
// Set the cursor to the same posiiton of the current word as was previous
|
|
var new_position = word_start_pos + this.autocomplete.matching_against_word.length;
|
|
if (place_cursor_after_word) {
|
|
new_position = word_start_pos + word.length;
|
|
}
|
|
|
|
// Move the cursor position to the new position
|
|
if ($inp[0].setSelectionRange) {
|
|
$inp[0].setSelectionRange(new_position, new_position);
|
|
} else if ($inp[0].createTextRange) { // IE8 support
|
|
range = $inp[0].createTextRange();
|
|
range.collapse(true);
|
|
range.moveEnd('character', new_position);
|
|
range.moveStart('character', new_position);
|
|
range.select();
|
|
}
|
|
},
|
|
|
|
showNickChange: function (ev) {
|
|
// Nick box already open? Don't do it again
|
|
if (this.nick_change)
|
|
return;
|
|
|
|
this.nick_change = new _kiwi.view.NickChangeBox();
|
|
this.nick_change.render();
|
|
|
|
this.listenTo(this.nick_change, 'close', function() {
|
|
delete this.nick_change;
|
|
});
|
|
},
|
|
|
|
inputKeyUp: function (ev) {
|
|
// If we're filtering the auto complete list, update the UI with our updated word
|
|
if (this.autocomplete.open && this.autocomplete.filter_list) {
|
|
var $inp = $(ev.currentTarget);
|
|
var tokens = $inp.val().trim().substring(0, $inp[0].selectionStart).split(' ');
|
|
this.autocomplete.update(tokens[tokens.length - 1]);
|
|
}
|
|
},
|
|
|
|
inputKeyDown: function (ev) {
|
|
var that = this,
|
|
inp = $(ev.currentTarget),
|
|
inp_val = inp.val(),
|
|
meta;
|
|
|
|
if (navigator.appVersion.indexOf("Mac") !== -1) {
|
|
meta = ev.metaKey;
|
|
} else {
|
|
meta = ev.altKey;
|
|
}
|
|
|
|
if (this.autocomplete.open) {
|
|
// A return value of true = dont process any other keys
|
|
if (this.autocomplete.onKeyDown(ev)) {
|
|
return;
|
|
}
|
|
}
|
|
|
|
switch (true) {
|
|
case (ev.keyCode === 13): // return
|
|
inp_val = inp_val.trim();
|
|
|
|
if (inp_val) {
|
|
$.each(inp_val.split('\n'), function (idx, line) {
|
|
try {
|
|
that.processInput(line);
|
|
} catch (err) {
|
|
window.console && console.error(err);
|
|
}
|
|
});
|
|
|
|
this.buffer.push(inp_val);
|
|
this.buffer_pos = this.buffer.length;
|
|
}
|
|
|
|
inp.val('');
|
|
|
|
// The auto complete may not have thrown a match if it was empty, so
|
|
// just make sure it's closed
|
|
if (this.autocomplete.open) {
|
|
this.autocomplete.close();
|
|
}
|
|
|
|
return false;
|
|
|
|
break;
|
|
|
|
case (ev.keyCode === 38): // up
|
|
if (this.buffer_pos > 0) {
|
|
this.buffer_pos--;
|
|
inp.val(this.buffer[this.buffer_pos]);
|
|
}
|
|
//suppress browsers default behavior as it would set the cursor at the beginning
|
|
return false;
|
|
|
|
case (ev.keyCode === 40): // down
|
|
if (this.buffer_pos < this.buffer.length) {
|
|
this.buffer_pos++;
|
|
inp.val(this.buffer[this.buffer_pos]);
|
|
}
|
|
break;
|
|
|
|
case (ev.keyCode === 219 && meta): // [ + meta
|
|
// Find all the tab elements and get the index of the active tab
|
|
var $tabs = $('#kiwi .tabs').find('li[class!=connection]');
|
|
var cur_tab_ind = (function() {
|
|
for (var idx=0; idx<$tabs.length; idx++){
|
|
if ($($tabs[idx]).hasClass('active'))
|
|
return idx;
|
|
}
|
|
})();
|
|
|
|
// Work out the previous tab along. Wrap around if needed
|
|
if (cur_tab_ind === 0) {
|
|
$prev_tab = $($tabs[$tabs.length - 1]);
|
|
} else {
|
|
$prev_tab = $($tabs[cur_tab_ind - 1]);
|
|
}
|
|
|
|
$prev_tab.click();
|
|
return false;
|
|
|
|
case (ev.keyCode === 221 && meta): // ] + meta
|
|
// Find all the tab elements and get the index of the active tab
|
|
var $tabs = $('#kiwi .tabs').find('li[class!=connection]');
|
|
var cur_tab_ind = (function() {
|
|
for (var idx=0; idx<$tabs.length; idx++){
|
|
if ($($tabs[idx]).hasClass('active'))
|
|
return idx;
|
|
}
|
|
})();
|
|
|
|
// Work out the next tab along. Wrap around if needed
|
|
if (cur_tab_ind === $tabs.length - 1) {
|
|
$next_tab = $($tabs[0]);
|
|
} else {
|
|
$next_tab = $($tabs[cur_tab_ind + 1]);
|
|
}
|
|
|
|
$next_tab.click();
|
|
return false;
|
|
|
|
case (ev.keyCode === 9 //Check if ONLY tab is pressed
|
|
&& !ev.shiftKey //(user could be using some browser
|
|
&& !ev.altKey //keyboard shortcut)
|
|
&& !ev.metaKey
|
|
&& !ev.ctrlKey):
|
|
|
|
ev.preventDefault();
|
|
|
|
// Get possible autocompletions
|
|
var autocomplete_list = [],
|
|
members = _kiwi.app.panels().active.get('members');
|
|
|
|
if (members) {
|
|
members.forEach(function (member) {
|
|
if (!member) return;
|
|
autocomplete_list.push({match: [member.get('nick')], type: 'nick'});
|
|
});
|
|
}
|
|
|
|
// Add this channels name into the auto complete list
|
|
autocomplete_list.push(_kiwi.app.panels().active.get('name'));
|
|
|
|
// Sort what we have alphabetically
|
|
autocomplete_list = _.sortBy(autocomplete_list, function (entry) {
|
|
// Nicks have a .type property of 'nick'
|
|
return entry.type === 'nick' ?
|
|
entry.match[0].toLowerCase() :
|
|
entry.toLowerCase();
|
|
});
|
|
|
|
this.showAutocomplete(autocomplete_list, 'nicks');
|
|
|
|
break;
|
|
|
|
case (ev.keyCode === 191 && inp_val === ''): // Forward slash in an empty box
|
|
this.showAutocomplete(this.autocomplete_command_list, 'command', true);
|
|
break;
|
|
}
|
|
},
|
|
|
|
|
|
setAutoCompleteCommands: function(commands) {
|
|
_.each(commands, function(command) {
|
|
this.autocomplete_command_list.push({
|
|
match: command.matches || [],
|
|
description: command.description
|
|
});
|
|
}, this);
|
|
|
|
/*
|
|
var command_list = [
|
|
{match: ['/join'], description: 'Join or start a channel'},
|
|
{match: ['/part', '/leave'], description: 'Leave the channel'},
|
|
{match: ['/me', '/action'], description: 'Do something physical'},
|
|
{match: ['/nick'], description: 'Change your nickname'},
|
|
{match: ['/topic'], description: 'Set the topic for the channel'},
|
|
];
|
|
*/
|
|
},
|
|
|
|
|
|
inputBlur: function(event) {
|
|
// IE hack. Mouse down on auto complete UI sets cancel_blur so we don't loose
|
|
// focus here.
|
|
if (this.autocomplete.cancel_blur) {
|
|
delete this.autocomplete.cancel_blur;
|
|
return;
|
|
}
|
|
|
|
this.autocomplete.cancel('lost_focus');
|
|
},
|
|
|
|
|
|
processInput: function (command_raw) {
|
|
var that = this,
|
|
command, params, events_data,
|
|
pre_processed;
|
|
|
|
// If sending a message when not in a channel or query window, automatically
|
|
// convert it into a command
|
|
if (command_raw[0] !== '/' && !_kiwi.app.panels().active.isChannel() && !_kiwi.app.panels().active.isQuery()) {
|
|
command_raw = '/' + command_raw;
|
|
}
|
|
|
|
// The default command
|
|
if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') {
|
|
// Remove any slash escaping at the start (ie. //)
|
|
command_raw = command_raw.replace(/^\/\//, '/');
|
|
|
|
// Prepend the default command
|
|
command_raw = '/msg ' + _kiwi.app.panels().active.get('name') + ' ' + command_raw;
|
|
}
|
|
|
|
// Process the raw command for any aliases
|
|
this.preprocessor.vars.server = _kiwi.app.connections.active_connection.get('name');
|
|
this.preprocessor.vars.channel = _kiwi.app.panels().active.get('name');
|
|
this.preprocessor.vars.destination = this.preprocessor.vars.channel;
|
|
command_raw = this.preprocessor.process(command_raw);
|
|
|
|
// Extract the command and parameters
|
|
params = command_raw.split(/\s/);
|
|
if (params[0][0] === '/') {
|
|
command = params[0].substr(1).toLowerCase();
|
|
params = params.splice(1, params.length - 1);
|
|
} else {
|
|
// Default command
|
|
command = 'msg';
|
|
params.unshift(_kiwi.app.panels().active.get('name'));
|
|
}
|
|
|
|
// Emit a plugin event for any modifications
|
|
events_data = {command: command, params: params};
|
|
|
|
_kiwi.global.events.emit('command', events_data)
|
|
.then(function() {
|
|
// Trigger the command events
|
|
that.trigger('command', {command: events_data.command, params: events_data.params});
|
|
that.trigger('command:' + events_data.command, {command: events_data.command, params: events_data.params});
|
|
|
|
// If we didn't have any listeners for this event, fire a special case
|
|
// TODO: This feels dirty. Should this really be done..?
|
|
if (!that._events['command:' + events_data.command]) {
|
|
that.trigger('unknown_command', {command: events_data.command, params: events_data.params});
|
|
}
|
|
});
|
|
},
|
|
|
|
|
|
showAutocomplete: function(list, type, filter_list) {
|
|
var $inp = this.$('.inp'),
|
|
tokens = $inp.val().trim().substring(0, $inp[0].selectionStart).split(' ');
|
|
|
|
this.autocomplete_token_idx = tokens.length - 1;
|
|
this.autocomplete_before = {
|
|
value: $inp.val(),
|
|
caret_pos: $inp[0].selectionStart
|
|
};
|
|
|
|
this.autocomplete.showUi(!!_kiwi.global.settings.get('show_autocomplete_slideout'));
|
|
this.autocomplete.setTitle(type);
|
|
this.autocomplete.setWords(list, filter_list);
|
|
this.autocomplete.update(tokens[tokens.length - 1]);
|
|
this.autocomplete.show();
|
|
},
|
|
|
|
|
|
addPluginIcon: function ($icon) {
|
|
var $tool = $('<div class="tool"></div>').append($icon);
|
|
this.$el.find('.input_tools').append($tool);
|
|
_kiwi.app.view.doLayout();
|
|
}
|
|
});
|
|
|