diff --git a/clientapp/models/me.js b/clientapp/models/me.js index 067e8da..7e6ade3 100644 --- a/clientapp/models/me.js +++ b/clientapp/models/me.js @@ -91,6 +91,15 @@ module.exports = HumanModel.define({ this._activeContact = curr.id; } }, + getName: function () { + return this.displayName; + }, + getNickname: function () { + return this.displayName != this.nick ? this.nick : ''; + }, + getAvatar: function () { + return this.avatar; + }, setAvatar: function (id, type, source) { var self = this; fetchAvatar('', id, type, source, function (avatar) { @@ -102,7 +111,10 @@ module.exports = HumanModel.define({ this.soundEnabled = enable; }, getContact: function (jid, alt) { - if (typeof jid === 'string') jid = new client.JID(jid); + if (typeof jid === 'string') { + if (SERVER_CONFIG.domain && jid.indexOf('@') == -1) jid += '@' + SERVER_CONFIG.domain; + jid = new client.JID(jid); + } if (typeof alt === 'string') alt = new client.JID(alt); if (this.isMe(jid)) { diff --git a/clientapp/models/muc.js b/clientapp/models/muc.js index ba9658d..cbb3249 100644 --- a/clientapp/models/muc.js +++ b/clientapp/models/muc.js @@ -76,6 +76,27 @@ module.exports = HumanModel.define({ resources: Resources, messages: Messages }, + getName: function (jid) { + var nickname = jid.split('/')[1]; + var name = nickname; + var xmppContact = me.getContact(nickname); + if (xmppContact) { + name = xmppContact.displayName; + } + return name != '' ? name : nickname; + }, + getNickname: function (jid) { + var nickname = jid.split('/')[1]; + return nickname != this.getName(jid) ? nickname : ''; + }, + getAvatar: function (jid) { + var avatar = ""; + var xmppContact = me.getContact(jid.split('/')[1]); + if (xmppContact) { + avatar = xmppContact.avatar; + } + return avatar || 'https://gravatar.com/avatar'; + }, addMessage: function (message, notify) { message.owner = me.jid.bare; diff --git a/clientapp/models/resources.js b/clientapp/models/resources.js index 3693dcb..215b159 100644 --- a/clientapp/models/resources.js +++ b/clientapp/models/resources.js @@ -3,7 +3,6 @@ var BaseCollection = require('./baseCollection'); var Resource = require('./resource'); - module.exports = BaseCollection.extend({ type: 'resources', model: Resource, @@ -42,5 +41,20 @@ module.exports = BaseCollection.extend({ return -1; } return 1; + }, + search : function (letters, removeMe, addAll) { + if(letters == "" && !removeMe) return this; + + var collection = new module.exports(this.models); + if (addAll) + collection.add({id: this.parent.jid.bare + '/all'}); + + var pattern = new RegExp('^' + letters + '.*$', "i"); + var filtered = collection.filter(function(data) { + var nick = data.get("mucDisplayName"); + if (nick === me.nick) return false; + return pattern.test(nick); + }); + return new module.exports(filtered); } }); diff --git a/clientapp/pages/groupchat.js b/clientapp/pages/groupchat.js index aee3cce..42dc9e9 100644 --- a/clientapp/pages/groupchat.js +++ b/clientapp/pages/groupchat.js @@ -80,12 +80,15 @@ module.exports = BasePage.extend({ this.$chatInput.val(app.composing[this.model.jid] || ''); this.$chatBox = this.$('.chatBox'); this.$messageList = this.$('.messages'); + this.$autoComplete = this.$('.autoComplete'); this.staydown = new StayDown(this.$messageList[0], 500); this.renderMessages(); + this.renderCollection(this.model.resources, MUCRosterItem, this.$('.groupRoster')); + this.listenTo(this, 'rosterItemClicked', this.rosterItemSelected); this.listenTo(this.model.messages, 'add', this.handleChatAdded); $(window).on('resize', _.bind(this.resizeInput, this)); @@ -117,19 +120,47 @@ module.exports = BasePage.extend({ }, handleKeyDown: function (e) { if (e.which === 13 && !e.shiftKey) { - app.composing[this.model.jid] = ''; - this.sendChat(); + if (this.$autoComplete.css('display') != 'none') { + var nickname = this.$autoComplete.find(">:nth-child(" + this.autoCompletePos + ")>:first-child").text(); + this.rosterItemSelected(nickname); + } else { + app.composing[this.model.jid] = ''; + this.sendChat(); + } e.preventDefault(); return false; - } else if (e.which === 38 && this.$chatInput.val() === '' && this.model.lastSentMessage) { - this.editMode = true; - this.$chatInput.addClass('editing'); - this.$chatInput.val(this.model.lastSentMessage.body); + } else if (e.which === 38) { // Up arrow + + if (this.$autoComplete.css('display') != 'none') { + var count = this.$autoComplete.find(">li").length; + var oldPos = this.autoCompletePos; + this.autoCompletePos = (oldPos - 1) < 1 ? count : oldPos - 1; + + this.$autoComplete.find(">:nth-child(" + oldPos + ")").removeClass('selected'); + this.$autoComplete.find(">:nth-child(" + this.autoCompletePos + ")").addClass('selected'); + + } + else if (this.$chatInput.val() === '' && this.model.lastSentMessage) { + this.editMode = true; + this.$chatInput.addClass('editing'); + this.$chatInput.val(this.model.lastSentMessage.body); + } e.preventDefault(); return false; - } else if (e.which === 40 && this.editMode) { - this.editMode = false; - this.$chatInput.removeClass('editing'); + } else if (e.which === 40) { // Down arrow + + if (this.$autoComplete.css('display') != 'none') { + var count = this.$autoComplete.find(">li").length; + var oldPos = this.autoCompletePos; + this.autoCompletePos = (oldPos + 1) > count ? 1 : oldPos + 1; + + this.$autoComplete.find(">:nth-child(" + oldPos + ")").removeClass('selected'); + this.$autoComplete.find(">:nth-child(" + this.autoCompletePos + ")").addClass('selected'); + } + else if (this.editMode) { + this.editMode = false; + this.$chatInput.removeClass('editing'); + } e.preventDefault(); return false; } else if (!e.ctrlKey && !e.metaKey) { @@ -158,6 +189,43 @@ module.exports = BasePage.extend({ } else if (this.typing) { this.pausedTyping(); } + + if (([38, 40, 13]).indexOf(e.which) === -1) { + var lastWord = this.$chatInput.val().split(' ').pop(); + if (lastWord.charAt(0) === '@') { + var models = this.model.resources.search(lastWord.substr(1) || '', true, true); + if (models.length) { + this.renderCollection(models, MUCRosterItem, this.$autoComplete); + this.autoCompletePos = 1; + this.$autoComplete.find(">:nth-child(" + this.autoCompletePos + ")").addClass('selected'); + this.$autoComplete.show(); + } + else + this.$autoComplete.hide(); + } + + if (this.$autoComplete.css('display') != 'none') { + if( lastWord === '') { + this.$autoComplete.hide(); + return; + } + } + } + }, + rosterItemSelected: function (nickName) { + if (nickName == me.nick) + nickName = 'me'; + var val = this.$chatInput.val(); + var splited = val.split(' '); + var length = splited.length-1; + var lastWord = splited.pop(); + if (('@' + nickName).indexOf(lastWord) > -1) + splited[length] = '@' + nickName + ' '; + else + splited.push('@' + nickName + ' '); + this.$chatInput.val(splited.join(' ')); + this.$autoComplete.hide(); + this.$chatInput.focus(); }, resizeInput: _.throttle(function () { var height; diff --git a/clientapp/templates.js b/clientapp/templates.js index 6b5746d..b70fb14 100644 --- a/clientapp/templates.js +++ b/clientapp/templates.js @@ -313,14 +313,14 @@ exports.includes.mucWrappedMessage = function anonymous(locals) { with (locals || {}) { var messageDate = Date.create(message.timestamp); buf.push('