1 _kiwi
.view
.Panel
= Backbone
.View
.extend({
3 className
: "panel messages",
6 "click .chan": "chanClick",
7 'click .media .open': 'mediaClick',
8 'mouseenter .msg .nick': 'msgEnter',
9 'mouseleave .msg .nick': 'msgLeave'
12 initialize: function (options
) {
13 this.initializePanel(options
);
16 initializePanel: function (options
) {
17 this.$el
.css('display', 'none');
18 options
= options
|| {};
20 // Containing element for this panel
21 if (options
.container
) {
22 this.$container
= $(options
.container
);
24 this.$container
= $('#kiwi .panels .container1');
27 this.$el
.appendTo(this.$container
);
31 this.model
.bind('msg', this.newMsg
, this);
34 this.model
.set({"view": this}, {"silent": true});
41 _
.each(this.model
.get('scrollback'), function (msg
) {
46 newMsg: function (msg
) {
47 var re
, line_msg
, $this = this.$el
,
48 nick_colour_hex
, nick_hex
, is_highlight
, msg_css_classes
= '',
50 sb
= this.model
.get('scrollback'),
51 prev_msg
= sb
[sb
.length
-2];
53 // Nick highlight detecting
54 if ((new RegExp('(^|\\W)(' + escapeRegex(_kiwi
.app
.connections
.active_connection
.get('nick')) + ')(\\W|$)', 'i')).test(msg
.msg
)) {
56 msg_css_classes
+= ' highlight';
59 // Escape any HTML that may be in here
60 msg
.msg
= $('<div />').text(msg
.msg
).html();
62 // Make the channels clickable
63 re
= new RegExp('(?:^|\\s)([' + escapeRegex(_kiwi
.gateway
.get('channel_prefix')) + '][^ ,.\\007]+)', 'g');
64 msg
.msg
= msg
.msg
.replace(re
, function (match
) {
65 return '<a class="chan" data-channel="' + match
.trim() + '">' + match
+ '</a>';
69 // Parse any links found
70 msg
.msg
= msg
.msg
.replace(/(([A-Za-z][A-Za-z0-9\-]*\:\/\/)|(www\.))([\w.\-]+)([a-zA-Z]{2,6})(:[0-9]+)?(\/[\w#!:.?$'()[\]*,;~+=&%@!\-\/]*)?/gi, function (url
) {
74 // Add the http if no protoocol was found
75 if (url
.match(/^www\./)) {
76 url
= 'http://' + url
;
79 // Shorten the displayed URL if it's going to be too long
80 if (nice
.length
> 100) {
81 nice
= nice
.substr(0, 100) + '...';
84 // Get any media HTML if supported
85 extra_html
= _kiwi
.view
.MediaMessage
.buildHtml(url
);
87 // Make the link clickable
88 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url
+ '">' + nice
+ '</a>' + extra_html
;
92 // Convert IRC formatting into HTML formatting
93 msg
.msg
= formatIRCMsg(msg
.msg
);
96 // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)
97 nick_colour_hex
= (function (nick
) {
98 var nick_int
= 0, rgb
;
100 _
.map(nick
.split(''), function (i
) { nick_int
+= i
.charCodeAt(0); });
101 rgb
= hsl2rgb(nick_int
% 255, 70, 35);
102 rgb
= rgb
[2] | (rgb
[1] << 8) | (rgb
[0] << 16);
104 return '#' + rgb
.toString(16);
107 msg
.nick_style
= 'color:' + nick_colour_hex
+ ';';
109 // Generate a hex string from the nick to be used as a CSS class name
110 nick_hex
= msg
.nick_css_class
= '';
112 _
.map(msg
.nick
.split(''), function (char) {
113 nick_hex
+= char.charCodeAt(0).toString(16);
115 msg_css_classes
+= ' nick_' + nick_hex
;
119 // Time difference between this message and the last (in minutes)
120 time_difference
= (msg
.date
.getTime() - prev_msg
.date
.getTime())/1000/60;
121 if (prev_msg
.nick
=== msg
.nick
&& time_difference
< 1) {
122 msg_css_classes
+= ' repeated_nick';
126 // Build up and add the line
127 msg
.msg_css_classes
= msg_css_classes
;
128 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>';
129 $this.append(_
.template(line_msg
, msg
));
131 // Activity/alerts based on the type of new message
132 if (msg
.type
.match(/^action /)) {
133 this.alert('action');
135 } else if (is_highlight
) {
136 _kiwi
.app
.view
.alertWindow('* ' + _kiwi
.global
.i18n
.translate('client_views_panel_activity').fetch());
137 _kiwi
.app
.view
.favicon
.newHighlight();
138 _kiwi
.app
.view
.playSound('highlight');
139 this.alert('highlight');
142 // If this is the active panel, send an alert out
143 if (this.model
.isActive()) {
144 _kiwi
.app
.view
.alertWindow('* ' + _kiwi
.global
.i18n
.translate('client_views_panel_activity').fetch());
146 this.alert('activity');
149 if (this.model
.isQuery() && !this.model
.isActive()) {
150 _kiwi
.app
.view
.alertWindow('* ' + _kiwi
.global
.i18n
.translate('client_views_panel_activity').fetch());
152 _kiwi
.app
.view
.favicon
.newHighlight();
154 _kiwi
.app
.view
.playSound('highlight');
157 // Update the activity counters
159 // Only inrement the counters if we're not the active panel
160 if (this.model
.isActive()) return;
162 var $act
= this.model
.tab
.find('.activity');
163 $act
.text((parseInt($act
.text(), 10) || 0) + 1);
164 if ($act
.text() === '0') {
165 $act
.addClass('zero');
167 $act
.removeClass('zero');
171 this.scrollToBottom();
173 // Make sure our DOM isn't getting too large (Acts as scrollback)
175 if (this.msg_count
> (parseInt(_kiwi
.global
.settings
.get('scrollback'), 10) || 250)) {
176 $('.msg:first', this.$el
).remove();
180 chanClick: function (event
) {
182 _kiwi
.gateway
.join(null, $(event
.target
).data('channel'));
185 _kiwi
.gateway
.join(null, $(event
.srcElement
).data('channel'));
189 mediaClick: function (event
) {
190 var $media
= $(event
.target
).parents('.media');
193 if ($media
.data('media')) {
194 media_message
= $media
.data('media');
196 media_message
= new _kiwi
.view
.MediaMessage({el
: $media
[0]});
198 // Cache this MediaMessage instance for when it's opened again
199 $media
.data('media', media_message
);
202 media_message
.open();
205 // Cursor hovers over a message
206 msgEnter: function (event
) {
209 // Find a valid class that this element has
210 _
.each($(event
.currentTarget
).parent('.msg').attr('class').split(' '), function (css_class
) {
211 if (css_class
.match(/^nick_[a-z0-9]+/i)) {
212 nick_class
= css_class
;
216 // If no class was found..
217 if (!nick_class
) return;
219 $('.'+nick_class
).addClass('global_nick_highlight');
222 // Cursor leaves message
223 msgLeave: function (event
) {
226 // Find a valid class that this element has
227 _
.each($(event
.currentTarget
).parent('.msg').attr('class').split(' '), function (css_class
) {
228 if (css_class
.match(/^nick_[a-z0-9]+/i)) {
229 nick_class
= css_class
;
233 // If no class was found..
234 if (!nick_class
) return;
236 $('.'+nick_class
).removeClass('global_nick_highlight');
240 var $this = this.$el
;
242 // Hide all other panels and show this one
243 this.$container
.children('.panel').css('display', 'none');
244 $this.css('display', 'block');
246 // Show this panels memberlist
247 var members
= this.model
.get("members");
249 $('#kiwi .memberlists').removeClass('disabled');
252 // Memberlist not found for this panel, hide any active ones
253 $('#kiwi .memberlists').addClass('disabled').children().removeClass('active');
256 // Remove any alerts and activity counters for this panel
258 this.model
.tab
.find('.activity').text('0').addClass('zero');
260 _kiwi
.app
.panels
.trigger('active', this.model
, _kiwi
.app
.panels().active
);
261 this.model
.trigger('active', this.model
);
263 _kiwi
.app
.view
.doLayout();
265 this.scrollToBottom(true);
269 alert: function (level
) {
270 // No need to highlight if this si the active panel
271 if (this.model
== _kiwi
.app
.panels().active
) return;
274 types
= ['none', 'action', 'activity', 'highlight'];
276 // Default alert level
277 level
= level
|| 'none';
279 // If this alert level does not exist, assume clearing current level
280 type_idx
= _
.indexOf(types
, level
);
286 // Only 'upgrade' the alert. Never down (unless clearing)
287 if (type_idx
!== 0 && type_idx
<= this.alert_level
) {
291 // Clear any existing levels
292 this.model
.tab
.removeClass(function (i
, css
) {
293 return (css
.match(/\balert_\S+/g) || []).join(' ');
296 // Add the new level if there is one
297 if (level
!== 'none') {
298 this.model
.tab
.addClass('alert_' + level
);
301 this.alert_level
= type_idx
;
305 // Scroll to the bottom of the panel
306 scrollToBottom: function (force_down
) {
307 // If this isn't the active panel, don't scroll
308 if (this.model
!== _kiwi
.app
.panels().active
) return;
310 // Don't scroll down if we're scrolled up the panel a little
311 if (force_down
|| this.$container
.scrollTop() + this.$container
.height() > this.$el
.outerHeight() - 150) {
312 this.$container
[0].scrollTop
= this.$container
[0].scrollHeight
;