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 | }); | |
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) === '//') { | |
19318643 VC |
229 | // Remove any slash escaping at the start (ie. //) |
230 | command_raw = command_raw.replace(/^\/\//, '/'); | |
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 | |
19318643 | 243 | params = command_raw.split(/\s/); |
50ac472f D |
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 | }); |