Merge branch 'pr/374' into notificationsMerge2
[KiwiIRC.git] / client / assets / 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
D
45
46 newMsg: function (msg) {
47 var re, line_msg,
48 nick_colour_hex, nick_hex, is_highlight, msg_css_classes = '',
49 time_difference,
50 sb = this.model.get('scrollback'),
51 prev_msg = sb[sb.length-2];
52
53 // Nick highlight detecting
54 if ((new RegExp('(^|\\W)(' + escapeRegex(_kiwi.app.connections.active_connection.get('nick')) + ')(\\W|$)', 'i')).test(msg.msg)) {
55 is_highlight = true;
56 msg_css_classes += ' highlight';
57 }
58
59 // Escape any HTML that may be in here
60 msg.msg = $('<div />').text(msg.msg).html();
61
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>';
66 });
67
68
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) {
71 var nice = url,
72 extra_html = '';
73
74 // Add the http if no protoocol was found
75 if (url.match(/^www\./)) {
76 url = 'http://' + url;
77 }
78
79 // Shorten the displayed URL if it's going to be too long
80 if (nice.length > 100) {
81 nice = nice.substr(0, 100) + '...';
82 }
83
84 // Get any media HTML if supported
85 extra_html = _kiwi.view.MediaMessage.buildHtml(url);
86
87 // Make the link clickable
88 return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '</a>' + extra_html;
89 });
90
91
92 // Convert IRC formatting into HTML formatting
93 msg.msg = formatIRCMsg(msg.msg);
94
95
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;
99
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);
103
104 return '#' + rgb.toString(16);
105 })(msg.nick);
106
107 msg.nick_style = 'color:' + nick_colour_hex + ';';
108
109 // Generate a hex string from the nick to be used as a CSS class name
110 nick_hex = msg.nick_css_class = '';
111 if (msg.nick) {
112 _.map(msg.nick.split(''), function (char) {
113 nick_hex += char.charCodeAt(0).toString(16);
114 });
115 msg_css_classes += ' nick_' + nick_hex;
116 }
117
118 if (prev_msg) {
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';
123 }
124 }
125
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.$messages.append(_.template(line_msg, msg));
130
131 // Activity/alerts based on the type of new message
132 if (msg.type.match(/^action /)) {
133 this.alert('action');
134
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');
3aecdb79 139 _kiwi.app.view.showNotification(msg.nick, msg.msg);
c794b877
D
140 this.alert('highlight');
141
142 } else {
143 // If this is the active panel, send an alert out
144 if (this.model.isActive()) {
145 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
146 }
147 this.alert('activity');
148 }
149
150 if (this.model.isQuery() && !this.model.isActive()) {
151 _kiwi.app.view.alertWindow('* ' + _kiwi.global.i18n.translate('client_views_panel_activity').fetch());
152 if (!is_highlight) {
153 _kiwi.app.view.favicon.newHighlight();
154 }
155 _kiwi.app.view.playSound('highlight');
156 }
157
158 // Update the activity counters
159 (function () {
160 // Only inrement the counters if we're not the active panel
161 if (this.model.isActive()) return;
162
163 var $act = this.model.tab.find('.activity');
164 $act.text((parseInt($act.text(), 10) || 0) + 1);
165 if ($act.text() === '0') {
166 $act.addClass('zero');
167 } else {
168 $act.removeClass('zero');
169 }
170 }).apply(this);
171
172 this.scrollToBottom();
173
174 // Make sure our DOM isn't getting too large (Acts as scrollback)
175 this.msg_count++;
176 if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {
177 $('.msg:first', this.$messages).remove();
178 this.msg_count--;
179 }
50ac472f
D
180 },
181
c794b877 182
50ac472f
D
183 topic: function (topic) {
184 if (typeof topic !== 'string' || !topic) {
185 topic = this.model.get("topic");
186 }
187
247dd7ac 188 this.model.addMsg('', '== ' + _kiwi.global.i18n.translate('client_views_channel_topic').fetch(this.model.get('name'), topic), 'topic');
50ac472f
D
189
190 // If this is the active channel then update the topic bar
191 if (_kiwi.app.panels().active === this) {
192 _kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));
193 }
dfb5209c
JA
194 },
195
196 // Click on a nickname
197 nickClick: function (event) {
198 var nick = $(event.currentTarget).text(),
199 members = this.model.get('members'),
200 member, query, userbox, menubox;
201
202 if (members) {
203 member = members.getByNick(nick);
204 if (member) {
dfb5209c
JA
205 userbox = new _kiwi.view.UserBox();
206 userbox.member = member;
207 userbox.channel = this.model;
208
0826460d
D
209 // Hide the op related items if we're not an op
210 if (!members.getByNick(_kiwi.app.connections.active_connection.get('nick')).get('is_op')) {
dfb5209c
JA
211 userbox.$el.children('.if_op').remove();
212 }
0826460d 213
dfb5209c
JA
214 menubox = new _kiwi.view.MenuBox(member.get('nick') || 'User');
215 menubox.addItem('userbox', userbox.$el);
dfb5209c 216 menubox.show();
0826460d 217
dfb5209c
JA
218 // Position the userbox + menubox
219 (function() {
220 var t = event.pageY,
221 m_bottom = t + menubox.$el.outerHeight(), // Where the bottom of menu will be
222 memberlist_bottom = this.$el.parent().offset().top + this.$el.parent().outerHeight();
223
224 // If the bottom of the userbox is going to be too low.. raise it
225 if (m_bottom > memberlist_bottom){
226 t = memberlist_bottom - menubox.$el.outerHeight();
227 }
228
229 // Set the new positon
230 menubox.$el.offset({
231 left: event.clientX,
232 top: t
233 });
234 }).call(this);
235 }
236 }
3499d625
D
237 },
238
239
240 chanClick: function (event) {
241 if (event.target) {
242 _kiwi.gateway.join(null, $(event.target).data('channel'));
243 } else {
244 // IE...
245 _kiwi.gateway.join(null, $(event.srcElement).data('channel'));
246 }
247 },
248
249
250 mediaClick: function (event) {
251 var $media = $(event.target).parents('.media');
252 var media_message;
253
254 if ($media.data('media')) {
255 media_message = $media.data('media');
256 } else {
257 media_message = new _kiwi.view.MediaMessage({el: $media[0]});
258
259 // Cache this MediaMessage instance for when it's opened again
260 $media.data('media', media_message);
261 }
262
263 media_message.toggle();
264 },
265
266
267 // Cursor hovers over a message
268 msgEnter: function (event) {
269 var nick_class;
270
271 // Find a valid class that this element has
272 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
273 if (css_class.match(/^nick_[a-z0-9]+/i)) {
274 nick_class = css_class;
275 }
276 });
277
278 // If no class was found..
279 if (!nick_class) return;
280
281 $('.'+nick_class).addClass('global_nick_highlight');
282 },
283
284
285 // Cursor leaves message
286 msgLeave: function (event) {
287 var nick_class;
288
289 // Find a valid class that this element has
290 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
291 if (css_class.match(/^nick_[a-z0-9]+/i)) {
292 nick_class = css_class;
293 }
294 });
295
296 // If no class was found..
297 if (!nick_class) return;
298
299 $('.'+nick_class).removeClass('global_nick_highlight');
300 },
dfb5209c 301});