1 _kiwi
.view
.Channel
= _kiwi
.view
.Panel
.extend({
3 var parent_events
= this.constructor.__super__
.events
;
5 if(_
.isFunction(parent_events
)){
6 parent_events
= parent_events();
8 return _
.extend({}, parent_events
, {
9 'click .msg .nick' : 'nickClick',
10 "click .chan": "chanClick",
11 'click .media .open': 'mediaClick',
12 'mouseenter .msg .nick': 'msgEnter',
13 'mouseleave .msg .nick': 'msgLeave'
17 initialize: function (options
) {
18 this.initializePanel(options
);
20 // Container for all the messages
21 this.$messages
= $('<div class="messages"></div>');
22 this.$el
.append(this.$messages
);
24 this.model
.bind('change:topic', this.topic
, this);
26 if (this.model
.get('members')) {
27 this.model
.get('members').bind('add', function (member
) {
28 if (member
.get('nick') === this.model
.collection
.network
.get('nick')) {
29 this.$el
.find('.initial_loader').slideUp(function () {
36 // Only show the loader if this is a channel (ie. not a query)
37 if (this.model
.isChannel()) {
38 this.$el
.append('<div class="initial_loader" style="margin:1em;text-align:center;"> ' + _kiwi
.global
.i18n
.translate('client_views_channel_joining').fetch() + ' <span class="loader"></span></div>');
41 this.model
.bind('msg', this.newMsg
, this);
49 this.$messages
.empty();
50 _
.each(this.model
.get('scrollback'), function (msg
) {
56 newMsg: function (msg
) {
58 nick_colour_hex
, nick_hex
, is_highlight
, msg_css_classes
= '',
60 sb
= this.model
.get('scrollback'),
61 prev_msg
= sb
[sb
.length
-2],
64 // Nick highlight detecting
65 if ((new RegExp('(^|\\W)(' + escapeRegex(_kiwi
.app
.connections
.active_connection
.get('nick')) + ')(\\W|$)', 'i')).test(msg
.msg
)) {
67 msg_css_classes
+= ' highlight';
70 // Escape any HTML that may be in here
71 msg
.msg
= $('<div />').text(msg
.msg
).html();
73 // Make the channels clickable
74 if ((network
= this.model
.get('network'))) {
75 re
= new RegExp('(?:^|\\s)([' + escapeRegex(network
.get('channel_prefix')) + '][^ ,\\007]+)', 'g');
76 msg
.msg
= msg
.msg
.replace(re
, function (match
) {
77 return '<a class="chan" data-channel="' + _
.escape(match
.trim()) + '">' + _
.escape(match
.trim()) + '</a>';
82 // Parse any links found
83 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
) {
87 // Add the http if no protoocol was found
88 if (url
.match(/^www\./)) {
89 url
= 'http://' + url
;
92 // Shorten the displayed URL if it's going to be too long
93 if (nice
.length
> 100) {
94 nice
= nice
.substr(0, 100) + '...';
97 // Get any media HTML if supported
98 extra_html
= _kiwi
.view
.MediaMessage
.buildHtml(url
);
100 // Make the link clickable
101 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url
+ '">' + nice
+ '</a>' + extra_html
;
105 // Convert IRC formatting into HTML formatting
106 msg
.msg
= formatIRCMsg(msg
.msg
);
108 // Replace text emoticons with images
109 if (_kiwi
.global
.settings
.get('show_emoticons')) {
110 msg
.msg
= emoticonFromText(msg
.msg
);
113 // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)
114 nick_colour_hex
= (function (nick
) {
115 var nick_int
= 0, rgb
;
117 _
.map(nick
.split(''), function (i
) { nick_int
+= i
.charCodeAt(0); });
118 rgb
= hsl2rgb(nick_int
% 255, 70, 35);
119 rgb
= rgb
[2] | (rgb
[1] << 8) | (rgb
[0] << 16);
121 return '#' + rgb
.toString(16);
124 msg
.nick_style
= 'color:' + nick_colour_hex
+ ';';
126 // Generate a hex string from the nick to be used as a CSS class name
127 nick_hex
= msg
.nick_css_class
= '';
129 _
.map(msg
.nick
.split(''), function (char) {
130 nick_hex
+= char.charCodeAt(0).toString(16);
132 msg_css_classes
+= ' nick_' + nick_hex
;
136 // Time difference between this message and the last (in minutes)
137 time_difference
= (msg
.time
.getTime() - prev_msg
.time
.getTime())/1000/60;
138 if (prev_msg
.nick
=== msg
.nick
&& time_difference
< 1) {
139 msg_css_classes
+= ' repeated_nick';
143 // Build up and add the line
144 msg
.msg_css_classes
= msg_css_classes
;
145 if (_kiwi
.global
.settings
.get('use_24_hour_timestamps')) {
146 msg
.time_string
= msg
.time
.getHours().toString().lpad(2, "0") + ":" + msg
.time
.getMinutes().toString().lpad(2, "0") + ":" + msg
.time
.getSeconds().toString().lpad(2, "0");
148 hour
= msg
.time
.getHours();
156 msg
.time_string
= _kiwi
.global
.i18n
.translate('client_views_panel_timestamp_pm').fetch(hour
+ ":" + msg
.time
.getMinutes().toString().lpad(2, "0") + ":" + msg
.time
.getSeconds().toString().lpad(2, "0"));
158 msg
.time_string
= _kiwi
.global
.i18n
.translate('client_views_panel_timestamp_am').fetch(hour
+ ":" + msg
.time
.getMinutes().toString().lpad(2, "0") + ":" + msg
.time
.getSeconds().toString().lpad(2, "0"));
161 line_msg
= '<div class="msg <%= type %> <%= msg_css_classes %>"><div class="time"><%- time_string %></div><div class="nick" style="<%= nick_style %>"><%- nick %></div><div class="text" style="<%= style %>"><%= msg %> </div></div>';
162 this.$messages
.append(_
.template(line_msg
, msg
));
164 // Activity/alerts based on the type of new message
165 if (msg
.type
.match(/^action /)) {
166 this.alert('action');
168 } else if (is_highlight
) {
169 _kiwi
.app
.view
.alertWindow('* ' + _kiwi
.global
.i18n
.translate('client_views_panel_activity').fetch());
170 _kiwi
.app
.view
.favicon
.newHighlight();
171 _kiwi
.app
.view
.playSound('highlight');
172 _kiwi
.app
.view
.showNotification(this.model
.get('name'), msg
.msg
);
173 this.alert('highlight');
176 // If this is the active panel, send an alert out
177 if (this.model
.isActive()) {
178 _kiwi
.app
.view
.alertWindow('* ' + _kiwi
.global
.i18n
.translate('client_views_panel_activity').fetch());
180 this.alert('activity');
183 if (this.model
.isQuery() && !this.model
.isActive()) {
184 _kiwi
.app
.view
.alertWindow('* ' + _kiwi
.global
.i18n
.translate('client_views_panel_activity').fetch());
186 // Highlights have already been dealt with above
188 _kiwi
.app
.view
.favicon
.newHighlight();
191 _kiwi
.app
.view
.showNotification(this.model
.get('name'), msg
.msg
);
192 _kiwi
.app
.view
.playSound('highlight');
195 // Update the activity counters
197 // Only inrement the counters if we're not the active panel
198 if (this.model
.isActive()) return;
200 var $act
= this.model
.tab
.find('.activity'),
201 count_all_activity
= _kiwi
.global
.settings
.get('count_all_activity'),
202 exclude_message_types
;
204 // Set the default config value
205 if (typeof count_all_activity
=== 'undefined') {
206 count_all_activity
= false;
209 // Do not increment the counter for these message types
210 exclude_message_types
= [
219 if (count_all_activity
|| _
.indexOf(exclude_message_types
, msg
.type
) === -1) {
220 $act
.text((parseInt($act
.text(), 10) || 0) + 1);
223 if ($act
.text() === '0') {
224 $act
.addClass('zero');
226 $act
.removeClass('zero');
230 if(this.model
.isActive()) this.scrollToBottom();
232 // Make sure our DOM isn't getting too large (Acts as scrollback)
234 if (this.msg_count
> (parseInt(_kiwi
.global
.settings
.get('scrollback'), 10) || 250)) {
235 $('.msg:first', this.$messages
).remove();
241 topic: function (topic
) {
242 if (typeof topic
!== 'string' || !topic
) {
243 topic
= this.model
.get("topic");
246 this.model
.addMsg('', '== ' + _kiwi
.global
.i18n
.translate('client_views_channel_topic').fetch(this.model
.get('name'), topic
), 'topic');
248 // If this is the active channel then update the topic bar
249 if (_kiwi
.app
.panels().active
=== this) {
250 _kiwi
.app
.topicbar
.setCurrentTopic(this.model
.get("topic"));
254 // Click on a nickname
255 nickClick: function (event
) {
256 var nick
= $(event
.currentTarget
).text(),
257 members
= this.model
.get('members'),
258 are_we_an_op
= !!members
.getByNick(_kiwi
.app
.connections
.active_connection
.get('nick')).get('is_op'),
259 member
, query
, userbox
, menubox
;
262 member
= members
.getByNick(nick
);
264 userbox
= new _kiwi
.view
.UserBox();
265 userbox
.setTargets(member
, this.model
);
266 userbox
.displayOpItems(are_we_an_op
);
268 menubox
= new _kiwi
.view
.MenuBox(member
.get('nick') || 'User');
269 menubox
.addItem('userbox', userbox
.$el
);
270 menubox
.showFooter(false);
273 // Position the userbox + menubox
276 m_bottom
= t
+ menubox
.$el
.outerHeight(), // Where the bottom of menu will be
277 memberlist_bottom
= this.$el
.parent().offset().top
+ this.$el
.parent().outerHeight();
279 // If the bottom of the userbox is going to be too low.. raise it
280 if (m_bottom
> memberlist_bottom
){
281 t
= memberlist_bottom
- menubox
.$el
.outerHeight();
284 // Set the new positon
295 chanClick: function (event
) {
296 var target
= (event
.target
) ? $(event
.target
).data('channel') : $(event
.srcElement
).data('channel');
298 _kiwi
.app
.connections
.active_connection
.gateway
.join(target
);
302 mediaClick: function (event
) {
303 var $media
= $(event
.target
).parents('.media');
306 if ($media
.data('media')) {
307 media_message
= $media
.data('media');
309 media_message
= new _kiwi
.view
.MediaMessage({el
: $media
[0]});
311 // Cache this MediaMessage instance for when it's opened again
312 $media
.data('media', media_message
);
315 media_message
.toggle();
319 // Cursor hovers over a message
320 msgEnter: function (event
) {
323 // Find a valid class that this element has
324 _
.each($(event
.currentTarget
).parent('.msg').attr('class').split(' '), function (css_class
) {
325 if (css_class
.match(/^nick_[a-z0-9]+/i)) {
326 nick_class
= css_class
;
330 // If no class was found..
331 if (!nick_class
) return;
333 $('.'+nick_class
).addClass('global_nick_highlight');
337 // Cursor leaves message
338 msgLeave: function (event
) {
341 // Find a valid class that this element has
342 _
.each($(event
.currentTarget
).parent('.msg').attr('class').split(' '), function (css_class
) {
343 if (css_class
.match(/^nick_[a-z0-9]+/i)) {
344 nick_class
= css_class
;
348 // If no class was found..
349 if (!nick_class
) return;
351 $('.'+nick_class
).removeClass('global_nick_highlight');