1 _kiwi
.view
.ControlBox
= Backbone
.View
.extend({
3 'keydown .inp': 'process',
4 'click .nick': 'showNickChange'
7 initialize: function () {
10 this.buffer
= []; // Stores previously run commands
11 this.buffer_pos
= 0; // The current position in the buffer
13 this.preprocessor
= new InputPreProcessor();
14 this.preprocessor
.recursive_depth
= 5;
16 // Hold tab autocomplete data
17 this.tabcomplete
= {active
: false, data
: [], prefix
: ''};
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
)
25 $('.nick', that
.$el
).text(connection
.get('nick'));
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'));
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();
41 showNickChange: function (ev
) {
42 // Nick box already open? Don't do it again
46 this.nick_change
= new _kiwi
.view
.NickChangeBox();
47 this.nick_change
.render();
49 this.listenTo(this.nick_change
, 'close', function() {
50 delete this.nick_change
;
54 process: function (ev
) {
56 inp
= $(ev
.currentTarget
),
60 if (navigator
.appVersion
.indexOf("Mac") !== -1) {
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
= '';
74 case (ev
.keyCode
=== 13): // return
75 inp_val
= inp_val
.trim();
78 $.each(inp_val
.split('\n'), function (idx
, line
) {
79 that
.processInput(line
);
82 this.buffer
.push(inp_val
);
83 this.buffer_pos
= this.buffer
.length
;
91 case (ev
.keyCode
=== 38): // up
92 if (this.buffer_pos
> 0) {
94 inp
.val(this.buffer
[this.buffer_pos
]);
96 //suppress browsers default behavior as it would set the cursor at the beginning
99 case (ev
.keyCode
=== 40): // down
100 if (this.buffer_pos
< this.buffer
.length
) {
102 inp
.val(this.buffer
[this.buffer_pos
]);
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'))
116 // Work out the previous tab along. Wrap around if needed
117 if (cur_tab_ind
=== 0) {
118 $prev_tab
= $($tabs
[$tabs
.length
- 1]);
120 $prev_tab
= $($tabs
[cur_tab_ind
- 1]);
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'))
136 // Work out the next tab along. Wrap around if needed
137 if (cur_tab_ind
=== $tabs
.length
- 1) {
138 $next_tab
= $($tabs
[0]);
140 $next_tab
= $($tabs
[cur_tab_ind
+ 1]);
146 case (ev
.keyCode
=== 9 //Check if ONLY tab is pressed
147 && !ev
.shiftKey
//(user could be using some browser
148 && !ev
.altKey
//keyboard shortcut)
151 this.tabcomplete
.active
= true;
152 if (_
.isEqual(this.tabcomplete
.data
, [])) {
153 // Get possible autocompletions
155 members
= _kiwi
.app
.panels().active
.get('members');
157 // If we have a members list, get the models. Otherwise empty array
158 members
= members
? members
.models
: [];
160 $.each(members
, function (i
, member
) {
162 ac_data
.push(member
.get('nick'));
165 ac_data
.push(_kiwi
.app
.panels().active
.get('name'));
167 ac_data
= _
.sortBy(ac_data
, function (nick
) {
170 this.tabcomplete
.data
= ac_data
;
173 if (inp_val
[inp
[0].selectionStart
- 1] === ' ') {
178 var tokens
, // Words before the cursor position
179 val
, // New value being built up
180 p1
, // Position in the value just before the nick
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
186 tokens
= inp_val
.substring(0, inp
[0].selectionStart
).split(' ');
187 if (tokens
[tokens
.length
-1] == ':')
190 // Only add the trailing text if not at the beginning of the line
191 if (tokens
.length
> 1)
194 nick
= tokens
[tokens
.length
- 1];
196 if (this.tabcomplete
.prefix
=== '') {
197 this.tabcomplete
.prefix
= nick
;
200 this.tabcomplete
.data
= _
.select(this.tabcomplete
.data
, function (n
) {
201 return (n
.toLowerCase().indexOf(that
.tabcomplete
.prefix
.toLowerCase()) === 0);
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
);
209 // Include the current selected nick
210 newnick
= this.tabcomplete
.data
.shift();
211 this.tabcomplete
.data
.push(newnick
);
214 if (inp_val
.substr(inp
[0].selectionStart
, 2) !== trailing
)
217 // Now include the rest of the current value
218 val
+= inp_val
.substr(inp
[0].selectionStart
);
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
);
239 processInput: function (command_raw
) {
243 // The default command
244 if (command_raw
[0] !== '/' || command_raw
.substr(0, 2) === '//') {
245 // Remove any slash escaping at the start (ie. //)
246 command_raw
= command_raw
.replace(/^\/\//, '/');
248 // Prepend the default command
249 command_raw
= '/msg ' + _kiwi
.app
.panels().active
.get('name') + ' ' + command_raw
;
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
);
258 // Extract the command and parameters
259 params
= command_raw
.split(/\s/);
260 if (params
[0][0] === '/') {
261 command
= params
[0].substr(1).toLowerCase();
262 params
= params
.splice(1, params
.length
- 1);
266 params
.unshift(_kiwi
.app
.panels().active
.get('name'));
269 // Trigger the command events
270 this.trigger('command', {command
: command
, params
: params
});
271 this.trigger('command:' + command
, {command
: command
, params
: params
});
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
});
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();