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 | ||
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 | |
50ac472f D |
243 | // The default command |
244 | if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') { | |
19318643 VC |
245 | // Remove any slash escaping at the start (ie. //) |
246 | command_raw = command_raw.replace(/^\/\//, '/'); | |
50ac472f D |
247 | |
248 | // Prepend the default command | |
249 | command_raw = '/msg ' + _kiwi.app.panels().active.get('name') + ' ' + command_raw; | |
250 | } | |
251 | ||
252 | // Process the raw command for any aliases | |
253 | this.preprocessor.vars.server = _kiwi.app.connections.active_connection.get('name'); | |
254 | this.preprocessor.vars.channel = _kiwi.app.panels().active.get('name'); | |
255 | this.preprocessor.vars.destination = this.preprocessor.vars.channel; | |
256 | command_raw = this.preprocessor.process(command_raw); | |
257 | ||
258 | // Extract the command and parameters | |
19318643 | 259 | params = command_raw.split(/\s/); |
50ac472f D |
260 | if (params[0][0] === '/') { |
261 | command = params[0].substr(1).toLowerCase(); | |
262 | params = params.splice(1, params.length - 1); | |
263 | } else { | |
264 | // Default command | |
265 | command = 'msg'; | |
266 | params.unshift(_kiwi.app.panels().active.get('name')); | |
267 | } | |
268 | ||
269 | // Trigger the command events | |
270 | this.trigger('command', {command: command, params: params}); | |
271 | this.trigger('command:' + command, {command: command, params: params}); | |
272 | ||
273 | // If we didn't have any listeners for this event, fire a special case | |
274 | // TODO: This feels dirty. Should this really be done..? | |
275 | if (!this._events['command:' + command]) { | |
276 | this.trigger('unknown_command', {command: command, params: params}); | |
277 | } | |
278 | }, | |
279 | ||
280 | ||
281 | addPluginIcon: function ($icon) { | |
282 | var $tool = $('<div class="tool"></div>').append($icon); | |
283 | this.$el.find('.input_tools').append($tool); | |
284 | _kiwi.app.view.doLayout(); | |
285 | } | |
c71ae076 | 286 | }); |