X-Git-Url: https://vcs.fsf.org/?a=blobdiff_plain;ds=sidebyside;f=client%2Fassets%2Fdev%2Fview.js;h=d9522a1e6783d561ff63d0b24efbe026a3721a9a;hb=fb989cb261eb6b6d7b53dc976011750b619495be;hp=20d8f02f2d6b2e023ac2e2f8f61088ae69ee82aa;hpb=59d5e9eac5f5f97df64b93b2afd766edf2352a5d;p=KiwiIRC.git
diff --git a/client/assets/dev/view.js b/client/assets/dev/view.js
index 20d8f02..d9522a1 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,30 @@ kiwi.view.MemberList = Backbone.View.extend({
var $this = $(this.el);
$this.empty();
this.model.forEach(function (member) {
- $('
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 +611,14 @@ 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({
events: {
'click li': 'tabClick',
'click li .part': 'partClick'
@@ -468,7 +634,7 @@ kiwi.view.Tabs = Backbone.View.extend({
this.tabs_applets = $('ul.applets', this.$el);
this.tabs_msg = $('ul.channels', this.$el);
- kiwi.gateway.on('change:name', function (gateway, new_val) {
+ _kiwi.gateway.on('change:name', function (gateway, new_val) {
$('span', this.model.server.tab).text(new_val);
}, this);
},
@@ -492,7 +658,7 @@ kiwi.view.Tabs = Backbone.View.extend({
.appendTo(panel.isApplet() ? this.tabs_applets : this.tabs_msg);
});
- kiwi.app.view.doLayout();
+ _kiwi.app.view.doLayout();
},
updateTabTitle: function (panel, new_title) {
@@ -501,7 +667,7 @@ kiwi.view.Tabs = Backbone.View.extend({
panelAdded: function (panel) {
// Add a tab to the panel
- panel.tab = $('').html(formatIRCMsg(new_topic));
- $('input', this.$el).val(new_topic.text());
+ $('div', this.$el).html(formatIRCMsg(_.escape(new_topic)));
}
});
-kiwi.view.ControlBox = Backbone.View.extend({
+_kiwi.view.ControlBox = Backbone.View.extend({
events: {
- 'keydown input.inp': 'process',
+ 'keydown .inp': 'process',
'click .nick': 'showNickChange'
},
@@ -626,13 +805,13 @@ 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'));
});
},
showNickChange: function (ev) {
- (new kiwi.view.NickChangeBox()).render();
+ (new _kiwi.view.NickChangeBox()).render();
},
process: function (ev) {
@@ -642,7 +821,7 @@ kiwi.view.ControlBox = Backbone.View.extend({
meta;
if (navigator.appVersion.indexOf("Mac") !== -1) {
- meta = ev.ctrlKey;
+ meta = ev.metaKey;
} else {
meta = ev.altKey;
}
@@ -659,13 +838,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;
@@ -683,12 +865,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
@@ -696,7 +878,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'));
});
@@ -711,12 +893,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;
}
@@ -726,21 +916,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();
}
}
@@ -755,13 +955,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);
@@ -773,16 +977,16 @@ 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});
}
}
@@ -791,7 +995,7 @@ kiwi.view.ControlBox = Backbone.View.extend({
-kiwi.view.StatusMessage = Backbone.View.extend({
+_kiwi.view.StatusMessage = Backbone.View.extend({
initialize: function () {
this.$el.hide();
@@ -806,7 +1010,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);
},
@@ -818,13 +1022,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) {
@@ -837,7 +1041,7 @@ kiwi.view.StatusMessage = Backbone.View.extend({
-kiwi.view.ResizeHandler = Backbone.View.extend({
+_kiwi.view.ResizeHandler = Backbone.View.extend({
events: {
'mousedown': 'startDrag',
'mouseup': 'stopDrag'
@@ -863,17 +1067,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({
+ events: {
+ 'click .settings': 'clickSettings'
+ },
+
+ initialize: function () {
+ },
+
+ clickSettings: function (event) {
+ _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();
@@ -881,22 +1112,72 @@ 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?';
}
};
},
+
+ 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') {
+ if ((ev.target.tagName.toLowerCase() === 'input') || (ev.target.tagName.toLowerCase() === 'textarea') || $(ev.target).attr('contenteditable')) {
return;
}
@@ -905,30 +1186,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);
},
@@ -1007,8 +1318,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);
@@ -1020,12 +1331,141 @@ 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();
}
}
+});
+
+
+
+
+
+
+
+
+
+_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;
+ }
});
\ No newline at end of file