class @ChatMessages init: (node) -> this.editing = {} this.messageMaxSize = RocketChat.settings.get('Message_MaxAllowedSize') this.wrapper = $(node).find(".wrapper") this.input = $(node).find(".input-message").get(0) this.bindEvents() return resize: -> dif = 60 + $(".messages-container").find("footer").outerHeight() $(".messages-box").css height: "calc(100% - #{dif}px)" toPrevMessage: -> msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)") if msgs.length if this.editing.element if msgs[this.editing.index - 1] this.edit msgs[this.editing.index - 1], this.editing.index - 1 else this.edit msgs[msgs.length - 1], msgs.length - 1 toNextMessage: -> if this.editing.element msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)") if msgs[this.editing.index + 1] this.edit msgs[this.editing.index + 1], this.editing.index + 1 else this.clearEditing() getEditingIndex: (element) -> msgs = this.wrapper.get(0).querySelectorAll(".own:not(.system)") index = 0 for msg in msgs if msg is element return index index++ return -1 edit: (element, index) -> id = element.getAttribute("id") message = ChatMessage.findOne { _id: id } hasPermission = RocketChat.authz.hasAtLeastOnePermission('edit-message', message.rid) editAllowed = RocketChat.settings.get 'Message_AllowEditing' editOwn = message?.u?._id is Meteor.userId() return unless hasPermission or (editAllowed and editOwn) return if element.classList.contains("system") blockEditInMinutes = RocketChat.settings.get 'Message_AllowEditing_BlockEditInMinutes' if blockEditInMinutes? and blockEditInMinutes isnt 0 msgTs = moment(message.ts) if message.ts? currentTsDiff = moment().diff(msgTs, 'minutes') if msgTs? if currentTsDiff > blockEditInMinutes return this.clearEditing() this.input.value = message.msg this.editing.element = element this.editing.index = index or this.getEditingIndex(element) this.editing.id = id element.classList.add("editing") this.input.classList.add("editing") $(this.input).closest('.message-form').addClass('editing'); setTimeout => this.input.focus() , 5 clearEditing: -> if this.editing.element this.editing.element.classList.remove("editing") this.input.classList.remove("editing") $(this.input).closest('.message-form').removeClass('editing'); this.editing.id = null this.editing.element = null this.editing.index = null this.input.value = this.editing.saved or "" else this.editing.saved = this.input.value send: (rid, input) -> if _.trim(input.value) isnt '' readMessage.enable() readMessage.readNow() $('.message.first-unread').removeClass('first-unread') if this.editing.id this.update(this.editing.id, rid, input) return if this.isMessageTooLong(input) return Errors.throw t('Error_message_too_long') KonchatNotification.removeRoomNotification(rid) msg = input.value input.value = '' msgObject = { _id: Random.id(), rid: rid, msg: msg} this.stopTyping(rid) #Check if message starts with /command if msg[0] is '/' match = msg.match(/^\/([^\s]+)(?:\s+(.*))?$/m) if(match?) command = match[1] param = match[2] Meteor.call 'slashCommand', {cmd: command, params: param, msg: msgObject } else #Run to allow local encryption #Meteor.call 'onClientBeforeSendMessage', {} Meteor.call 'sendMessage', msgObject deleteMsg: (message) -> Meteor.call 'deleteMessage', message, (error, result) -> if error return Errors.throw error.reason pinMsg: (message) -> message.pinned = true Meteor.call 'pinMessage', message, (error, result) -> if error return Errors.throw error.reason unpinMsg: (message) -> message.pinned = false Meteor.call 'unpinMessage', message, (error, result) -> if error return Errors.throw error.reason update: (id, rid, input) -> if _.trim(input.value) isnt '' msg = input.value Meteor.call 'updateMessage', { _id: id, msg: msg, rid: rid } this.clearEditing() this.stopTyping(rid) startTyping: (rid, input) -> if _.trim(input.value) isnt '' MsgTyping.start(rid) else MsgTyping.stop(rid) stopTyping: (rid) -> MsgTyping.stop(rid) bindEvents: -> if this.wrapper?.length $(".input-message").autogrow postGrowCallback: => this.resize() tryCompletion: (input) -> value = input.value.match(/[^\s]+$/) if value?.length > 0 value = value[0] re = new RegExp value, 'i' user = Meteor.users.findOne username: re if user? input.value = input.value.replace value, "@#{user.username} " keyup: (rid, event) -> input = event.currentTarget k = event.which keyCodes = [ 13, # Enter 20, # Caps lock 16, # Shift 9, # Tab 27, # Escape Key 17, # Control Key 91, # Windows Command Key 19, # Pause Break 18, # Alt Key 93, # Right Click Point Key 45, # Insert Key 34, # Page Down 35, # Page Up 144, # Num Lock 145 # Scroll Lock ] keyCodes.push i for i in [35..40] # Home, End, Arrow Keys keyCodes.push i for i in [112..123] # F1 - F12 unless k in keyCodes this.startTyping(rid, input) keydown: (rid, event) -> input = event.currentTarget k = event.which this.resize(input) if k is 13 and not event.shiftKey event.preventDefault() event.stopPropagation() this.send(rid, input) return if k is 9 event.preventDefault() event.stopPropagation() @tryCompletion input if k is 27 if this.editing.id event.preventDefault() event.stopPropagation() this.clearEditing() return else if k is 38 or k is 40 # Arrow Up or down return true if event.shiftKey return true if $(input).val().length and !this.editing?.id if k is 38 return if input.value.slice(0, input.selectionStart).match(/[\n]/) isnt null this.toPrevMessage() else return if input.value.slice(input.selectionEnd, input.value.length).match(/[\n]/) isnt null this.toNextMessage() event.preventDefault() event.stopPropagation() # ctrl (command) + shift + k -> clear room messages else if k is 75 and ((navigator?.platform?.indexOf('Mac') isnt -1 and event.metaKey and event.shiftKey) or (navigator?.platform?.indexOf('Mac') is -1 and event.ctrlKey and event.shiftKey)) RoomHistoryManager.clear rid isMessageTooLong: (input) -> input?.value.length > this.messageMaxSize