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 // Nick highlight detecting
51 if ((new RegExp('\\b' + _kiwi
.app
.connections
.active_connection
.get('nick') + '\\b', 'i')).test(msg
.msg
)) {
53 msg_css_classes
+= ' highlight';
56 // Escape any HTML that may be in here
57 msg
.msg
= $('<div />').text(msg
.msg
).html();
59 // Make the channels clickable
60 re
= new RegExp('(?:^|\\s)([' + _kiwi
.gateway
.get('channel_prefix') + '][^ ,.\\007]+)', 'g');
61 msg
.msg
= msg
.msg
.replace(re
, function (match
) {
62 return '<a class="chan" data-channel="' + match
.trim() + '">' + match
+ '</a>';
66 // Parse any links found
67 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
) {
71 // Add the http if no protoocol was found
72 if (url
.match(/^www\./)) {
73 url
= 'http://' + url
;
76 // Shorten the displayed URL if it's going to be too long
77 if (nice
.length
> 100) {
78 nice
= nice
.substr(0, 100) + '...';
81 // Get any media HTML if supported
82 extra_html
= _kiwi
.view
.MediaMessage
.buildHtml(url
);
84 // Make the link clickable
85 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url
+ '">' + nice
+ '</a> ' + extra_html
;
89 // Convert IRC formatting into HTML formatting
90 msg
.msg
= formatIRCMsg(msg
.msg
);
93 // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)
94 nick_colour_hex
= (function (nick
) {
95 var nick_int
= 0, rgb
;
97 _
.map(nick
.split(''), function (i
) { nick_int
+= i
.charCodeAt(0); });
98 rgb
= hsl2rgb(nick_int
% 255, 70, 35);
99 rgb
= rgb
[2] | (rgb
[1] << 8) | (rgb
[0] << 16);
101 return '#' + rgb
.toString(16);
104 msg
.nick_style
= 'color:' + nick_colour_hex
+ ';';
106 // Generate a hex string from the nick to be used as a CSS class name
107 nick_hex
= msg
.nick_css_class
= '';
109 _
.map(msg
.nick
.split(''), function (char) {
110 nick_hex
+= char.charCodeAt(0).toString(16);
112 msg_css_classes
+= ' nick_' + nick_hex
;
115 // Build up and add the line
116 msg
.msg_css_classes
= msg_css_classes
;
117 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>';
118 $this.append(_
.template(line_msg
, msg
));
120 // Activity/alerts based on the type of new message
121 if (msg
.type
.match(/^action /)) {
122 this.alert('action');
124 } else if (is_highlight
) {
125 _kiwi
.app
.view
.alertWindow('* People are talking!');
126 _kiwi
.app
.view
.playSound('highlight');
127 this.alert('highlight');
130 // If this is the active panel, send an alert out
131 if (this.model
.isActive()) {
132 _kiwi
.app
.view
.alertWindow('* People are talking!');
134 this.alert('activity');
137 if (this.model
.isQuery() && !this.model
.isActive()) {
138 _kiwi
.app
.view
.alertWindow('* People are talking!');
139 _kiwi
.app
.view
.playSound('highlight');
142 // Update the activity counters
144 // Only inrement the counters if we're not the active panel
145 if (this.model
.isActive()) return;
147 var $act
= this.model
.tab
.find('.activity');
148 $act
.text((parseInt($act
.text(), 10) || 0) + 1);
149 if ($act
.text() === '0') {
150 $act
.addClass('zero');
152 $act
.removeClass('zero');
156 this.scrollToBottom();
158 // Make sure our DOM isn't getting too large (Acts as scrollback)
160 if (this.msg_count
> (parseInt(_kiwi
.global
.settings
.get('scrollback'), 10) || 250)) {
161 $('.msg:first', this.$el
).remove();
165 chanClick: function (event
) {
167 _kiwi
.gateway
.join(null, $(event
.target
).data('channel'));
170 _kiwi
.gateway
.join(null, $(event
.srcElement
).data('channel'));
174 mediaClick: function (event
) {
175 var $media
= $(event
.target
).parents('.media');
178 if ($media
.data('media')) {
179 media_message
= $media
.data('media');
181 media_message
= new _kiwi
.view
.MediaMessage({el
: $media
[0]});
182 $media
.data('media', media_message
);
185 $media
.data('media', media_message
);
187 media_message
.open();
190 // Cursor hovers over a message
191 msgEnter: function (event
) {
194 // Find a valid class that this element has
195 _
.each($(event
.currentTarget
).parent('.msg').attr('class').split(' '), function (css_class
) {
196 if (css_class
.match(/^nick_[a-z0-9]+/i)) {
197 nick_class
= css_class
;
201 // If no class was found..
202 if (!nick_class
) return;
204 $('.'+nick_class
).addClass('global_nick_highlight');
207 // Cursor leaves message
208 msgLeave: function (event
) {
211 // Find a valid class that this element has
212 _
.each($(event
.currentTarget
).parent('.msg').attr('class').split(' '), function (css_class
) {
213 if (css_class
.match(/^nick_[a-z0-9]+/i)) {
214 nick_class
= css_class
;
218 // If no class was found..
219 if (!nick_class
) return;
221 $('.'+nick_class
).removeClass('global_nick_highlight');
225 var $this = this.$el
;
227 // Hide all other panels and show this one
228 this.$container
.children('.panel').css('display', 'none');
229 $this.css('display', 'block');
231 // Show this panels memberlist
232 var members
= this.model
.get("members");
234 $('#kiwi .memberlists').removeClass('disabled');
237 // Memberlist not found for this panel, hide any active ones
238 $('#kiwi .memberlists').addClass('disabled').children().removeClass('active');
241 // Remove any alerts and activity counters for this panel
243 this.model
.tab
.find('.activity').text('0').addClass('zero');
245 _kiwi
.app
.panels
.trigger('active', this.model
, _kiwi
.app
.panels().active
);
246 this.model
.trigger('active', this.model
);
248 _kiwi
.app
.view
.doLayout();
250 this.scrollToBottom(true);
254 alert: function (level
) {
255 // No need to highlight if this si the active panel
256 if (this.model
== _kiwi
.app
.panels().active
) return;
259 types
= ['none', 'action', 'activity', 'highlight'];
261 // Default alert level
262 level
= level
|| 'none';
264 // If this alert level does not exist, assume clearing current level
265 type_idx
= _
.indexOf(types
, level
);
271 // Only 'upgrade' the alert. Never down (unless clearing)
272 if (type_idx
!== 0 && type_idx
<= this.alert_level
) {
276 // Clear any existing levels
277 this.model
.tab
.removeClass(function (i
, css
) {
278 return (css
.match(/\balert_\S+/g) || []).join(' ');
281 // Add the new level if there is one
282 if (level
!== 'none') {
283 this.model
.tab
.addClass('alert_' + level
);
286 this.alert_level
= type_idx
;
290 // Scroll to the bottom of the panel
291 scrollToBottom: function (force_down
) {
292 // If this isn't the active panel, don't scroll
293 if (this.model
!== _kiwi
.app
.panels().active
) return;
295 // Don't scroll down if we're scrolled up the panel a little
296 if (force_down
|| this.$container
.scrollTop() + this.$container
.height() > this.$el
.outerHeight() - 150) {
297 this.$container
[0].scrollTop
= this.$container
[0].scrollHeight
;