Merge branch 'development' of https://github.com/tonylechner/KiwiIRC into tonylechner...
[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'),
61 prev_msg = sb[sb.length-2];
62
63 // Nick highlight detecting
64 if ((new RegExp('(^|\\W)(' + escapeRegex(_kiwi.app.connections.active_connection.get('nick')) + ')(\\W|$)', 'i')).test(msg.msg)) {
65 is_highlight = true;
66 msg_css_classes += ' highlight';
67 }
68
69 // Escape any HTML that may be in here
70 msg.msg = $('<div />').text(msg.msg).html();
71
72 // Make the channels clickable
73 re = new RegExp('(?:^|\\s)([' + escapeRegex(_kiwi.gateway.get('channel_prefix')) + '][^ ,\\007]+)', 'g');
74 msg.msg = msg.msg.replace(re, function (match) {
75 return '<a class="chan" data-channel="' + match.trim() + '">' + match + '</a>';
76 });
77
78
79 // Parse any links found
80 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) {
81 var nice = url,
82 extra_html = '';
83
84 // Add the http if no protoocol was found
85 if (url.match(/^www\./)) {
86 url = 'http://' + url;
87 }
88
89 // Shorten the displayed URL if it's going to be too long
90 if (nice.length > 100) {
91 nice = nice.substr(0, 100) + '...';
92 }
93
94 // Get any media HTML if supported
95 extra_html = _kiwi.view.MediaMessage.buildHtml(url);
96
97 // Make the link clickable
98 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '</a>' + extra_html;
99 });
100
101
102 // Convert IRC formatting into HTML formatting
103 msg.msg = formatIRCMsg(msg.msg);
104
2eacc942
JA
105 // Replace text emoticons with images
106 if (_kiwi.global.settings.get('show_emoticons')) {
107 msg.msg = emoticonFromText(msg.msg);
108 }
c794b877
D
109
110 // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)
111 nick_colour_hex = (function (nick) {
112 var nick_int = 0, rgb;
113
114 _.map(nick.split(''), function (i) { nick_int += i.charCodeAt(0); });
115 rgb = hsl2rgb(nick_int % 255, 70, 35);
116 rgb = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16);
117
118 return '#' + rgb.toString(16);
119 })(msg.nick);
120
121 msg.nick_style = 'color:' + nick_colour_hex + ';';
122
123 // Generate a hex string from the nick to be used as a CSS class name
124 nick_hex = msg.nick_css_class = '';
125 if (msg.nick) {
126 _.map(msg.nick.split(''), function (char) {
127 nick_hex += char.charCodeAt(0).toString(16);
128 });
129 msg_css_classes += ' nick_' + nick_hex;
130 }
131
132 if (prev_msg) {
133 // Time difference between this message and the last (in minutes)
697a76c5 134 time_difference = (msg.time.getTime() - prev_msg.time.getTime())/1000/60;
c794b877
D
135 if (prev_msg.nick === msg.nick && time_difference < 1) {
136 msg_css_classes += ' repeated_nick';
137 }
138 }
139
140 // Build up and add the line
141 msg.msg_css_classes = msg_css_classes;
697a76c5
JA
142 msg.time_string = msg.time.getHours().toString().lpad(2, "0") + ":" + msg.time.getMinutes().toString().lpad(2, "0") + ":" + msg.time.getSeconds().toString().lpad(2, "0");
143 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>';
c794b877
D
144 this.$messages.append(_.template(line_msg, msg));
145
146 // Activity/alerts based on the type of new message
147 if (msg.type.match(/^action /)) {
148 this.alert('action');
149
150 } else if (is_highlight) {
151 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
152 _kiwi.app.view.favicon.newHighlight();
153 _kiwi.app.view.playSound('highlight');
ee2f0962 154 _kiwi.app.view.showNotification(this.model.get('name'), msg.msg);
c794b877
D
155 this.alert('highlight');
156
157 } else {
158 // If this is the active panel, send an alert out
159 if (this.model.isActive()) {
160 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
161 }
162 this.alert('activity');
163 }
164
165 if (this.model.isQuery() && !this.model.isActive()) {
166 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
ee2f0962
D
167
168 // Highlights have already been dealt with above
c794b877
D
169 if (!is_highlight) {
170 _kiwi.app.view.favicon.newHighlight();
171 }
ee2f0962
D
172
173 _kiwi.app.view.showNotification(this.model.get('name'), msg.msg);
c794b877
D
174 _kiwi.app.view.playSound('highlight');
175 }
176
177 // Update the activity counters
178 (function () {
179 // Only inrement the counters if we're not the active panel
180 if (this.model.isActive()) return;
181
182 var $act = this.model.tab.find('.activity');
223d53e5
TL
183 var count_joins_parts = _kiwi.global.settings.get('count_joins_parts');
184 if (typeof count_joins_parts === 'undefined') {
185 count_joins_parts = true;
186 }
187
188 if (count_joins_parts || msg.type === 'privmsg') {
189 $act.text((parseInt($act.text(), 10) || 0) + 1);
190 } else {
191 $act.text((parseInt($act.text(), 10) || 0));
192 }
193
c794b877
D
194 if ($act.text() === '0') {
195 $act.addClass('zero');
196 } else {
197 $act.removeClass('zero');
198 }
199 }).apply(this);
200
201 this.scrollToBottom();
202
203 // Make sure our DOM isn't getting too large (Acts as scrollback)
204 this.msg_count++;
205 if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {
206 $('.msg:first', this.$messages).remove();
207 this.msg_count--;
208 }
50ac472f
D
209 },
210
c794b877 211
50ac472f
D
212 topic: function (topic) {
213 if (typeof topic !== 'string' || !topic) {
214 topic = this.model.get("topic");
215 }
216
247dd7ac 217 this.model.addMsg('', '== ' + _kiwi.global.i18n.translate('client_views_channel_topic').fetch(this.model.get('name'), topic), 'topic');
50ac472f
D
218
219 // If this is the active channel then update the topic bar
220 if (_kiwi.app.panels().active === this) {
221 _kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));
222 }
dfb5209c
JA
223 },
224
225 // Click on a nickname
226 nickClick: function (event) {
227 var nick = $(event.currentTarget).text(),
228 members = this.model.get('members'),
229 member, query, userbox, menubox;
230
231 if (members) {
232 member = members.getByNick(nick);
233 if (member) {
dfb5209c
JA
234 userbox = new _kiwi.view.UserBox();
235 userbox.member = member;
236 userbox.channel = this.model;
237
0826460d
D
238 // Hide the op related items if we're not an op
239 if (!members.getByNick(_kiwi.app.connections.active_connection.get('nick')).get('is_op')) {
dfb5209c
JA
240 userbox.$el.children('.if_op').remove();
241 }
0826460d 242
dfb5209c
JA
243 menubox = new _kiwi.view.MenuBox(member.get('nick') || 'User');
244 menubox.addItem('userbox', userbox.$el);
dfb5209c 245 menubox.show();
0826460d 246
dfb5209c
JA
247 // Position the userbox + menubox
248 (function() {
249 var t = event.pageY,
250 m_bottom = t + menubox.$el.outerHeight(), // Where the bottom of menu will be
251 memberlist_bottom = this.$el.parent().offset().top + this.$el.parent().outerHeight();
252
253 // If the bottom of the userbox is going to be too low.. raise it
254 if (m_bottom > memberlist_bottom){
255 t = memberlist_bottom - menubox.$el.outerHeight();
256 }
257
258 // Set the new positon
259 menubox.$el.offset({
260 left: event.clientX,
261 top: t
262 });
263 }).call(this);
264 }
265 }
3499d625
D
266 },
267
268
269 chanClick: function (event) {
270 if (event.target) {
271 _kiwi.gateway.join(null, $(event.target).data('channel'));
272 } else {
273 // IE...
274 _kiwi.gateway.join(null, $(event.srcElement).data('channel'));
275 }
276 },
277
278
279 mediaClick: function (event) {
280 var $media = $(event.target).parents('.media');
281 var media_message;
282
283 if ($media.data('media')) {
284 media_message = $media.data('media');
285 } else {
286 media_message = new _kiwi.view.MediaMessage({el: $media[0]});
287
288 // Cache this MediaMessage instance for when it's opened again
289 $media.data('media', media_message);
290 }
291
292 media_message.toggle();
293 },
294
295
296 // Cursor hovers over a message
297 msgEnter: function (event) {
298 var nick_class;
299
300 // Find a valid class that this element has
301 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
302 if (css_class.match(/^nick_[a-z0-9]+/i)) {
303 nick_class = css_class;
304 }
305 });
306
307 // If no class was found..
308 if (!nick_class) return;
309
310 $('.'+nick_class).addClass('global_nick_highlight');
311 },
312
313
314 // Cursor leaves message
315 msgLeave: function (event) {
316 var nick_class;
317
318 // Find a valid class that this element has
319 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
320 if (css_class.match(/^nick_[a-z0-9]+/i)) {
321 nick_class = css_class;
322 }
323 });
324
325 // If no class was found..
326 if (!nick_class) return;
327
328 $('.'+nick_class).removeClass('global_nick_highlight');
329 },
dfb5209c 330});