Remove template_vars
[KiwiIRC.git] / client / assets / src / views / panel.js
1 _kiwi.view.Panel = Backbone.View.extend({
2 tagName: "div",
3 className: "panel messages",
4
5 events: {
6 "click .chan": "chanClick",
7 'click .media .open': 'mediaClick',
8 'mouseenter .msg .nick': 'msgEnter',
9 'mouseleave .msg .nick': 'msgLeave'
10 },
11
12 initialize: function (options) {
13 this.initializePanel(options);
14 },
15
16 initializePanel: function (options) {
17 this.$el.css('display', 'none');
18 options = options || {};
19
20 // Containing element for this panel
21 if (options.container) {
22 this.$container = $(options.container);
23 } else {
24 this.$container = $('#kiwi .panels .container1');
25 }
26
27 this.$el.appendTo(this.$container);
28
29 this.alert_level = 0;
30
31 this.model.bind('msg', this.newMsg, this);
32 this.msg_count = 0;
33
34 this.model.set({"view": this}, {"silent": true});
35 },
36
37 render: function () {
38 var that = this;
39
40 this.$el.empty();
41 _.each(this.model.get('scrollback'), function (msg) {
42 that.newMsg(msg);
43 });
44 },
45
46 newMsg: function (msg) {
47 var re, line_msg, $this = this.$el,
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.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('* People are talking!');
137 _kiwi.app.view.playSound('highlight');
138 this.alert('highlight');
139
140 } else {
141 // If this is the active panel, send an alert out
142 if (this.model.isActive()) {
143 _kiwi.app.view.alertWindow('* People are talking!');
144 }
145 this.alert('activity');
146 }
147
148 if (this.model.isQuery() && !this.model.isActive()) {
149 _kiwi.app.view.alertWindow('* People are talking!');
150 _kiwi.app.view.playSound('highlight');
151 }
152
153 // Update the activity counters
154 (function () {
155 // Only inrement the counters if we're not the active panel
156 if (this.model.isActive()) return;
157
158 var $act = this.model.tab.find('.activity');
159 $act.text((parseInt($act.text(), 10) || 0) + 1);
160 if ($act.text() === '0') {
161 $act.addClass('zero');
162 } else {
163 $act.removeClass('zero');
164 }
165 }).apply(this);
166
167 this.scrollToBottom();
168
169 // Make sure our DOM isn't getting too large (Acts as scrollback)
170 this.msg_count++;
171 if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {
172 $('.msg:first', this.$el).remove();
173 this.msg_count--;
174 }
175 },
176 chanClick: function (event) {
177 if (event.target) {
178 _kiwi.gateway.join(null, $(event.target).data('channel'));
179 } else {
180 // IE...
181 _kiwi.gateway.join(null, $(event.srcElement).data('channel'));
182 }
183 },
184
185 mediaClick: function (event) {
186 var $media = $(event.target).parents('.media');
187 var media_message;
188
189 if ($media.data('media')) {
190 media_message = $media.data('media');
191 } else {
192 media_message = new _kiwi.view.MediaMessage({el: $media[0]});
193
194 // Cache this MediaMessage instance for when it's opened again
195 $media.data('media', media_message);
196 }
197
198 media_message.open();
199 },
200
201 // Cursor hovers over a message
202 msgEnter: function (event) {
203 var nick_class;
204
205 // Find a valid class that this element has
206 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
207 if (css_class.match(/^nick_[a-z0-9]+/i)) {
208 nick_class = css_class;
209 }
210 });
211
212 // If no class was found..
213 if (!nick_class) return;
214
215 $('.'+nick_class).addClass('global_nick_highlight');
216 },
217
218 // Cursor leaves message
219 msgLeave: function (event) {
220 var nick_class;
221
222 // Find a valid class that this element has
223 _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
224 if (css_class.match(/^nick_[a-z0-9]+/i)) {
225 nick_class = css_class;
226 }
227 });
228
229 // If no class was found..
230 if (!nick_class) return;
231
232 $('.'+nick_class).removeClass('global_nick_highlight');
233 },
234
235 show: function () {
236 var $this = this.$el;
237
238 // Hide all other panels and show this one
239 this.$container.children('.panel').css('display', 'none');
240 $this.css('display', 'block');
241
242 // Show this panels memberlist
243 var members = this.model.get("members");
244 if (members) {
245 $('#kiwi .memberlists').removeClass('disabled');
246 members.view.show();
247 } else {
248 // Memberlist not found for this panel, hide any active ones
249 $('#kiwi .memberlists').addClass('disabled').children().removeClass('active');
250 }
251
252 // Remove any alerts and activity counters for this panel
253 this.alert('none');
254 this.model.tab.find('.activity').text('0').addClass('zero');
255
256 _kiwi.app.panels.trigger('active', this.model, _kiwi.app.panels().active);
257 this.model.trigger('active', this.model);
258
259 _kiwi.app.view.doLayout();
260
261 this.scrollToBottom(true);
262 },
263
264
265 alert: function (level) {
266 // No need to highlight if this si the active panel
267 if (this.model == _kiwi.app.panels().active) return;
268
269 var types, type_idx;
270 types = ['none', 'action', 'activity', 'highlight'];
271
272 // Default alert level
273 level = level || 'none';
274
275 // If this alert level does not exist, assume clearing current level
276 type_idx = _.indexOf(types, level);
277 if (!type_idx) {
278 level = 'none';
279 type_idx = 0;
280 }
281
282 // Only 'upgrade' the alert. Never down (unless clearing)
283 if (type_idx !== 0 && type_idx <= this.alert_level) {
284 return;
285 }
286
287 // Clear any existing levels
288 this.model.tab.removeClass(function (i, css) {
289 return (css.match(/\balert_\S+/g) || []).join(' ');
290 });
291
292 // Add the new level if there is one
293 if (level !== 'none') {
294 this.model.tab.addClass('alert_' + level);
295 }
296
297 this.alert_level = type_idx;
298 },
299
300
301 // Scroll to the bottom of the panel
302 scrollToBottom: function (force_down) {
303 // If this isn't the active panel, don't scroll
304 if (this.model !== _kiwi.app.panels().active) return;
305
306 // Don't scroll down if we're scrolled up the panel a little
307 if (force_down || this.$container.scrollTop() + this.$container.height() > this.$el.outerHeight() - 150) {
308 this.$container[0].scrollTop = this.$container[0].scrollHeight;
309 }
310 }
311 });