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