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 | ||
130 | case (ev.keyCode === 9): // tab | |
131 | this.tabcomplete.active = true; | |
132 | if (_.isEqual(this.tabcomplete.data, [])) { | |
133 | // Get possible autocompletions | |
134 | var ac_data = [], | |
135 | members = _kiwi.app.panels().active.get('members'); | |
136 | ||
137 | // If we have a members list, get the models. Otherwise empty array | |
138 | members = members ? members.models : []; | |
139 | ||
140 | $.each(members, function (i, member) { | |
141 | if (!member) return; | |
142 | ac_data.push(member.get('nick')); | |
143 | }); | |
144 | ||
145 | ac_data.push(_kiwi.app.panels().active.get('name')); | |
146 | ||
147 | ac_data = _.sortBy(ac_data, function (nick) { | |
148 | return nick; | |
149 | }); | |
150 | this.tabcomplete.data = ac_data; | |
151 | } | |
152 | ||
153 | if (inp_val[inp[0].selectionStart - 1] === ' ') { | |
154 | return false; | |
155 | } | |
156 | ||
157 | (function () { | |
158 | var tokens, // Words before the cursor position | |
159 | val, // New value being built up | |
160 | p1, // Position in the value just before the nick | |
161 | newnick, // New nick to be displayed (cycles through) | |
162 | range, // TextRange for setting new text cursor position | |
163 | nick, // Current nick in the value | |
164 | trailing = ': '; // Text to be inserted after a tabbed nick | |
165 | ||
166 | tokens = inp_val.substring(0, inp[0].selectionStart).split(' '); | |
167 | if (tokens[tokens.length-1] == ':') | |
168 | tokens.pop(); | |
169 | ||
170 | nick = tokens[tokens.length - 1]; | |
171 | ||
172 | if (this.tabcomplete.prefix === '') { | |
173 | this.tabcomplete.prefix = nick; | |
174 | } | |
175 | ||
176 | this.tabcomplete.data = _.select(this.tabcomplete.data, function (n) { | |
177 | return (n.toLowerCase().indexOf(that.tabcomplete.prefix.toLowerCase()) === 0); | |
178 | }); | |
179 | ||
180 | if (this.tabcomplete.data.length > 0) { | |
181 | // Get the current value before cursor position | |
182 | p1 = inp[0].selectionStart - (nick.length); | |
183 | val = inp_val.substr(0, p1); | |
184 | ||
185 | // Include the current selected nick | |
186 | newnick = this.tabcomplete.data.shift(); | |
187 | this.tabcomplete.data.push(newnick); | |
188 | val += newnick; | |
189 | ||
190 | if (inp_val.substr(inp[0].selectionStart, 2) !== trailing) | |
191 | val += trailing; | |
192 | ||
193 | // Now include the rest of the current value | |
194 | val += inp_val.substr(inp[0].selectionStart); | |
195 | ||
196 | inp.val(val); | |
197 | ||
198 | // Move the cursor position to the end of the nick | |
199 | if (inp[0].setSelectionRange) { | |
200 | inp[0].setSelectionRange(p1 + newnick.length + trailing.length, p1 + newnick.length + trailing.length); | |
201 | } else if (inp[0].createTextRange) { // not sure if this bit is actually needed.... | |
202 | range = inp[0].createTextRange(); | |
203 | range.collapse(true); | |
204 | range.moveEnd('character', p1 + newnick.length + trailing.length); | |
205 | range.moveStart('character', p1 + newnick.length + trailing.length); | |
206 | range.select(); | |
207 | } | |
208 | } | |
209 | }).apply(this); | |
210 | return false; | |
211 | } | |
212 | }, | |
213 | ||
214 | ||
215 | processInput: function (command_raw) { | |
216 | var command, params, | |
217 | pre_processed; | |
218 | ||
219 | // The default command | |
220 | if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') { | |
221 | // Remove any slash escaping at the start (ie. //) | |
222 | command_raw = command_raw.replace(/^\/\//, '/'); | |
223 | ||
224 | // Prepend the default command | |
225 | command_raw = '/msg ' + _kiwi.app.panels().active.get('name') + ' ' + command_raw; | |
226 | } | |
227 | ||
228 | // Process the raw command for any aliases | |
229 | this.preprocessor.vars.server = _kiwi.app.connections.active_connection.get('name'); | |
230 | this.preprocessor.vars.channel = _kiwi.app.panels().active.get('name'); | |
231 | this.preprocessor.vars.destination = this.preprocessor.vars.channel; | |
232 | command_raw = this.preprocessor.process(command_raw); | |
233 | ||
234 | // Extract the command and parameters | |
235 | params = command_raw.split(' '); | |
236 | if (params[0][0] === '/') { | |
237 | command = params[0].substr(1).toLowerCase(); | |
238 | params = params.splice(1, params.length - 1); | |
239 | } else { | |
240 | // Default command | |
241 | command = 'msg'; | |
242 | params.unshift(_kiwi.app.panels().active.get('name')); | |
243 | } | |
244 | ||
245 | // Trigger the command events | |
246 | this.trigger('command', {command: command, params: params}); | |
247 | this.trigger('command:' + command, {command: command, params: params}); | |
248 | ||
249 | // If we didn't have any listeners for this event, fire a special case | |
250 | // TODO: This feels dirty. Should this really be done..? | |
251 | if (!this._events['command:' + command]) { | |
252 | this.trigger('unknown_command', {command: command, params: params}); | |
253 | } | |
254 | }, | |
255 | ||
256 | ||
257 | addPluginIcon: function ($icon) { | |
258 | var $tool = $('<div class="tool"></div>').append($icon); | |
259 | this.$el.find('.input_tools').append($tool); | |
260 | _kiwi.app.view.doLayout(); | |
261 | } | |
262 | }); |