diff --git a/public/assets/chat/chat.coffee b/public/assets/chat/chat.coffee index ac57c6f6a..4d15ef45c 100644 --- a/public/assets/chat/chat.coffee +++ b/public/assets/chat/chat.coffee @@ -8,13 +8,12 @@ do($ = window.jQuery, window) -> class ZammadChat defaults: - invitationPhrase: 'Chat with us!' - agentPhrase: ' is helping you' show: true target: $('body') host: '' port: 6042 debug: false + buttonSelector: '.open-zammad-chat' _messageCount: 0 isOpen: true @@ -25,14 +24,15 @@ do($ = window.jQuery, window) -> lastAddedType: null inputTimeout: null isTyping: false - isOnline: true + state: 'offline' initialQueueDelay: 10000 wsReconnectEnable: true strings: + 'Chat with us!': 'Chatten sie mit uns!' 'Online': 'Online' 'Offline': 'Offline' 'Connecting': 'Verbinden' - 'Connection re-established': 'Connection re-established' + 'Connection re-established': 'Verbindung wiederhergestellt' 'Today': 'Heute' 'Send': 'Senden' 'Compose your message...': 'Ihre Nachricht...' @@ -64,6 +64,8 @@ do($ = window.jQuery, window) -> options = {} options.T = @T + options.background = @options.background + options.flat = @options.flat return window.zammadChatTemplates[name](options) constructor: (options) -> @@ -74,18 +76,20 @@ do($ = window.jQuery, window) -> return @options = $.extend {}, @defaults, options - @el = $(@view('chat')(@options)) + @el = $(@view('chat')()) @options.target.append @el @input = @el.find('.zammad-chat-input') - @el.find('.js-chat-open').click => @open() + @el.find('.js-chat-open').click @open @el.find('.js-chat-close').click @close @el.find('.zammad-chat-controls').on 'submit', @onSubmit @input.on keydown: @checkForEnter input: @onInput + $(@options.buttonSelector).click @open + @wsConnect() checkForEnter: (event) => @@ -127,13 +131,13 @@ do($ = window.jQuery, window) -> @onReady() @log 'debug', 'Zammad Chat: ready' when 'offline' - @log 'debug', 'Zammad Chat: No agent online' + @onError 'Zammad Chat: No agent online' @wsClose() when 'chat_disabled' - @log 'debug', 'Zammad Chat: Chat is disabled' + @onError 'Zammad Chat: Chat is disabled' @wsClose() when 'no_seats_available' - @log 'debug', 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue + @onError 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue @wsClose() when 'reconnect' @log 'debug', 'old messages', pipe.data.session @@ -143,6 +147,10 @@ do($ = window.jQuery, window) -> if @options.show @show() + onError: (message) => + @log 'debug', message + $(@options.buttonSelector).hide() + reopenSession: (data) => unfinishedMessage = sessionStorage.getItem 'unfinished_message' @@ -421,7 +429,6 @@ do($ = window.jQuery, window) -> @log 'debug', 'close websocket connection' if @wsReconnectEnable @reconnect() - @setAgentOnlineState(false) @ws.onerror = (e) => @log 'debug', 'ws:onerror', e @@ -442,21 +449,21 @@ do($ = window.jQuery, window) -> @send 'chat_status_customer', session_id: @sessionId - @setAgentOnlineState(true) + @setAgentOnlineState 'online' reconnect: => # set status to connecting @log 'notice', 'reconnecting' @disableInput() @lastAddedType = 'status' - @el.find('.zammad-chat-agent-status').attr('data-status', 'connecting').text @T('Reconnecting') + @setAgentOnlineState 'connecting' @addStatus @T('Connection lost') @wsReconnect() onConnectionReestablished: => # set status back to online @lastAddedType = 'status' - @el.find('.zammad-chat-agent-status').attr('data-status', 'online').text @T('Online') + @setAgentOnlineState 'online' @addStatus @T('Connection re-established') onSessionClosed: (data) -> @@ -508,10 +515,11 @@ do($ = window.jQuery, window) -> @el.find('.zammad-chat-body').html @view('loader')() setAgentOnlineState: (state) => - @isOnline = state + @state = state + capitalizedState = state.charAt(0).toUpperCase() + state.slice(1) @el .find('.zammad-chat-agent-status') - .toggleClass('zammad-chat-is-online', state) - .text if state then @T('Online') else @T('Offline') + .attr('data-status', state) + .text @T(capitalizedState) window.ZammadChat = ZammadChat diff --git a/public/assets/chat/chat.css b/public/assets/chat/chat.css index 762778633..56fff2478 100644 --- a/public/assets/chat/chat.css +++ b/public/assets/chat/chat.css @@ -30,7 +30,7 @@ background: #379ad7; color: white; line-height: 2.5em; - box-shadow: 0 -1px #247fb7, 0 1px rgba(255, 255, 255, 0.3) inset, 0 -1px #247fb7 inset, 0 1px 1px rgba(0, 0, 0, 0.13); + box-shadow: 0 -1px rgba(0, 0, 0, 0.1), 0 1px rgba(255, 255, 255, 0.3) inset, 0 -1px rgba(0, 0, 0, 0.1) inset, 0 1px 1px rgba(0, 0, 0, 0.13); position: relative; border-radius: 5px 5px 0 0; overflow: hidden; @@ -89,8 +89,8 @@ line-height: 2em; padding: 0 0.7em; border-radius: 1em; - background: #288ecc; - box-shadow: 0 0 0 1px #2582bb; } + background: rgba(0, 0, 0, 0.1); + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04); } .zammad-chat-agent-status:before { content: ""; @@ -101,7 +101,8 @@ border-radius: 100%; position: relative; margin-right: 0.3em; - box-shadow: 0 0 0 1px #2582bb; } + vertical-align: middle; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04); } .zammad-chat-agent-status[data-status="online"]:before { background: #52c782; } @@ -185,7 +186,7 @@ padding: 0.5em 1em; line-height: 1.4; border-radius: 1em; - background: #f6f8f9; + background: #ededed; display: inline-block; max-width: 70%; white-space: pre-line; @@ -223,7 +224,7 @@ display: inline-block; } .zammad-chat-loading-circle { - background: #c5dded; + background: #d9d9d9; border-radius: 100%; height: 0.72em; width: 0.72em; @@ -267,7 +268,7 @@ -webkit-align-items: flex-start; -ms-flex-align: start; align-items: flex-start; - border-top: 1px solid #e3f0f7; + border-top: 1px solid #ededed; padding: 0; line-height: 1.4em; box-shadow: 0 1px rgba(0, 0, 0, 0.01), 0 -1px rgba(0, 0, 0, 0.02); @@ -294,7 +295,7 @@ max-height: 6em; } .zammad-chat-input::-webkit-input-placeholder { - color: #bdc9d0; } + color: #d9d9d9; } .zammad-chat-button { -webkit-appearance: none; @@ -309,7 +310,7 @@ cursor: pointer; border: none; border-radius: 1.5em; - box-shadow: 0 2px rgba(255, 255, 255, 0.25) inset, 0 0 0 1px #247fb7 inset, 0 1px rgba(0, 0, 0, 0.1); + box-shadow: 0 2px rgba(255, 255, 255, 0.25) inset, 0 0 0 1px rgba(0, 0, 0, 0.1) inset, 0 1px rgba(0, 0, 0, 0.1); outline: none; display: inline-block; } @@ -332,10 +333,10 @@ border: none; } .zammad-chat--flat .zammad-chat-header { - box-shadow: 0 1px 1px rgba(0, 0, 0, 0.13); } + box-shadow: none; } .zammad-chat--flat .zammad-chat-message-body { - box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.08) inset; } + box-shadow: none; } .zammad-chat--flat .zammad-chat-agent-status, .zammad-chat--flat .zammad-chat-button, diff --git a/public/assets/chat/chat.js b/public/assets/chat/chat.js index 109d445c2..11a96fe29 100644 --- a/public/assets/chat/chat.js +++ b/public/assets/chat/chat.js @@ -1,3 +1,64 @@ +if (!window.zammadChatTemplates) { + window.zammadChatTemplates = {}; +} +window.zammadChatTemplates["agent"] = function (__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + if (this.agent.avatar) { + __out.push('\n\n'); + } + + __out.push('\n\n '); + + __out.push(__sanitize(this.agent.name)); + + __out.push('\n'); + + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}; + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, slice = [].slice; @@ -8,17 +69,17 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); scriptHost = myScript.src.match(".*://([^:/]*).*")[1]; ZammadChat = (function() { ZammadChat.prototype.defaults = { - invitationPhrase: 'Chat with us!', - agentPhrase: ' is helping you', - show: false, + show: true, target: $('body'), host: '', - port: 6042 + port: 6042, + debug: false, + buttonSelector: '.open-zammad-chat' }; ZammadChat.prototype._messageCount = 0; - ZammadChat.prototype.isOpen = false; + ZammadChat.prototype.isOpen = true; ZammadChat.prototype.blinkOnlineInterval = null; @@ -34,19 +95,18 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.isTyping = false; - ZammadChat.prototype.isOnline = true; + ZammadChat.prototype.state = 'offline'; ZammadChat.prototype.initialQueueDelay = 10000; - ZammadChat.prototype.debug = true; - ZammadChat.prototype.wsReconnectEnable = true; ZammadChat.prototype.strings = { + 'Chat with us!': 'Chatten sie mit uns!', 'Online': 'Online', 'Offline': 'Offline', 'Connecting': 'Verbinden', - 'Connection re-established': 'Connection re-established', + 'Connection re-established': 'Verbindung wiederhergestellt', 'Today': 'Heute', 'Send': 'Senden', 'Compose your message...': 'Ihre Nachricht...', @@ -78,7 +138,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.log = function() { var level, string; level = arguments[0], string = 2 <= arguments.length ? slice.call(arguments, 1) : []; - if (!this.debug && level === 'debug') { + if (!this.options.debug && level === 'debug') { return; } string.unshift(level); @@ -92,6 +152,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); options = {}; } options.T = _this.T; + options.background = _this.options.background; + options.flat = _this.options.flat; return window.zammadChatTemplates[name](options); }; })(this); @@ -122,6 +184,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.onTypingEnd = bind(this.onTypingEnd, this); this.onInput = bind(this.onInput, this); this.reopenSession = bind(this.reopenSession, this); + this.onError = bind(this.onError, this); this.onReady = bind(this.onReady, this); this.onWebSocketMessage = bind(this.onWebSocketMessage, this); this.send = bind(this.send, this); @@ -129,25 +192,22 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.view = bind(this.view, this); this.log = bind(this.log, this); this.T = bind(this.T, this); + if (!window.WebSocket || !sessionStorage) { + this.log('notice', 'Chat: Browser not supported!'); + return; + } this.options = $.extend({}, this.defaults, options); - this.el = $(this.view('chat')(this.options)); + this.el = $(this.view('chat')()); this.options.target.append(this.el); this.input = this.el.find('.zammad-chat-input'); - this.el.find('.js-chat-open').click((function(_this) { - return function() { - return _this.open(); - }; - })(this)); + this.el.find('.js-chat-open').click(this.open); this.el.find('.js-chat-close').click(this.close); this.el.find('.zammad-chat-controls').on('submit', this.onSubmit); this.input.on({ keydown: this.checkForEnter, input: this.onInput }); - if (!window.WebSocket || !sessionStorage) { - this.log('notice', 'Chat: Browser not supported!'); - return; - } + $(this.options.buttonSelector).click(this.open); this.wsConnect(); } @@ -207,15 +267,15 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.log('debug', 'Zammad Chat: ready'); break; case 'offline': - this.log('debug', 'Zammad Chat: No agent online'); + this.onError('Zammad Chat: No agent online'); this.wsClose(); break; case 'chat_disabled': - this.log('debug', 'Zammad Chat: Chat is disabled'); + this.onError('Zammad Chat: Chat is disabled'); this.wsClose(); break; case 'no_seats_available': - this.log('debug', 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue); + this.onError('Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue); this.wsClose(); break; case 'reconnect': @@ -232,21 +292,31 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); } }; + ZammadChat.prototype.onError = function(message) { + this.log('debug', message); + return $(this.options.buttonSelector).hide(); + }; + ZammadChat.prototype.reopenSession = function(data) { var i, len, message, ref, unfinishedMessage; unfinishedMessage = sessionStorage.getItem('unfinished_message'); - this.onConnectionEstablished(data); - ref = data.session; - for (i = 0, len = ref.length; i < len; i++) { - message = ref[i]; - this.renderMessage({ - message: message.content, - id: message.id, - from: message.created_by_id ? 'agent' : 'customer' - }); + if (data.agent) { + this.onConnectionEstablished(data); + ref = data.session; + for (i = 0, len = ref.length; i < len; i++) { + message = ref[i]; + this.renderMessage({ + message: message.content, + id: message.id, + from: message.created_by_id ? 'agent' : 'customer' + }); + } + if (unfinishedMessage) { + this.input.val(unfinishedMessage); + } } - if (unfinishedMessage) { - this.input.val(unfinishedMessage); + if (data.position) { + this.onQueue(data); } this.show(); this.open(); @@ -518,9 +588,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return function(e) { _this.log('debug', 'close websocket connection'); if (_this.wsReconnectEnable) { - _this.reconnect(); + return _this.reconnect(); } - return _this.setAgentOnlineState(false); }; })(this); return this.ws.onerror = (function(_this) { @@ -548,21 +617,21 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.send('chat_status_customer', { session_id: this.sessionId }); - return this.setAgentOnlineState(true); + return this.setAgentOnlineState('online'); }; ZammadChat.prototype.reconnect = function() { this.log('notice', 'reconnecting'); this.disableInput(); this.lastAddedType = 'status'; - this.el.find('.zammad-chat-agent-status').attr('data-status', 'connecting').text(this.T('Reconnecting')); + this.setAgentOnlineState('connecting'); this.addStatus(this.T('Connection lost')); return this.wsReconnect(); }; ZammadChat.prototype.onConnectionReestablished = function() { this.lastAddedType = 'status'; - this.el.find('.zammad-chat-agent-status').attr('data-status', 'online').text(this.T('Online')); + this.setAgentOnlineState('online'); return this.addStatus(this.T('Connection re-established')); }; @@ -622,8 +691,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; ZammadChat.prototype.setAgentOnlineState = function(state) { - this.isOnline = state; - return this.el.find('.zammad-chat-agent-status').toggleClass('zammad-chat-is-online', state).text(state ? this.T('Online') : this.T('Offline')); + var capitalizedState; + this.state = state; + capitalizedState = state.charAt(0).toUpperCase() + state.slice(1); + return this.el.find('.zammad-chat-agent-status').attr('data-status', state).text(this.T(capitalizedState)); }; return ZammadChat; @@ -632,71 +703,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return window.ZammadChat = ZammadChat; })(window.jQuery, window); -if (!window.zammadChatTemplates) { - window.zammadChatTemplates = {}; -} -window.zammadChatTemplates["agent"] = function (__obj) { - if (!__obj) __obj = {}; - var __out = [], __capture = function(callback) { - var out = __out, result; - __out = []; - callback.call(this); - result = __out.join(''); - __out = out; - return __safe(result); - }, __sanitize = function(value) { - if (value && value.ecoSafe) { - return value; - } else if (typeof value !== 'undefined' && value != null) { - return __escape(value); - } else { - return ''; - } - }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; - __safe = __obj.safe = function(value) { - if (value && value.ecoSafe) { - return value; - } else { - if (!(typeof value !== 'undefined' && value != null)) value = ''; - var result = new String(value); - result.ecoSafe = true; - return result; - } - }; - if (!__escape) { - __escape = __obj.escape = function(value) { - return ('' + value) - .replace(/&/g, '&') - .replace(//g, '>') - .replace(/"/g, '"'); - }; - } - (function() { - (function() { - if (this.agent.avatar) { - __out.push('\n\n'); - } - - __out.push('\n\n '); - - __out.push(__sanitize(this.agent.name)); - - __out.push(' '); - - __out.push(this.agentPhrase); - - __out.push('\n'); - - }).call(this); - - }).call(__obj); - __obj.safe = __objSafe, __obj.escape = __escape; - return __out.join(''); -}; - /*! * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): @@ -816,15 +822,33 @@ window.zammadChatTemplates["chat"] = function (__obj) { } (function() { (function() { - __out.push('