X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;f=client%2Fassets%2Fdev%2Fview.js;h=b104c7c002b47a805fd840d0b1985538fdefd7e4;hb=0e546dd4622fbb302b822dbc247f639d51a94c97;hp=64889d29cc89b72689bad617d3bdb0bb657cfae2;hpb=cee337bb57e62637ff0dc0a4a645d792a8755aa7;p=KiwiIRC.git
diff --git a/client/assets/dev/view.js b/client/assets/dev/view.js
index 64889d2..b104c7c 100644
--- a/client/assets/dev/view.js
+++ b/client/assets/dev/view.js
@@ -1,7 +1,7 @@
/*jslint white:true, regexp: true, nomen: true, devel: true, undef: true, browser: true, continue: true, sloppy: true, forin: true, newcap: true, plusplus: true, maxerr: 50, indent: 4 */
/*global kiwi */
-kiwi.view.MemberList = Backbone.View.extend({
+_kiwi.view.MemberList = Backbone.View.extend({
tagName: "ul",
events: {
"click .nick": "nickClick"
@@ -14,19 +14,54 @@ kiwi.view.MemberList = Backbone.View.extend({
var $this = $(this.el);
$this.empty();
this.model.forEach(function (member) {
- $('
' + member.get("prefix") + '' + member.get("nick") + '')
+ var prefix_css_class = (member.get('modes') || []).join(' ');
+ $('' + member.get("prefix") + '' + member.get("nick") + '')
.appendTo($this)
.data('member', member);
});
},
- nickClick: function (x) {
- var target = $(x.currentTarget).parent('li'),
- member = target.data('member'),
- userbox = new kiwi.view.UserBox();
+ nickClick: function (event) {
+ var $target = $(event.currentTarget).parent('li'),
+ member = $target.data('member'),
+ userbox;
+ event.stopPropagation();
+
+ // If the userbox already exists here, hide it
+ if ($target.find('.userbox').length > 0) {
+ $('.userbox', this.$el).remove();
+ return;
+ }
+
+ userbox = new _kiwi.view.UserBox();
userbox.member = member;
- $('.userbox', this.$el).remove();
- target.append(userbox.$el);
+ userbox.channel = this.model.channel;
+
+ if (!this.model.getByNick(_kiwi.gateway.get('nick')).get('is_op')) {
+ userbox.$el.children('.if_op').remove();
+ }
+
+ var menu = new _kiwi.view.MenuBox(member.get('nick') || 'User');
+ menu.addItem('userbox', userbox.$el);
+ menu.show();
+
+ // Position the userbox + menubox
+ (function() {
+ var t = event.pageY,
+ m_bottom = t + menu.$el.outerHeight(), // Where the bottom of menu will be
+ memberlist_bottom = this.$el.parent().offset().top + this.$el.parent().outerHeight();
+
+ // If the bottom of the userbox is going to be too low.. raise it
+ if (m_bottom > memberlist_bottom){
+ t = memberlist_bottom - menu.$el.outerHeight();
+ }
+
+ // Set the new positon
+ menu.$el.offset({
+ left: _kiwi.app.view.$el.width() - menu.$el.outerWidth() - 20,
+ top: t
+ });
+ }).call(this);
},
show: function () {
$('#memberlists').children().removeClass('active');
@@ -36,11 +71,17 @@ kiwi.view.MemberList = Backbone.View.extend({
-kiwi.view.UserBox = Backbone.View.extend({
+_kiwi.view.UserBox = Backbone.View.extend({
events: {
'click .query': 'queryClick',
'click .info': 'infoClick',
- 'click .slap': 'slapClick'
+ 'click .slap': 'slapClick',
+ 'click .op': 'opClick',
+ 'click .deop': 'deopClick',
+ 'click .voice': 'voiceClick',
+ 'click .devoice': 'devoiceClick',
+ 'click .kick': 'kickClick',
+ 'click .ban': 'banClick'
},
initialize: function () {
@@ -48,22 +89,47 @@ kiwi.view.UserBox = Backbone.View.extend({
},
queryClick: function (event) {
- var panel = new kiwi.model.Channel({name: this.member.get('nick')});
- panel.set('members', undefined);
- kiwi.app.panels.add(panel);
+ var panel = new _kiwi.model.Query({name: this.member.get('nick')});
+ _kiwi.app.panels.add(panel);
panel.view.show();
},
infoClick: function (event) {
- kiwi.app.controlbox.processInput('/whois ' + this.member.get('nick'));
+ _kiwi.app.controlbox.processInput('/whois ' + this.member.get('nick'));
},
slapClick: function (event) {
- kiwi.app.controlbox.processInput('/slap ' + this.member.get('nick'));
+ _kiwi.app.controlbox.processInput('/slap ' + this.member.get('nick'));
+ },
+
+ opClick: function (event) {
+ _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' +o ' + this.member.get('nick'));
+ },
+
+ deopClick: function (event) {
+ _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' -o ' + this.member.get('nick'));
+ },
+
+ voiceClick: function (event) {
+ _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' +v ' + this.member.get('nick'));
+ },
+
+ devoiceClick: function (event) {
+ _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' -v ' + this.member.get('nick'));
+ },
+
+ kickClick: function (event) {
+ // TODO: Enable the use of a custom kick message
+ _kiwi.app.controlbox.processInput('/kick ' + this.member.get('nick') + ' Bye!');
+ },
+
+ banClick: function (event) {
+ // TODO: Set ban on host, not just on nick
+ _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' +b ' + this.member.get('nick') + '!*');
}
});
-kiwi.view.NickChangeBox = Backbone.View.extend({
+_kiwi.view.NickChangeBox = Backbone.View.extend({
events: {
'submit': 'changeNick',
'click .cancel': 'close'
@@ -75,10 +141,10 @@ kiwi.view.NickChangeBox = Backbone.View.extend({
render: function () {
// Add the UI component and give it focus
- kiwi.app.controlbox.$el.prepend(this.$el);
+ _kiwi.app.controlbox.$el.prepend(this.$el);
this.$el.find('input').focus();
- this.$el.css('bottom', kiwi.app.controlbox.$el.outerHeight(true));
+ this.$el.css('bottom', _kiwi.app.controlbox.$el.outerHeight(true));
},
close: function () {
@@ -88,40 +154,67 @@ kiwi.view.NickChangeBox = Backbone.View.extend({
changeNick: function (event) {
var that = this;
- kiwi.gateway.changeNick(this.$el.find('input').val(), function (err, val) {
+ _kiwi.gateway.changeNick(this.$el.find('input').val(), function (err, val) {
that.close();
});
return false;
}
});
-kiwi.view.ServerSelect = function () {
+_kiwi.view.ServerSelect = function () {
// Are currently showing all the controlls or just a nick_change box?
var state = 'all';
var model = Backbone.View.extend({
events: {
'submit form': 'submitForm',
- 'click .show_more': 'showMore'
+ 'click .show_more': 'showMore',
+ 'change .have_pass input': 'showPass'
},
initialize: function () {
+ var that = this;
+
this.$el = $($('#tmpl_server_select').html());
- kiwi.gateway.bind('onconnect', this.networkConnected, this);
- kiwi.gateway.bind('connecting', this.networkConnecting, this);
+ // Remove the 'more' link if the server has disabled server changing
+ if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {
+ if (!_kiwi.app.server_settings.connection.allow_change) {
+ this.$el.find('.show_more').remove();
+ this.$el.addClass('single_server');
+ }
+ }
+
- kiwi.gateway.bind('onirc_error', function (data) {
+ _kiwi.gateway.bind('onconnect', this.networkConnected, this);
+ _kiwi.gateway.bind('connecting', this.networkConnecting, this);
+
+ _kiwi.gateway.bind('onirc_error', function (data) {
$('button', this.$el).attr('disabled', null);
if (data.error == 'nickname_in_use') {
this.setStatus('Nickname already taken');
this.show('nick_change');
}
+
+ if (data.error == 'password_mismatch') {
+ this.setStatus('Incorrect Password');
+ this.show('nick_change');
+ that.$el.find('.password').select();
+ }
}, this);
},
submitForm: function (event) {
+ event.preventDefault();
+
+ // Make sure a nick is chosen
+ if (!$('input.nick', this.$el).val().trim()) {
+ this.setStatus('Select a nickname first!');
+ $('input.nick', this.$el).select();
+ return;
+ }
+
if (state === 'nick_change') {
this.submitNickChange(event);
} else {
@@ -129,7 +222,7 @@ kiwi.view.ServerSelect = function () {
}
$('button', this.$el).attr('disabled', 1);
- return false;
+ return;
},
submitLogin: function (event) {
@@ -137,29 +230,38 @@ kiwi.view.ServerSelect = function () {
if ($('button', this.$el).attr('disabled')) return;
var values = {
- nick: $('.nick', this.$el).val(),
- server: $('.server', this.$el).val(),
- port: $('.port', this.$el).val(),
- ssl: $('.ssl', this.$el).prop('checked'),
- password: $('.password', this.$el).val(),
- channel: $('.channel', this.$el).val()
+ nick: $('input.nick', this.$el).val(),
+ server: $('input.server', this.$el).val(),
+ port: $('input.port', this.$el).val(),
+ ssl: $('input.ssl', this.$el).prop('checked'),
+ password: $('input.password', this.$el).val(),
+ channel: $('input.channel', this.$el).val(),
+ channel_key: $('input.channel_key', this.$el).val()
};
this.trigger('server_connect', values);
},
submitNickChange: function (event) {
- kiwi.gateway.changeNick($('.nick', this.$el).val());
+ _kiwi.gateway.changeNick($('input.nick', this.$el).val());
this.networkConnecting();
},
+ showPass: function (event) {
+ if (this.$el.find('tr.have_pass input').is(':checked')) {
+ this.$el.find('tr.pass').show().find('input').focus();
+ } else {
+ this.$el.find('tr.pass').hide().find('input').val('');
+ }
+ },
+
showMore: function (event) {
$('.more', this.$el).slideDown('fast');
- $('.server', this.$el).select();
+ $('input.server', this.$el).select();
},
populateFields: function (defaults) {
- var nick, server, channel;
+ var nick, server, port, channel, channel_key, ssl, password;
defaults = defaults || {};
@@ -169,13 +271,15 @@ kiwi.view.ServerSelect = function () {
ssl = defaults.ssl || 0;
password = defaults.password || '';
channel = defaults.channel || '';
-
- $('.nick', this.$el).val(nick);
- $('.server', this.$el).val(server);
- $('.port', this.$el).val(port);
- $('.ssl', this.$el).prop('checked', ssl);
- $('.password', this.$el).val(password);
- $('.channel', this.$el).val(channel);
+ channel_key = defaults.channel_key || '';
+
+ $('input.nick', this.$el).val(nick);
+ $('input.server', this.$el).val(server);
+ $('input.port', this.$el).val(port);
+ $('input.ssl', this.$el).prop('checked', ssl);
+ $('input.password', this.$el).val(password);
+ $('input.channel', this.$el).val(channel);
+ $('input.channel_key', this.$el).val(channel_key);
},
hide: function () {
@@ -196,6 +300,7 @@ kiwi.view.ServerSelect = function () {
} else if (new_state === 'nick_change') {
$('.more', this.$el).hide();
$('.show_more', this.$el).hide();
+ $('input.nick', this.$el).select();
}
state = new_state;
@@ -205,7 +310,7 @@ kiwi.view.ServerSelect = function () {
$('.status', this.$el)
.text(text)
.attr('class', 'status')
- .addClass(class_name)
+ .addClass(class_name||'')
.show();
},
clearStatus: function () {
@@ -233,11 +338,14 @@ kiwi.view.ServerSelect = function () {
};
-kiwi.view.Panel = Backbone.View.extend({
+_kiwi.view.Panel = Backbone.View.extend({
tagName: "div",
className: "messages",
events: {
- "click .chan": "chanClick"
+ "click .chan": "chanClick",
+ 'click .media .open': 'mediaClick',
+ 'mouseenter .msg .nick': 'msgEnter',
+ 'mouseleave .msg .nick': 'msgLeave'
},
initialize: function (options) {
@@ -266,39 +374,54 @@ kiwi.view.Panel = Backbone.View.extend({
},
render: function () {
+ var that = this;
+
this.$el.empty();
- this.model.get("backscroll").forEach(this.newMsg);
+ _.each(this.model.get('scrollback'), function (msg) {
+ that.newMsg(msg);
+ });
},
+
newMsg: function (msg) {
- // TODO: make sure that the message pane is scrolled to the bottom (Or do we? ~Darren)
var re, line_msg, $this = this.$el,
- nick_colour_hex;
+ nick_colour_hex, nick_hex, is_highlight, msg_css_classes = '';
+
+ // Nick highlight detecting
+ if ((new RegExp('\\b' + _kiwi.gateway.get('nick') + '\\b', 'i')).test(msg.msg)) {
+ is_highlight = true;
+ msg_css_classes += ' highlight';
+ }
// Escape any HTML that may be in here
msg.msg = $('').text(msg.msg).html();
// Make the channels clickable
- re = new RegExp('\\B([' + kiwi.gateway.get('channel_prefix') + '][^ ,.\\007]+)', 'g');
+ re = new RegExp('(?:^|\\s)([' + _kiwi.gateway.get('channel_prefix') + '][^ ,.\\007]+)', 'g');
msg.msg = msg.msg.replace(re, function (match) {
- return '' + match + '';
+ return '' + match + '';
});
- // Make links clickable
- msg.msg = msg.msg.replace(/((https?\:\/\/|ftp\:\/\/)|(www\.))(\S+)(\w{2,4})(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-\/]*))?/gi, function (url) {
- var nice;
+ // Parse any links found
+ msg.msg = msg.msg.replace(/(([A-Za-z0-9\-]+\:\/\/)|(www\.))([\w.\-]+)([a-zA-Z]{2,6})(:[0-9]+)?(\/[\w#!:.?$'()[\]*,;~+=&%@!\-\/]*)?/gi, function (url) {
+ var nice = url,
+ extra_html = '';
- // Add the http is no protoocol was found
+ // Add the http if no protoocol was found
if (url.match(/^www\./)) {
url = 'http://' + url;
}
- nice = url;
+ // Shorten the displayed URL if it's going to be too long
if (nice.length > 100) {
nice = nice.substr(0, 100) + '...';
}
- return '' + nice + '';
+ // Get any media HTML if supported
+ extra_html = _kiwi.view.MediaMessage.buildHtml(url);
+
+ // Make the link clickable
+ return '' + nice + ' ' + extra_html;
});
@@ -319,41 +442,124 @@ kiwi.view.Panel = Backbone.View.extend({
msg.nick_style = 'color:' + nick_colour_hex + ';';
+ // Generate a hex string from the nick to be used as a CSS class name
+ nick_hex = msg.nick_css_class = '';
+ if (msg.nick) {
+ _.map(msg.nick.split(''), function (char) {
+ nick_hex += char.charCodeAt(0).toString(16);
+ });
+ msg_css_classes += ' nick_' + nick_hex;
+ }
+
// Build up and add the line
- line_msg = '<%- time %>
<%- nick %>
<%= msg %>
';
+ msg.msg_css_classes = msg_css_classes;
+ line_msg = '<%- time %>
<%- nick %>
<%= msg %>
';
$this.append(_.template(line_msg, msg));
// Activity/alerts based on the type of new message
if (msg.type.match(/^action /)) {
this.alert('action');
- } else if (msg.msg.indexOf(kiwi.gateway.get('nick')) > -1) {
- kiwi.app.view.alertWindow('* People are talking!');
+
+ } else if (is_highlight) {
+ _kiwi.app.view.alertWindow('* People are talking!');
+ _kiwi.app.view.playSound('highlight');
this.alert('highlight');
+
} else {
// If this is the active panel, send an alert out
if (this.model.isActive()) {
- kiwi.app.view.alertWindow('* People are talking!');
+ _kiwi.app.view.alertWindow('* People are talking!');
}
this.alert('activity');
}
+ if (this.model.isQuery() && !this.model.isActive()) {
+ _kiwi.app.view.alertWindow('* People are talking!');
+ _kiwi.app.view.playSound('highlight');
+ }
+
+ // Update the activity counters
+ (function () {
+ // Only inrement the counters if we're not the active panel
+ if (this.model.isActive()) return;
+
+ var $act = this.model.tab.find('.activity');
+ $act.text((parseInt($act.text(), 10) || 0) + 1);
+ if ($act.text() === '0') {
+ $act.addClass('zero');
+ } else {
+ $act.removeClass('zero');
+ }
+ }).apply(this);
+
this.scrollToBottom();
// Make sure our DOM isn't getting too large (Acts as scrollback)
this.msg_count++;
- if (this.msg_count > 250) {
+ if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {
$('.msg:first', this.$el).remove();
this.msg_count--;
}
},
chanClick: function (event) {
if (event.target) {
- kiwi.gateway.join($(event.target).text());
+ _kiwi.gateway.join($(event.target).data('channel'));
} else {
// IE...
- kiwi.gateway.join($(event.srcElement).text());
+ _kiwi.gateway.join($(event.srcElement).data('channel'));
}
},
+
+ mediaClick: function (event) {
+ var $media = $(event.target).parents('.media');
+ var media_message;
+
+ if ($media.data('media')) {
+ media_message = $media.data('media');
+ } else {
+ media_message = new _kiwi.view.MediaMessage({el: $media[0]});
+ $media.data('media', media_message);
+ }
+
+ $media.data('media', media_message);
+
+ media_message.open();
+ },
+
+ // Cursor hovers over a message
+ msgEnter: function (event) {
+ var nick_class;
+
+ // Find a valid class that this element has
+ _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
+ if (css_class.match(/^nick_[a-z0-9]+/i)) {
+ nick_class = css_class;
+ }
+ });
+
+ // If no class was found..
+ if (!nick_class) return;
+
+ $('.'+nick_class).addClass('global_nick_highlight');
+ },
+
+ // Cursor leaves message
+ msgLeave: function (event) {
+ var nick_class;
+
+ // Find a valid class that this element has
+ _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {
+ if (css_class.match(/^nick_[a-z0-9]+/i)) {
+ nick_class = css_class;
+ }
+ });
+
+ // If no class was found..
+ if (!nick_class) return;
+
+ $('.'+nick_class).removeClass('global_nick_highlight');
+ },
+
show: function () {
var $this = this.$el;
@@ -364,26 +570,29 @@ kiwi.view.Panel = Backbone.View.extend({
// Show this panels memberlist
var members = this.model.get("members");
if (members) {
- $('#memberlists').show();
+ $('#memberlists').removeClass('disabled');
members.view.show();
} else {
// Memberlist not found for this panel, hide any active ones
- $('#memberlists').hide().children().removeClass('active');
+ $('#memberlists').addClass('disabled').children().removeClass('active');
}
- kiwi.app.view.doLayout();
+ _kiwi.app.view.doLayout();
- this.scrollToBottom();
+ // Remove any alerts and activity counters for this panel
this.alert('none');
+ this.model.tab.find('.activity').text('0').addClass('zero');
this.trigger('active', this.model);
- kiwi.app.panels.trigger('active', this.model);
+ _kiwi.app.panels.trigger('active', this.model, _kiwi.app.panels.active);
+
+ this.scrollToBottom(true);
},
alert: function (level) {
// No need to highlight if this si the active panel
- if (this.model == kiwi.app.panels.active) return;
+ if (this.model == _kiwi.app.panels.active) return;
var types, type_idx;
types = ['none', 'action', 'activity', 'highlight'];
@@ -418,23 +627,42 @@ kiwi.view.Panel = Backbone.View.extend({
// Scroll to the bottom of the panel
- scrollToBottom: function () {
- // TODO: Don't scroll down if we're scrolled up the panel a little
- this.$container[0].scrollTop = this.$container[0].scrollHeight;
+ scrollToBottom: function (force_down) {
+ // If this isn't the active panel, don't scroll
+ if (this.model !== _kiwi.app.panels.active) return;
+
+ // Don't scroll down if we're scrolled up the panel a little
+ if (force_down || this.$container.scrollTop() + this.$container.height() > this.$el.outerHeight() - 150) {
+ this.$container[0].scrollTop = this.$container[0].scrollHeight;
+ }
}
});
-kiwi.view.Applet = kiwi.view.Panel.extend({
+_kiwi.view.Applet = _kiwi.view.Panel.extend({
className: 'applet',
initialize: function (options) {
this.initializePanel(options);
}
});
-kiwi.view.Channel = kiwi.view.Panel.extend({
+_kiwi.view.Channel = _kiwi.view.Panel.extend({
initialize: function (options) {
this.initializePanel(options);
this.model.bind('change:topic', this.topic, this);
+
+ // Only show the loader if this is a channel (ie. not a query)
+ if (this.model.isChannel()) {
+ this.$el.append('Joining channel..
');
+ }
+ },
+
+ // Override the existing newMsg() method to remove the joining channel loader
+ newMsg: function () {
+ this.$el.find('.initial_loader').slideUp(function () {
+ $(this).remove();
+ });
+
+ return this.constructor.__super__.newMsg.apply(this, arguments);
},
topic: function (topic) {
@@ -445,14 +673,16 @@ kiwi.view.Channel = kiwi.view.Panel.extend({
this.model.addMsg('', '== Topic for ' + this.model.get('name') + ' is: ' + topic, 'topic');
// If this is the active channel then update the topic bar
- if (kiwi.app.panels.active === this) {
- kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));
+ if (_kiwi.app.panels.active === this) {
+ _kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));
}
}
});
-// Model for this = kiwi.model.PanelList
-kiwi.view.Tabs = Backbone.View.extend({
+// Model for this = _kiwi.model.PanelList
+_kiwi.view.Tabs = Backbone.View.extend({
+ tagName: 'ul',
+
events: {
'click li': 'tabClick',
'click li .part': 'partClick'
@@ -465,22 +695,24 @@ kiwi.view.Tabs = Backbone.View.extend({
this.model.on('active', this.panelActive, this);
- this.tabs_applets = $('ul.applets', this.$el);
- this.tabs_msg = $('ul.channels', this.$el);
-
- kiwi.gateway.on('change:name', function (gateway, new_val) {
+ this.model.network.on('change:name', function (network, new_val) {
$('span', this.model.server.tab).text(new_val);
}, this);
+
+ this.$tab_container = $('#kiwi .panellist.channels');
+ this.$tab_container.append(this.$el);
},
+
render: function () {
var that = this;
- this.tabs_msg.empty();
+ this.$el.empty();
// Add the server tab first
this.model.server.tab
- .data('panel_id', this.model.server.cid)
- .appendTo(this.tabs_msg);
+ .data('panel', this.model.server)
+ .data('connection_id', this.model.network.get('connection_id'))
+ .appendTo(this.$el);
// Go through each panel adding its tab
this.model.forEach(function (panel) {
@@ -488,11 +720,12 @@ kiwi.view.Tabs = Backbone.View.extend({
if (panel == that.model.server) return;
panel.tab
- .data('panel_id', panel.cid)
- .appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);
+ .data('panel', panel)
+ .appendTo(that.$el);
+ //.appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);
});
- kiwi.app.view.doLayout();
+ _kiwi.app.view.doLayout();
},
updateTabTitle: function (panel, new_title) {
@@ -501,43 +734,45 @@ kiwi.view.Tabs = Backbone.View.extend({
panelAdded: function (panel) {
// Add a tab to the panel
- panel.tab = $('' + (panel.get('title') || panel.get('name')) + '');
+ panel.tab = $('' + (panel.get('title') || panel.get('name')) + '');
if (panel.isServer()) {
panel.tab.addClass('server');
+ panel.tab.addClass('icon-nonexistant');
}
- panel.tab.data('panel_id', panel.cid)
- .appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);
+ panel.tab.data('panel', panel)
+ .data('connection_id', this.model.network.get('connection_id'))
+ .appendTo(this.$el);
+ //.appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);
panel.bind('change:title', this.updateTabTitle);
- kiwi.app.view.doLayout();
+ _kiwi.app.view.doLayout();
},
panelRemoved: function (panel) {
panel.tab.remove();
delete panel.tab;
- kiwi.app.view.doLayout();
+ _kiwi.app.view.doLayout();
},
- panelActive: function (panel) {
+ panelActive: function (panel, previously_active_panel) {
// Remove any existing tabs or part images
$('.part', this.$el).remove();
- this.tabs_applets.children().removeClass('active');
- this.tabs_msg.children().removeClass('active');
+ this.$tab_container.find('.active').removeClass('active');
panel.tab.addClass('active');
// Only show the part image on non-server tabs
if (!panel.isServer()) {
- panel.tab.append('');
+ panel.tab.append('');
}
},
tabClick: function (e) {
var tab = $(e.currentTarget);
-
- var panel = this.model.getByCid(tab.data('panel_id'));
+
+ var panel = tab.data('panel');
if (!panel) {
// A panel wasn't found for this tab... wadda fuck
return;
@@ -548,26 +783,26 @@ kiwi.view.Tabs = Backbone.View.extend({
partClick: function (e) {
var tab = $(e.currentTarget).parent();
- var panel = this.model.getByCid(tab.data('panel_id'));
+ var panel = this.model.getByCid(tab.data('panel'));
// Only need to part if it's a channel
// If the nicklist is empty, we haven't joined the channel as yet
if (panel.isChannel() && panel.get('members').models.length > 0) {
- kiwi.gateway.part(panel.get('name'));
+ this.model.network.gateway.part(panel.get('name'));
} else {
panel.close();
}
},
next: function () {
- var next = kiwi.app.panels.active.tab.next();
- if (!next.length) next = $('li:first', this.tabs_msgs);
+ var next = this.$tab_container.find('.active').next();
+ if (!next.length) next = $('li:first', this.$tab_container);
next.click();
},
prev: function () {
- var prev = kiwi.app.panels.active.tab.prev();
- if (!prev.length) prev = $('li:last', this.tabs_msgs);
+ var prev = this.$tab_container.find('.active').prev();
+ if (!prev.length) prev = $('li:last', this.$tab_container);
prev.click();
}
@@ -575,14 +810,23 @@ kiwi.view.Tabs = Backbone.View.extend({
-kiwi.view.TopicBar = Backbone.View.extend({
+_kiwi.view.TopicBar = Backbone.View.extend({
events: {
'keydown div': 'process'
},
initialize: function () {
- kiwi.app.panels.bind('active', function (active_panel) {
- this.setCurrentTopic(active_panel.get('topic'));
+ _kiwi.app.panels.bind('active', function (active_panel) {
+ // If it's a channel topic, update and make editable
+ if (active_panel.isChannel()) {
+ this.setCurrentTopic(active_panel.get('topic') || '');
+ this.$el.find('div').attr('contentEditable', true);
+
+ } else {
+ // Not a channel topic.. clear and make uneditable
+ this.$el.find('div').attr('contentEditable', false)
+ .text('');
+ }
}, this);
},
@@ -590,13 +834,16 @@ kiwi.view.TopicBar = Backbone.View.extend({
var inp = $(ev.currentTarget),
inp_val = inp.text();
- if (kiwi.app.panels.active.isChannel()) {
- if (ev.keyCode !== 13) return;
+ // Only allow topic editing if this is a channel panel
+ if (!_kiwi.app.panels.active.isChannel()) {
+ return false;
+ }
- kiwi.gateway.topic(kiwi.app.panels.active.get('name'), inp_val);
+ // If hit return key, update the current topic
+ if (ev.keyCode === 13) {
+ _kiwi.gateway.topic(_kiwi.app.panels.active.get('name'), inp_val);
+ return false;
}
-
- return false;
},
setCurrentTopic: function (new_topic) {
@@ -609,9 +856,9 @@ kiwi.view.TopicBar = Backbone.View.extend({
-kiwi.view.ControlBox = Backbone.View.extend({
+_kiwi.view.ControlBox = Backbone.View.extend({
events: {
- 'keydown input.inp': 'process',
+ 'keydown .inp': 'process',
'click .nick': 'showNickChange'
},
@@ -627,13 +874,17 @@ kiwi.view.ControlBox = Backbone.View.extend({
// Hold tab autocomplete data
this.tabcomplete = {active: false, data: [], prefix: ''};
- kiwi.gateway.bind('change:nick', function () {
+ _kiwi.gateway.bind('change:nick', function () {
$('.nick', that.$el).text(this.get('nick'));
});
+
+ _kiwi.app.connections.on('active', function(panel, connection) {
+ $('.nick', that.$el).text(connection.get('nick'));
+ });
},
showNickChange: function (ev) {
- (new kiwi.view.NickChangeBox()).render();
+ (new _kiwi.view.NickChangeBox()).render();
},
process: function (ev) {
@@ -643,7 +894,7 @@ kiwi.view.ControlBox = Backbone.View.extend({
meta;
if (navigator.appVersion.indexOf("Mac") !== -1) {
- meta = ev.ctrlKey;
+ meta = ev.metaKey;
} else {
meta = ev.altKey;
}
@@ -660,13 +911,16 @@ kiwi.view.ControlBox = Backbone.View.extend({
inp_val = inp_val.trim();
if (inp_val) {
- this.processInput(inp_val);
+ $.each(inp_val.split('\n'), function (idx, line) {
+ that.processInput(line);
+ });
this.buffer.push(inp_val);
this.buffer_pos = this.buffer.length;
}
inp.val('');
+ return false;
break;
@@ -684,12 +938,12 @@ kiwi.view.ControlBox = Backbone.View.extend({
}
break;
- case (ev.keyCode === 37 && meta): // left
- kiwi.app.panels.view.prev();
+ case (ev.keyCode === 219 && meta): // [ + meta
+ _kiwi.app.panels.view.prev();
return false;
- case (ev.keyCode === 39 && meta): // right
- kiwi.app.panels.view.next();
+ case (ev.keyCode === 221 && meta): // ] + meta
+ _kiwi.app.panels.view.next();
return false;
case (ev.keyCode === 9): // tab
@@ -697,7 +951,7 @@ kiwi.view.ControlBox = Backbone.View.extend({
if (_.isEqual(this.tabcomplete.data, [])) {
// Get possible autocompletions
var ac_data = [];
- $.each(kiwi.app.panels.active.get('members').models, function (i, member) {
+ $.each(_kiwi.app.panels.active.get('members').models, function (i, member) {
if (!member) return;
ac_data.push(member.get('nick'));
});
@@ -712,12 +966,20 @@ kiwi.view.ControlBox = Backbone.View.extend({
}
(function () {
- var tokens = inp_val.substring(0, inp[0].selectionStart).split(' '),
- val,
- p1,
- newnick,
- range,
- nick = tokens[tokens.length - 1];
+ var tokens, // Words before the cursor position
+ val, // New value being built up
+ p1, // Position in the value just before the nick
+ newnick, // New nick to be displayed (cycles through)
+ range, // TextRange for setting new text cursor position
+ nick, // Current nick in the value
+ trailing = ': '; // Text to be inserted after a tabbed nick
+
+ tokens = inp_val.substring(0, inp[0].selectionStart).split(' ');
+ if (tokens[tokens.length-1] == ':')
+ tokens.pop();
+
+ nick = tokens[tokens.length - 1];
+
if (this.tabcomplete.prefix === '') {
this.tabcomplete.prefix = nick;
}
@@ -727,21 +989,31 @@ kiwi.view.ControlBox = Backbone.View.extend({
});
if (this.tabcomplete.data.length > 0) {
+ // Get the current value before cursor position
p1 = inp[0].selectionStart - (nick.length);
val = inp_val.substr(0, p1);
+
+ // Include the current selected nick
newnick = this.tabcomplete.data.shift();
this.tabcomplete.data.push(newnick);
val += newnick;
+
+ if (inp_val.substr(inp[0].selectionStart, 2) !== trailing)
+ val += trailing;
+
+ // Now include the rest of the current value
val += inp_val.substr(inp[0].selectionStart);
+
inp.val(val);
+ // Move the cursor position to the end of the nick
if (inp[0].setSelectionRange) {
- inp[0].setSelectionRange(p1 + newnick.length, p1 + newnick.length);
+ inp[0].setSelectionRange(p1 + newnick.length + trailing.length, p1 + newnick.length + trailing.length);
} else if (inp[0].createTextRange) { // not sure if this bit is actually needed....
range = inp[0].createTextRange();
range.collapse(true);
- range.moveEnd('character', p1 + newnick.length);
- range.moveStart('character', p1 + newnick.length);
+ range.moveEnd('character', p1 + newnick.length + trailing.length);
+ range.moveStart('character', p1 + newnick.length + trailing.length);
range.select();
}
}
@@ -756,13 +1028,17 @@ kiwi.view.ControlBox = Backbone.View.extend({
pre_processed;
// The default command
- if (command_raw[0] !== '/') {
- command_raw = '/msg ' + kiwi.app.panels.active.get('name') + ' ' + command_raw;
+ if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') {
+ // Remove any slash escaping at the start (ie. //)
+ command_raw = command_raw.replace(/^\/\//, '/');
+
+ // Prepend the default command
+ command_raw = '/msg ' + _kiwi.app.panels.active.get('name') + ' ' + command_raw;
}
// Process the raw command for any aliases
- this.preprocessor.vars.server = kiwi.gateway.get('name');
- this.preprocessor.vars.channel = kiwi.app.panels.active.get('name');
+ this.preprocessor.vars.server = _kiwi.gateway.get('name');
+ this.preprocessor.vars.channel = _kiwi.app.panels.active.get('name');
this.preprocessor.vars.destination = this.preprocessor.vars.channel;
command_raw = this.preprocessor.process(command_raw);
@@ -774,25 +1050,32 @@ kiwi.view.ControlBox = Backbone.View.extend({
} else {
// Default command
command = 'msg';
- params.unshift(kiwi.app.panels.active.get('name'));
+ params.unshift(_kiwi.app.panels.active.get('name'));
}
// Trigger the command events
this.trigger('command', {command: command, params: params});
- this.trigger('command_' + command, {command: command, params: params});
+ this.trigger('command:' + command, {command: command, params: params});
// If we didn't have any listeners for this event, fire a special case
// TODO: This feels dirty. Should this really be done..?
- if (!this._callbacks['command_' + command]) {
+ if (!this._callbacks['command:' + command]) {
this.trigger('unknown_command', {command: command, params: params});
}
+ },
+
+
+ addPluginIcon: function ($icon) {
+ var $tool = $('').append($icon);
+ this.$el.find('.input_tools').append($tool);
+ _kiwi.app.view.doLayout();
}
});
-kiwi.view.StatusMessage = Backbone.View.extend({
+_kiwi.view.StatusMessage = Backbone.View.extend({
initialize: function () {
this.$el.hide();
@@ -807,7 +1090,7 @@ kiwi.view.StatusMessage = Backbone.View.extend({
opt.timeout = opt.timeout || 5000;
this.$el.text(text).attr('class', opt.type);
- this.$el.slideDown(kiwi.app.view.doLayout);
+ this.$el.slideDown($.proxy(_kiwi.app.view.doLayout, this));
if (opt.timeout) this.doTimeout(opt.timeout);
},
@@ -819,13 +1102,13 @@ kiwi.view.StatusMessage = Backbone.View.extend({
opt.timeout = opt.timeout || 5000;
this.$el.html(text).attr('class', opt.type);
- this.$el.slideDown(kiwi.app.view.doLayout);
+ this.$el.slideDown(_kiwi.app.view.doLayout);
if (opt.timeout) this.doTimeout(opt.timeout);
},
hide: function () {
- this.$el.slideUp(kiwi.app.view.doLayout);
+ this.$el.slideUp($.proxy(_kiwi.app.view.doLayout, this));
},
doTimeout: function (length) {
@@ -838,7 +1121,7 @@ kiwi.view.StatusMessage = Backbone.View.extend({
-kiwi.view.ResizeHandler = Backbone.View.extend({
+_kiwi.view.ResizeHandler = Backbone.View.extend({
events: {
'mousedown': 'startDrag',
'mouseup': 'stopDrag'
@@ -864,34 +1147,44 @@ kiwi.view.ResizeHandler = Backbone.View.extend({
this.$el.css('left', event.clientX - (this.$el.outerWidth(true) / 2));
$('#memberlists').css('width', this.$el.parent().width() - (this.$el.position().left + this.$el.outerWidth()));
- kiwi.app.view.doLayout();
+ _kiwi.app.view.doLayout();
}
});
-kiwi.view.AppToolbar = Backbone.View.extend({
+_kiwi.view.AppToolbar = Backbone.View.extend({
events: {
'click .settings': 'clickSettings'
},
initialize: function () {
- console.log('apptoolbar created', this.$el);
},
clickSettings: function (event) {
- console.log('clicked');
- kiwi.app.controlbox.processInput('/settings');
+ _kiwi.app.controlbox.processInput('/settings');
}
});
-kiwi.view.Application = Backbone.View.extend({
+_kiwi.view.Application = Backbone.View.extend({
initialize: function () {
- $(window).resize(this.doLayout);
- $('#toolbar').resize(this.doLayout);
- $('#controlbox').resize(this.doLayout);
+ var that = this;
+
+ $(window).resize(function() { that.doLayout.apply(that); });
+ $('#toolbar').resize(function() { that.doLayout.apply(that); });
+ $('#controlbox').resize(function() { that.doLayout.apply(that); });
+
+ // Change the theme when the config is changed
+ _kiwi.global.settings.on('change:theme', this.updateTheme, this);
+ this.updateTheme(getQueryVariable('theme'));
+
+ _kiwi.global.settings.on('change:channel_list_style', this.setTabLayout, this);
+ this.setTabLayout(_kiwi.global.settings.get('channel_list_style'));
+
+ _kiwi.global.settings.on('change:show_timestamps', this.displayTimestamps, this);
+ this.displayTimestamps(_kiwi.global.settings.get('show_timestamps'));
this.doLayout();
@@ -899,22 +1192,74 @@ kiwi.view.Application = Backbone.View.extend({
// Confirmation require to leave the page
window.onbeforeunload = function () {
- if (kiwi.gateway.isConnected()) {
+ if (_kiwi.gateway.isConnected()) {
return 'This will close all KiwiIRC conversations. Are you sure you want to close this window?';
}
};
+
+ this.initSound();
+ },
+
+
+
+ updateTheme: function (theme_name) {
+ // If called by the settings callback, get the correct new_value
+ if (theme_name === _kiwi.global.settings) {
+ theme_name = arguments[1];
+ }
+
+ // If we have no theme specified, get it from the settings
+ if (!theme_name) theme_name = _kiwi.global.settings.get('theme');
+
+ // Clear any current theme
+ this.$el.removeClass(function (i, css) {
+ return (css.match(/\btheme_\S+/g) || []).join(' ');
+ });
+
+ // Apply the new theme
+ this.$el.addClass('theme_' + (theme_name || 'relaxed'));
+ },
+
+
+ setTabLayout: function (layout_style) {
+ // If called by the settings callback, get the correct new_value
+ if (layout_style === _kiwi.global.settings) {
+ layout_style = arguments[1];
+ }
+
+ if (layout_style == 'list') {
+ this.$el.addClass('chanlist_treeview');
+ } else {
+ this.$el.removeClass('chanlist_treeview');
+ }
+
+ this.doLayout();
+ },
+
+
+ displayTimestamps: function (show_timestamps) {
+ // If called by the settings callback, get the correct new_value
+ if (show_timestamps === _kiwi.global.settings) {
+ show_timestamps = arguments[1];
+ }
+
+ if (show_timestamps) {
+ this.$el.addClass('timestamps');
+ } else {
+ this.$el.removeClass('timestamps');
+ }
},
// Globally shift focus to the command input box on a keypress
setKeyFocus: function (ev) {
// If we're copying text, don't shift focus
- if (ev.ctrlKey || ev.altKey) {
+ if (ev.ctrlKey || ev.altKey || ev.metaKey) {
return;
}
// If we're typing into an input box somewhere, ignore
- if ((ev.target.tagName.toLowerCase() === 'input') || (ev.target.id === 'edittopic')) {
+ if ((ev.target.tagName.toLowerCase() === 'input') || (ev.target.tagName.toLowerCase() === 'textarea') || $(ev.target).attr('contenteditable')) {
return;
}
@@ -923,30 +1268,60 @@ kiwi.view.Application = Backbone.View.extend({
doLayout: function () {
- var el_panels = $('#panels');
- var el_memberlists = $('#memberlists');
- var el_toolbar = $('#toolbar');
- var el_controlbox = $('#controlbox');
- var el_resize_handle = $('#memberlists_resize_handle');
+ var el_kiwi = this.$el;
+ var el_panels = $('#kiwi #panels');
+ var el_memberlists = $('#kiwi #memberlists');
+ var el_toolbar = $('#kiwi #toolbar');
+ var el_controlbox = $('#kiwi #controlbox');
+ var el_resize_handle = $('#kiwi #memberlists_resize_handle');
var css_heights = {
top: el_toolbar.outerHeight(true),
bottom: el_controlbox.outerHeight(true)
};
+
+ // If any elements are not visible, full size the panals instead
+ if (!el_toolbar.is(':visible')) {
+ css_heights.top = 0;
+ }
+
+ if (!el_controlbox.is(':visible')) {
+ css_heights.bottom = 0;
+ }
+
+ // Apply the CSS sizes
el_panels.css(css_heights);
el_memberlists.css(css_heights);
el_resize_handle.css(css_heights);
+ // If we have channel tabs on the side, adjust the height
+ if (el_kiwi.hasClass('chanlist_treeview')) {
+ $('#tabs', el_kiwi).css(css_heights);
+ }
+
+ // Determine if we have a narrow window (mobile/tablet/or even small desktop window)
+ if (el_kiwi.outerWidth() < 400) {
+ el_kiwi.addClass('narrow');
+ } else {
+ el_kiwi.removeClass('narrow');
+ }
+
+ // Set the panels width depending on the memberlist visibility
if (el_memberlists.css('display') != 'none') {
- // Handle + panels to the side of the memberlist
- el_panels.css('right', el_memberlists.outerWidth(true) + el_resize_handle.outerWidth(true));
- el_resize_handle.css('left', el_memberlists.position().left - el_resize_handle.outerWidth(true));
+ // Panels to the side of the memberlist
+ el_panels.css('right', el_memberlists.outerWidth(true));
+ // The resize handle sits overlapping the panels and memberlist
+ el_resize_handle.css('left', el_memberlists.position().left - (el_resize_handle.outerWidth(true) / 2));
} else {
- // Memberlist is hidden so handle + panels to the right edge
- el_panels.css('right', el_resize_handle.outerWidth(true));
+ // Memberlist is hidden so panels to the right edge
+ el_panels.css('right', 0);
+ // And move the handle just out of sight to the right
el_resize_handle.css('left', el_panels.outerWidth(true));
}
+
+ var input_wrap_width = parseInt($('#kiwi #controlbox .input_tools').outerWidth());
+ el_controlbox.find('.input_wrap').css('right', input_wrap_width + 7);
},
@@ -1025,8 +1400,8 @@ kiwi.view.Application = Backbone.View.extend({
var that = this;
if (!instant) {
- $('#toolbar').slideUp({queue: false, duration: 400, step: this.doLayout});
- $('#controlbox').slideUp({queue: false, duration: 400, step: this.doLayout});
+ $('#toolbar').slideUp({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});
+ $('#controlbox').slideUp({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});
} else {
$('#toolbar').slideUp(0);
$('#controlbox').slideUp(0);
@@ -1038,12 +1413,267 @@ kiwi.view.Application = Backbone.View.extend({
var that = this;
if (!instant) {
- $('#toolbar').slideDown({queue: false, duration: 400, step: this.doLayout});
- $('#controlbox').slideDown({queue: false, duration: 400, step: this.doLayout});
+ $('#toolbar').slideDown({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});
+ $('#controlbox').slideDown({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});
} else {
$('#toolbar').slideDown(0);
$('#controlbox').slideDown(0);
this.doLayout();
}
+ },
+
+
+ initSound: function () {
+ var that = this,
+ base_path = this.model.get('base_path');
+
+ $script(base_path + '/assets/libs/soundmanager2/soundmanager2-nodebug-jsmin.js', function() {
+ if (typeof soundManager === 'undefined')
+ return;
+
+ soundManager.setup({
+ url: base_path + '/assets/libs/soundmanager2/',
+ flashVersion: 9, // optional: shiny features (default = 8)// optional: ignore Flash where possible, use 100% HTML5 mode
+ preferFlash: true,
+
+ onready: function() {
+ that.sound_object = soundManager.createSound({
+ id: 'highlight',
+ url: base_path + '/assets/sound/highlight.mp3'
+ });
+ }
+ });
+ });
+ },
+
+
+ playSound: function (sound_id) {
+ if (!this.sound_object) return;
+
+ if (_kiwi.global.settings.get('mute_sounds'))
+ return;
+
+ soundManager.play(sound_id);
+ }
+});
+
+
+
+
+
+
+
+
+
+_kiwi.view.MediaMessage = Backbone.View.extend({
+ events: {
+ 'click .media_close': 'close'
+ },
+
+ initialize: function () {
+ // Get the URL from the data
+ this.url = this.$el.data('url');
+ },
+
+ // Close the media content and remove it from display
+ close: function () {
+ var that = this;
+ this.$content.slideUp('fast', function () {
+ that.$content.remove();
+ });
+ },
+
+ // Open the media content within its wrapper
+ open: function () {
+ // Create the content div if we haven't already
+ if (!this.$content) {
+ this.$content = $('');
+ this.$content.find('.content').append(this.mediaTypes[this.$el.data('type')].apply(this, []) || 'Not found :(');
+ }
+
+ // Now show the content if not already
+ if (!this.$content.is(':visible')) {
+ // Hide it first so the slideDown always plays
+ this.$content.hide();
+
+ // Add the media content and slide it into view
+ this.$el.append(this.$content);
+ this.$content.slideDown();
+ }
+ },
+
+
+
+ // Generate the media content for each recognised type
+ mediaTypes: {
+ twitter: function () {
+ var tweet_id = this.$el.data('tweetid');
+ var that = this;
+
+ $.getJSON('https://api.twitter.com/1/statuses/oembed.json?id=' + tweet_id + '&callback=?', function (data) {
+ that.$content.find('.content').html(data.html);
+ });
+
+ return $('Loading tweet..
');
+ },
+
+
+ image: function () {
+ return $('
');
+ },
+
+
+ reddit: function () {
+ var that = this;
+ var matches = (/reddit\.com\/r\/([a-zA-Z0-9_\-]+)\/comments\/([a-z0-9]+)\/([^\/]+)?/gi).exec(this.url);
+
+ $.getJSON('http://www.' + matches[0] + '.json?jsonp=?', function (data) {
+ console.log('Loaded reddit data', data);
+ var post = data[0].data.children[0].data;
+ var thumb = '';
+
+ // Show a thumbnail if there is one
+ if (post.thumbnail) {
+ //post.thumbnail = 'http://www.eurotunnel.com/uploadedImages/commercial/back-steps-icon-arrow.png';
+
+ // Hide the thumbnail if an over_18 image
+ if (post.over_18) {
+ thumb = '';
+ thumb += 'Show
NSFW
';
+ thumb += '
';
+ thumb += '';
+ } else {
+ thumb = '
';
+ }
+ }
+
+ // Build the template string up
+ var tmpl = '' + thumb + '
<%- title %>Posted by <%- author %>. ';
+ tmpl += '
<%- ups %>
<%- downs %>
';
+ tmpl += '<%- num_comments %> comments made.
View post ';
+
+ that.$content.find('.content').html(_.template(tmpl, post));
+ });
+
+ return $('Loading Reddit thread..
');
+ }
+ }
+
+}, {
+
+ // Build the closed media HTML from a URL
+ buildHtml: function (url) {
+ var html = '', matches;
+
+ // Is it an image?
+ if (url.match(/(\.jpe?g|\.gif|\.bmp|\.png)\??$/i)) {
+ html += '';
+ }
+
+ // Is it a tweet?
+ matches = (/https?:\/\/twitter.com\/([a-zA-Z0-9_]+)\/status\/([0-9]+)/ig).exec(url);
+ if (matches) {
+ html += '';
+ }
+
+ // Is reddit?
+ matches = (/reddit\.com\/r\/([a-zA-Z0-9_\-]+)\/comments\/([a-z0-9]+)\/([^\/]+)?/gi).exec(url);
+ if (matches) {
+ html += '';
+ }
+
+ return html;
+ }
+});
+
+
+
+_kiwi.view.MenuBox = Backbone.View.extend({
+ events: {
+ 'click .ui_menu_foot .close': 'dispose'
+ },
+
+ initialize: function(title) {
+ var that = this;
+
+ this.$el = $('');
+
+ this._title = title || '';
+ this._items = {};
+ this._display_footer = true;
+
+ this._close_proxy = function(event) {
+ that.onDocumentClick(event);
+ };
+ $(document).on('click', this._close_proxy);
+ },
+
+
+ render: function() {
+ var that = this;
+
+ this.$el.find('*').remove();
+
+ if (this._title) {
+ $('')
+ .text(this._title)
+ .appendTo(this.$el);
+ }
+
+
+ _.each(this._items, function(item) {
+ var $item = $('')
+ .append(item);
+
+ that.$el.append($item);
+ });
+
+ if (this._display_footer)
+ this.$el.append('');
+ },
+
+
+ onDocumentClick: function(event) {
+ var $target = $(event.target);
+
+ // If this is not itself AND we don't contain this element, dispose $el
+ if ($target[0] != this.$el[0] && this.$el.has($target).length === 0)
+ this.dispose();
+ },
+
+
+ dispose: function() {
+ _.each(this._items, function(item) {
+ item.dispose && item.dispose();
+ item.remove && item.remove();
+ });
+
+ this._items = null;
+ this.remove();
+
+ $(document).off('click', this._close_proxy);
+ },
+
+
+ addItem: function(item_name, $item) {
+ $item = $($item);
+ if ($item.is('a')) $item.addClass('icon-chevron-right');
+ this._items[item_name] = $item;
+ },
+
+
+ removeItem: function(item_name) {
+ delete this._items[item_name];
+ },
+
+
+ showFooter: function(show) {
+ this._show_footer = show;
+ },
+
+
+ show: function() {
+ this.render();
+ this.$el.appendTo(_kiwi.app.view.$el);
}
-});
\ No newline at end of file
+});