diff --git a/lefttabs/lefttabs.js b/lefttabs/lefttabs.js index a5bce2f..b4962a8 100644 --- a/lefttabs/lefttabs.js +++ b/lefttabs/lefttabs.js @@ -3,46 +3,181 @@ * Author: Melissa Adamaitis */ -/* global window, Candy, jQuery */ - var CandyShop = (function(self) { return self; }(CandyShop || {})); CandyShop.LeftTabs = (function(self, Candy, $) { + /** Object: about + * + * Contains: + * (String) name - Candy Plugin Layout with Left Tabs + Bootstrap3 + * (Float) version - Candy Plugin Layout with Left Tabs + Bootstrap3 + */ + self.about = { + name: 'Candy Plugin Layout with Left Tabs + Bootstrap3', + version: '1.0' + }; + + self.autoScroll = true; + /** * Initializes the LeftTabs plugin with the default settings. */ self.init = function(){ + // Attempt to detect when a mobile keyboard is shown and adjust the height + $(Candy).on('candy:view.room.after-add', function(ev, obj) { + var form = obj.element.find('form.message-form'), + input = form.find('textarea[name="message"]'), + messagePane = obj.element.find('.message-pane'), + + resetHeightOfPane = function (messagePane, height) { + messagePane.height('calc(100% - ' + (height || input.outerHeight()) + 'px)'); + + return { + andScrollToBottom: function () { + messagePane.scrollTop(messagePane[0].scrollHeight); + } + }; + }; + + resetHeightOfPane(messagePane); + + form.bind('submit', function(e) { + var roomPane = $(e.target).closest('.room-pane'); + var messagePane = $('.message-pane', roomPane); + + resetHeightOfPane(messagePane).andScrollToBottom(); + }); + + input.bind('focus', self.resetHeight); + input.bind('blur', self.resetHeight); + + input.on('input', $.throttle(500, function() { + this.style.height = '2em'; + var newHeight = Math.min(this.scrollHeight, '168') + 'px'; + this.style.height = newHeight; + + var messagePane = $('.room-pane[data-roomjid="' + Candy.View.getCurrent().roomJid + '"] .message-pane-wrapper .message-pane'); + resetHeightOfPane(messagePane, newHeight) + + })).on('keydown', function(ev) { + if (ev.which === 13) { // enter + if (input.data('ready-to-send')) { + form.submit(); + } else { + // Allow other plugins to delay sending of the message + ev.preventDefault(); + } + return false; + } + }); + + + form.on('submit', function(ev) { + $(ev.currentTarget).find('textarea').css('height', '2em'); + resetHeightOfPane(messagePane); + }); + + // Hide emoticons option for now. + obj.element.find('#emoticons-icon').hide(); + + $(Candy).on('candy:view.connection.status-' + Strophe.Status.CONNFAIL, function() { + sweetAlert({ + customClass: 'status-2', + title: 'Whoops!', + text: 'Connection failed, reconnecting...' + }); + return false; + }); + + $(Candy).on('candy:view.connection.status-' + Strophe.Status.CONNECTED, function() { + if ($('.sweet-alert.status-2').length > 0) { + sweetAlert.close(); + } + }) + }); + + $(Candy).on('candy:view.room.after-show', function(ev, obj) { + var messagePane = obj.element.find('.message-pane'); + self.adjustTextareaWidth(messagePane); + }); + + // Monkeypatch: No need to calculate width for vertically-stacked tabs + // CSS fixes it at 100% + Candy.View.Pane.Chat.fitTabs = function() { return; } + + Candy.View.Translation.en.dateFormat = 'mmm-dd h:MM tt'; + Candy.View.Translation.en.timeFormat = 'h:MM tt'; + + Candy.View.Template.Roster = { + pane: '
', + user: '
' + + ' ' + + '{{displayNick}}' + + '
' + }; + Candy.View.Template.Message = { + pane: '
', + item: '
  • {{{avatar}}}
    ' + + '
    {{displayName}}' + + '{{time}}
    {{{message}}}' + + '
  • ' + }; + + Candy.View.Template.Room = { + pane: '
    ' + + '
    {{> messages}}{{> form}}
    ' + + '
    {{> roster}}
    ', + subject: '
  • ' + + '{{time}}' + + '{{_roomSubject}} {{{subject}}}' + + '
  • ', + form: '
    ' + + '
    ' + + '' + + '' + + '
    ' + + '
    ' + }; + + Candy.View.Template.Chat = { - pane: '
    {{> tabs}}{{> toolbar}}{{> rooms}}
    {{> modal}}', - rooms: '
    ', - tabs: '
    ', - tab: '
  • ' + - '{{#privateUserChat}} {{/privateUserChat}}{{name}}' + - '\u00D7' + - '
  • ', - modal: '
    \u00D7' + + pane: '
    ' + + '
    ' + + '
    {{> tabs}}{{> rooms}}
    ' + + '{{> toolbar}}{{> modal}}', + rooms: '
    ', + tabs: '
    ', + tab: '
  • ' + + '' + + '
    ' + + '
    '+ + '

    {{name}}

    ' + + '' + + '{{formattedStatus}}' + + '
    ' + + '
  • ', + modal: '
    \u00D7' + '' + - '' + '
    ', - adminMessage: '
  • {{time}}
    ' + - '{{sender}}' + - '{{subject}} {{message}}
  • ', - infoMessage: '
  • {{time}}
    ' + - '{{subject}} {{message}}
  • ', + adminMessage: '
  • ' + + '{{sender}}' + + '{{time}}' + + '{{subject}} {{{message}}}' + + '
  • ', + infoMessage: '
  • ' + + '{{time}}' + + '{{subject}} {{{message}}}' + + '
  • ', toolbar: '', - soundcontrol: '' + - '', Context: { menu: '
    ' + '
      ', @@ -58,46 +193,57 @@ CandyShop.LeftTabs = (function(self, Candy, $) { '
      ' }; - // Make the message pane the full height of the window. - self.heights(); + $(Candy).on('candy:view.room.after-add', function(ev, room) { + if (typeof CandyShop.CreateRoom === 'object') { + self.resetHeight(); - // Make sure that the window heights are the right size after the window is resized. - $(window).resize(function() { - self.heights(); - }); + var roomId = '#tab-' + room.element[0].id; - // Make sure that the window heights are the right size after a new room is added. - $(Candy).on('candy:view.room.after-add', function() { - self.heights(); - if(typeof CandyShop.CreateRoom === "object") { - self.createRoomPluginCompatibility(); + if (room.type === 'groupchat'){ + // Rooms stay above the divider + $('#chat-tabs #people-head').before($(roomId)); + if (typeof CandyShop.LeftPaneHead === 'object') { + // See if it's a private/members-only muc, if so give it a lock icon. + if (CandyShop.LeftPaneHead.isMembersOnly(room.roomJid)) { + $(roomId + ' i.connect-group-chat-icon').removeClass('connect-group-chat-icon').addClass('fa fa-lock'); + } + } + } else if (room.type === 'chat'){ + // Personal rooms move below to people tabs + $('#chat-tabs').append($(roomId)); + $(roomId + ' i.connect-group-chat-icon').removeClass('connect-group-chat-icon').addClass('fa fa-user'); + } } }); - $(Candy).on('candy:view.message.after-show', function(ev, obj) { - if(Candy.View.Pane.Window.autoscroll) { - $('div[data-roomjid="' + obj.roomJid + '"] .message-pane').scrollTop($('div[data-roomjid="' + obj.roomJid + '"] .message-pane').prop('scrollHeight') + $('div[data-roomjid="' + obj.roomJid + '"] .message-form-wrapper').height()); - } + $(Candy).on('candy:view.connection.status-' + Strophe.Status.CONNECTED, self.scheduleResetHeight); + $(Candy).on('candy:view.connection.status-' + Strophe.Status.ATTACHED, self.scheduleResetHeight); + + $(window).resize(function() { + self.resetHeight(); + self.adjustTextareaWidth(); }); + }; + self.scheduleResetHeight = function(timeout) { + timeout = timeout || 10000 + // FIXME: Is there a better way?! + // This happens before the roster loads, but the only roster callback we get would be invoked once for each roster entry, which is far too much. + // Use this hack to correct the viewport a few seconds after login, just in case. + setTimeout(self.resetHeight, timeout); }; - self.heights = function() { - var barless_height = $(window).height() - $('.message-form-wrapper').height(); - var message_pane_height = barless_height; - var message_pane_wrapper_height = (barless_height - parseInt($('.message-pane-wrapper').css('padding-bottom'))); - if(CandyShop.RoomBar) { - message_pane_height = barless_height - parseInt($('.roombar').css('height')); - $('.message-pane').css('margin-top', parseInt($('.roombar').css('height')) + 'px'); + self.resetHeight = function(value) { + if (typeof value === 'undefined' || value === '') { + value = $(window).height(); } - $('.message-pane-wrapper').height(message_pane_wrapper_height + 'px'); - $('.message-pane').height(message_pane_height + 'px'); - $('.roster-pane').height(barless_height + 'px'); + value = value - $('#header.connect').height(); + $('.full-height').css({height: value}); }; - self.createRoomPluginCompatibility = function() { - $('#create-group-form button').addClass('btn'); - $('#create-group-form .close-button').html(''); + self.adjustTextareaWidth = function(messagePane) { + messagePane = messagePane || $('.room-pane[data-roomjid="' + Candy.View.getCurrent().roomJid + '"] .message-pane-wrapper .message-pane'); + $('textarea[name="message"]').width(messagePane.width() - 20); // margin/padding/border consideration }; return self;