+++ /dev/null
-/*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 */\r
-/*global kiwi */\r
-_kiwi.view.Member = Backbone.View.extend({\r
- tagName: "li",\r
- initialize: function (options) {\r
- this.model.bind('change', this.render, this);\r
- this.render();\r
- },\r
- render: function () {\r
- var $this = this.$el,\r
- prefix_css_class = (this.model.get('modes') || []).join(' ');\r
-\r
- $this.addClass('mode ' + prefix_css_class);\r
- $this.html('<a class="nick"><span class="prefix">' + this.model.get("prefix") + '</span>' + this.model.get("nick") + '</a>');\r
- $this.data('member', this.model);\r
- return this;\r
- }\r
-});\r
-\r
-_kiwi.view.MemberList = Backbone.View.extend({\r
- tagName: "ul",\r
- events: {\r
- "click .nick": "nickClick"\r
- },\r
- initialize: function (options) {\r
- this.model.bind('all', this.render, this);\r
- $(this.el).appendTo('#kiwi .memberlists');\r
- },\r
- render: function () {\r
- var $this = this.$el;\r
- $this.empty();\r
- this.model.forEach(function (member) {\r
- $this.append(member.view.el);\r
- });\r
- return this;\r
- },\r
- nickClick: function (event) {\r
- var $target = $(event.currentTarget).parent('li'),\r
- member = $target.data('member'),\r
- userbox;\r
-\r
- event.stopPropagation();\r
-\r
- // If the userbox already exists here, hide it\r
- if ($target.find('.userbox').length > 0) {\r
- $('.userbox', this.$el).remove();\r
- return;\r
- }\r
-\r
- userbox = new _kiwi.view.UserBox();\r
- userbox.member = member;\r
- userbox.channel = this.model.channel;\r
-\r
- if (!this.model.getByNick(_kiwi.app.connections.active_connection.get('nick')).get('is_op')) {\r
- userbox.$el.children('.if_op').remove();\r
- }\r
-\r
- var menu = new _kiwi.view.MenuBox(member.get('nick') || 'User');\r
- menu.addItem('userbox', userbox.$el);\r
- menu.show();\r
-\r
- // Position the userbox + menubox\r
- (function() {\r
- var t = event.pageY,\r
- m_bottom = t + menu.$el.outerHeight(), // Where the bottom of menu will be\r
- memberlist_bottom = this.$el.parent().offset().top + this.$el.parent().outerHeight();\r
-\r
- // If the bottom of the userbox is going to be too low.. raise it\r
- if (m_bottom > memberlist_bottom){\r
- t = memberlist_bottom - menu.$el.outerHeight();\r
- }\r
-\r
- // Set the new positon\r
- menu.$el.offset({\r
- left: _kiwi.app.view.$el.width() - menu.$el.outerWidth() - 20,\r
- top: t\r
- });\r
- }).call(this);\r
- },\r
- show: function () {\r
- $('#kiwi .memberlists').children().removeClass('active');\r
- $(this.el).addClass('active');\r
- }\r
-});\r
-\r
-\r
-\r
-_kiwi.view.UserBox = Backbone.View.extend({\r
- events: {\r
- 'click .query': 'queryClick',\r
- 'click .info': 'infoClick',\r
- 'click .slap': 'slapClick',\r
- 'click .op': 'opClick',\r
- 'click .deop': 'deopClick',\r
- 'click .voice': 'voiceClick',\r
- 'click .devoice': 'devoiceClick',\r
- 'click .kick': 'kickClick',\r
- 'click .ban': 'banClick'\r
- },\r
-\r
- initialize: function () {\r
- this.$el = $($('#tmpl_userbox').html());\r
- },\r
-\r
- queryClick: function (event) {\r
- var panel = new _kiwi.model.Query({name: this.member.get('nick')});\r
- _kiwi.app.connections.active_connection.panels.add(panel);\r
- panel.view.show();\r
- },\r
-\r
- infoClick: function (event) {\r
- _kiwi.app.controlbox.processInput('/whois ' + this.member.get('nick'));\r
- },\r
-\r
- slapClick: function (event) {\r
- _kiwi.app.controlbox.processInput('/slap ' + this.member.get('nick'));\r
- },\r
-\r
- opClick: function (event) {\r
- _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' +o ' + this.member.get('nick'));\r
- },\r
-\r
- deopClick: function (event) {\r
- _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' -o ' + this.member.get('nick'));\r
- },\r
-\r
- voiceClick: function (event) {\r
- _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' +v ' + this.member.get('nick'));\r
- },\r
-\r
- devoiceClick: function (event) {\r
- _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' -v ' + this.member.get('nick'));\r
- },\r
-\r
- kickClick: function (event) {\r
- // TODO: Enable the use of a custom kick message\r
- _kiwi.app.controlbox.processInput('/kick ' + this.member.get('nick') + ' Bye!');\r
- },\r
-\r
- banClick: function (event) {\r
- // TODO: Set ban on host, not just on nick\r
- _kiwi.app.controlbox.processInput('/mode ' + this.channel.get('name') + ' +b ' + this.member.get('nick') + '!*');\r
- }\r
-});\r
-\r
-_kiwi.view.NickChangeBox = Backbone.View.extend({\r
- events: {\r
- 'submit': 'changeNick',\r
- 'click .cancel': 'close'\r
- },\r
- \r
- initialize: function () {\r
- this.$el = $($('#tmpl_nickchange').html());\r
- },\r
- \r
- render: function () {\r
- // Add the UI component and give it focus\r
- _kiwi.app.controlbox.$el.prepend(this.$el);\r
- this.$el.find('input').focus();\r
-\r
- this.$el.css('bottom', _kiwi.app.controlbox.$el.outerHeight(true));\r
- },\r
- \r
- close: function () {\r
- this.$el.remove();\r
-\r
- },\r
-\r
- changeNick: function (event) {\r
- var that = this;\r
-\r
- event.preventDefault();\r
-\r
- _kiwi.app.connections.active_connection.gateway.changeNick(this.$el.find('input').val(), function (err, val) {\r
- that.close();\r
- });\r
- return false;\r
- }\r
-});\r
-\r
-_kiwi.view.ServerSelect = function () {\r
- // Are currently showing all the controlls or just a nick_change box?\r
- var state = 'all';\r
-\r
- var model = Backbone.View.extend({\r
- events: {\r
- 'submit form': 'submitForm',\r
- 'click .show_more': 'showMore',\r
- 'change .have_pass input': 'showPass',\r
- 'change .have_key input': 'showKey',\r
- 'click .icon-key': 'channelKeyIconClick'\r
- },\r
-\r
- initialize: function () {\r
- var that = this;\r
-\r
- this.$el = $($('#tmpl_server_select').html());\r
-\r
- // Remove the 'more' link if the server has disabled server changing\r
- if (_kiwi.app.server_settings && _kiwi.app.server_settings.connection) {\r
- if (!_kiwi.app.server_settings.connection.allow_change) {\r
- this.$el.find('.show_more').remove();\r
- this.$el.addClass('single_server');\r
- }\r
- }\r
-\r
- _kiwi.gateway.bind('onconnect', this.networkConnected, this);\r
- _kiwi.gateway.bind('connecting', this.networkConnecting, this);\r
- _kiwi.gateway.bind('onirc_error', this.onIrcError, this);\r
- },\r
-\r
- dispose: function() {\r
- _kiwi.gateway.off('onconnect', this.networkConnected, this);\r
- _kiwi.gateway.off('connecting', this.networkConnecting, this);\r
- _kiwi.gateway.off('onirc_error', this.onIrcError, this);\r
-\r
- this.$el.remove();\r
- },\r
-\r
- submitForm: function (event) {\r
- event.preventDefault();\r
-\r
- // Make sure a nick is chosen\r
- if (!$('input.nick', this.$el).val().trim()) {\r
- this.setStatus('Select a nickname first!');\r
- $('input.nick', this.$el).select();\r
- return;\r
- }\r
-\r
- if (state === 'nick_change') {\r
- this.submitNickChange(event);\r
- } else {\r
- this.submitLogin(event);\r
- }\r
-\r
- $('button', this.$el).attr('disabled', 1);\r
- return;\r
- },\r
-\r
- submitLogin: function (event) {\r
- // If submitting is disabled, don't do anything\r
- if ($('button', this.$el).attr('disabled')) return;\r
-\r
- var values = {\r
- nick: $('input.nick', this.$el).val(),\r
- server: $('input.server', this.$el).val(),\r
- port: $('input.port', this.$el).val(),\r
- ssl: $('input.ssl', this.$el).prop('checked'),\r
- password: $('input.password', this.$el).val(),\r
- channel: $('input.channel', this.$el).val(),\r
- channel_key: $('input.channel_key', this.$el).val()\r
- };\r
-\r
- this.trigger('server_connect', values);\r
- },\r
-\r
- submitNickChange: function (event) {\r
- _kiwi.gateway.changeNick(null, $('input.nick', this.$el).val());\r
- this.networkConnecting();\r
- },\r
-\r
- showPass: function (event) {\r
- if (this.$el.find('tr.have_pass input').is(':checked')) {\r
- this.$el.find('tr.pass').show().find('input').focus();\r
- } else {\r
- this.$el.find('tr.pass').hide().find('input').val('');\r
- }\r
- },\r
-\r
- channelKeyIconClick: function (event) {\r
- this.$el.find('tr.have_key input').click();\r
- },\r
-\r
- showKey: function (event) {\r
- if (this.$el.find('tr.have_key input').is(':checked')) {\r
- this.$el.find('tr.key').show().find('input').focus();\r
- } else {\r
- this.$el.find('tr.key').hide().find('input').val('');\r
- }\r
- },\r
-\r
- showMore: function (event) {\r
- $('.more', this.$el).slideDown('fast');\r
- $('input.server', this.$el).select();\r
- },\r
-\r
- populateFields: function (defaults) {\r
- var nick, server, port, channel, channel_key, ssl, password;\r
-\r
- defaults = defaults || {};\r
-\r
- nick = defaults.nick || '';\r
- server = defaults.server || '';\r
- port = defaults.port || 6667;\r
- ssl = defaults.ssl || 0;\r
- password = defaults.password || '';\r
- channel = defaults.channel || '';\r
- channel_key = defaults.channel_key || '';\r
-\r
- $('input.nick', this.$el).val(nick);\r
- $('input.server', this.$el).val(server);\r
- $('input.port', this.$el).val(port);\r
- $('input.ssl', this.$el).prop('checked', ssl);\r
- $('input#server_select_show_pass', this.$el).prop('checked', !(!password));\r
- $('input.password', this.$el).val(password);\r
- if (!(!password)) {\r
- $('tr.pass', this.$el).show();\r
- }\r
- $('input.channel', this.$el).val(channel);\r
- $('input#server_select_show_channel_key', this.$el).prop('checked', !(!channel_key));\r
- $('input.channel_key', this.$el).val(channel_key);\r
- if (!(!channel_key)) {\r
- $('tr.key', this.$el).show();\r
- }\r
- },\r
-\r
- hide: function () {\r
- this.$el.slideUp();\r
- },\r
-\r
- show: function (new_state) {\r
- new_state = new_state || 'all';\r
-\r
- this.$el.show();\r
-\r
- if (new_state === 'all') {\r
- $('.show_more', this.$el).show();\r
-\r
- } else if (new_state === 'more') {\r
- $('.more', this.$el).slideDown('fast');\r
-\r
- } else if (new_state === 'nick_change') {\r
- $('.more', this.$el).hide();\r
- $('.show_more', this.$el).hide();\r
- $('input.nick', this.$el).select();\r
- }\r
-\r
- state = new_state;\r
- },\r
-\r
- infoBoxShow: function() {\r
- var $side_panel = this.$el.find('.side_panel');\r
- this.$el.animate({\r
- width: parseInt($side_panel.css('left'), 10) + $side_panel.find('.content:first').outerWidth()\r
- });\r
- },\r
-\r
- infoBoxHide: function() {\r
- var $side_panel = this.$el.find('.side_panel');\r
- this.$el.animate({\r
- width: parseInt($side_panel.css('left'), 10)\r
- });\r
- },\r
-\r
- infoBoxSet: function($info_view) {\r
- this.$el.find('.side_panel .content')\r
- .empty()\r
- .append($info_view);\r
- },\r
-\r
- setStatus: function (text, class_name) {\r
- $('.status', this.$el)\r
- .text(text)\r
- .attr('class', 'status')\r
- .addClass(class_name||'')\r
- .show();\r
- },\r
- clearStatus: function () {\r
- $('.status', this.$el).hide();\r
- },\r
-\r
- networkConnected: function (event) {\r
- this.setStatus('Connected :)', 'ok');\r
- $('form', this.$el).hide();\r
- },\r
-\r
- networkConnecting: function (event) {\r
- this.setStatus('Connecting..', 'ok');\r
- },\r
-\r
- onIrcError: function (data) {\r
- $('button', this.$el).attr('disabled', null);\r
-\r
- if (data.error == 'nickname_in_use') {\r
- this.setStatus('Nickname already taken');\r
- this.show('nick_change');\r
- }\r
-\r
- if (data.error == 'password_mismatch') {\r
- this.setStatus('Incorrect Password');\r
- this.show('nick_change');\r
- that.$el.find('.password').select();\r
- }\r
- },\r
-\r
- showError: function (error_reason) {\r
- var err_text = 'Error Connecting';\r
-\r
- if (error_reason) {\r
- switch (error_reason) {\r
- case 'ENOTFOUND':\r
- err_text = 'Server not found';\r
- break;\r
-\r
- case 'ECONNREFUSED':\r
- err_text += ' (Connection refused)';\r
- break;\r
-\r
- default:\r
- err_text += ' (' + error_reason + ')';\r
- }\r
- }\r
-\r
- this.setStatus(err_text, 'error');\r
- $('button', this.$el).attr('disabled', null);\r
- this.show();\r
- }\r
- });\r
-\r
-\r
- return new model(arguments);\r
-};\r
-\r
-\r
-_kiwi.view.Panel = Backbone.View.extend({\r
- tagName: "div",\r
- className: "panel messages",\r
-\r
- events: {\r
- "click .chan": "chanClick",\r
- 'click .media .open': 'mediaClick',\r
- 'mouseenter .msg .nick': 'msgEnter',\r
- 'mouseleave .msg .nick': 'msgLeave'\r
- },\r
-\r
- initialize: function (options) {\r
- this.initializePanel(options);\r
- },\r
-\r
- initializePanel: function (options) {\r
- this.$el.css('display', 'none');\r
- options = options || {};\r
-\r
- // Containing element for this panel\r
- if (options.container) {\r
- this.$container = $(options.container);\r
- } else {\r
- this.$container = $('#kiwi .panels .container1');\r
- }\r
-\r
- this.$el.appendTo(this.$container);\r
-\r
- this.alert_level = 0;\r
-\r
- this.model.bind('msg', this.newMsg, this);\r
- this.msg_count = 0;\r
-\r
- this.model.set({"view": this}, {"silent": true});\r
- },\r
-\r
- render: function () {\r
- var that = this;\r
-\r
- this.$el.empty();\r
- _.each(this.model.get('scrollback'), function (msg) {\r
- that.newMsg(msg);\r
- });\r
- },\r
-\r
- newMsg: function (msg) {\r
- var re, line_msg, $this = this.$el,\r
- nick_colour_hex, nick_hex, is_highlight, msg_css_classes = '';\r
-\r
- // Nick highlight detecting\r
- if ((new RegExp('\\b' + _kiwi.app.connections.active_connection.get('nick') + '\\b', 'i')).test(msg.msg)) {\r
- is_highlight = true;\r
- msg_css_classes += ' highlight';\r
- }\r
-\r
- // Escape any HTML that may be in here\r
- msg.msg = $('<div />').text(msg.msg).html();\r
-\r
- // Make the channels clickable\r
- re = new RegExp('(?:^|\\s)([' + _kiwi.gateway.get('channel_prefix') + '][^ ,.\\007]+)', 'g');\r
- msg.msg = msg.msg.replace(re, function (match) {\r
- return '<a class="chan" data-channel="' + match.trim() + '">' + match + '</a>';\r
- });\r
-\r
-\r
- // Parse any links found\r
- 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) {\r
- var nice = url,\r
- extra_html = '';\r
-\r
- // Add the http if no protoocol was found\r
- if (url.match(/^www\./)) {\r
- url = 'http://' + url;\r
- }\r
-\r
- // Shorten the displayed URL if it's going to be too long\r
- if (nice.length > 100) {\r
- nice = nice.substr(0, 100) + '...';\r
- }\r
-\r
- // Get any media HTML if supported\r
- extra_html = _kiwi.view.MediaMessage.buildHtml(url);\r
-\r
- // Make the link clickable\r
- return '<a class="link_ext" target="_blank" rel="nofollow" href="' + url + '">' + nice + '</a> ' + extra_html;\r
- });\r
-\r
-\r
- // Convert IRC formatting into HTML formatting\r
- msg.msg = formatIRCMsg(msg.msg);\r
-\r
-\r
- // Add some colours to the nick (Method based on IRSSIs nickcolor.pl)\r
- nick_colour_hex = (function (nick) {\r
- var nick_int = 0, rgb;\r
-\r
- _.map(nick.split(''), function (i) { nick_int += i.charCodeAt(0); });\r
- rgb = hsl2rgb(nick_int % 255, 70, 35);\r
- rgb = rgb[2] | (rgb[1] << 8) | (rgb[0] << 16);\r
-\r
- return '#' + rgb.toString(16);\r
- })(msg.nick);\r
-\r
- msg.nick_style = 'color:' + nick_colour_hex + ';';\r
-\r
- // Generate a hex string from the nick to be used as a CSS class name\r
- nick_hex = msg.nick_css_class = '';\r
- if (msg.nick) {\r
- _.map(msg.nick.split(''), function (char) {\r
- nick_hex += char.charCodeAt(0).toString(16);\r
- });\r
- msg_css_classes += ' nick_' + nick_hex;\r
- }\r
-\r
- // Build up and add the line\r
- msg.msg_css_classes = msg_css_classes;\r
- 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>';\r
- $this.append(_.template(line_msg, msg));\r
-\r
- // Activity/alerts based on the type of new message\r
- if (msg.type.match(/^action /)) {\r
- this.alert('action');\r
-\r
- } else if (is_highlight) {\r
- _kiwi.app.view.alertWindow('* People are talking!');\r
- _kiwi.app.view.playSound('highlight');\r
- this.alert('highlight');\r
-\r
- } else {\r
- // If this is the active panel, send an alert out\r
- if (this.model.isActive()) {\r
- _kiwi.app.view.alertWindow('* People are talking!');\r
- }\r
- this.alert('activity');\r
- }\r
-\r
- if (this.model.isQuery() && !this.model.isActive()) {\r
- _kiwi.app.view.alertWindow('* People are talking!');\r
- _kiwi.app.view.playSound('highlight');\r
- }\r
-\r
- // Update the activity counters\r
- (function () {\r
- // Only inrement the counters if we're not the active panel\r
- if (this.model.isActive()) return;\r
-\r
- var $act = this.model.tab.find('.activity');\r
- $act.text((parseInt($act.text(), 10) || 0) + 1);\r
- if ($act.text() === '0') {\r
- $act.addClass('zero');\r
- } else {\r
- $act.removeClass('zero');\r
- }\r
- }).apply(this);\r
-\r
- this.scrollToBottom();\r
-\r
- // Make sure our DOM isn't getting too large (Acts as scrollback)\r
- this.msg_count++;\r
- if (this.msg_count > (parseInt(_kiwi.global.settings.get('scrollback'), 10) || 250)) {\r
- $('.msg:first', this.$el).remove();\r
- this.msg_count--;\r
- }\r
- },\r
- chanClick: function (event) {\r
- if (event.target) {\r
- _kiwi.gateway.join(null, $(event.target).data('channel'));\r
- } else {\r
- // IE...\r
- _kiwi.gateway.join(null, $(event.srcElement).data('channel'));\r
- }\r
- },\r
-\r
- mediaClick: function (event) {\r
- var $media = $(event.target).parents('.media');\r
- var media_message;\r
-\r
- if ($media.data('media')) {\r
- media_message = $media.data('media');\r
- } else {\r
- media_message = new _kiwi.view.MediaMessage({el: $media[0]});\r
- $media.data('media', media_message);\r
- }\r
-\r
- $media.data('media', media_message);\r
-\r
- media_message.open();\r
- },\r
-\r
- // Cursor hovers over a message\r
- msgEnter: function (event) {\r
- var nick_class;\r
-\r
- // Find a valid class that this element has\r
- _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {\r
- if (css_class.match(/^nick_[a-z0-9]+/i)) {\r
- nick_class = css_class;\r
- }\r
- });\r
-\r
- // If no class was found..\r
- if (!nick_class) return;\r
-\r
- $('.'+nick_class).addClass('global_nick_highlight');\r
- },\r
-\r
- // Cursor leaves message\r
- msgLeave: function (event) {\r
- var nick_class;\r
-\r
- // Find a valid class that this element has\r
- _.each($(event.currentTarget).parent('.msg').attr('class').split(' '), function (css_class) {\r
- if (css_class.match(/^nick_[a-z0-9]+/i)) {\r
- nick_class = css_class;\r
- }\r
- });\r
-\r
- // If no class was found..\r
- if (!nick_class) return;\r
-\r
- $('.'+nick_class).removeClass('global_nick_highlight');\r
- },\r
-\r
- show: function () {\r
- var $this = this.$el;\r
-\r
- // Hide all other panels and show this one\r
- this.$container.children('.panel').css('display', 'none');\r
- $this.css('display', 'block');\r
-\r
- // Show this panels memberlist\r
- var members = this.model.get("members");\r
- if (members) {\r
- $('#kiwi .memberlists').removeClass('disabled');\r
- members.view.show();\r
- } else {\r
- // Memberlist not found for this panel, hide any active ones\r
- $('#kiwi .memberlists').addClass('disabled').children().removeClass('active');\r
- }\r
-\r
- // Remove any alerts and activity counters for this panel\r
- this.alert('none');\r
- this.model.tab.find('.activity').text('0').addClass('zero');\r
-\r
- _kiwi.app.panels.trigger('active', this.model, _kiwi.app.panels().active);\r
- this.model.trigger('active', this.model);\r
-\r
- _kiwi.app.view.doLayout();\r
-\r
- this.scrollToBottom(true);\r
- },\r
-\r
-\r
- alert: function (level) {\r
- // No need to highlight if this si the active panel\r
- if (this.model == _kiwi.app.panels().active) return;\r
-\r
- var types, type_idx;\r
- types = ['none', 'action', 'activity', 'highlight'];\r
-\r
- // Default alert level\r
- level = level || 'none';\r
-\r
- // If this alert level does not exist, assume clearing current level\r
- type_idx = _.indexOf(types, level);\r
- if (!type_idx) {\r
- level = 'none';\r
- type_idx = 0;\r
- }\r
-\r
- // Only 'upgrade' the alert. Never down (unless clearing)\r
- if (type_idx !== 0 && type_idx <= this.alert_level) {\r
- return;\r
- }\r
-\r
- // Clear any existing levels\r
- this.model.tab.removeClass(function (i, css) {\r
- return (css.match(/\balert_\S+/g) || []).join(' ');\r
- });\r
-\r
- // Add the new level if there is one\r
- if (level !== 'none') {\r
- this.model.tab.addClass('alert_' + level);\r
- }\r
-\r
- this.alert_level = type_idx;\r
- },\r
-\r
-\r
- // Scroll to the bottom of the panel\r
- scrollToBottom: function (force_down) {\r
- // If this isn't the active panel, don't scroll\r
- if (this.model !== _kiwi.app.panels().active) return;\r
-\r
- // Don't scroll down if we're scrolled up the panel a little\r
- if (force_down || this.$container.scrollTop() + this.$container.height() > this.$el.outerHeight() - 150) {\r
- this.$container[0].scrollTop = this.$container[0].scrollHeight;\r
- }\r
- }\r
-});\r
-\r
-_kiwi.view.Applet = _kiwi.view.Panel.extend({\r
- className: 'panel applet',\r
- initialize: function (options) {\r
- this.initializePanel(options);\r
- }\r
-});\r
-\r
-_kiwi.view.Channel = _kiwi.view.Panel.extend({\r
- initialize: function (options) {\r
- this.initializePanel(options);\r
- this.model.bind('change:topic', this.topic, this);\r
-\r
- // Only show the loader if this is a channel (ie. not a query)\r
- if (this.model.isChannel()) {\r
- this.$el.append('<div class="initial_loader" style="margin:1em;text-align:center;">Joining channel.. <span class="loader"></span></div>');\r
- }\r
- },\r
-\r
- // Override the existing newMsg() method to remove the joining channel loader\r
- newMsg: function () {\r
- this.$el.find('.initial_loader').slideUp(function () {\r
- $(this).remove();\r
- });\r
-\r
- return this.constructor.__super__.newMsg.apply(this, arguments);\r
- },\r
-\r
- topic: function (topic) {\r
- if (typeof topic !== 'string' || !topic) {\r
- topic = this.model.get("topic");\r
- }\r
-\r
- this.model.addMsg('', '== Topic for ' + this.model.get('name') + ' is: ' + topic, 'topic');\r
-\r
- // If this is the active channel then update the topic bar\r
- if (_kiwi.app.panels().active === this) {\r
- _kiwi.app.topicbar.setCurrentTopic(this.model.get("topic"));\r
- }\r
- }\r
-});\r
-\r
-\r
-\r
-// Model for this = _kiwi.model.NetworkPanelList\r
-_kiwi.view.NetworkTabs = Backbone.View.extend({\r
- tagName: 'ul',\r
- className: 'connections',\r
-\r
- initialize: function() {\r
- this.model.on('add', this.networkAdded, this);\r
- this.model.on('remove', this.networkRemoved, this);\r
-\r
- this.$el.appendTo($('#kiwi .tabs'));\r
- },\r
-\r
- networkAdded: function(network) {\r
- $('<li class="connection"></li>')\r
- .append(network.panels.view.$el)\r
- .appendTo(this.$el);\r
- },\r
-\r
- networkRemoved: function(network) {\r
- network.panels.view.remove();\r
-\r
- _kiwi.app.view.doLayout();\r
- }\r
-});\r
-\r
-\r
-\r
-// Model for this = _kiwi.model.PanelList\r
-_kiwi.view.Tabs = Backbone.View.extend({\r
- tagName: 'ul',\r
- className: 'panellist',\r
-\r
- events: {\r
- 'click li': 'tabClick',\r
- 'click li .part': 'partClick'\r
- },\r
-\r
- initialize: function () {\r
- this.model.on("add", this.panelAdded, this);\r
- this.model.on("remove", this.panelRemoved, this);\r
- this.model.on("reset", this.render, this);\r
-\r
- this.model.on('active', this.panelActive, this);\r
-\r
- // Network tabs start with a server, so determine what we are now\r
- this.is_network = false;\r
-\r
- if (this.model.network) {\r
- this.is_network = true;\r
-\r
- this.model.network.on('change:name', function (network, new_val) {\r
- $('span', this.model.server.tab).text(new_val);\r
- }, this);\r
- }\r
- },\r
-\r
- render: function () {\r
- var that = this;\r
-\r
- this.$el.empty();\r
- \r
- if (this.is_network) {\r
- // Add the server tab first\r
- this.model.server.tab\r
- .data('panel', this.model.server)\r
- .data('connection_id', this.model.network.get('connection_id'))\r
- .appendTo(this.$el);\r
- }\r
-\r
- // Go through each panel adding its tab\r
- this.model.forEach(function (panel) {\r
- // If this is the server panel, ignore as it's already added\r
- if (this.is_network && panel == that.model.server)\r
- return;\r
-\r
- panel.tab.data('panel', panel);\r
-\r
- if (this.is_network)\r
- panel.tab.data('connection_id', this.model.network.get('connection_id'));\r
-\r
- panel.tab.appendTo(that.$el);\r
- });\r
-\r
- _kiwi.app.view.doLayout();\r
- },\r
-\r
- updateTabTitle: function (panel, new_title) {\r
- $('span', panel.tab).text(new_title);\r
- },\r
-\r
- panelAdded: function (panel) {\r
- // Add a tab to the panel\r
- panel.tab = $('<li><span>' + (panel.get('title') || panel.get('name')) + '</span><div class="activity"></div></li>');\r
-\r
- if (panel.isServer()) {\r
- panel.tab.addClass('server');\r
- panel.tab.addClass('icon-nonexistant');\r
- }\r
-\r
- panel.tab.data('panel', panel);\r
-\r
- if (this.is_network)\r
- panel.tab.data('connection_id', this.model.network.get('connection_id'));\r
-\r
- panel.tab.appendTo(this.$el);\r
-\r
- panel.bind('change:title', this.updateTabTitle);\r
- panel.bind('change:name', this.updateTabTitle);\r
-\r
- _kiwi.app.view.doLayout();\r
- },\r
- panelRemoved: function (panel) {\r
- panel.tab.remove();\r
- delete panel.tab;\r
-\r
- _kiwi.app.view.doLayout();\r
- },\r
-\r
- panelActive: function (panel, previously_active_panel) {\r
- // Remove any existing tabs or part images\r
- _kiwi.app.view.$el.find('.panellist .part').remove();\r
- _kiwi.app.view.$el.find('.panellist .active').removeClass('active');\r
-\r
- panel.tab.addClass('active');\r
-\r
- // Only show the part image on non-server tabs\r
- if (!panel.isServer()) {\r
- panel.tab.append('<span class="part icon-nonexistant"></span>');\r
- }\r
- },\r
-\r
- tabClick: function (e) {\r
- var tab = $(e.currentTarget);\r
-\r
- var panel = tab.data('panel');\r
- if (!panel) {\r
- // A panel wasn't found for this tab... wadda fuck\r
- return;\r
- }\r
-\r
- panel.view.show();\r
- },\r
-\r
- partClick: function (e) {\r
- var tab = $(e.currentTarget).parent();\r
- var panel = tab.data('panel');\r
-\r
- if (!panel) return;\r
-\r
- // Only need to part if it's a channel\r
- // If the nicklist is empty, we haven't joined the channel as yet\r
- if (panel.isChannel() && panel.get('members').models.length > 0) {\r
- this.model.network.gateway.part(panel.get('name'));\r
- } else {\r
- panel.close();\r
- }\r
- }\r
-});\r
-\r
-\r
-\r
-_kiwi.view.TopicBar = Backbone.View.extend({\r
- events: {\r
- 'keydown div': 'process'\r
- },\r
-\r
- initialize: function () {\r
- _kiwi.app.panels.bind('active', function (active_panel) {\r
- // If it's a channel topic, update and make editable\r
- if (active_panel.isChannel()) {\r
- this.setCurrentTopic(active_panel.get('topic') || '');\r
- this.$el.find('div').attr('contentEditable', true);\r
-\r
- } else {\r
- // Not a channel topic.. clear and make uneditable\r
- this.$el.find('div').attr('contentEditable', false)\r
- .text('');\r
- }\r
- }, this);\r
- },\r
-\r
- process: function (ev) {\r
- var inp = $(ev.currentTarget),\r
- inp_val = inp.text();\r
- \r
- // Only allow topic editing if this is a channel panel\r
- if (!_kiwi.app.panels().active.isChannel()) {\r
- return false;\r
- }\r
-\r
- // If hit return key, update the current topic\r
- if (ev.keyCode === 13) {\r
- _kiwi.gateway.topic(null, _kiwi.app.panels().active.get('name'), inp_val);\r
- return false;\r
- }\r
- },\r
-\r
- setCurrentTopic: function (new_topic) {\r
- new_topic = new_topic || '';\r
-\r
- // We only want a plain text version\r
- $('div', this.$el).html(formatIRCMsg(_.escape(new_topic)));\r
- }\r
-});\r
-\r
-\r
-\r
-_kiwi.view.ControlBox = Backbone.View.extend({\r
- events: {\r
- 'keydown .inp': 'process',\r
- 'click .nick': 'showNickChange'\r
- },\r
-\r
- initialize: function () {\r
- var that = this;\r
-\r
- this.buffer = []; // Stores previously run commands\r
- this.buffer_pos = 0; // The current position in the buffer\r
-\r
- this.preprocessor = new InputPreProcessor();\r
- this.preprocessor.recursive_depth = 5;\r
-\r
- // Hold tab autocomplete data\r
- this.tabcomplete = {active: false, data: [], prefix: ''};\r
-\r
- // Keep the nick view updated with nick changes\r
- _kiwi.app.connections.on('change:nick', function(connection) {\r
- // Only update the nick view if it's the active connection\r
- if (connection !== _kiwi.app.connections.active_connection)\r
- return;\r
-\r
- $('.nick', that.$el).text(connection.get('nick'));\r
- });\r
-\r
- // Update our nick view as we flick between connections\r
- _kiwi.app.connections.on('active', function(panel, connection) {\r
- $('.nick', that.$el).text(connection.get('nick'));\r
- });\r
- },\r
-\r
- showNickChange: function (ev) {\r
- (new _kiwi.view.NickChangeBox()).render();\r
- },\r
-\r
- process: function (ev) {\r
- var that = this,\r
- inp = $(ev.currentTarget),\r
- inp_val = inp.val(),\r
- meta;\r
-\r
- if (navigator.appVersion.indexOf("Mac") !== -1) {\r
- meta = ev.metaKey;\r
- } else {\r
- meta = ev.altKey;\r
- }\r
-\r
- // If not a tab key, reset the tabcomplete data\r
- if (this.tabcomplete.active && ev.keyCode !== 9) {\r
- this.tabcomplete.active = false;\r
- this.tabcomplete.data = [];\r
- this.tabcomplete.prefix = '';\r
- }\r
- \r
- switch (true) {\r
- case (ev.keyCode === 13): // return\r
- inp_val = inp_val.trim();\r
-\r
- if (inp_val) {\r
- $.each(inp_val.split('\n'), function (idx, line) {\r
- that.processInput(line);\r
- });\r
-\r
- this.buffer.push(inp_val);\r
- this.buffer_pos = this.buffer.length;\r
- }\r
-\r
- inp.val('');\r
- return false;\r
-\r
- break;\r
-\r
- case (ev.keyCode === 38): // up\r
- if (this.buffer_pos > 0) {\r
- this.buffer_pos--;\r
- inp.val(this.buffer[this.buffer_pos]);\r
- }\r
- //suppress browsers default behavior as it would set the cursor at the beginning\r
- return false;\r
-\r
- case (ev.keyCode === 40): // down\r
- if (this.buffer_pos < this.buffer.length) {\r
- this.buffer_pos++;\r
- inp.val(this.buffer[this.buffer_pos]);\r
- }\r
- break;\r
-\r
- case (ev.keyCode === 219 && meta): // [ + meta\r
- // Find all the tab elements and get the index of the active tab\r
- var $tabs = $('#kiwi .tabs').find('li[class!=connection]');\r
- var cur_tab_ind = (function() {\r
- for (var idx=0; idx<$tabs.length; idx++){\r
- if ($($tabs[idx]).hasClass('active'))\r
- return idx;\r
- }\r
- })();\r
-\r
- // Work out the previous tab along. Wrap around if needed\r
- if (cur_tab_ind === 0) {\r
- $prev_tab = $($tabs[$tabs.length - 1]);\r
- } else {\r
- $prev_tab = $($tabs[cur_tab_ind - 1]);\r
- }\r
-\r
- $prev_tab.click();\r
- return false;\r
-\r
- case (ev.keyCode === 221 && meta): // ] + meta\r
- // Find all the tab elements and get the index of the active tab\r
- var $tabs = $('#kiwi .tabs').find('li[class!=connection]');\r
- var cur_tab_ind = (function() {\r
- for (var idx=0; idx<$tabs.length; idx++){\r
- if ($($tabs[idx]).hasClass('active'))\r
- return idx;\r
- }\r
- })();\r
-\r
- // Work out the next tab along. Wrap around if needed\r
- if (cur_tab_ind === $tabs.length - 1) {\r
- $next_tab = $($tabs[0]);\r
- } else {\r
- $next_tab = $($tabs[cur_tab_ind + 1]);\r
- }\r
-\r
- $next_tab.click();\r
- return false;\r
-\r
- case (ev.keyCode === 9): // tab\r
- this.tabcomplete.active = true;\r
- if (_.isEqual(this.tabcomplete.data, [])) {\r
- // Get possible autocompletions\r
- var ac_data = [],\r
- members = _kiwi.app.panels().active.get('members');\r
-\r
- // If we have a members list, get the models. Otherwise empty array\r
- members = members ? members.models : [];\r
-\r
- $.each(members, function (i, member) {\r
- if (!member) return;\r
- ac_data.push(member.get('nick'));\r
- });\r
-\r
- ac_data.push(_kiwi.app.panels().active.get('name'));\r
-\r
- ac_data = _.sortBy(ac_data, function (nick) {\r
- return nick;\r
- });\r
- this.tabcomplete.data = ac_data;\r
- }\r
-\r
- if (inp_val[inp[0].selectionStart - 1] === ' ') {\r
- return false;\r
- }\r
- \r
- (function () {\r
- var tokens, // Words before the cursor position\r
- val, // New value being built up\r
- p1, // Position in the value just before the nick \r
- newnick, // New nick to be displayed (cycles through)\r
- range, // TextRange for setting new text cursor position\r
- nick, // Current nick in the value\r
- trailing = ': '; // Text to be inserted after a tabbed nick\r
-\r
- tokens = inp_val.substring(0, inp[0].selectionStart).split(' ');\r
- if (tokens[tokens.length-1] == ':')\r
- tokens.pop();\r
-\r
- nick = tokens[tokens.length - 1];\r
-\r
- if (this.tabcomplete.prefix === '') {\r
- this.tabcomplete.prefix = nick;\r
- }\r
-\r
- this.tabcomplete.data = _.select(this.tabcomplete.data, function (n) {\r
- return (n.toLowerCase().indexOf(that.tabcomplete.prefix.toLowerCase()) === 0);\r
- });\r
-\r
- if (this.tabcomplete.data.length > 0) {\r
- // Get the current value before cursor position\r
- p1 = inp[0].selectionStart - (nick.length);\r
- val = inp_val.substr(0, p1);\r
-\r
- // Include the current selected nick\r
- newnick = this.tabcomplete.data.shift();\r
- this.tabcomplete.data.push(newnick);\r
- val += newnick;\r
-\r
- if (inp_val.substr(inp[0].selectionStart, 2) !== trailing)\r
- val += trailing;\r
-\r
- // Now include the rest of the current value\r
- val += inp_val.substr(inp[0].selectionStart);\r
-\r
- inp.val(val);\r
-\r
- // Move the cursor position to the end of the nick\r
- if (inp[0].setSelectionRange) {\r
- inp[0].setSelectionRange(p1 + newnick.length + trailing.length, p1 + newnick.length + trailing.length);\r
- } else if (inp[0].createTextRange) { // not sure if this bit is actually needed....\r
- range = inp[0].createTextRange();\r
- range.collapse(true);\r
- range.moveEnd('character', p1 + newnick.length + trailing.length);\r
- range.moveStart('character', p1 + newnick.length + trailing.length);\r
- range.select();\r
- }\r
- }\r
- }).apply(this);\r
- return false;\r
- }\r
- },\r
-\r
-\r
- processInput: function (command_raw) {\r
- var command, params,\r
- pre_processed;\r
- \r
- // The default command\r
- if (command_raw[0] !== '/' || command_raw.substr(0, 2) === '//') {\r
- // Remove any slash escaping at the start (ie. //)\r
- command_raw = command_raw.replace(/^\/\//, '/');\r
-\r
- // Prepend the default command\r
- command_raw = '/msg ' + _kiwi.app.panels().active.get('name') + ' ' + command_raw;\r
- }\r
-\r
- // Process the raw command for any aliases\r
- this.preprocessor.vars.server = _kiwi.app.connections.active_connection.get('name');\r
- this.preprocessor.vars.channel = _kiwi.app.panels().active.get('name');\r
- this.preprocessor.vars.destination = this.preprocessor.vars.channel;\r
- command_raw = this.preprocessor.process(command_raw);\r
-\r
- // Extract the command and parameters\r
- params = command_raw.split(' ');\r
- if (params[0][0] === '/') {\r
- command = params[0].substr(1).toLowerCase();\r
- params = params.splice(1, params.length - 1);\r
- } else {\r
- // Default command\r
- command = 'msg';\r
- params.unshift(_kiwi.app.panels().active.get('name'));\r
- }\r
-\r
- // Trigger the command events\r
- this.trigger('command', {command: command, params: params});\r
- this.trigger('command:' + command, {command: command, params: params});\r
-\r
- // If we didn't have any listeners for this event, fire a special case\r
- // TODO: This feels dirty. Should this really be done..?\r
- if (!this._events['command:' + command]) {\r
- this.trigger('unknown_command', {command: command, params: params});\r
- }\r
- },\r
-\r
-\r
- addPluginIcon: function ($icon) {\r
- var $tool = $('<div class="tool"></div>').append($icon);\r
- this.$el.find('.input_tools').append($tool);\r
- _kiwi.app.view.doLayout();\r
- }\r
-});\r
-\r
-\r
-\r
-\r
-_kiwi.view.StatusMessage = Backbone.View.extend({\r
- initialize: function () {\r
- this.$el.hide();\r
-\r
- // Timer for hiding the message after X seconds\r
- this.tmr = null;\r
- },\r
-\r
- text: function (text, opt) {\r
- // Defaults\r
- opt = opt || {};\r
- opt.type = opt.type || '';\r
- opt.timeout = opt.timeout || 5000;\r
-\r
- this.$el.text(text).addClass(opt.type);\r
- this.$el.slideDown($.proxy(_kiwi.app.view.doLayout, _kiwi.app.view));\r
-\r
- if (opt.timeout) this.doTimeout(opt.timeout);\r
- },\r
-\r
- html: function (html, opt) {\r
- // Defaults\r
- opt = opt || {};\r
- opt.type = opt.type || '';\r
- opt.timeout = opt.timeout || 5000;\r
-\r
- this.$el.html(text).addClass(opt.type);\r
- this.$el.slideDown($.proxy(_kiwi.app.view.doLayout, _kiwi.app.view));\r
-\r
- if (opt.timeout) this.doTimeout(opt.timeout);\r
- },\r
-\r
- hide: function () {\r
- this.$el.slideUp($.proxy(_kiwi.app.view.doLayout, _kiwi.app.view));\r
- },\r
-\r
- doTimeout: function (length) {\r
- if (this.tmr) clearTimeout(this.tmr);\r
- var that = this;\r
- this.tmr = setTimeout(function () { that.hide(); }, length);\r
- }\r
-});\r
-\r
-\r
-\r
-\r
-_kiwi.view.ResizeHandler = Backbone.View.extend({\r
- events: {\r
- 'mousedown': 'startDrag',\r
- 'mouseup': 'stopDrag'\r
- },\r
-\r
- initialize: function () {\r
- this.dragging = false;\r
- this.starting_width = {};\r
-\r
- $(window).on('mousemove', $.proxy(this.onDrag, this));\r
- },\r
-\r
- startDrag: function (event) {\r
- this.dragging = true;\r
- },\r
-\r
- stopDrag: function (event) {\r
- this.dragging = false;\r
- },\r
-\r
- onDrag: function (event) {\r
- if (!this.dragging) return;\r
-\r
- this.$el.css('left', event.clientX - (this.$el.outerWidth(true) / 2));\r
- $('#kiwi .memberlists').css('width', this.$el.parent().width() - (this.$el.position().left + this.$el.outerWidth()));\r
- _kiwi.app.view.doLayout();\r
- }\r
-});\r
-\r
-\r
-\r
-_kiwi.view.AppToolbar = Backbone.View.extend({\r
- events: {\r
- 'click .settings': 'clickSettings'\r
- },\r
-\r
- initialize: function () {\r
- },\r
-\r
- clickSettings: function (event) {\r
- _kiwi.app.controlbox.processInput('/settings');\r
- }\r
-});\r
-\r
-\r
-\r
-_kiwi.view.Application = Backbone.View.extend({\r
- initialize: function () {\r
- var that = this;\r
-\r
- $(window).resize(function() { that.doLayout.apply(that); });\r
- this.$el.find('.toolbar').resize(function() { that.doLayout.apply(that); });\r
- $('#kiwi .controlbox').resize(function() { that.doLayout.apply(that); });\r
-\r
- // Change the theme when the config is changed\r
- _kiwi.global.settings.on('change:theme', this.updateTheme, this);\r
- this.updateTheme(getQueryVariable('theme'));\r
-\r
- _kiwi.global.settings.on('change:channel_list_style', this.setTabLayout, this);\r
- this.setTabLayout(_kiwi.global.settings.get('channel_list_style'));\r
-\r
- _kiwi.global.settings.on('change:show_timestamps', this.displayTimestamps, this);\r
- this.displayTimestamps(_kiwi.global.settings.get('show_timestamps'));\r
-\r
- this.doLayout();\r
-\r
- $(document).keydown(this.setKeyFocus);\r
-\r
- // Confirmation require to leave the page\r
- window.onbeforeunload = function () {\r
- if (_kiwi.gateway.isConnected()) {\r
- return 'This will close all KiwiIRC conversations. Are you sure you want to close this window?';\r
- }\r
- };\r
-\r
- this.initSound();\r
- },\r
-\r
-\r
-\r
- updateTheme: function (theme_name) {\r
- // If called by the settings callback, get the correct new_value\r
- if (theme_name === _kiwi.global.settings) {\r
- theme_name = arguments[1];\r
- }\r
-\r
- // If we have no theme specified, get it from the settings\r
- if (!theme_name) theme_name = _kiwi.global.settings.get('theme');\r
-\r
- // Clear any current theme\r
- this.$el.removeClass(function (i, css) {\r
- return (css.match(/\btheme_\S+/g) || []).join(' ');\r
- });\r
-\r
- // Apply the new theme\r
- this.$el.addClass('theme_' + (theme_name || 'relaxed'));\r
- },\r
-\r
-\r
- setTabLayout: function (layout_style) {\r
- // If called by the settings callback, get the correct new_value\r
- if (layout_style === _kiwi.global.settings) {\r
- layout_style = arguments[1];\r
- }\r
- \r
- if (layout_style == 'list') {\r
- this.$el.addClass('chanlist_treeview');\r
- } else {\r
- this.$el.removeClass('chanlist_treeview');\r
- }\r
- \r
- this.doLayout();\r
- },\r
-\r
-\r
- displayTimestamps: function (show_timestamps) {\r
- // If called by the settings callback, get the correct new_value\r
- if (show_timestamps === _kiwi.global.settings) {\r
- show_timestamps = arguments[1];\r
- }\r
-\r
- if (show_timestamps) {\r
- this.$el.addClass('timestamps');\r
- } else {\r
- this.$el.removeClass('timestamps');\r
- }\r
- },\r
-\r
-\r
- // Globally shift focus to the command input box on a keypress\r
- setKeyFocus: function (ev) {\r
- // If we're copying text, don't shift focus\r
- if (ev.ctrlKey || ev.altKey || ev.metaKey) {\r
- return;\r
- }\r
-\r
- // If we're typing into an input box somewhere, ignore\r
- if ((ev.target.tagName.toLowerCase() === 'input') || (ev.target.tagName.toLowerCase() === 'textarea') || $(ev.target).attr('contenteditable')) {\r
- return;\r
- }\r
-\r
- $('#kiwi .controlbox .inp').focus();\r
- },\r
-\r
-\r
- doLayout: function () {\r
- var el_kiwi = this.$el;\r
- var el_panels = $('#kiwi .panels');\r
- var el_memberlists = $('#kiwi .memberlists');\r
- var el_toolbar = this.$el.find('.toolbar');\r
- var el_controlbox = $('#kiwi .controlbox');\r
- var el_resize_handle = $('#kiwi .memberlists_resize_handle');\r
-\r
- var css_heights = {\r
- top: el_toolbar.outerHeight(true),\r
- bottom: el_controlbox.outerHeight(true)\r
- };\r
-\r
-\r
- // If any elements are not visible, full size the panals instead\r
- if (!el_toolbar.is(':visible')) {\r
- css_heights.top = 0;\r
- }\r
-\r
- if (!el_controlbox.is(':visible')) {\r
- css_heights.bottom = 0;\r
- }\r
-\r
- // Apply the CSS sizes\r
- el_panels.css(css_heights);\r
- el_memberlists.css(css_heights);\r
- el_resize_handle.css(css_heights);\r
-\r
- // If we have channel tabs on the side, adjust the height\r
- if (el_kiwi.hasClass('chanlist_treeview')) {\r
- this.$el.find('.tabs', el_kiwi).css(css_heights);\r
- }\r
-\r
- // Determine if we have a narrow window (mobile/tablet/or even small desktop window)\r
- if (el_kiwi.outerWidth() < 400) {\r
- el_kiwi.addClass('narrow');\r
- } else {\r
- el_kiwi.removeClass('narrow');\r
- }\r
-\r
- // Set the panels width depending on the memberlist visibility\r
- if (el_memberlists.css('display') != 'none') {\r
- // Panels to the side of the memberlist\r
- el_panels.css('right', el_memberlists.outerWidth(true));\r
- // The resize handle sits overlapping the panels and memberlist\r
- el_resize_handle.css('left', el_memberlists.position().left - (el_resize_handle.outerWidth(true) / 2));\r
- } else {\r
- // Memberlist is hidden so panels to the right edge\r
- el_panels.css('right', 0);\r
- // And move the handle just out of sight to the right\r
- el_resize_handle.css('left', el_panels.outerWidth(true));\r
- }\r
-\r
- var input_wrap_width = parseInt($('#kiwi .controlbox .input_tools').outerWidth());\r
- el_controlbox.find('.input_wrap').css('right', input_wrap_width + 7);\r
- },\r
-\r
-\r
- alertWindow: function (title) {\r
- if (!this.alertWindowTimer) {\r
- this.alertWindowTimer = new (function () {\r
- var that = this;\r
- var tmr;\r
- var has_focus = true;\r
- var state = 0;\r
- var default_title = 'Kiwi IRC';\r
- var title = 'Kiwi IRC';\r
-\r
- this.setTitle = function (new_title) {\r
- new_title = new_title || default_title;\r
- window.document.title = new_title;\r
- return new_title;\r
- };\r
-\r
- this.start = function (new_title) {\r
- // Don't alert if we already have focus\r
- if (has_focus) return;\r
-\r
- title = new_title;\r
- if (tmr) return;\r
- tmr = setInterval(this.update, 1000);\r
- };\r
-\r
- this.stop = function () {\r
- // Stop the timer and clear the title\r
- if (tmr) clearInterval(tmr);\r
- tmr = null;\r
- this.setTitle();\r
-\r
- // Some browsers don't always update the last title correctly\r
- // Wait a few seconds and then reset\r
- setTimeout(this.reset, 2000);\r
- };\r
-\r
- this.reset = function () {\r
- if (tmr) return;\r
- that.setTitle();\r
- };\r
-\r
-\r
- this.update = function () {\r
- if (state === 0) {\r
- that.setTitle(title);\r
- state = 1;\r
- } else {\r
- that.setTitle();\r
- state = 0;\r
- }\r
- };\r
-\r
- $(window).focus(function (event) {\r
- has_focus = true;\r
- that.stop();\r
-\r
- // Some browsers don't always update the last title correctly\r
- // Wait a few seconds and then reset\r
- setTimeout(that.reset, 2000);\r
- });\r
-\r
- $(window).blur(function (event) {\r
- has_focus = false;\r
- });\r
- })();\r
- }\r
-\r
- this.alertWindowTimer.start(title);\r
- },\r
-\r
-\r
- barsHide: function (instant) {\r
- var that = this;\r
-\r
- if (!instant) {\r
- this.$el.find('.toolbar').slideUp({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
- $('#kiwi .controlbox').slideUp({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
- } else {\r
- this.$el.find('.toolbar').slideUp(0);\r
- $('#kiwi .controlbox').slideUp(0);\r
- this.doLayout();\r
- }\r
- },\r
-\r
- barsShow: function (instant) {\r
- var that = this;\r
-\r
- if (!instant) {\r
- this.$el.find('.toolbar').slideDown({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
- $('#kiwi .controlbox').slideDown({queue: false, duration: 400, step: $.proxy(this.doLayout, this)});\r
- } else {\r
- this.$el.find('.toolbar').slideDown(0);\r
- $('#kiwi .controlbox').slideDown(0);\r
- this.doLayout();\r
- }\r
- },\r
-\r
-\r
- initSound: function () {\r
- var that = this,\r
- base_path = this.model.get('base_path');\r
-\r
- $script(base_path + '/assets/libs/soundmanager2/soundmanager2-nodebug-jsmin.js', function() {\r
- if (typeof soundManager === 'undefined')\r
- return;\r
-\r
- soundManager.setup({\r
- url: base_path + '/assets/libs/soundmanager2/',\r
- flashVersion: 9, // optional: shiny features (default = 8)// optional: ignore Flash where possible, use 100% HTML5 mode\r
- preferFlash: true,\r
-\r
- onready: function() {\r
- that.sound_object = soundManager.createSound({\r
- id: 'highlight',\r
- url: base_path + '/assets/sound/highlight.mp3'\r
- });\r
- }\r
- });\r
- });\r
- },\r
-\r
-\r
- playSound: function (sound_id) {\r
- if (!this.sound_object) return;\r
-\r
- if (_kiwi.global.settings.get('mute_sounds'))\r
- return;\r
- \r
- soundManager.play(sound_id);\r
- }\r
-});\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-\r
-_kiwi.view.MediaMessage = Backbone.View.extend({\r
- events: {\r
- 'click .media_close': 'close'\r
- },\r
-\r
- initialize: function () {\r
- // Get the URL from the data\r
- this.url = this.$el.data('url');\r
- },\r
-\r
- // Close the media content and remove it from display\r
- close: function () {\r
- var that = this;\r
- this.$content.slideUp('fast', function () {\r
- that.$content.remove();\r
- });\r
- },\r
-\r
- // Open the media content within its wrapper\r
- open: function () {\r
- // Create the content div if we haven't already\r
- if (!this.$content) {\r
- this.$content = $('<div class="media_content"><a class="media_close"><i class="icon-chevron-up"></i> Close media</a><br /><div class="content"></div></div>');\r
- this.$content.find('.content').append(this.mediaTypes[this.$el.data('type')].apply(this, []) || 'Not found :(');\r
- }\r
-\r
- // Now show the content if not already\r
- if (!this.$content.is(':visible')) {\r
- // Hide it first so the slideDown always plays\r
- this.$content.hide();\r
-\r
- // Add the media content and slide it into view\r
- this.$el.append(this.$content);\r
- this.$content.slideDown();\r
- }\r
- },\r
-\r
-\r
-\r
- // Generate the media content for each recognised type\r
- mediaTypes: {\r
- twitter: function () {\r
- var tweet_id = this.$el.data('tweetid');\r
- var that = this;\r
-\r
- $.getJSON('https://api.twitter.com/1/statuses/oembed.json?id=' + tweet_id + '&callback=?', function (data) {\r
- that.$content.find('.content').html(data.html);\r
- });\r
-\r
- return $('<div>Loading tweet..</div>');\r
- },\r
-\r
-\r
- image: function () {\r
- return $('<a href="' + this.url + '" target="_blank"><img height="100" src="' + this.url + '" /></a>');\r
- },\r
-\r
-\r
- reddit: function () {\r
- var that = this;\r
- var matches = (/reddit\.com\/r\/([a-zA-Z0-9_\-]+)\/comments\/([a-z0-9]+)\/([^\/]+)?/gi).exec(this.url);\r
-\r
- $.getJSON('http://www.' + matches[0] + '.json?jsonp=?', function (data) {\r
- console.log('Loaded reddit data', data);\r
- var post = data[0].data.children[0].data;\r
- var thumb = '';\r
-\r
- // Show a thumbnail if there is one\r
- if (post.thumbnail) {\r
- //post.thumbnail = 'http://www.eurotunnel.com/uploadedImages/commercial/back-steps-icon-arrow.png';\r
-\r
- // Hide the thumbnail if an over_18 image\r
- if (post.over_18) {\r
- thumb = '<span class="thumbnail_nsfw" onclick="$(this).find(\'p\').remove(); $(this).find(\'img\').css(\'visibility\', \'visible\');">';\r
- thumb += '<p style="font-size:0.9em;line-height:1.2em;cursor:pointer;">Show<br />NSFW</p>';\r
- thumb += '<img src="' + post.thumbnail + '" class="thumbnail" style="visibility:hidden;" />';\r
- thumb += '</span>';\r
- } else {\r
- thumb = '<img src="' + post.thumbnail + '" class="thumbnail" />';\r
- }\r
- }\r
-\r
- // Build the template string up\r
- var tmpl = '<div>' + thumb + '<b><%- title %></b><br />Posted by <%- author %>. ';\r
- tmpl += '<i class="icon-arrow-up"></i> <%- ups %> <i class="icon-arrow-down"></i> <%- downs %><br />';\r
- tmpl += '<%- num_comments %> comments made. <a href="http://www.reddit.com<%- permalink %>">View post</a></div>';\r
-\r
- that.$content.find('.content').html(_.template(tmpl, post));\r
- });\r
-\r
- return $('<div>Loading Reddit thread..</div>');\r
- }\r
- }\r
-\r
-}, {\r
-\r
- // Build the closed media HTML from a URL\r
- buildHtml: function (url) {\r
- var html = '', matches;\r
-\r
- // Is it an image?\r
- if (url.match(/(\.jpe?g|\.gif|\.bmp|\.png)\??$/i)) {\r
- html += '<span class="media image" data-type="image" data-url="' + url + '" title="Open Image"><a class="open"><i class="icon-chevron-right"></i></a></span>';\r
- }\r
-\r
- // Is it a tweet?\r
- matches = (/https?:\/\/twitter.com\/([a-zA-Z0-9_]+)\/status\/([0-9]+)/ig).exec(url);\r
- if (matches) {\r
- html += '<span class="media twitter" data-type="twitter" data-url="' + url + '" data-tweetid="' + matches[2] + '" title="Show tweet information"><a class="open"><i class="icon-chevron-right"></i></a></span>';\r
- }\r
-\r
- // Is reddit?\r
- matches = (/reddit\.com\/r\/([a-zA-Z0-9_\-]+)\/comments\/([a-z0-9]+)\/([^\/]+)?/gi).exec(url);\r
- if (matches) {\r
- html += '<span class="media reddit" data-type="reddit" data-url="' + url + '" title="Reddit thread"><a class="open"><i class="icon-chevron-right"></i></a></span>';\r
- }\r
-\r
- return html;\r
- }\r
-});\r
-\r
-\r
-\r
-_kiwi.view.MenuBox = Backbone.View.extend({\r
- events: {\r
- 'click .ui_menu_foot .close': 'dispose'\r
- },\r
-\r
- initialize: function(title) {\r
- var that = this;\r
-\r
- this.$el = $('<div class="ui_menu"></div>');\r
-\r
- this._title = title || '';\r
- this._items = {};\r
- this._display_footer = true;\r
- this._close_on_blur = true;\r
-\r
- this._close_proxy = function(event) {\r
- that.onDocumentClick(event);\r
- };\r
- $(document).on('click', this._close_proxy);\r
- },\r
-\r
-\r
- render: function() {\r
- var that = this;\r
-\r
- this.$el.find('*').remove();\r
-\r
- if (this._title) {\r
- $('<div class="ui_menu_title"></div>')\r
- .text(this._title)\r
- .appendTo(this.$el);\r
- }\r
-\r
-\r
- _.each(this._items, function(item) {\r
- var $item = $('<div class="ui_menu_content hover"></div>')\r
- .append(item);\r
-\r
- that.$el.append($item);\r
- });\r
-\r
- if (this._display_footer)\r
- this.$el.append('<div class="ui_menu_foot"><a class="close" onclick="">Close <i class="icon-remove"></i></a></div>');\r
- },\r
-\r
-\r
- onDocumentClick: function(event) {\r
- var $target = $(event.target);\r
-\r
- if (!this._close_on_blur)\r
- return;\r
-\r
- // If this is not itself AND we don't contain this element, dispose $el\r
- if ($target[0] != this.$el[0] && this.$el.has($target).length === 0)\r
- this.dispose();\r
- },\r
-\r
-\r
- dispose: function() {\r
- _.each(this._items, function(item) {\r
- item.dispose && item.dispose();\r
- item.remove && item.remove();\r
- });\r
-\r
- this._items = null;\r
- this.remove();\r
-\r
- $(document).off('click', this._close_proxy);\r
- },\r
-\r
-\r
- addItem: function(item_name, $item) {\r
- $item = $($item);\r
- if ($item.is('a')) $item.addClass('icon-chevron-right');\r
- this._items[item_name] = $item;\r
- },\r
-\r
-\r
- removeItem: function(item_name) {\r
- delete this._items[item_name];\r
- },\r
-\r
-\r
- showFooter: function(show) {\r
- this._display_footer = show;\r
- },\r
-\r
-\r
- closeOnBlur: function(close_it) {\r
- this._close_on_blur = close_it;\r
- },\r
-\r
-\r
- show: function() {\r
- this.render();\r
- this.$el.appendTo(_kiwi.app.view.$el);\r
- }\r
-});\r