Display any IRC connection errors in the client
[KiwiIRC.git] / client / src / views / controlbox.js
CommitLineData
50ac472f
D
1_kiwi.view.ControlBox = Backbone.View.extend({
2 events: {
3 'keydown .inp': 'process',
4 'click .nick': 'showNickChange'
5 },
6
7 initialize: function () {
8 var that = this;
9
10 this.buffer = []; // Stores previously run commands
11 this.buffer_pos = 0; // The current position in the buffer
12
13 this.preprocessor = new InputPreProcessor();
14 this.preprocessor.recursive_depth = 5;
15
16 // Hold tab autocomplete data
17 this.tabcomplete = {active: false, data: [], prefix: ''};
18
19 // Keep the nick view updated with nick changes
20 _kiwi.app.connections.on('change:nick', function(connection) {
21 // Only update the nick view if it's the active connection
22 if (connection !== _kiwi.app.connections.active_connection)
23 return;
24
25 $('.nick', that.$el).text(connection.get('nick'));
26 });
27
28 // Update our nick view as we flick between connections
29 _kiwi.app.connections.on('active', function(panel, connection) {
30 $('.nick', that.$el).text(connection.get('nick'));
31 });
9076c054
D
32
33 // Keep focus on the input box as we flick between panels
34 _kiwi.app.panels.bind('active', function (active_panel) {
35 if (active_panel.isChannel() || active_panel.isServer() || active_panel.isQuery()) {
36 that.$('.inp').focus();
37 }
38 });
50ac472f
D
39 },
40
41 showNickChange: function (ev) {
4caae868
D
42 // Nick box already open? Don't do it again
43 if (this.nick_change)
44 return;
45
46 this.nick_change = new _kiwi.view.NickChangeBox();
47 this.nick_change.render();
48
49 this.listenTo(this.nick_change, 'close', function() {
50 delete this.nick_change;
51 });
50ac472f
D
52 },
53
54 process: function (ev) {
55 var that = this,
56 inp = $(ev.currentTarget),
57 inp_val = inp.val(),
58 meta;
59
60 if (navigator.appVersion.indexOf("Mac") !== -1) {
61 meta = ev.metaKey;
62 } else {
63 meta = ev.altKey;
64 }
65
66 // If not a tab key, reset the tabcomplete data
67 if (this.tabcomplete.active && ev.keyCode !== 9) {
68 this.tabcomplete.active = false;
69 this.tabcomplete.data = [];
70 this.tabcomplete.prefix = '';
71 }
4caae868 72
50ac472f
D
73 switch (true) {
74 case (ev.keyCode === 13): // return
75 inp_val = inp_val.trim();
76
77 if (inp_val) {
78 $.each(inp_val.split('\n'), function (idx, line) {
79 that.processInput(line);
80 });
81
82 this.buffer.push(inp_val);
83 this.buffer_pos = this.buffer.length;
84 }
85
86 inp.val('');
87 return false;
88
89 break;
90
91 case (ev.keyCode === 38): // up
92 if (this.buffer_pos > 0) {
93 this.buffer_pos--;
94 inp.val(this.buffer[this.buffer_pos]);
95 }
96 //suppress browsers default behavior as it would set the cursor at the beginning
97 return false;
98
99 case (ev.keyCode === 40): // down
100 if (this.buffer_pos < this.buffer.length) {
101 this.buffer_pos++;
102 inp.val(this.buffer[this.buffer_pos]);
103 }
104 break;
105
106 case (ev.keyCode === 219 && meta): // [ + meta
107 // Find all the tab elements and get the index of the active tab
108 var $tabs = $('#kiwi .tabs').find('li[class!=connection]');
109 var cur_tab_ind = (function() {
110 for (var idx=0; idx<$tabs.length; idx++){
111 if ($($tabs[idx]).hasClass('active'))
112 return idx;
113 }
114 })();
115
116 // Work out the previous tab along. Wrap around if needed
117 if (cur_tab_ind === 0) {
118 $prev_tab = $($tabs[$tabs.length - 1]);
119 } else {
120 $prev_tab = $($tabs[cur_tab_ind - 1]);
121 }
122
123 $prev_tab.click();
124 return false;
125
126 case (ev.keyCode === 221 && meta): // ] + meta
127 // Find all the tab elements and get the index of the active tab
128 var $tabs = $('#kiwi .tabs').find('li[class!=connection]');
129 var cur_tab_ind = (function() {
130 for (var idx=0; idx<$tabs.length; idx++){
131 if ($($tabs[idx]).hasClass('active'))
132 return idx;
133 }
134 })();
135
136 // Work out the next tab along. Wrap around if needed
137 if (cur_tab_ind === $tabs.length - 1) {
138 $next_tab = $($tabs[0]);
139 } else {
140 $next_tab = $($tabs[cur_tab_ind + 1]);
141 }
142
143 $next_tab.click();
144 return false;
145
c71ae076 146 case (ev.keyCode === 9 //Check if ONLY tab is pressed
4caae868 147 && !ev.shiftKey //(user could be using some browser
c71ae076 148 && !ev.altKey //keyboard shortcut)
4caae868
D
149 && !ev.metaKey
150 && !ev.ctrlKey):
50ac472f
D
151 this.tabcomplete.active = true;
152 if (_.isEqual(this.tabcomplete.data, [])) {
153 // Get possible autocompletions
154 var ac_data = [],
155 members = _kiwi.app.panels().active.get('members');
156
157 // If we have a members list, get the models. Otherwise empty array
158 members = members ? members.models : [];
159
160 $.each(members, function (i, member) {
161 if (!member) return;
162 ac_data.push(member.get('nick'));
163 });
164
165 ac_data.push(_kiwi.app.panels().active.get('name'));
166
167 ac_data = _.sortBy(ac_data, function (nick) {
168 return nick;
169 });
170 this.tabcomplete.data = ac_data;
171 }
172
173 if (inp_val[inp[0].selectionStart - 1] === ' ') {
174 return false;
175 }
4caae868 176
50ac472f
D
177 (function () {
178 var tokens, // Words before the cursor position
179 val, // New value being built up
4caae868 180 p1, // Position in the value just before the nick
50ac472f
D
181 newnick, // New nick to be displayed (cycles through)
182 range, // TextRange for setting new text cursor position
183 nick, // Current nick in the value
184 trailing = ': '; // Text to be inserted after a tabbed nick
185
186 tokens = inp_val.substring(0, inp[0].selectionStart).split(' ');
187 if (tokens[tokens.length-1] == ':')
188 tokens.pop();
189
9a0720d3
D
190 // Only add the trailing text if not at the beginning of the line
191 if (tokens.length > 1)
192 trailing = '';
193
50ac472f
D
194 nick = tokens[tokens.length - 1];
195
196 if (this.tabcomplete.prefix === '') {
197 this.tabcomplete.prefix = nick;
198 }
199
200 this.tabcomplete.data = _.select(this.tabcomplete.data, function (n) {
201 return (n.toLowerCase().indexOf(that.tabcomplete.prefix.toLowerCase()) === 0);
202 });
203
204 if (this.tabcomplete.data.length > 0) {
205 // Get the current value before cursor position
206 p1 = inp[0].selectionStart - (nick.length);
207 val = inp_val.substr(0, p1);
208
209 // Include the current selected nick
210 newnick = this.tabcomplete.data.shift();
211 this.tabcomplete.data.push(newnick);
212 val += newnick;
213
214 if (inp_val.substr(inp[0].selectionStart, 2) !== trailing)
215 val += trailing;
216
217 // Now include the rest of the current value
218 val += inp_val.substr(inp[0].selectionStart);
219
220 inp.val(val);
221
222 // Move the cursor position to the end of the nick
223 if (inp[0].setSelectionRange) {
224 inp[0].setSelectionRange(p1 + newnick.length + trailing.length, p1 + newnick.length + trailing.length);
225 } else if (inp[0].createTextRange) { // not sure if this bit is actually needed....
226 range = inp[0].createTextRange();
227 range.collapse(true);
228 range.moveEnd('character', p1 + newnick.length + trailing.length);
229 range.moveStart('character', p1 + newnick.length + trailing.length);
230 range.select();
231 }
232 }
233 }).apply(this);
234 return false;
235 }
236 },
237
238
239 processInput: function (command_raw) {
240 var command, params,
241 pre_processed;
4caae868 242
039146ea
D
243 // If sending a message when not in a channel or query window, automatically
244 // convert it into a command
245 if (command_raw[0] !== '/' && !_kiwi.app.panels().active.isChannel() && !_kiwi.app.panels().active.isQuery()) {
246 command_raw = '/' + command_raw;
247 }
248
50ac472f
D
249 // The default command
250 if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') {
19318643
VC
251 // Remove any slash escaping at the start (ie. //)
252 command_raw = command_raw.replace(/^\/\//, '/');
50ac472f
D
253
254 // Prepend the default command
255 command_raw = '/msg ' + _kiwi.app.panels().active.get('name') + ' ' + command_raw;
256 }
257
258 // Process the raw command for any aliases
259 this.preprocessor.vars.server = _kiwi.app.connections.active_connection.get('name');
260 this.preprocessor.vars.channel = _kiwi.app.panels().active.get('name');
261 this.preprocessor.vars.destination = this.preprocessor.vars.channel;
262 command_raw = this.preprocessor.process(command_raw);
263
264 // Extract the command and parameters
19318643 265 params = command_raw.split(/\s/);
50ac472f
D
266 if (params[0][0] === '/') {
267 command = params[0].substr(1).toLowerCase();
268 params = params.splice(1, params.length - 1);
269 } else {
270 // Default command
271 command = 'msg';
272 params.unshift(_kiwi.app.panels().active.get('name'));
273 }
274
275 // Trigger the command events
276 this.trigger('command', {command: command, params: params});
277 this.trigger('command:' + command, {command: command, params: params});
278
279 // If we didn't have any listeners for this event, fire a special case
280 // TODO: This feels dirty. Should this really be done..?
281 if (!this._events['command:' + command]) {
282 this.trigger('unknown_command', {command: command, params: params});
283 }
284 },
285
286
287 addPluginIcon: function ($icon) {
288 var $tool = $('<div class="tool"></div>').append($icon);
289 this.$el.find('.input_tools').append($tool);
290 _kiwi.app.view.doLayout();
291 }
c71ae076 292});