Server: NOTICE sending correct parameters
[KiwiIRC.git] / client / assets / dev / view.js
CommitLineData
696a66f8
D
1/*jslint white:true, regexp: true, nomen: true, devel: true, undef: true, browser: true, continue: true, sloppy: true, forin: true, newcap: true, plusplus: true, maxerr: 50, indent: 4 */\r
2/*global kiwi */\r
3\r
eaaf73b0 4_kiwi.view.MemberList = Backbone.View.extend({\r
696a66f8
D
5 tagName: "ul",\r
6 events: {\r
7 "click .nick": "nickClick"\r
8 },\r
9 initialize: function (options) {\r
10 this.model.bind('all', this.render, this);\r
11 $(this.el).appendTo('#memberlists');\r
12 },\r
13 render: function () {\r
14 var $this = $(this.el);\r
15 $this.empty();\r
16 this.model.forEach(function (member) {\r
9d5e7df8
D
17 var prefix_css_class = (member.get('modes') || []).join(' ');\r
18 $('<li class="mode ' + prefix_css_class + '"><a class="nick"><span class="prefix">' + member.get("prefix") + '</span>' + member.get("nick") + '</a></li>')\r
696a66f8
D
19 .appendTo($this)\r
20 .data('member', member);\r
21 });\r
22 },\r
23 nickClick: function (x) {\r
495a8dc7
D
24 var $target = $(x.currentTarget).parent('li'),\r
25 member = $target.data('member'),\r
26 userbox;\r
696a66f8 27 \r
495a8dc7
D
28 // If the userbox already exists here, hide it\r
29 if ($target.find('.userbox').length > 0) {\r
30 $('.userbox', this.$el).remove();\r
31 return;\r
32 }\r
33\r
34 userbox = new _kiwi.view.UserBox();\r
696a66f8 35 userbox.member = member;\r
495a8dc7
D
36\r
37 // Remove any existing userboxes\r
696a66f8 38 $('.userbox', this.$el).remove();\r
648e2c01 39\r
495a8dc7 40 $target.append(userbox.$el);\r
696a66f8
D
41 },\r
42 show: function () {\r
43 $('#memberlists').children().removeClass('active');\r
44 $(this.el).addClass('active');\r
45 }\r
46});\r
47\r
48\r
49\r
eaaf73b0 50_kiwi.view.UserBox = Backbone.View.extend({\r
696a66f8
D
51 events: {\r
52 'click .query': 'queryClick',\r
53 'click .info': 'infoClick',\r
54 'click .slap': 'slapClick'\r
55 },\r
56\r
57 initialize: function () {\r
58 this.$el = $($('#tmpl_userbox').html());\r
59 },\r
60\r
61 queryClick: function (event) {\r
eaaf73b0
D
62 var panel = new _kiwi.model.Query({name: this.member.get('nick')});\r
63 _kiwi.app.panels.add(panel);\r
696a66f8
D
64 panel.view.show();\r
65 },\r
66\r
67 infoClick: function (event) {\r
eaaf73b0 68 _kiwi.app.controlbox.processInput('/whois ' + this.member.get('nick'));\r
696a66f8
D
69 },\r
70\r
71 slapClick: function (event) {\r
eaaf73b0 72 _kiwi.app.controlbox.processInput('/slap ' + this.member.get('nick'));\r
696a66f8
D
73 }\r
74});\r
75\r
eaaf73b0 76_kiwi.view.NickChangeBox = Backbone.View.extend({\r
696a66f8
D
77 events: {\r
78 'submit': 'changeNick',\r
79 'click .cancel': 'close'\r
80 },\r
81 \r
82 initialize: function () {\r
83 this.$el = $($('#tmpl_nickchange').html());\r
84 },\r
85 \r
86 render: function () {\r
87 // Add the UI component and give it focus\r
eaaf73b0 88 _kiwi.app.controlbox.$el.prepend(this.$el);\r
696a66f8
D
89 this.$el.find('input').focus();\r
90\r
eaaf73b0 91 this.$el.css('bottom', _kiwi.app.controlbox.$el.outerHeight(true));\r
696a66f8
D
92 },\r
93 \r
94 close: function () {\r
95 this.$el.remove();\r
96\r
97 },\r
98\r
99 changeNick: function (event) {\r
100 var that = this;\r
eaaf73b0 101 _kiwi.gateway.changeNick(this.$el.find('input').val(), function (err, val) {\r
696a66f8
D
102 that.close();\r
103 });\r
104 return false;\r
105 }\r
106});\r
107\r
eaaf73b0 108_kiwi.view.ServerSelect = function () {\r
696a66f8
D
109 // Are currently showing all the controlls or just a nick_change box?\r
110 var state = 'all';\r
111\r
112 var model = Backbone.View.extend({\r
113 events: {\r
114 'submit form': 'submitForm',\r
bac3c32e
D
115 'click .show_more': 'showMore',\r
116 'change .have_pass input': 'showPass'\r
696a66f8
D
117 },\r
118\r
119 initialize: function () {\r
3adc7463
D
120 var that = this;\r
121\r
696a66f8
D
122 this.$el = $($('#tmpl_server_select').html());\r
123\r
93e84f75
D
124 // Remove the 'more' link if the server has disabled server changing\r
125 if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {\r
126 if (!_kiwi.app.server_settings.connection.allow_change) {\r
127 this.$el.find('.show_more').remove();\r
128 this.$el.addClass('single_server');\r
129 }\r
130 }\r
131\r
132\r
eaaf73b0
D
133 _kiwi.gateway.bind('onconnect', this.networkConnected, this);\r
134 _kiwi.gateway.bind('connecting', this.networkConnecting, this);\r
696a66f8 135\r
eaaf73b0 136 _kiwi.gateway.bind('onirc_error', function (data) {\r
696a66f8
D
137 $('button', this.$el).attr('disabled', null);\r
138\r
139 if (data.error == 'nickname_in_use') {\r
140 this.setStatus('Nickname already taken');\r
141 this.show('nick_change');\r
142 }\r
3adc7463
D
143\r
144 if (data.error == 'password_mismatch') {\r
145 this.setStatus('Incorrect Password');\r
146 this.show('nick_change');\r
147 that.$el.find('.password').select();\r
148 }\r
696a66f8
D
149 }, this);\r
150 },\r
151\r
152 submitForm: function (event) {\r
848e2dca
D
153 event.preventDefault();\r
154\r
155 // Make sure a nick is chosen\r
156 if (!$('input.nick', this.$el).val().trim()) {\r
157 this.setStatus('Select a nickname first!');\r
158 $('input.nick', this.$el).select();\r
159 return;\r
160 }\r
161\r
696a66f8
D
162 if (state === 'nick_change') {\r
163 this.submitNickChange(event);\r
164 } else {\r
165 this.submitLogin(event);\r
166 }\r
167\r
168 $('button', this.$el).attr('disabled', 1);\r
848e2dca 169 return;\r
696a66f8
D
170 },\r
171\r
172 submitLogin: function (event) {\r
173 // If submitting is disabled, don't do anything\r
174 if ($('button', this.$el).attr('disabled')) return;\r
175 \r
176 var values = {\r
bac3c32e
D
177 nick: $('input.nick', this.$el).val(),\r
178 server: $('input.server', this.$el).val(),\r
179 port: $('input.port', this.$el).val(),\r
180 ssl: $('input.ssl', this.$el).prop('checked'),\r
181 password: $('input.password', this.$el).val(),\r
182 channel: $('input.channel', this.$el).val(),\r
183 channel_key: $('input.channel_key', this.$el).val()\r
696a66f8
D
184 };\r
185\r
186 this.trigger('server_connect', values);\r
187 },\r
188\r
189 submitNickChange: function (event) {\r
bac3c32e 190 _kiwi.gateway.changeNick($('input.nick', this.$el).val());\r
696a66f8
D
191 this.networkConnecting();\r
192 },\r
193\r
bac3c32e
D
194 showPass: function (event) {\r
195 if (this.$el.find('tr.have_pass input').is(':checked')) {\r
196 this.$el.find('tr.pass').show().find('input').focus();\r
197 } else {\r
198 this.$el.find('tr.pass').hide().find('input').val('');\r
199 }\r
200 },\r
201\r
696a66f8
D
202 showMore: function (event) {\r
203 $('.more', this.$el).slideDown('fast');\r
bac3c32e 204 $('input.server', this.$el).select();\r
696a66f8
D
205 },\r
206\r
207 populateFields: function (defaults) {\r
2f54e55e 208 var nick, server, port, channel, channel_key, ssl, password;\r
696a66f8
D
209\r
210 defaults = defaults || {};\r
211\r
212 nick = defaults.nick || '';\r
213 server = defaults.server || '';\r
214 port = defaults.port || 6667;\r
215 ssl = defaults.ssl || 0;\r
216 password = defaults.password || '';\r
217 channel = defaults.channel || '';\r
2f54e55e 218 channel_key = defaults.channel_key || '';\r
696a66f8 219\r
bac3c32e
D
220 $('input.nick', this.$el).val(nick);\r
221 $('input.server', this.$el).val(server);\r
222 $('input.port', this.$el).val(port);\r
223 $('input.ssl', this.$el).prop('checked', ssl);\r
224 $('input.password', this.$el).val(password);\r
225 $('input.channel', this.$el).val(channel);\r
226 $('input.channel_key', this.$el).val(channel_key);\r
696a66f8
D
227 },\r
228\r
229 hide: function () {\r
230 this.$el.slideUp();\r
231 },\r
232\r
233 show: function (new_state) {\r
234 new_state = new_state || 'all';\r
235\r
236 this.$el.show();\r
237\r
238 if (new_state === 'all') {\r
239 $('.show_more', this.$el).show();\r
240\r
241 } else if (new_state === 'more') {\r
242 $('.more', this.$el).slideDown('fast');\r
243\r
244 } else if (new_state === 'nick_change') {\r
245 $('.more', this.$el).hide();\r
246 $('.show_more', this.$el).hide();\r
bac3c32e 247 $('input.nick', this.$el).select();\r
696a66f8
D
248 }\r
249\r
250 state = new_state;\r
251 },\r
252\r
253 setStatus: function (text, class_name) {\r
254 $('.status', this.$el)\r
255 .text(text)\r
256 .attr('class', 'status')\r
848e2dca 257 .addClass(class_name||'')\r
696a66f8
D
258 .show();\r
259 },\r
260 clearStatus: function () {\r
261 $('.status', this.$el).hide();\r
262 },\r
263\r
264 networkConnected: function (event) {\r
265 this.setStatus('Connected :)', 'ok');\r
266 $('form', this.$el).hide();\r
267 },\r
268\r
269 networkConnecting: function (event) {\r
270 this.setStatus('Connecting..', 'ok');\r
271 },\r
272\r
273 showError: function (event) {\r
274 this.setStatus('Error connecting', 'error');\r
275 $('button', this.$el).attr('disabled', null);\r
276 this.show();\r
277 }\r
278 });\r
279\r
280\r
281 return new model(arguments);\r
282};\r
283\r
284\r
eaaf73b0 285_kiwi.view.Panel = Backbone.View.extend({\r
696a66f8
D
286 tagName: "div",\r
287 className: "messages",\r
288 events: {\r
0197b21c 289 "click .chan": "chanClick",\r
04d615ad 290 'click .media .open': 'mediaClick',\r
0197b21c
D
291 'mouseenter .msg .nick': 'msgEnter',\r
292 'mouseleave .msg .nick': 'msgLeave'\r
696a66f8
D
293 },\r
294\r
295 initialize: function (options) {\r
296 this.initializePanel(options);\r
297 },\r
298\r
299 initializePanel: function (options) {\r
300 this.$el.css('display', 'none');\r
301 options = options || {};\r
302\r
303 // Containing element for this panel\r
304 if (options.container) {\r
305 this.$container = $(options.container);\r
306 } else {\r
307 this.$container = $('#panels .container1');\r
308 }\r
309\r
310 this.$el.appendTo(this.$container);\r
311\r
312 this.alert_level = 0;\r
313\r
314 this.model.bind('msg', this.newMsg, this);\r
315 this.msg_count = 0;\r
316\r
317 this.model.set({"view": this}, {"silent": true});\r
318 },\r
319\r
320 render: function () {\r
648e2c01
D
321 var that = this;\r
322\r
696a66f8 323 this.$el.empty();\r
648e2c01
D
324 _.each(this.model.get('scrollback'), function (msg) {\r
325 that.newMsg(msg);\r
326 });\r
696a66f8 327 },\r
648e2c01 328\r
696a66f8 329 newMsg: function (msg) {\r
696a66f8 330 var re, line_msg, $this = this.$el,\r
bf3bd4e5
D
331 nick_colour_hex, nick_hex, is_highlight, msg_css_classes = '';\r
332\r
333 // Nick highlight detecting\r
334 if ((new RegExp('\\b' + _kiwi.gateway.get('nick') + '\\b', 'i')).test(msg.msg)) {\r
335 is_highlight = true;\r
336 msg_css_classes += ' highlight';\r
337 }\r
696a66f8
D
338\r
339 // Escape any HTML that may be in here\r
340 msg.msg = $('<div />').text(msg.msg).html();\r
341\r
342 // Make the channels clickable\r
04d615ad 343 re = new RegExp('(?:^|\\s)([' + _kiwi.gateway.get('channel_prefix') + '][^ ,.\\007]+)', 'g');\r
696a66f8 344 msg.msg = msg.msg.replace(re, function (match) {\r
04d615ad 345 return '<a class="chan" data-channel="' + match.trim() + '">' + match + '</a>';\r
696a66f8
D
346 });\r
347\r
348\r
04d615ad 349 // Parse any links found\r
d064a195 350 msg.msg = msg.msg.replace(/(([A-Za-z0-9\-]+\:\/\/)|(www\.))([\w.\-]+)([a-zA-Z]{2,6})(:[0-9]+)?(\/[\w#!:.?$'()[\]*,;~+=&%@!\-\/]*)?/gi, function (url) {\r
8e8c21e1
D
351 var nice = url,\r
352 extra_html = '';\r
696a66f8 353\r
04d615ad 354 // Add the http if no protoocol was found\r
696a66f8
D
355 if (url.match(/^www\./)) {\r
356 url = 'http://' + url;\r
357 }\r
358\r
04d615ad 359 // Shorten the displayed URL if it's going to be too long\r
696a66f8
D
360 if (nice.length > 100) {\r
361 nice = nice.substr(0, 100) + '...';\r
362 }\r
363\r
04d615ad
D
364 // Get any media HTML if supported\r
365 extra_html = _kiwi.view.MediaMessage.buildHtml(url);\r
366\r
367 // Make the link clickable\r
368 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '</a> ' + extra_html;\r
696a66f8
D
369 });\r
370\r
371\r
372 // Convert IRC formatting into HTML formatting\r
373 msg.msg = formatIRCMsg(msg.msg);\r
374\r
375\r
376 // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)\r
377 nick_colour_hex = (function (nick) {\r
378 var nick_int = 0, rgb;\r
379\r
380 _.map(nick.split(''), function (i) { nick_int += i.charCodeAt(0); });\r
381 rgb = hsl2rgb(nick_int % 255, 70, 35);\r
382 rgb = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16);\r
383\r
384 return '#' + rgb.toString(16);\r
385 })(msg.nick);\r
386\r
387 msg.nick_style = 'color:' + nick_colour_hex + ';';\r
388\r
0197b21c
D
389 // Generate a hex string from the nick to be used as a CSS class name\r
390 nick_hex = msg.nick_css_class = '';\r
391 if (msg.nick) {\r
392 _.map(msg.nick.split(''), function (char) {\r
393 nick_hex += char.charCodeAt(0).toString(16);\r
394 });\r
bf3bd4e5 395 msg_css_classes += ' nick_' + nick_hex;\r
0197b21c
D
396 }\r
397\r
696a66f8 398 // Build up and add the line\r
bf3bd4e5
D
399 msg.msg_css_classes = msg_css_classes;\r
400 line_msg = '<div class="msg <%= type %> <%= msg_css_classes %>"><div class="time"><%- time %></div><div class="nick" style="<%= nick_style %>"><%- nick %></div><div class="text" style="<%= style %>"><%= msg %> </div></div>';\r
696a66f8
D
401 $this.append(_.template(line_msg, msg));\r
402\r
403 // Activity/alerts based on the type of new message\r
404 if (msg.type.match(/^action /)) {\r
405 this.alert('action');\r
3adc7463 406\r
bf3bd4e5 407 } else if (is_highlight) {\r
eaaf73b0 408 _kiwi.app.view.alertWindow('* People are talking!');\r
696a66f8 409 this.alert('highlight');\r
3adc7463 410\r
696a66f8
D
411 } else {\r
412 // If this is the active panel, send an alert out\r
413 if (this.model.isActive()) {\r
eaaf73b0 414 _kiwi.app.view.alertWindow('* People are talking!');\r
696a66f8
D
415 }\r
416 this.alert('activity');\r
417 }\r
418\r
3adc7463
D
419 // Update the activity counters\r
420 (function () {\r
421 // Only inrement the counters if we're not the active panel\r
422 if (this.model.isActive()) return;\r
423\r
424 var $act = this.model.tab.find('.activity');\r
425 $act.text((parseInt($act.text(), 10) || 0) + 1);\r
426 if ($act.text() === '0') {\r
427 $act.addClass('zero');\r
428 } else {\r
429 $act.removeClass('zero');\r
430 }\r
431 }).apply(this);\r
432\r
696a66f8
D
433 this.scrollToBottom();\r
434\r
435 // Make sure our DOM isn't getting too large (Acts as scrollback)\r
436 this.msg_count++;\r
f0999cef 437 if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {\r
696a66f8
D
438 $('.msg:first', this.$el).remove();\r
439 this.msg_count--;\r
440 }\r
441 },\r
442 chanClick: function (event) {\r
443 if (event.target) {\r
04d615ad 444 _kiwi.gateway.join($(event.target).data('channel'));\r
696a66f8
D
445 } else {\r
446 // IE...\r
04d615ad 447 _kiwi.gateway.join($(event.srcElement).data('channel'));\r
696a66f8
D
448 }\r
449 },\r
0197b21c 450\r
04d615ad
D
451 mediaClick: function (event) {\r
452 var $media = $(event.target).parents('.media');\r
453 var media_message;\r
454\r
455 if ($media.data('media')) {\r
456 media_message = $media.data('media');\r
457 } else {\r
458 media_message = new _kiwi.view.MediaMessage({el: $media[0]});\r
459 $media.data('media', media_message);\r
460 }\r
461\r
462 $media.data('media', media_message);\r
463\r
464 media_message.open();\r
465 },\r
466\r
3adc7463 467 // Cursor hovers over a message\r
0197b21c
D
468 msgEnter: function (event) {\r
469 var nick_class;\r
470\r
471 // Find a valid class that this element has\r
472 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {\r
473 if (css_class.match(/^nick_[a-z0-9]+/i)) {\r
474 nick_class = css_class;\r
475 }\r
476 });\r
477\r
478 // If no class was found..\r
479 if (!nick_class) return;\r
480\r
481 $('.'+nick_class).addClass('global_nick_highlight');\r
482 },\r
483\r
3adc7463 484 // Cursor leaves message\r
0197b21c
D
485 msgLeave: function (event) {\r
486 var nick_class;\r
487\r
488 // Find a valid class that this element has\r
489 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {\r
490 if (css_class.match(/^nick_[a-z0-9]+/i)) {\r
491 nick_class = css_class;\r
492 }\r
493 });\r
494\r
495 // If no class was found..\r
496 if (!nick_class) return;\r
497\r
498 $('.'+nick_class).removeClass('global_nick_highlight');\r
499 },\r
500\r
696a66f8
D
501 show: function () {\r
502 var $this = this.$el;\r
503\r
504 // Hide all other panels and show this one\r
505 this.$container.children().css('display', 'none');\r
506 $this.css('display', 'block');\r
507\r
508 // Show this panels memberlist\r
509 var members = this.model.get("members");\r
510 if (members) {\r
e579ff0d 511 $('#memberlists').removeClass('disabled');\r
696a66f8
D
512 members.view.show();\r
513 } else {\r
514 // Memberlist not found for this panel, hide any active ones\r
e579ff0d 515 $('#memberlists').addClass('disabled').children().removeClass('active');\r
696a66f8
D
516 }\r
517\r
eaaf73b0 518 _kiwi.app.view.doLayout();\r
3adc7463
D
519\r
520 // Remove any alerts and activity counters for this panel\r
696a66f8 521 this.alert('none');\r
3adc7463 522 this.model.tab.find('.activity').text('0').addClass('zero');\r
696a66f8
D
523\r
524 this.trigger('active', this.model);\r
3adc7463 525 _kiwi.app.panels.trigger('active', this.model, _kiwi.app.panels.active);\r
aad21f17
D
526\r
527 this.scrollToBottom(true);\r
696a66f8
D
528 },\r
529\r
530\r
531 alert: function (level) {\r
532 // No need to highlight if this si the active panel\r
eaaf73b0 533 if (this.model == _kiwi.app.panels.active) return;\r
696a66f8
D
534\r
535 var types, type_idx;\r
536 types = ['none', 'action', 'activity', 'highlight'];\r
537\r
538 // Default alert level\r
539 level = level || 'none';\r
540\r
541 // If this alert level does not exist, assume clearing current level\r
542 type_idx = _.indexOf(types, level);\r
543 if (!type_idx) {\r
544 level = 'none';\r
545 type_idx = 0;\r
546 }\r
547\r
548 // Only 'upgrade' the alert. Never down (unless clearing)\r
549 if (type_idx !== 0 && type_idx <= this.alert_level) {\r
550 return;\r
551 }\r
552\r
553 // Clear any existing levels\r
554 this.model.tab.removeClass(function (i, css) {\r
555 return (css.match(/\balert_\S+/g) || []).join(' ');\r
556 });\r
557\r
558 // Add the new level if there is one\r
559 if (level !== 'none') {\r
560 this.model.tab.addClass('alert_' + level);\r
561 }\r
562\r
563 this.alert_level = type_idx;\r
564 },\r
565\r
566\r
567 // Scroll to the bottom of the panel\r
aad21f17
D
568 scrollToBottom: function (force_down) {\r
569 // If this isn't the active panel, don't scroll\r
570 if (this.model !== _kiwi.app.panels.active) return;\r
571\r
572 // Don't scroll down if we're scrolled up the panel a little\r
573 if (force_down || this.$container.scrollTop() + this.$container.height() > this.$el.outerHeight() - 150) {\r
574 this.$container[0].scrollTop = this.$container[0].scrollHeight;\r
575 }\r
696a66f8
D
576 }\r
577});\r
578\r
eaaf73b0 579_kiwi.view.Applet = _kiwi.view.Panel.extend({\r
696a66f8
D
580 className: 'applet',\r
581 initialize: function (options) {\r
582 this.initializePanel(options);\r
583 }\r
584});\r
585\r
eaaf73b0 586_kiwi.view.Channel = _kiwi.view.Panel.extend({\r
696a66f8
D
587 initialize: function (options) {\r
588 this.initializePanel(options);\r
589 this.model.bind('change:topic', this.topic, this);\r
567a2f79 590\r
4c2d8d02
D
591 // Only show the loader if this is a channel (ie. not a query)\r
592 if (this.model.isChannel()) {\r
593 this.$el.append('<div class="initial_loader" style="margin:1em;text-align:center;">Joining channel.. <span class="loader"></span></div>');\r
594 }\r
567a2f79
D
595 },\r
596\r
597 // Override the existing newMsg() method to remove the joining channel loader\r
598 newMsg: function () {\r
599 this.$el.find('.initial_loader').slideUp(function () {\r
600 $(this).remove();\r
601 });\r
4c2d8d02 602\r
567a2f79 603 return this.constructor.__super__.newMsg.apply(this, arguments);\r
696a66f8
D
604 },\r
605\r
606 topic: function (topic) {\r
607 if (typeof topic !== 'string' || !topic) {\r
608 topic = this.model.get("topic");\r
609 }\r
610 \r
611 this.model.addMsg('', '== Topic for ' + this.model.get('name') + ' is: ' + topic, 'topic');\r
612\r
613 // If this is the active channel then update the topic bar\r
eaaf73b0
D
614 if (_kiwi.app.panels.active === this) {\r
615 _kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));\r
696a66f8
D
616 }\r
617 }\r
618});\r
619\r
eaaf73b0
D
620// Model for this = _kiwi.model.PanelList\r
621_kiwi.view.Tabs = Backbone.View.extend({\r
696a66f8
D
622 events: {\r
623 'click li': 'tabClick',\r
624 'click li .part': 'partClick'\r
625 },\r
626\r
627 initialize: function () {\r
628 this.model.on("add", this.panelAdded, this);\r
629 this.model.on("remove", this.panelRemoved, this);\r
630 this.model.on("reset", this.render, this);\r
631\r
632 this.model.on('active', this.panelActive, this);\r
633\r
634 this.tabs_applets = $('ul.applets', this.$el);\r
635 this.tabs_msg = $('ul.channels', this.$el);\r
636\r
eaaf73b0 637 _kiwi.gateway.on('change:name', function (gateway, new_val) {\r
696a66f8
D
638 $('span', this.model.server.tab).text(new_val);\r
639 }, this);\r
640 },\r
641 render: function () {\r
642 var that = this;\r
643\r
644 this.tabs_msg.empty();\r
645 \r
646 // Add the server tab first\r
647 this.model.server.tab\r
648 .data('panel_id', this.model.server.cid)\r
649 .appendTo(this.tabs_msg);\r
650\r
651 // Go through each panel adding its tab\r
652 this.model.forEach(function (panel) {\r
653 // If this is the server panel, ignore as it's already added\r
654 if (panel == that.model.server) return;\r
655\r
656 panel.tab\r
657 .data('panel_id', panel.cid)\r
658 .appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);\r
659 });\r
660\r
eaaf73b0 661 _kiwi.app.view.doLayout();\r
696a66f8
D
662 },\r
663\r
664 updateTabTitle: function (panel, new_title) {\r
665 $('span', panel.tab).text(new_title);\r
666 },\r
667\r
668 panelAdded: function (panel) {\r
669 // Add a tab to the panel\r
3adc7463 670 panel.tab = $('<li><span>' + (panel.get('title') || panel.get('name')) + '</span><div class="activity"></div></li>');\r
696a66f8
D
671\r
672 if (panel.isServer()) {\r
673 panel.tab.addClass('server');\r
674 }\r
675\r
676 panel.tab.data('panel_id', panel.cid)\r
677 .appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);\r
678\r
679 panel.bind('change:title', this.updateTabTitle);\r
eaaf73b0 680 _kiwi.app.view.doLayout();\r
696a66f8
D
681 },\r
682 panelRemoved: function (panel) {\r
683 panel.tab.remove();\r
684 delete panel.tab;\r
685\r
eaaf73b0 686 _kiwi.app.view.doLayout();\r
696a66f8
D
687 },\r
688\r
3adc7463 689 panelActive: function (panel, previously_active_panel) {\r
696a66f8
D
690 // Remove any existing tabs or part images\r
691 $('.part', this.$el).remove();\r
692 this.tabs_applets.children().removeClass('active');\r
693 this.tabs_msg.children().removeClass('active');\r
694\r
695 panel.tab.addClass('active');\r
696\r
697 // Only show the part image on non-server tabs\r
698 if (!panel.isServer()) {\r
699 panel.tab.append('<span class="part"></span>');\r
700 }\r
701 },\r
702\r
703 tabClick: function (e) {\r
704 var tab = $(e.currentTarget);\r
705\r
706 var panel = this.model.getByCid(tab.data('panel_id'));\r
707 if (!panel) {\r
708 // A panel wasn't found for this tab... wadda fuck\r
709 return;\r
710 }\r
711\r
712 panel.view.show();\r
713 },\r
714\r
715 partClick: function (e) {\r
716 var tab = $(e.currentTarget).parent();\r
717 var panel = this.model.getByCid(tab.data('panel_id'));\r
718\r
719 // Only need to part if it's a channel\r
720 // If the nicklist is empty, we haven't joined the channel as yet\r
721 if (panel.isChannel() && panel.get('members').models.length > 0) {\r
eaaf73b0 722 _kiwi.gateway.part(panel.get('name'));\r
696a66f8
D
723 } else {\r
724 panel.close();\r
725 }\r
726 },\r
727\r
728 next: function () {\r
eaaf73b0 729 var next = _kiwi.app.panels.active.tab.next();\r
696a66f8
D
730 if (!next.length) next = $('li:first', this.tabs_msgs);\r
731\r
732 next.click();\r
733 },\r
734 prev: function () {\r
eaaf73b0 735 var prev = _kiwi.app.panels.active.tab.prev();\r
696a66f8
D
736 if (!prev.length) prev = $('li:last', this.tabs_msgs);\r
737\r
738 prev.click();\r
739 }\r
740});\r
741\r
742\r
743\r
eaaf73b0 744_kiwi.view.TopicBar = Backbone.View.extend({\r
696a66f8 745 events: {\r
cee337bb 746 'keydown div': 'process'\r
696a66f8
D
747 },\r
748\r
749 initialize: function () {\r
eaaf73b0 750 _kiwi.app.panels.bind('active', function (active_panel) {\r
6c719c05
D
751 // If it's a channel topic, update and make editable\r
752 if (active_panel.isChannel()) {\r
753 this.setCurrentTopic(active_panel.get('topic') || '');\r
754 this.$el.find('div').attr('contentEditable', true);\r
755\r
756 } else {\r
757 // Not a channel topic.. clear and make uneditable\r
758 this.$el.find('div').attr('contentEditable', false)\r
759 .text('');\r
760 }\r
696a66f8
D
761 }, this);\r
762 },\r
763\r
764 process: function (ev) {\r
765 var inp = $(ev.currentTarget),\r
cee337bb
JA
766 inp_val = inp.text();\r
767 \r
6c719c05 768 // Only allow topic editing if this is a channel panel\r
eaaf73b0 769 if (!_kiwi.app.panels.active.isChannel()) {\r
6c719c05
D
770 return false;\r
771 }\r
cee337bb 772\r
6c719c05
D
773 // If hit return key, update the current topic\r
774 if (ev.keyCode === 13) {\r
eaaf73b0 775 _kiwi.gateway.topic(_kiwi.app.panels.active.get('name'), inp_val);\r
6c719c05 776 return false;\r
696a66f8
D
777 }\r
778 },\r
779\r
780 setCurrentTopic: function (new_topic) {\r
781 new_topic = new_topic || '';\r
782\r
783 // We only want a plain text version\r
cee337bb 784 $('div', this.$el).html(formatIRCMsg(_.escape(new_topic)));\r
696a66f8
D
785 }\r
786});\r
787\r
788\r
789\r
eaaf73b0 790_kiwi.view.ControlBox = Backbone.View.extend({\r
696a66f8 791 events: {\r
5998fd56 792 'keydown .inp': 'process',\r
696a66f8
D
793 'click .nick': 'showNickChange'\r
794 },\r
795\r
796 initialize: function () {\r
797 var that = this;\r
798\r
799 this.buffer = []; // Stores previously run commands\r
800 this.buffer_pos = 0; // The current position in the buffer\r
801\r
802 this.preprocessor = new InputPreProcessor();\r
803 this.preprocessor.recursive_depth = 5;\r
804\r
805 // Hold tab autocomplete data\r
806 this.tabcomplete = {active: false, data: [], prefix: ''};\r
807\r
eaaf73b0 808 _kiwi.gateway.bind('change:nick', function () {\r
696a66f8
D
809 $('.nick', that.$el).text(this.get('nick'));\r
810 });\r
811 },\r
812\r
813 showNickChange: function (ev) {\r
eaaf73b0 814 (new _kiwi.view.NickChangeBox()).render();\r
696a66f8
D
815 },\r
816\r
817 process: function (ev) {\r
818 var that = this,\r
819 inp = $(ev.currentTarget),\r
820 inp_val = inp.val(),\r
821 meta;\r
822\r
823 if (navigator.appVersion.indexOf("Mac") !== -1) {\r
7bfc8043 824 meta = ev.metaKey;\r
696a66f8
D
825 } else {\r
826 meta = ev.altKey;\r
827 }\r
828\r
829 // If not a tab key, reset the tabcomplete data\r
830 if (this.tabcomplete.active && ev.keyCode !== 9) {\r
831 this.tabcomplete.active = false;\r
832 this.tabcomplete.data = [];\r
833 this.tabcomplete.prefix = '';\r
834 }\r
835 \r
836 switch (true) {\r
837 case (ev.keyCode === 13): // return\r
838 inp_val = inp_val.trim();\r
839\r
840 if (inp_val) {\r
5998fd56
D
841 $.each(inp_val.split('\n'), function (idx, line) {\r
842 that.processInput(line);\r
843 });\r
696a66f8
D
844\r
845 this.buffer.push(inp_val);\r
846 this.buffer_pos = this.buffer.length;\r
847 }\r
848\r
849 inp.val('');\r
5998fd56 850 return false;\r
696a66f8
D
851\r
852 break;\r
853\r
854 case (ev.keyCode === 38): // up\r
855 if (this.buffer_pos > 0) {\r
856 this.buffer_pos--;\r
857 inp.val(this.buffer[this.buffer_pos]);\r
858 }\r
859 break;\r
860\r
861 case (ev.keyCode === 40): // down\r
862 if (this.buffer_pos < this.buffer.length) {\r
863 this.buffer_pos++;\r
864 inp.val(this.buffer[this.buffer_pos]);\r
865 }\r
866 break;\r
867\r
7bfc8043 868 case (ev.keyCode === 219 && meta): // [ + meta\r
eaaf73b0 869 _kiwi.app.panels.view.prev();\r
696a66f8
D
870 return false;\r
871\r
7bfc8043 872 case (ev.keyCode === 221 && meta): // ] + meta\r
eaaf73b0 873 _kiwi.app.panels.view.next();\r
696a66f8
D
874 return false;\r
875\r
876 case (ev.keyCode === 9): // tab\r
877 this.tabcomplete.active = true;\r
878 if (_.isEqual(this.tabcomplete.data, [])) {\r
879 // Get possible autocompletions\r
880 var ac_data = [];\r
eaaf73b0 881 $.each(_kiwi.app.panels.active.get('members').models, function (i, member) {\r
696a66f8
D
882 if (!member) return;\r
883 ac_data.push(member.get('nick'));\r
884 });\r
885 ac_data = _.sortBy(ac_data, function (nick) {\r
886 return nick;\r
887 });\r
888 this.tabcomplete.data = ac_data;\r
889 }\r
890\r
891 if (inp_val[inp[0].selectionStart - 1] === ' ') {\r
892 return false;\r
893 }\r
894 \r
895 (function () {\r
896 var tokens = inp_val.substring(0, inp[0].selectionStart).split(' '),\r
897 val,\r
898 p1,\r
899 newnick,\r
900 range,\r
901 nick = tokens[tokens.length - 1];\r
902 if (this.tabcomplete.prefix === '') {\r
903 this.tabcomplete.prefix = nick;\r
904 }\r
905\r
906 this.tabcomplete.data = _.select(this.tabcomplete.data, function (n) {\r
907 return (n.toLowerCase().indexOf(that.tabcomplete.prefix.toLowerCase()) === 0);\r
908 });\r
909\r
910 if (this.tabcomplete.data.length > 0) {\r
911 p1 = inp[0].selectionStart - (nick.length);\r
912 val = inp_val.substr(0, p1);\r
913 newnick = this.tabcomplete.data.shift();\r
914 this.tabcomplete.data.push(newnick);\r
915 val += newnick;\r
916 val += inp_val.substr(inp[0].selectionStart);\r
917 inp.val(val);\r
918\r
919 if (inp[0].setSelectionRange) {\r
920 inp[0].setSelectionRange(p1 + newnick.length, p1 + newnick.length);\r
921 } else if (inp[0].createTextRange) { // not sure if this bit is actually needed....\r
922 range = inp[0].createTextRange();\r
923 range.collapse(true);\r
924 range.moveEnd('character', p1 + newnick.length);\r
925 range.moveStart('character', p1 + newnick.length);\r
926 range.select();\r
927 }\r
928 }\r
929 }).apply(this);\r
930 return false;\r
931 }\r
932 },\r
933\r
934\r
935 processInput: function (command_raw) {\r
936 var command, params,\r
937 pre_processed;\r
938 \r
939 // The default command\r
c6f09dd0
D
940 if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') {\r
941 // Remove any slash escaping at the start (ie. //)\r
942 command_raw = command_raw.replace(/^\/\//, '/');\r
943\r
944 // Prepend the default command\r
eaaf73b0 945 command_raw = '/msg ' + _kiwi.app.panels.active.get('name') + ' ' + command_raw;\r
696a66f8
D
946 }\r
947\r
948 // Process the raw command for any aliases\r
eaaf73b0
D
949 this.preprocessor.vars.server = _kiwi.gateway.get('name');\r
950 this.preprocessor.vars.channel = _kiwi.app.panels.active.get('name');\r
696a66f8
D
951 this.preprocessor.vars.destination = this.preprocessor.vars.channel;\r
952 command_raw = this.preprocessor.process(command_raw);\r
953\r
954 // Extract the command and parameters\r
955 params = command_raw.split(' ');\r
956 if (params[0][0] === '/') {\r
957 command = params[0].substr(1).toLowerCase();\r
958 params = params.splice(1, params.length - 1);\r
959 } else {\r
960 // Default command\r
961 command = 'msg';\r
eaaf73b0 962 params.unshift(_kiwi.app.panels.active.get('name'));\r
696a66f8
D
963 }\r
964\r
965 // Trigger the command events\r
966 this.trigger('command', {command: command, params: params});\r
6c58c492 967 this.trigger('command:' + command, {command: command, params: params});\r
696a66f8
D
968\r
969 // If we didn't have any listeners for this event, fire a special case\r
970 // TODO: This feels dirty. Should this really be done..?\r
6c58c492 971 if (!this._callbacks['command:' + command]) {\r
696a66f8
D
972 this.trigger('unknown_command', {command: command, params: params});\r
973 }\r
974 }\r
975});\r
976\r
977\r
978\r
979\r
eaaf73b0 980_kiwi.view.StatusMessage = Backbone.View.extend({\r
696a66f8
D
981 initialize: function () {\r
982 this.$el.hide();\r
983\r
984 // Timer for hiding the message after X seconds\r
985 this.tmr = null;\r
986 },\r
987\r
988 text: function (text, opt) {\r
989 // Defaults\r
990 opt = opt || {};\r
991 opt.type = opt.type || '';\r
992 opt.timeout = opt.timeout || 5000;\r
993\r
994 this.$el.text(text).attr('class', opt.type);\r
eaaf73b0 995 this.$el.slideDown(_kiwi.app.view.doLayout);\r
696a66f8
D
996\r
997 if (opt.timeout) this.doTimeout(opt.timeout);\r
998 },\r
999\r
1000 html: function (html, opt) {\r
1001 // Defaults\r
1002 opt = opt || {};\r
1003 opt.type = opt.type || '';\r
1004 opt.timeout = opt.timeout || 5000;\r
1005\r
1006 this.$el.html(text).attr('class', opt.type);\r
eaaf73b0 1007 this.$el.slideDown(_kiwi.app.view.doLayout);\r
696a66f8
D
1008\r
1009 if (opt.timeout) this.doTimeout(opt.timeout);\r
1010 },\r
1011\r
1012 hide: function () {\r
eaaf73b0 1013 this.$el.slideUp(_kiwi.app.view.doLayout);\r
696a66f8
D
1014 },\r
1015\r
1016 doTimeout: function (length) {\r
1017 if (this.tmr) clearTimeout(this.tmr);\r
1018 var that = this;\r
1019 this.tmr = setTimeout(function () { that.hide(); }, length);\r
1020 }\r
1021});\r
1022\r
1023\r
1024\r
1025\r
eaaf73b0 1026_kiwi.view.ResizeHandler = Backbone.View.extend({\r
696a66f8
D
1027 events: {\r
1028 'mousedown': 'startDrag',\r
1029 'mouseup': 'stopDrag'\r
1030 },\r
1031\r
1032 initialize: function () {\r
1033 this.dragging = false;\r
1034 this.starting_width = {};\r
1035\r
1036 $(window).on('mousemove', $.proxy(this.onDrag, this));\r
1037 },\r
1038\r
1039 startDrag: function (event) {\r
1040 this.dragging = true;\r
1041 },\r
1042\r
1043 stopDrag: function (event) {\r
1044 this.dragging = false;\r
1045 },\r
1046\r
1047 onDrag: function (event) {\r
1048 if (!this.dragging) return;\r
1049\r
1050 this.$el.css('left', event.clientX - (this.$el.outerWidth(true) / 2));\r
1051 $('#memberlists').css('width', this.$el.parent().width() - (this.$el.position().left + this.$el.outerWidth()));\r
eaaf73b0 1052 _kiwi.app.view.doLayout();\r
696a66f8
D
1053 }\r
1054});\r
1055\r
1056\r
1057\r
eaaf73b0 1058_kiwi.view.AppToolbar = Backbone.View.extend({\r
7de3dd03
D
1059 events: {\r
1060 'click .settings': 'clickSettings'\r
1061 },\r
1062\r
1063 initialize: function () {\r
7de3dd03
D
1064 },\r
1065\r
1066 clickSettings: function (event) {\r
eaaf73b0 1067 _kiwi.app.controlbox.processInput('/settings');\r
7de3dd03
D
1068 }\r
1069});\r
1070\r
1071\r
1072\r
eaaf73b0 1073_kiwi.view.Application = Backbone.View.extend({\r
696a66f8
D
1074 initialize: function () {\r
1075 $(window).resize(this.doLayout);\r
1076 $('#toolbar').resize(this.doLayout);\r
1077 $('#controlbox').resize(this.doLayout);\r
1078\r
5bed0536
D
1079 // Change the theme when the config is changed\r
1080 _kiwi.global.settings.on('change:theme', this.updateTheme, this);\r
f55cb404 1081 this.updateTheme(getQueryVariable('theme'));\r
5bed0536 1082\r
039a3156
D
1083 _kiwi.global.settings.on('change:channel_list_style', this.setTabLayout, this);\r
1084 this.setTabLayout(_kiwi.global.settings.get('channel_list_style'));\r
1085\r
696a66f8
D
1086 this.doLayout();\r
1087\r
1088 $(document).keydown(this.setKeyFocus);\r
1089\r
1090 // Confirmation require to leave the page\r
1091 window.onbeforeunload = function () {\r
eaaf73b0 1092 if (_kiwi.gateway.isConnected()) {\r
696a66f8
D
1093 return 'This will close all KiwiIRC conversations. Are you sure you want to close this window?';\r
1094 }\r
1095 };\r
1096 },\r
1097\r
1098\r
5bed0536
D
1099\r
1100 updateTheme: function (theme_name) {\r
1101 // If called by the settings callback, get the correct new_value\r
1102 if (theme_name === _kiwi.global.settings) {\r
1103 theme_name = arguments[1];\r
1104 }\r
1105\r
1106 // If we have no theme specified, get it from the settings\r
1107 if (!theme_name) theme_name = _kiwi.global.settings.get('theme');\r
1108\r
1109 // Clear any current theme\r
1110 this.$el.removeClass(function (i, css) {\r
43ad0845 1111 return (css.match(/\btheme_\S+/g) || []).join(' ');\r
5bed0536
D
1112 });\r
1113\r
1114 // Apply the new theme\r
6d6365e4 1115 this.$el.addClass('theme_' + (theme_name || 'relaxed'));\r
5bed0536
D
1116 },\r
1117\r
1118\r
039a3156
D
1119 setTabLayout: function (layout_style) {\r
1120 // If called by the settings callback, get the correct new_value\r
1121 if (layout_style === _kiwi.global.settings) {\r
1122 layout_style = arguments[1];\r
1123 }\r
1124 \r
1125 if (layout_style == 'list') {\r
1126 this.$el.addClass('chanlist_treeview');\r
1127 } else {\r
1128 this.$el.removeClass('chanlist_treeview');\r
1129 }\r
1130 \r
1131 this.doLayout();\r
1132 },\r
1133\r
1134\r
696a66f8
D
1135 // Globally shift focus to the command input box on a keypress\r
1136 setKeyFocus: function (ev) {\r
1137 // If we're copying text, don't shift focus\r
6c80fe5c 1138 if (ev.ctrlKey || ev.altKey || ev.metaKey) {\r
696a66f8
D
1139 return;\r
1140 }\r
1141\r
1142 // If we're typing into an input box somewhere, ignore\r
22373da6 1143 if ((ev.target.tagName.toLowerCase() === 'input') || (ev.target.tagName.toLowerCase() === 'textarea') || $(ev.target).attr('contenteditable')) {\r
696a66f8
D
1144 return;\r
1145 }\r
1146\r
1147 $('#controlbox .inp').focus();\r
1148 },\r
1149\r
1150\r
1151 doLayout: function () {\r
e579ff0d 1152 var el_kiwi = $('#kiwi');\r
696a66f8
D
1153 var el_panels = $('#panels');\r
1154 var el_memberlists = $('#memberlists');\r
1155 var el_toolbar = $('#toolbar');\r
1156 var el_controlbox = $('#controlbox');\r
1157 var el_resize_handle = $('#memberlists_resize_handle');\r
1158\r
1159 var css_heights = {\r
1160 top: el_toolbar.outerHeight(true),\r
1161 bottom: el_controlbox.outerHeight(true)\r
1162 };\r
1163\r
cacc0359
D
1164\r
1165 // If any elements are not visible, full size the panals instead\r
1166 if (!el_toolbar.is(':visible')) {\r
1167 css_heights.top = 0;\r
1168 }\r
1169\r
1170 if (!el_controlbox.is(':visible')) {\r
1171 css_heights.bottom = 0;\r
1172 }\r
1173\r
1174 // Apply the CSS sizes\r
696a66f8
D
1175 el_panels.css(css_heights);\r
1176 el_memberlists.css(css_heights);\r
1177 el_resize_handle.css(css_heights);\r
1178\r
3adc7463
D
1179 // If we have channel tabs on the side, adjust the height\r
1180 if (el_kiwi.hasClass('chanlist_treeview')) {\r
1181 $('#kiwi #tabs').css(css_heights);\r
1182 }\r
1183\r
e579ff0d
D
1184 // Determine if we have a narrow window (mobile/tablet/or even small desktop window)\r
1185 if (el_kiwi.outerWidth() < 400) {\r
1186 el_kiwi.addClass('narrow');\r
1187 } else {\r
1188 el_kiwi.removeClass('narrow');\r
1189 }\r
1190\r
cacc0359 1191 // Set the panels width depending on the memberlist visibility\r
696a66f8 1192 if (el_memberlists.css('display') != 'none') {\r
daab5c1b
D
1193 // Panels to the side of the memberlist\r
1194 el_panels.css('right', el_memberlists.outerWidth(true));\r
1195 // The resize handle sits overlapping the panels and memberlist\r
1196 el_resize_handle.css('left', el_memberlists.position().left - (el_resize_handle.outerWidth(true) / 2));\r
696a66f8 1197 } else {\r
daab5c1b
D
1198 // Memberlist is hidden so panels to the right edge\r
1199 el_panels.css('right', 0);\r
1200 // And move the handle just out of sight to the right\r
696a66f8
D
1201 el_resize_handle.css('left', el_panels.outerWidth(true));\r
1202 }\r
1203 },\r
1204\r
1205\r
1206 alertWindow: function (title) {\r
1207 if (!this.alertWindowTimer) {\r
1208 this.alertWindowTimer = new (function () {\r
1209 var that = this;\r
1210 var tmr;\r
1211 var has_focus = true;\r
1212 var state = 0;\r
1213 var default_title = 'Kiwi IRC';\r
1214 var title = 'Kiwi IRC';\r
1215\r
1216 this.setTitle = function (new_title) {\r
1217 new_title = new_title || default_title;\r
1218 window.document.title = new_title;\r
1219 return new_title;\r
1220 };\r
1221\r
1222 this.start = function (new_title) {\r
1223 // Don't alert if we already have focus\r
1224 if (has_focus) return;\r
1225\r
1226 title = new_title;\r
1227 if (tmr) return;\r
1228 tmr = setInterval(this.update, 1000);\r
1229 };\r
1230\r
1231 this.stop = function () {\r
1232 // Stop the timer and clear the title\r
1233 if (tmr) clearInterval(tmr);\r
1234 tmr = null;\r
1235 this.setTitle();\r
1236\r
1237 // Some browsers don't always update the last title correctly\r
1238 // Wait a few seconds and then reset\r
1239 setTimeout(this.reset, 2000);\r
1240 };\r
1241\r
1242 this.reset = function () {\r
1243 if (tmr) return;\r
1244 that.setTitle();\r
1245 };\r
1246\r
1247\r
1248 this.update = function () {\r
1249 if (state === 0) {\r
1250 that.setTitle(title);\r
1251 state = 1;\r
1252 } else {\r
1253 that.setTitle();\r
1254 state = 0;\r
1255 }\r
1256 };\r
1257\r
1258 $(window).focus(function (event) {\r
1259 has_focus = true;\r
1260 that.stop();\r
1261\r
1262 // Some browsers don't always update the last title correctly\r
1263 // Wait a few seconds and then reset\r
1264 setTimeout(that.reset, 2000);\r
1265 });\r
1266\r
1267 $(window).blur(function (event) {\r
1268 has_focus = false;\r
1269 });\r
1270 })();\r
1271 }\r
1272\r
1273 this.alertWindowTimer.start(title);\r
1274 },\r
1275\r
1276\r
1277 barsHide: function (instant) {\r
1278 var that = this;\r
1279\r
1280 if (!instant) {\r
1281 $('#toolbar').slideUp({queue: false, duration: 400, step: this.doLayout});\r
1282 $('#controlbox').slideUp({queue: false, duration: 400, step: this.doLayout});\r
1283 } else {\r
1284 $('#toolbar').slideUp(0);\r
1285 $('#controlbox').slideUp(0);\r
1286 this.doLayout();\r
1287 }\r
1288 },\r
1289\r
1290 barsShow: function (instant) {\r
1291 var that = this;\r
1292\r
1293 if (!instant) {\r
1294 $('#toolbar').slideDown({queue: false, duration: 400, step: this.doLayout});\r
1295 $('#controlbox').slideDown({queue: false, duration: 400, step: this.doLayout});\r
1296 } else {\r
1297 $('#toolbar').slideDown(0);\r
1298 $('#controlbox').slideDown(0);\r
1299 this.doLayout();\r
1300 }\r
1301 }\r
04d615ad
D
1302});\r
1303\r
1304\r
1305\r
1306\r
1307\r
1308\r
1309\r
1310\r
1311\r
1312_kiwi.view.MediaMessage = Backbone.View.extend({\r
1313 events: {\r
1314 'click .media_close': 'close'\r
1315 },\r
1316\r
1317 initialize: function () {\r
b025bb57 1318 // Get the URL from the data\r
04d615ad
D
1319 this.url = this.$el.data('url');\r
1320 },\r
1321\r
b025bb57 1322 // Close the media content and remove it from display\r
04d615ad 1323 close: function () {\r
b025bb57
D
1324 var that = this;\r
1325 this.$content.slideUp('fast', function () {\r
1326 that.$content.remove();\r
1327 });\r
04d615ad
D
1328 },\r
1329\r
b025bb57 1330 // Open the media content within its wrapper\r
04d615ad 1331 open: function () {\r
b025bb57
D
1332 // Create the content div if we haven't already\r
1333 if (!this.$content) {\r
f443e240 1334 this.$content = $('<div class="media_content"><a class="media_close"><i class="icon-chevron-up"></i> Close media</a><br /><div class="content"></div></div>');\r
f443e240 1335 this.$content.find('.content').append(this.mediaTypes[this.$el.data('type')].apply(this, []) || 'Not found :(');\r
b025bb57 1336 }\r
04d615ad 1337\r
b025bb57
D
1338 // Now show the content if not already\r
1339 if (!this.$content.is(':visible')) {\r
1340 // Hide it first so the slideDown always plays\r
1341 this.$content.hide();\r
1342\r
1343 // Add the media content and slide it into view\r
1344 this.$el.append(this.$content);\r
1345 this.$content.slideDown();\r
1346 }\r
f443e240
D
1347 },\r
1348\r
1349\r
1350\r
1351 // Generate the media content for each recognised type\r
1352 mediaTypes: {\r
1353 twitter: function () {\r
1354 var tweet_id = this.$el.data('tweetid');\r
1355 var that = this;\r
1356\r
1357 $.getJSON('https://api.twitter.com/1/statuses/oembed.json?id=' + tweet_id + '&callback=?', function (data) {\r
b9149cd0 1358 that.$content.find('.content').html(data.html);\r
f443e240
D
1359 });\r
1360\r
1361 return $('<div>Loading tweet..</div>');\r
1362 },\r
1363\r
1364\r
1365 image: function () {\r
1366 return $('<a href="' + this.url + '" target="_blank"><img height="100" src="' + this.url + '" /></a>');\r
b9149cd0
D
1367 },\r
1368\r
1369\r
1370 reddit: function () {\r
1371 var that = this;\r
1372 var matches = (/reddit\.com\/r\/([a-zA-Z0-9_\-]+)\/comments\/([a-z0-9]+)\/([^\/]+)?/gi).exec(this.url);\r
1373\r
1374 $.getJSON('http://www.' + matches[0] + '.json?jsonp=?', function (data) {\r
1375 console.log('Loaded reddit data', data);\r
1376 var post = data[0].data.children[0].data;\r
1377 var thumb = '';\r
1378\r
1379 // Show a thumbnail if there is one\r
1380 if (post.thumbnail) {\r
1381 //post.thumbnail = 'http://www.eurotunnel.com/uploadedImages/commercial/back-steps-icon-arrow.png';\r
1382\r
1383 // Hide the thumbnail if an over_18 image\r
1384 if (post.over_18) {\r
1385 thumb = '<span class="thumbnail_nsfw" onclick="$(this).find(\'p\').remove(); $(this).find(\'img\').css(\'visibility\', \'visible\');">';\r
1386 thumb += '<p style="font-size:0.9em;line-height:1.2em;cursor:pointer;">Show<br />NSFW</p>';\r
1387 thumb += '<img src="' + post.thumbnail + '" class="thumbnail" style="visibility:hidden;" />';\r
1388 thumb += '</span>';\r
1389 } else {\r
1390 thumb = '<img src="' + post.thumbnail + '" class="thumbnail" />';\r
1391 }\r
1392 }\r
1393\r
1394 // Build the template string up\r
1395 var tmpl = '<div>' + thumb + '<b><%- title %></b><br />Posted by <%- author %>. &nbsp;&nbsp; ';\r
1396 tmpl += '<i class="icon-arrow-up"></i> <%- ups %> &nbsp;&nbsp; <i class="icon-arrow-down"></i> <%- downs %><br />';\r
1397 tmpl += '<%- num_comments %> comments made. <a href="http://www.reddit.com<%- permalink %>">View post</a></div>';\r
1398\r
1399 that.$content.find('.content').html(_.template(tmpl, post));\r
1400 });\r
1401\r
1402 return $('<div>Loading Reddit thread..</div>');\r
f443e240 1403 }\r
04d615ad
D
1404 }\r
1405\r
1406}, {\r
1407\r
b025bb57 1408 // Build the closed media HTML from a URL\r
04d615ad 1409 buildHtml: function (url) {\r
f443e240 1410 var html = '', matches;\r
04d615ad
D
1411\r
1412 // Is it an image?\r
1413 if (url.match(/(\.jpe?g|\.gif|\.bmp|\.png)\??$/i)) {\r
f443e240
D
1414 html += '<span class="media image" data-type="image" data-url="' + url + '" title="Open Image"><a class="open"><i class="icon-chevron-right"></i></a></span>';\r
1415 }\r
1416\r
1417 // Is it a tweet?\r
1418 matches = (/https?:\/\/twitter.com\/([a-zA-Z0-9_]+)\/status\/([0-9]+)/ig).exec(url);\r
1419 if (matches) {\r
1420 html += '<span class="media twitter" data-type="twitter" data-url="' + url + '" data-tweetid="' + matches[2] + '" title="Show tweet information"><a class="open"><i class="icon-chevron-right"></i></a></span>';\r
04d615ad
D
1421 }\r
1422\r
b9149cd0
D
1423 // Is reddit?\r
1424 matches = (/reddit\.com\/r\/([a-zA-Z0-9_\-]+)\/comments\/([a-z0-9]+)\/([^\/]+)?/gi).exec(url);\r
1425 if (matches) {\r
1426 html += '<span class="media reddit" data-type="reddit" data-url="' + url + '" title="Reddit thread"><a class="open"><i class="icon-chevron-right"></i></a></span>';\r
1427 }\r
1428\r
04d615ad
D
1429 return html;\r
1430 }\r
f443e240 1431});