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