Including nick prefix into message objects
[KiwiIRC.git] / client / src / views / channel.js
CommitLineData
50ac472f 1_kiwi.view.Channel = _kiwi.view.Panel.extend({
9b807765 2 events: function(){
c794b877 3 var parent_events = this.constructor.__super__.events;
3499d625 4
9b807765
D
5 if(_.isFunction(parent_events)){
6 parent_events = parent_events();
7 }
8 return _.extend({}, parent_events, {
3499d625
D
9 'click .msg .nick' : 'nickClick',
10 "click .chan": "chanClick",
11 'click .media .open': 'mediaClick',
12 'mouseenter .msg .nick': 'msgEnter',
13 'mouseleave .msg .nick': 'msgLeave'
9b807765 14 });
dfb5209c
JA
15 },
16
50ac472f
D
17 initialize: function (options) {
18 this.initializePanel(options);
c794b877
D
19
20 // Container for all the messages
21 this.$messages = $('<div class="messages"></div>');
22 this.$el.append(this.$messages);
23
50ac472f
D
24 this.model.bind('change:topic', this.topic, this);
25
7d2a2771
D
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 () {
30 $(this).remove();
31 });
32 }
33 }, this);
34 }
660e1427 35
50ac472f
D
36 // Only show the loader if this is a channel (ie. not a query)
37 if (this.model.isChannel()) {
247dd7ac 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>');
50ac472f 39 }
c794b877
D
40
41 this.model.bind('msg', this.newMsg, this);
42 this.msg_count = 0;
50ac472f
D
43 },
44
c794b877 45
41a9c836
D
46 render: function () {
47 var that = this;
48
49 this.$messages.empty();
50 _.each(this.model.get('scrollback'), function (msg) {
51 that.newMsg(msg);
52 });
53 },
54
55
c794b877
D
56 newMsg: function (msg) {
57 var re, line_msg,
58 nick_colour_hex, nick_hex, is_highlight, msg_css_classes = '',
59 time_difference,
60 sb = this.model.get('scrollback'),
425efe7a 61 prev_msg = sb[sb.length-2],
e9c86682 62 network, hour, pm;
c794b877
D
63
64 // Nick highlight detecting
86f7df86 65 var nick = _kiwi.app.connections.active_connection.get('nick');
66 if ((new RegExp('(^|\\W)(' + escapeRegex(nick) + ')(\\W|$)', 'i')).test(msg.msg)) {
67 // Do not highlight the user's own input
68 if (msg.nick.localeCompare(nick) !== 0) {
69 is_highlight = true;
70 msg_css_classes += ' highlight';
71 }
c794b877
D
72 }
73
74 // Escape any HTML that may be in here
75 msg.msg = $('<div />').text(msg.msg).html();
76
77 // Make the channels clickable
425efe7a 78 if ((network = this.model.get('network'))) {
6547f118
CC
79 re = new RegExp('(^|\\s)([' + escapeRegex(network.get('channel_prefix')) + '][^ ,\\007]+)', 'g');
80 msg.msg = msg.msg.replace(re, function (m1, m2) {
81 return m2 + '<a class="chan" data-channel="' + _.escape(m1.trim()) + '">' + _.escape(m1.trim()) + '</a>';
425efe7a
JA
82 });
83 }
c794b877
D
84
85
86 // Parse any links found
87 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) {
88 var nice = url,
89 extra_html = '';
90
c67de46d
P
91 if (url.match(/^javascript:/)) {
92 return url;
93 }
94
c794b877
D
95 // Add the http if no protoocol was found
96 if (url.match(/^www\./)) {
97 url = 'http://' + url;
98 }
99
100 // Shorten the displayed URL if it's going to be too long
101 if (nice.length > 100) {
102 nice = nice.substr(0, 100) + '...';
103 }
104
105 // Get any media HTML if supported
106 extra_html = _kiwi.view.MediaMessage.buildHtml(url);
107
108 // Make the link clickable
109 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '</a>' + extra_html;
110 });
111
112
113 // Convert IRC formatting into HTML formatting
114 msg.msg = formatIRCMsg(msg.msg);
115
2eacc942
JA
116 // Replace text emoticons with images
117 if (_kiwi.global.settings.get('show_emoticons')) {
118 msg.msg = emoticonFromText(msg.msg);
119 }
c794b877
D
120
121 // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)
122 nick_colour_hex = (function (nick) {
123 var nick_int = 0, rgb;
124
125 _.map(nick.split(''), function (i) { nick_int += i.charCodeAt(0); });
126 rgb = hsl2rgb(nick_int % 255, 70, 35);
127 rgb = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16);
128
129 return '#' + rgb.toString(16);
130 })(msg.nick);
131
132 msg.nick_style = 'color:' + nick_colour_hex + ';';
133
134 // Generate a hex string from the nick to be used as a CSS class name
135 nick_hex = msg.nick_css_class = '';
136 if (msg.nick) {
137 _.map(msg.nick.split(''), function (char) {
138 nick_hex += char.charCodeAt(0).toString(16);
139 });
140 msg_css_classes += ' nick_' + nick_hex;
141 }
142
143 if (prev_msg) {
144 // Time difference between this message and the last (in minutes)
697a76c5 145 time_difference = (msg.time.getTime() - prev_msg.time.getTime())/1000/60;
c794b877
D
146 if (prev_msg.nick === msg.nick && time_difference < 1) {
147 msg_css_classes += ' repeated_nick';
148 }
149 }
150
151 // Build up and add the line
152 msg.msg_css_classes = msg_css_classes;
62d1e896
JA
153 if (_kiwi.global.settings.get('use_24_hour_timestamps')) {
154 msg.time_string = msg.time.getHours().toString().lpad(2, "0") + ":" + msg.time.getMinutes().toString().lpad(2, "0") + ":" + msg.time.getSeconds().toString().lpad(2, "0");
155 } else {
156 hour = msg.time.getHours();
157 pm = hour > 11;
36fb5f87
D
158
159 hour = hour % 12;
160 if (hour === 0)
161 hour = 12;
162
62d1e896
JA
163 if (pm) {
164 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"));
165 } else {
166 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"));
167 }
168 }
c794b877 169
c1c51f22
D
170 _kiwi.global.events.emit('message:display', {panel: this.model, message: msg})
171 .done(_.bind(function() {
172 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>';
173 this.$messages.append(_.template(line_msg, msg));
c794b877 174
c1c51f22
D
175 // Activity/alerts based on the type of new message
176 if (msg.type.match(/^action /)) {
177 this.alert('action');
c794b877 178
c1c51f22 179 } else if (is_highlight) {
c794b877 180 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
c1c51f22
D
181 _kiwi.app.view.favicon.newHighlight();
182 _kiwi.app.view.playSound('highlight');
183 _kiwi.app.view.showNotification(this.model.get('name'), msg.msg);
184 this.alert('highlight');
185
186 } else {
187 // If this is the active panel, send an alert out
188 if (this.model.isActive()) {
189 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
190 }
191 this.alert('activity');
c794b877 192 }
c794b877 193
c1c51f22
D
194 if (this.model.isQuery() && !this.model.isActive()) {
195 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
ee2f0962 196
c1c51f22
D
197 // Highlights have already been dealt with above
198 if (!is_highlight) {
199 _kiwi.app.view.favicon.newHighlight();
200 }
ee2f0962 201
c1c51f22
D
202 _kiwi.app.view.showNotification(this.model.get('name'), msg.msg);
203 _kiwi.app.view.playSound('highlight');
204 }
c794b877 205
c1c51f22
D
206 // Update the activity counters
207 (function () {
208 // Only inrement the counters if we're not the active panel
209 if (this.model.isActive()) return;
c794b877 210
c1c51f22
D
211 var $act = this.model.tab.find('.activity'),
212 count_all_activity = _kiwi.global.settings.get('count_all_activity'),
213 exclude_message_types;
7ba064d9 214
c1c51f22
D
215 // Set the default config value
216 if (typeof count_all_activity === 'undefined') {
217 count_all_activity = false;
218 }
223d53e5 219
c1c51f22
D
220 // Do not increment the counter for these message types
221 exclude_message_types = [
222 'action join',
223 'action quit',
224 'action part',
225 'action kick',
226 'action nick',
227 'action mode'
228 ];
229
230 if (count_all_activity || _.indexOf(exclude_message_types, msg.type) === -1) {
231 $act.text((parseInt($act.text(), 10) || 0) + 1);
232 }
223d53e5 233
c1c51f22
D
234 if ($act.text() === '0') {
235 $act.addClass('zero');
236 } else {
237 $act.removeClass('zero');
238 }
239 }).apply(this);
c794b877 240
c1c51f22 241 if(this.model.isActive()) this.scrollToBottom();
c794b877 242
c1c51f22
D
243 // Make sure our DOM isn't getting too large (Acts as scrollback)
244 this.msg_count++;
245 if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {
246 $('.msg:first', this.$messages).remove();
247 this.msg_count--;
248 }
249 }, this));
50ac472f
D
250 },
251
c794b877 252
50ac472f
D
253 topic: function (topic) {
254 if (typeof topic !== 'string' || !topic) {
255 topic = this.model.get("topic");
256 }
257
9ec48c54 258 this.model.addMsg('', styleText('channel_topic', {text: translateText('client_views_channel_topic', [this.model.get('name'), topic]), channel: this.model.get('name')}), 'topic');
50ac472f
D
259
260 // If this is the active channel then update the topic bar
261 if (_kiwi.app.panels().active === this) {
262 _kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));
263 }
dfb5209c
JA
264 },
265
266 // Click on a nickname
267 nickClick: function (event) {
268 var nick = $(event.currentTarget).text(),
269 members = this.model.get('members'),
d62fa271 270 are_we_an_op = !!members.getByNick(_kiwi.app.connections.active_connection.get('nick')).get('is_op'),
dfb5209c
JA
271 member, query, userbox, menubox;
272
273 if (members) {
274 member = members.getByNick(nick);
275 if (member) {
dfb5209c 276 userbox = new _kiwi.view.UserBox();
d62fa271
D
277 userbox.setTargets(member, this.model);
278 userbox.displayOpItems(are_we_an_op);
0826460d 279
dfb5209c
JA
280 menubox = new _kiwi.view.MenuBox(member.get('nick') || 'User');
281 menubox.addItem('userbox', userbox.$el);
279bf34b 282 menubox.showFooter(false);
dfb5209c 283 menubox.show();
0826460d 284
dfb5209c
JA
285 // Position the userbox + menubox
286 (function() {
287 var t = event.pageY,
288 m_bottom = t + menubox.$el.outerHeight(), // Where the bottom of menu will be
289 memberlist_bottom = this.$el.parent().offset().top + this.$el.parent().outerHeight();
290
291 // If the bottom of the userbox is going to be too low.. raise it
292 if (m_bottom > memberlist_bottom){
293 t = memberlist_bottom - menubox.$el.outerHeight();
294 }
295
296 // Set the new positon
297 menubox.$el.offset({
298 left: event.clientX,
299 top: t
300 });
301 }).call(this);
302 }
303 }
3499d625
D
304 },
305
306
307 chanClick: function (event) {
425efe7a
JA
308 var target = (event.target) ? $(event.target).data('channel') : $(event.srcElement).data('channel');
309
310 _kiwi.app.connections.active_connection.gateway.join(target);
3499d625
D
311 },
312
313
314 mediaClick: function (event) {
315 var $media = $(event.target).parents('.media');
316 var media_message;
317
318 if ($media.data('media')) {
319 media_message = $media.data('media');
320 } else {
321 media_message = new _kiwi.view.MediaMessage({el: $media[0]});
322
323 // Cache this MediaMessage instance for when it's opened again
324 $media.data('media', media_message);
325 }
326
327 media_message.toggle();
328 },
329
330
331 // Cursor hovers over a message
332 msgEnter: function (event) {
333 var nick_class;
334
335 // Find a valid class that this element has
336 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
337 if (css_class.match(/^nick_[a-z0-9]+/i)) {
338 nick_class = css_class;
339 }
340 });
341
342 // If no class was found..
343 if (!nick_class) return;
344
345 $('.'+nick_class).addClass('global_nick_highlight');
346 },
347
348
349 // Cursor leaves message
350 msgLeave: function (event) {
351 var nick_class;
352
353 // Find a valid class that this element has
354 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
355 if (css_class.match(/^nick_[a-z0-9]+/i)) {
356 nick_class = css_class;
357 }
358 });
359
360 // If no class was found..
361 if (!nick_class) return;
362
363 $('.'+nick_class).removeClass('global_nick_highlight');
364 },
dfb5209c 365});