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