diff --git a/packages/rocketchat-external/app/.meteor/packages b/packages/rocketchat-external/app/.meteor/packages index 537f9ccc83b4..99c0ef6edf1a 100644 --- a/packages/rocketchat-external/app/.meteor/packages +++ b/packages/rocketchat-external/app/.meteor/packages @@ -22,3 +22,12 @@ underscore jquery random ejson +coffeescript +arunoda:streams +kadira:flow-router +kadira:blaze-layout +konecty:nrr +less +underscorestring:underscore.string +momentjs:moment +mizzao:timesync diff --git a/packages/rocketchat-external/app/.meteor/versions b/packages/rocketchat-external/app/.meteor/versions index 36edd40e71e0..22a2bc14a7d6 100644 --- a/packages/rocketchat-external/app/.meteor/versions +++ b/packages/rocketchat-external/app/.meteor/versions @@ -1,3 +1,4 @@ +arunoda:streams@0.1.17 base64@1.0.3 binary-heap@1.0.3 blaze@2.1.2 @@ -5,20 +6,29 @@ blaze-tools@1.0.3 boilerplate-generator@1.0.3 callback-hook@1.0.3 check@1.0.5 +coffeescript@1.0.6 +cosmos:browserify@0.5.0 ddp@1.1.0 deps@1.0.7 ejson@1.0.6 geojson-utils@1.0.3 html-tools@1.0.4 htmljs@1.0.4 +http@1.1.0 id-map@1.0.3 jquery@1.11.3_2 json@1.0.3 +kadira:blaze-layout@2.0.0 +kadira:flow-router@2.1.0 +konecty:nrr@2.0.2 +less@1.0.14 livedata@1.0.13 logging@1.0.7 meteor@1.1.6 minifiers@1.1.5 minimongo@1.0.8 +mizzao:timesync@0.3.3 +momentjs:moment@2.10.6 mongo@1.1.0 observe-sequence@1.0.6 ordered-dict@1.0.3 @@ -34,5 +44,7 @@ templating@1.1.1 tracker@1.0.7 ui@1.0.6 underscore@1.0.3 +underscorestring:underscore.string@3.1.1 +url@1.0.4 webapp@1.2.0 webapp-hashing@1.0.3 diff --git a/packages/rocketchat-external/app/client/lib/avatar.coffee b/packages/rocketchat-external/app/client/lib/avatar.coffee new file mode 100644 index 000000000000..7988062501b2 --- /dev/null +++ b/packages/rocketchat-external/app/client/lib/avatar.coffee @@ -0,0 +1,18 @@ +@getAvatarUrlFromUsername = (username) -> + key = "avatar_random_#{username}" + random = Session.keys[key] or 0 + if not username? + return + + return "#{Meteor.absoluteUrl()}avatar/#{username}.jpg?_dc=#{random}" + +@updateAvatarOfUsername = (username) -> + key = "avatar_random_#{username}" + Session.set key, Math.round(Math.random() * 1000) + + for key, room of RoomManager.openedRooms + url = getAvatarUrlFromUsername username + + $(room.dom).find(".message[data-username='#{username}'] .avatar-image").css('background-image', "url(#{url})"); + + return true diff --git a/packages/rocketchat-external/app/client/lib/chatMessages.coffee b/packages/rocketchat-external/app/client/lib/chatMessages.coffee new file mode 100644 index 000000000000..041d6586abd7 --- /dev/null +++ b/packages/rocketchat-external/app/client/lib/chatMessages.coffee @@ -0,0 +1,193 @@ +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) -> + return if element.classList.contains("system") + this.clearEditing() + id = element.getAttribute("id") + message = ChatMessage.findOne { _id: id, 'u._id': Meteor.userId() } + 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") + setTimeout => + this.input.focus() + , 5 + + clearEditing: -> + if this.editing.element + this.editing.element.classList.remove("editing") + this.input.classList.remove("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 '' + 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 'sendMessageExternal', msgObject + + deleteMsg: (message) -> + Meteor.call 'deleteMessage', 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 } + 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() + if this.editing.id + this.update(this.editing.id, rid, input) + else + 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 + 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 diff --git a/packages/rocketchat-external/app/client/lib/collections.coffee b/packages/rocketchat-external/app/client/lib/collections.coffee new file mode 100644 index 000000000000..c658d09a0fc0 --- /dev/null +++ b/packages/rocketchat-external/app/client/lib/collections.coffee @@ -0,0 +1 @@ +@ChatMessage = new Meteor.Collection null diff --git a/packages/rocketchat-external/app/client/lib/underscore.string.js b/packages/rocketchat-external/app/client/lib/underscore.string.js new file mode 100644 index 000000000000..6c0564b8be5f --- /dev/null +++ b/packages/rocketchat-external/app/client/lib/underscore.string.js @@ -0,0 +1,5 @@ +// This will add underscore.string methods to Underscore.js +// except for include, contains, reverse and join that are +// dropped because they collide with the functions already +// defined by Underscore.js. +_.mixin(s.exports()) diff --git a/packages/rocketchat-external/app/client/methods/sendMessageExternal.coffee b/packages/rocketchat-external/app/client/methods/sendMessageExternal.coffee new file mode 100644 index 000000000000..50e3366ec9a1 --- /dev/null +++ b/packages/rocketchat-external/app/client/methods/sendMessageExternal.coffee @@ -0,0 +1,18 @@ +Meteor.methods + sendMessageExternal: (message) -> + # if not Meteor.userId() + # throw new Meteor.Error 203, t('User_logged_out') + + if _.trim(message.msg) isnt '' + + message.ts = new Date(Date.now() + TimeSync.serverOffset()) + + message.u = + # _id: Meteor.userId() + username: 'visitor' + + message.temp = true + + # message = RocketChat.callbacks.run 'beforeSaveMessage', message + + ChatMessage.insert message diff --git a/packages/rocketchat-external/app/client/routes/router.coffee b/packages/rocketchat-external/app/client/routes/router.coffee new file mode 100644 index 000000000000..559a3ed8e4b1 --- /dev/null +++ b/packages/rocketchat-external/app/client/routes/router.coffee @@ -0,0 +1,5 @@ +FlowRouter.route '/', + name: 'index' + + action: -> + BlazeLayout.render 'main', {center: 'room'} diff --git a/packages/rocketchat-external/app/client/stylesheets/main.less b/packages/rocketchat-external/app/client/stylesheets/main.less new file mode 100644 index 000000000000..fc874885348f --- /dev/null +++ b/packages/rocketchat-external/app/client/stylesheets/main.less @@ -0,0 +1,32 @@ +body { + padding: 0; + margin: 0; +} +.external-room { + position: fixed; + top: 0; + bottom: 0; + background-color: blue; + .title { + background-color: green; + position: fixed; + top: 0; + width: 100%; + height: 60px; + } + .wrapper { + background-color: purple; + top: 60px; + bottom: 60px; + position: fixed; + width: 100%; + overflow-y: auto; + } + .footer { + background-color: red; + position: fixed; + bottom: 0; + width: 100%; + height: 60px; + } +} diff --git a/packages/rocketchat-external/app/client/test.html b/packages/rocketchat-external/app/client/test.html deleted file mode 100644 index 1db3493c0e24..000000000000 --- a/packages/rocketchat-external/app/client/test.html +++ /dev/null @@ -1,3 +0,0 @@ - diff --git a/packages/rocketchat-external/app/client/test.js b/packages/rocketchat-external/app/client/test.js deleted file mode 100644 index 161be057a727..000000000000 --- a/packages/rocketchat-external/app/client/test.js +++ /dev/null @@ -1,12 +0,0 @@ -Template.hello.helpers({ - counter: function () { - return Session.get('counter'); - } - }); - - Template.hello.events({ - 'click button': function () { - // increment the counter when button is clicked - Session.set('counter', Session.get('counter') + 1); - } - }); \ No newline at end of file diff --git a/packages/rocketchat-external/app/client/views/avatar.coffee b/packages/rocketchat-external/app/client/views/avatar.coffee new file mode 100644 index 000000000000..b0abe8754a17 --- /dev/null +++ b/packages/rocketchat-external/app/client/views/avatar.coffee @@ -0,0 +1,14 @@ +Template.avatar.helpers + imageUrl: -> + username = this.username + if not username? and this.userId? + username = Meteor.users.findOne(this.userId)?.username + + if not username? + return + + Session.get "avatar_random_#{username}" + + url = getAvatarUrlFromUsername(username) + + return "background-image:url(#{url});" diff --git a/packages/rocketchat-external/app/client/views/avatar.html b/packages/rocketchat-external/app/client/views/avatar.html new file mode 100644 index 000000000000..70781f54c7b5 --- /dev/null +++ b/packages/rocketchat-external/app/client/views/avatar.html @@ -0,0 +1,5 @@ + diff --git a/packages/rocketchat-external/app/client/views/main.html b/packages/rocketchat-external/app/client/views/main.html new file mode 100644 index 000000000000..fcddfd3b9d52 --- /dev/null +++ b/packages/rocketchat-external/app/client/views/main.html @@ -0,0 +1,18 @@ + + Rocket.Chat + + + + + + + + + + + + + + diff --git a/packages/rocketchat-external/app/client/views/message.coffee b/packages/rocketchat-external/app/client/views/message.coffee new file mode 100644 index 000000000000..278f4f9cf88d --- /dev/null +++ b/packages/rocketchat-external/app/client/views/message.coffee @@ -0,0 +1,77 @@ +Template.message.helpers + + own: -> + return '' + # return 'own' if this.u?._id is Meteor.userId() + + time: -> + return moment(this.ts).format('HH:mm') + + date: -> + return moment(this.ts).format('LL') + + isTemp: -> + if @temp is true + return 'temp' + return + + body: -> + switch this.t + when 'r' then t('Room_name_changed', { room_name: this.msg, user_by: this.u.username }) + when 'au' then t('User_added_by', { user_added: this.msg, user_by: this.u.username }) + when 'ru' then t('User_removed_by', { user_removed: this.msg, user_by: this.u.username }) + when 'ul' then tr('User_left', { context: this.u.gender }, { user_left: this.u.username }) + when 'nu' then t('User_added', { user_added: this.u.username }) + when 'uj' then tr('User_joined_channel', { context: this.u.gender }, { user: this.u.username }) + when 'wm' then t('Welcome', { user: this.u.username }) + # when 'rtc' then RocketChat.callbacks.run 'renderRtcMessage', this + else + this.html = this.msg + if _.trim(this.html) isnt '' + this.html = _.escapeHTML this.html + # message = RocketChat.callbacks.run 'renderMessage', this + message = this + this.html = message.html.replace /\n/gm, '
' + return this.html + + system: -> + return 'system' if this.t in ['s', 'p', 'f', 'r', 'au', 'ru', 'ul', 'nu', 'wm', 'uj'] + + +Template.message.onViewRendered = (context) -> + view = this + this._domrange.onAttached (domRange) -> + lastNode = domRange.lastNode() + if lastNode.previousElementSibling?.dataset?.date isnt lastNode.dataset.date + $(lastNode).addClass('new-day') + $(lastNode).removeClass('sequential') + else if lastNode.previousElementSibling?.dataset?.username isnt lastNode.dataset.username + $(lastNode).removeClass('sequential') + + if lastNode.nextElementSibling?.dataset?.date is lastNode.dataset.date + $(lastNode.nextElementSibling).removeClass('new-day') + $(lastNode.nextElementSibling).addClass('sequential') + else + $(lastNode.nextElementSibling).addClass('new-day') + $(lastNode.nextElementSibling).removeClass('sequential') + + if lastNode.nextElementSibling?.dataset?.username isnt lastNode.dataset.username + $(lastNode.nextElementSibling).removeClass('sequential') + + ul = lastNode.parentElement + wrapper = ul.parentElement + + if context.urls?.length > 0 and Template.oembedBaseWidget? + for item in context.urls + do (item) -> + urlNode = lastNode.querySelector('.body a[href="'+item.url+'"]') + if urlNode? + $(urlNode).replaceWith Blaze.toHTMLWithData Template.oembedBaseWidget, item + + if not lastNode.nextElementSibling? + if lastNode.classList.contains('own') is true + view.parentView.parentView.parentView.parentView.parentView.templateInstance().atBottom = true + # else + # if view.parentView.parentView.parentView.parentView.parentView.templateInstance().atBottom isnt true + # newMessage = document.querySelector(".new-message") + # newMessage.className = "new-message" diff --git a/packages/rocketchat-external/app/client/views/message.html b/packages/rocketchat-external/app/client/views/message.html new file mode 100644 index 000000000000..3fdbf560cb9b --- /dev/null +++ b/packages/rocketchat-external/app/client/views/message.html @@ -0,0 +1,17 @@ + diff --git a/packages/rocketchat-external/app/client/views/room.coffee b/packages/rocketchat-external/app/client/views/room.coffee new file mode 100644 index 000000000000..3bd347862ac3 --- /dev/null +++ b/packages/rocketchat-external/app/client/views/room.coffee @@ -0,0 +1,17 @@ +Template.room.helpers + messages: -> + return ChatMessage.find { rid: this._id, t: { '$ne': 't' } }, { sort: { ts: 1 } } + +Template.room.events + + 'keyup .input-message': (event) -> + Template.instance().chatMessages.keyup(@_id, event, Template.instance()) + + 'keydown .input-message': (event) -> + Template.instance().chatMessages.keydown(@_id, event, Template.instance()) + + + +Template.room.onRendered -> + this.chatMessages = new ChatMessages + this.chatMessages.init(this.firstNode) diff --git a/packages/rocketchat-external/app/client/views/room.html b/packages/rocketchat-external/app/client/views/room.html new file mode 100644 index 000000000000..d99e2e9f1ab4 --- /dev/null +++ b/packages/rocketchat-external/app/client/views/room.html @@ -0,0 +1,17 @@ + diff --git a/packages/rocketchat-external/app/empty.css b/packages/rocketchat-external/app/empty.css deleted file mode 100644 index eaef2ae0d11f..000000000000 --- a/packages/rocketchat-external/app/empty.css +++ /dev/null @@ -1,4 +0,0 @@ -/* CSS declarations go here */ -body { - background-color: #ccc; -} diff --git a/packages/rocketchat-external/app/empty.html b/packages/rocketchat-external/app/empty.html deleted file mode 100644 index 427342697ef5..000000000000 --- a/packages/rocketchat-external/app/empty.html +++ /dev/null @@ -1,14 +0,0 @@ - - empty - - - -

Funciona mesmo mesmo

- - {{> hello}} - - - diff --git a/packages/rocketchat-external/app/empty.js b/packages/rocketchat-external/app/empty.js deleted file mode 100644 index 9096b102199f..000000000000 --- a/packages/rocketchat-external/app/empty.js +++ /dev/null @@ -1,10 +0,0 @@ -if (Meteor.isClient) { - // counter starts at 0 - Session.setDefault('counter', 0); -} - -if (Meteor.isServer) { - Meteor.startup(function () { - // code to run on server at startup - }); -} diff --git a/packages/rocketchat-external/methods.coffee b/packages/rocketchat-external/methods.coffee new file mode 100644 index 000000000000..10e73670c916 --- /dev/null +++ b/packages/rocketchat-external/methods.coffee @@ -0,0 +1,3 @@ +Meteor.methods + sendMessageExternal: -> + console.log 'sendMessageExternal ->',arguments diff --git a/packages/rocketchat-external/package.js b/packages/rocketchat-external/package.js index a9857031ae46..e6125c609f27 100755 --- a/packages/rocketchat-external/package.js +++ b/packages/rocketchat-external/package.js @@ -23,6 +23,7 @@ Package.onUse(function(api) { api.use('autoupdate', 'server'); api.addFiles('external.coffee', 'server'); + api.addFiles('methods.coffee', 'server'); api.addFiles('public/external.css', 'client', {isAsset: true}); api.addFiles('public/external.js', 'client', {isAsset: true});