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