From ba6834978910ce91f5e95c787e70b84fb3f7cb0f Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 11 Nov 2015 21:44:54 +0100 Subject: [PATCH] Added reconnecting of chat app. Improved logging of chat app. --- .../javascripts/app/controllers/chat.coffee | 26 +- app/models/chat.rb | 16 +- lib/sessions/event/chat_base.rb | 6 +- lib/sessions/event/chat_session_close.rb | 34 +- lib/sessions/event/chat_session_start.rb | 6 +- lib/sessions/event/chat_status_agent.rb | 2 - public/assets/chat/chat.coffee | 115 +++++-- public/assets/chat/chat.js | 302 +++++++++++------- public/assets/chat/chat.min.js | 2 +- public/assets/chat/views/chat.eco | 5 +- public/assets/chat/views/waiting.eco | 4 +- 11 files changed, 346 insertions(+), 172 deletions(-) diff --git a/app/assets/javascripts/app/controllers/chat.coffee b/app/assets/javascripts/app/controllers/chat.coffee index 34cf3bd8c..8050ef8f8 100644 --- a/app/assets/javascripts/app/controllers/chat.coffee +++ b/app/assets/javascripts/app/controllers/chat.coffee @@ -27,20 +27,25 @@ class App.CustomerChat extends App.Controller @render() - App.Event.bind( + @bind( 'chat_status_agent' (data) => @meta = data @updateMeta() @interval(@pushState, 20000, 'pushState') ) - App.Event.bind( + @bind( 'chat_session_start' (data) => App.WebSocket.send(event:'chat_status_agent') if data.session @addChat(data.session) ) + @bind( + 'ws:login' + -> + App.WebSocket.send(event:'chat_status_agent') + ) App.WebSocket.send(event:'chat_status_agent') @@ -190,20 +195,28 @@ class chatWindow extends App.Controller @on 'layout-change', @scrollToBottom - App.Event.bind( + @bind( 'chat_session_typing' (data) => return if data.session_id isnt @session.session_id return if data.self_written @showWritingLoader() ) - App.Event.bind( + @bind( 'chat_session_message' (data) => return if data.session_id isnt @session.session_id return if data.self_written @receiveMessage(data.message.content) ) + @bind( + 'chat_session_left chat_session_closed' + (data) => + return if data.session_id isnt @session.session_id + return if data.self_written + @addStatusMessage("#{data.realname} was leaving the conversation") + @goOffline() + ) render: -> @html App.view('layout_ref/customer_chat_window') @@ -223,7 +236,7 @@ class chatWindow extends App.Controller @addMessage message.content, 'customer' # set focus - @input.get(0).focus() + #@input.get(0).focus() onTransitionend: (event) => # chat window is done with animation - adjust scroll-bars @@ -246,6 +259,7 @@ class chatWindow extends App.Controller release: => @trigger 'closed' + @el.remove() super clearUnread: => @@ -360,7 +374,7 @@ class chatWindow extends App.Controller @$('.js-loader').remove() goOffline: => - @addStatusMessage("#{ @options.name }'s connection got closed") + #@addStatusMessage("#{ @options.name }'s connection got closed") @status.attr('data-status', 'offline') @el.addClass('is-offline') @input.attr('disabled', true) diff --git a/app/models/chat.rb b/app/models/chat.rb index afaca3180..2bba381fd 100644 --- a/app/models/chat.rb +++ b/app/models/chat.rb @@ -46,7 +46,7 @@ class Chat < ApplicationModel end def self.running_chat_count - Chat::Session.where(state: ['waiting']).count + Chat::Session.where(state: ['running']).count end def active_chat_count @@ -129,19 +129,19 @@ class Chat::Session < ApplicationModel end def add_recipient(client_id, store = false) - if !self.preferences[:participants] - self.preferences[:participants] = [] + if !preferences[:participants] + preferences[:participants] = [] end - return self.preferences[:participants] if self.preferences[:participants].include?(client_id) - self.preferences[:participants].push client_id + return preferences[:participants] if preferences[:participants].include?(client_id) + preferences[:participants].push client_id if store - self.save + save end - self.preferences[:participants] + preferences[:participants] end def send_to_recipients(message, ignore_client_id) - self.preferences[:participants].each {|local_client_id| + preferences[:participants].each {|local_client_id| next if local_client_id == ignore_client_id Sessions.send(local_client_id, message) } diff --git a/lib/sessions/event/chat_base.rb b/lib/sessions/event/chat_base.rb index 6df4a7dcd..66a35b44b 100644 --- a/lib/sessions/event/chat_base.rb +++ b/lib/sessions/event/chat_base.rb @@ -7,7 +7,7 @@ class Sessions::Event::ChatBase end - def pre_check + def pre # check if feature is enabled if !Setting.get('chat') @@ -22,4 +22,8 @@ class Sessions::Event::ChatBase false end + def post + false + end + end diff --git a/lib/sessions/event/chat_session_close.rb b/lib/sessions/event/chat_session_close.rb index 99cafd22f..4856dfe63 100644 --- a/lib/sessions/event/chat_session_close.rb +++ b/lib/sessions/event/chat_session_close.rb @@ -16,15 +16,41 @@ class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase return { event: 'chat_status_close', data: { - state: "No such session id #{data['data']['session_id']}", + state: "No such session id #{@data['data']['session_id']}", }, } end - chat_session.state = 'closed' - chat_session.save + realname = 'anonymous' + if @session && @session['id'] + realname = User.find(@session['id']).fullname + end - # return new session + # notify about "leaving" + if @session && chat_session.user_id == @session['id'] + message = { + event: 'chat_session_closed', + data: { + session_id: chat_session.session_id, + realname: realname, + }, + } + + # close session if host is closing it + chat_session.state = 'closed' + chat_session.save + else + message = { + event: 'chat_session_left', + data: { + session_id: chat_session.session_id, + realname: realname, + }, + } + end + chat_session.send_to_recipients(message, @client_id) + + # notifiy participients { event: 'chat_status_close', data: { diff --git a/lib/sessions/event/chat_session_start.rb b/lib/sessions/event/chat_session_start.rb index c1b75e46e..13eed93f7 100644 --- a/lib/sessions/event/chat_session_start.rb +++ b/lib/sessions/event/chat_session_start.rb @@ -20,9 +20,13 @@ class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase # send chat_session_init to client chat_user = User.find(chat_session.user_id) + url = nil + if chat_user.image && chat_user.image != 'none' + url = "/api/v1/users/image/#{chat_user.image}" + end user = { name: chat_user.fullname, - avatar: chat_user.image, + avatar: url, } data = { event: 'chat_session_start', diff --git a/lib/sessions/event/chat_status_agent.rb b/lib/sessions/event/chat_status_agent.rb index 884b47103..7e723a118 100644 --- a/lib/sessions/event/chat_status_agent.rb +++ b/lib/sessions/event/chat_status_agent.rb @@ -4,12 +4,10 @@ class Sessions::Event::ChatStatusAgent < Sessions::Event::ChatBase # check if user has permissions - # renew timestamps state = Chat::Agent.state(@session['id']) Chat::Agent.state(@session['id'], state) - # update recipients of existing sessions Chat::Session.where(state: 'running', user_id: @session['id']).order('created_at ASC').each {|chat_session| chat_session.add_recipient(@client_id, true) diff --git a/public/assets/chat/chat.coffee b/public/assets/chat/chat.coffee index 4843b9208..2b6bc05c9 100644 --- a/public/assets/chat/chat.coffee +++ b/public/assets/chat/chat.coffee @@ -19,15 +19,35 @@ do($ = window.jQuery, window) -> inputTimeout: null isTyping: false isOnline: true + debug: true + host: 'ws://localhost:6042' strings: 'Online': 'Online' 'Offline': 'Offline' - 'Connecting': 'Connecting' + 'Connecting': 'Verbinden' 'Connection re-established': 'Connection re-established' - 'Today': 'Today' + 'Today': 'Heute' + 'Send': 'Senden' + 'Compose your message...': 'Ihre Nachricht...' + 'All colleges are busy.': 'Alle Kollegen sind belegt.' + 'You are on waiting list position %s.': 'Sie sind in der Warteliste an der Position %s.' + '': '' + '': '' + '': '' - T: (string) => - return @strings[string] + T: (string, items...) => + if !@strings[string] + @log 'notice', "Translation needed for '#{string}'" + translation = @strings[string] || string + if items + for item in items + translation = translation.replace(/%s/, item) + + translation + + log: (level, string...) => + return if !@debug && level is 'debug' + console.log level, string view: (name) => return (options) => @@ -42,8 +62,6 @@ do($ = window.jQuery, window) -> @el = $(@view('chat')(@options)) @options.target.append @el - @setAgentOnlineState @isOnline - @el.find('.js-chat-open').click @open @el.find('.js-chat-close').click @close @el.find('.zammad-chat-controls').on 'submit', @onSubmit @@ -54,24 +72,10 @@ do($ = window.jQuery, window) -> @session_id = undefined if !window.WebSocket - console.log('Zammad Chat: Browser not supported') + @log 'notice', 'Chat: Browser not supported!' return - zammad_host = 'ws://localhost:6042' - @ws = new window.WebSocket(zammad_host) - console.log("Connecting to #{zammad_host}") - - @ws.onopen = => - console.log('ws connected') - @send 'chat_status_customer' - - @ws.onmessage = @onWebSocketMessage - - @ws.onclose = (e) => - console.log 'debug', 'close websocket connection' - - @ws.onerror = (e) => - console.log 'debug', 'ws:onerror', e + @connect() @onReady() @@ -81,7 +85,7 @@ do($ = window.jQuery, window) -> @sendMessage() send: (event, data) => - console.log 'debug', 'ws:send', event, data + @log 'debug', 'ws:send', event, data pipe = JSON.stringify event: event data: data @@ -89,7 +93,7 @@ do($ = window.jQuery, window) -> onWebSocketMessage: (e) => pipes = JSON.parse( e.data ) - console.log 'debug', 'ws:onmessage', pipes + @log 'debug', 'ws:onmessage', pipes for pipe in pipes switch pipe.event @@ -110,17 +114,21 @@ do($ = window.jQuery, window) -> when 'queue' @onQueue pipe.data.position @session_id = pipe.data.session_id + when 'chat_session_closed' + @onSessionClosed pipe.data + when 'chat_session_left' + @onSessionClosed pipe.data when 'chat_status_customer' switch pipe.data.state when 'online' @onReady() - console.log 'Zammad Chat: ready' + @log 'debug', 'Zammad Chat: ready' when 'offline' - console.log 'Zammad Chat: No agent online' + @log 'debug', 'Zammad Chat: No agent online' when 'chat_disabled' - console.log 'Zammad Chat: Chat is disabled' + @log 'debug', 'Zammad Chat: Chat is disabled' when 'no_seats_available' - console.log 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue + @log 'debug', 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue onReady: => if @options.show @@ -157,8 +165,7 @@ do($ = window.jQuery, window) -> sendMessage: -> message = @el.find('.zammad-chat-input').val() - if !message - return + return if !message messageElement = @view('message') message: message @@ -216,7 +223,7 @@ do($ = window.jQuery, window) -> # setTimeout @onConnectionEstablished, 1180 # setTimeout @onAgentTypingStart, 2000 # setTimeout @receiveMessage, 5000, "Hello! How can I help you?" - @connect() + @session_init() close: (event) => event.stopPropagation() if event @@ -228,6 +235,8 @@ do($ = window.jQuery, window) -> @disconnect() @isOpen = false + @send 'chat_session_close', {session_id: @session_id} + hide: -> @el.removeClass('zammad-chat-is-visible') @@ -238,8 +247,16 @@ do($ = window.jQuery, window) -> @el.css 'bottom', -remainerHeight + disableInput: -> + @el.find('.zammad-chat-input').prop('disabled', true) + @el.find('.zammad-chat-send').prop('disabled', true) + + enableInput: -> + @el.find('.zammad-chat-input').prop('disabled', false) + @el.find('.zammad-chat-send').prop('disabled', false) + onQueue: (position) => - console.log "onQueue", position + @log 'notice', 'onQueue', position @inQueue = true @el.find('.zammad-chat-body').html @view('waiting') @@ -294,21 +311,49 @@ do($ = window.jQuery, window) -> scrollToBottom: -> @el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight')) - connect: -> + session_init: -> @send('chat_session_init') + connect: => + @log 'notice', "Connecting to #{@host}" + @ws = new window.WebSocket(@host) + @ws.onopen = => + @log 'debug', 'ws connected' + @send 'chat_status_customer' + @setAgentOnlineState(true) + + @ws.onmessage = @onWebSocketMessage + + @ws.onclose = (e) => + @log 'debug', 'close websocket connection' + @reconnect() + @setAgentOnlineState(false) + + @ws.onerror = (e) => + @log 'debug', 'ws:onerror', e + reconnect: => # set status to connecting + @log 'notice', 'reconnecting' + @disableInput() @lastAddedType = 'status' - @el.find('.zammad-chat-agent-status').attr('data-status', 'connecting').text @T('Connecting') + @el.find('.zammad-chat-agent-status').attr('data-status', 'connecting').text @T('Reconnecting') @addStatus @T('Connection lost') + if @reconnectDelayId + clearTimeout(@reconnectDelayId) + @reconnectDelayId = setTimeout(@connect, 5000) + onConnectionReestablished: => # set status back to online @lastAddedType = 'status' @el.find('.zammad-chat-agent-status').attr('data-status', 'online').text @T('Online') @addStatus @T('Connection re-established') + onSessionClosed: (data) -> + @addStatus @T('Chat closed by %s', data.realname) + @disableInput() + disconnect: -> @showLoader() @el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden') @@ -322,6 +367,8 @@ do($ = window.jQuery, window) -> @el.find('.zammad-chat-agent').html @view('agent') agent: agent + @enableInput() + @el.find('.zammad-chat-body').empty() @el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden') @el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden') diff --git a/public/assets/chat/chat.js b/public/assets/chat/chat.js index 4603c0f90..7fca04e38 100644 --- a/public/assets/chat/chat.js +++ b/public/assets/chat/chat.js @@ -1,4 +1,70 @@ -var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }; +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(''); +}; + +var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, + slice = [].slice; (function($, window) { var ZammadChat; @@ -30,16 +96,48 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.isOnline = true; + ZammadChat.prototype.debug = true; + + ZammadChat.prototype.host = 'ws://localhost:6042'; + ZammadChat.prototype.strings = { 'Online': 'Online', 'Offline': 'Offline', - 'Connecting': 'Connecting', + 'Connecting': 'Verbinden', 'Connection re-established': 'Connection re-established', - 'Today': 'Today' + 'Today': 'Heute', + 'Send': 'Senden', + 'Compose your message...': 'Ihre Nachricht...', + 'All colleges are busy.': 'Alle Kollegen sind belegt.', + 'You are on waiting list position %s.': 'Sie sind in der Warteliste an der Position %s.', + '': '', + '': '', + '': '' }; - ZammadChat.prototype.T = function(string) { - return this.strings[string]; + ZammadChat.prototype.T = function() { + var i, item, items, len, string, translation; + string = arguments[0], items = 2 <= arguments.length ? slice.call(arguments, 1) : []; + if (!this.strings[string]) { + this.log('notice', "Translation needed for '" + string + "'"); + } + translation = this.strings[string] || string; + if (items) { + for (i = 0, len = items.length; i < len; i++) { + item = items[i]; + translation = translation.replace(/%s/, item); + } + } + return translation; + }; + + ZammadChat.prototype.log = function() { + var level, string; + level = arguments[0], string = 2 <= arguments.length ? slice.call(arguments, 1) : []; + if (!this.debug && level === 'debug') { + return; + } + return console.log(level, string); }; ZammadChat.prototype.view = function(name) { @@ -59,6 +157,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.onConnectionEstablished = bind(this.onConnectionEstablished, this); this.onConnectionReestablished = bind(this.onConnectionReestablished, this); this.reconnect = bind(this.reconnect, this); + this.connect = bind(this.connect, this); this.onAgentTypingEnd = bind(this.onAgentTypingEnd, this); this.onAgentTypingStart = bind(this.onAgentTypingStart, this); this.onQueue = bind(this.onQueue, this); @@ -75,12 +174,11 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.send = bind(this.send, this); this.checkForEnter = bind(this.checkForEnter, this); this.view = bind(this.view, this); + this.log = bind(this.log, this); this.T = bind(this.T, this); - var zammad_host; this.options = $.extend({}, this.defaults, options); this.el = $(this.view('chat')(this.options)); this.options.target.append(this.el); - this.setAgentOnlineState(this.isOnline); 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); @@ -90,29 +188,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }); this.session_id = void 0; if (!window.WebSocket) { - console.log('Zammad Chat: Browser not supported'); + this.log('notice', 'Chat: Browser not supported!'); return; } - zammad_host = 'ws://localhost:6042'; - this.ws = new window.WebSocket(zammad_host); - console.log("Connecting to " + zammad_host); - this.ws.onopen = (function(_this) { - return function() { - console.log('ws connected'); - return _this.send('chat_status_customer'); - }; - })(this); - this.ws.onmessage = this.onWebSocketMessage; - this.ws.onclose = (function(_this) { - return function(e) { - return console.log('debug', 'close websocket connection'); - }; - })(this); - this.ws.onerror = (function(_this) { - return function(e) { - return console.log('debug', 'ws:onerror', e); - }; - })(this); + this.connect(); this.onReady(); } @@ -125,7 +204,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.send = function(event, data) { var pipe; - console.log('debug', 'ws:send', event, data); + this.log('debug', 'ws:send', event, data); pipe = JSON.stringify({ event: event, data: data @@ -136,7 +215,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.onWebSocketMessage = function(e) { var i, len, pipe, pipes; pipes = JSON.parse(e.data); - console.log('debug', 'ws:onmessage', pipes); + this.log('debug', 'ws:onmessage', pipes); for (i = 0, len = pipes.length; i < len; i++) { pipe = pipes[i]; switch (pipe.event) { @@ -168,20 +247,26 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.session_id = pipe.data.session_id; } break; + case 'chat_session_closed': + this.onSessionClosed(pipe.data); + break; + case 'chat_session_left': + this.onSessionClosed(pipe.data); + break; case 'chat_status_customer': switch (pipe.data.state) { case 'online': this.onReady(); - console.log('Zammad Chat: ready'); + this.log('debug', 'Zammad Chat: ready'); break; case 'offline': - console.log('Zammad Chat: No agent online'); + this.log('debug', 'Zammad Chat: No agent online'); break; case 'chat_disabled': - console.log('Zammad Chat: Chat is disabled'); + this.log('debug', 'Zammad Chat: Chat is disabled'); break; case 'no_seats_available': - console.log('Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue); + this.log('debug', 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue); } } } @@ -280,7 +365,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; ZammadChat.prototype.onOpenAnimationEnd = function() { - return this.connect(); + return this.session_init(); }; ZammadChat.prototype.close = function(event) { @@ -297,7 +382,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.onCloseAnimationEnd = function() { this.el.removeClass('zammad-chat-is-open'); this.disconnect(); - return this.isOpen = false; + this.isOpen = false; + return this.send('chat_session_close', { + session_id: this.session_id + }); }; ZammadChat.prototype.hide = function() { @@ -311,8 +399,18 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return this.el.css('bottom', -remainerHeight); }; + ZammadChat.prototype.disableInput = function() { + this.el.find('.zammad-chat-input').prop('disabled', true); + return this.el.find('.zammad-chat-send').prop('disabled', true); + }; + + ZammadChat.prototype.enableInput = function() { + this.el.find('.zammad-chat-input').prop('disabled', false); + return this.el.find('.zammad-chat-send').prop('disabled', false); + }; + ZammadChat.prototype.onQueue = function(position) { - console.log("onQueue", position); + this.log('notice', 'onQueue', position); this.inQueue = true; return this.el.find('.zammad-chat-body').html(this.view('waiting')({ position: position @@ -371,14 +469,45 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return this.el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight')); }; - ZammadChat.prototype.connect = function() { + ZammadChat.prototype.session_init = function() { return this.send('chat_session_init'); }; + ZammadChat.prototype.connect = function() { + this.log('notice', "Connecting to " + this.host); + this.ws = new window.WebSocket(this.host); + this.ws.onopen = (function(_this) { + return function() { + _this.log('debug', 'ws connected'); + _this.send('chat_status_customer'); + return _this.setAgentOnlineState(true); + }; + })(this); + this.ws.onmessage = this.onWebSocketMessage; + this.ws.onclose = (function(_this) { + return function(e) { + _this.log('debug', 'close websocket connection'); + _this.reconnect(); + return _this.setAgentOnlineState(false); + }; + })(this); + return this.ws.onerror = (function(_this) { + return function(e) { + return _this.log('debug', 'ws:onerror', e); + }; + })(this); + }; + 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('Connecting')); - return this.addStatus(this.T('Connection lost')); + this.el.find('.zammad-chat-agent-status').attr('data-status', 'connecting').text(this.T('Reconnecting')); + this.addStatus(this.T('Connection lost')); + if (this.reconnectDelayId) { + clearTimeout(this.reconnectDelayId); + } + return this.reconnectDelayId = setTimeout(this.connect, 5000); }; ZammadChat.prototype.onConnectionReestablished = function() { @@ -387,6 +516,11 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return this.addStatus(this.T('Connection re-established')); }; + ZammadChat.prototype.onSessionClosed = function(data) { + this.addStatus(this.T('Chat closed by %s', data.realname)); + return this.disableInput(); + }; + ZammadChat.prototype.disconnect = function() { this.showLoader(); this.el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden'); @@ -400,6 +534,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.el.find('.zammad-chat-agent').html(this.view('agent')({ agent: agent })); + this.enableInput(); this.el.find('.zammad-chat-body').empty(); this.el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden'); this.el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden'); @@ -501,71 +636,6 @@ jQuery.fn.autoGrow = function(options) { }); }; -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(''); -}; - if (!window.zammadChatTemplates) { window.zammadChatTemplates = {}; } @@ -608,11 +678,19 @@ window.zammadChatTemplates["chat"] = function (__obj) { } (function() { (function() { - __out.push('
\n
\n
\n Online\n \n \n \n \n
\n
\n \n
\n
\n \n '); + __out.push('
\n
\n
\n Online\n \n \n \n \n
\n
\n
\n
\n \n '); __out.push(this.invitationPhrase); - __out.push('\n
\n
\n
\n
\n \n \n
\n
'); + __out.push('
\n
\n
\n
\n
\n \n \n
\n
'); }).call(this); @@ -887,11 +965,15 @@ window.zammadChatTemplates["waiting"] = function (__obj) { } (function() { (function() { - __out.push('
\n
\n \n \n \n \n \n Leider sind gerade alle Mitarbeiter belegt.
\n Warteliste-Position: '); + __out.push('
\n
\n \n \n \n \n \n '); - __out.push(__sanitize(this.position)); + __out.push(this.T('All colleges are busy.')); - __out.push('\n
\n
'); + __out.push('
\n '); + + __out.push(this.T('You are on waiting list position %s.', this.position)); + + __out.push('\n
\n
'); }).call(this); diff --git a/public/assets/chat/chat.min.js b/public/assets/chat/chat.min.js index 63ee42a06..47cdc579a 100644 --- a/public/assets/chat/chat.min.js +++ b/public/assets/chat/chat.min.js @@ -1 +1 @@ -var bind=function(t,e){return function(){return t.apply(e,arguments)}};!function(t,e){var n;return n=function(){function n(n,a){this.setAgentOnlineState=bind(this.setAgentOnlineState,this),this.onConnectionEstablished=bind(this.onConnectionEstablished,this),this.onConnectionReestablished=bind(this.onConnectionReestablished,this),this.reconnect=bind(this.reconnect,this),this.onAgentTypingEnd=bind(this.onAgentTypingEnd,this),this.onAgentTypingStart=bind(this.onAgentTypingStart,this),this.onQueue=bind(this.onQueue,this),this.onCloseAnimationEnd=bind(this.onCloseAnimationEnd,this),this.close=bind(this.close,this),this.onOpenAnimationEnd=bind(this.onOpenAnimationEnd,this),this.open=bind(this.open,this),this.receiveMessage=bind(this.receiveMessage,this),this.onSubmit=bind(this.onSubmit,this),this.onTypingEnd=bind(this.onTypingEnd,this),this.onInput=bind(this.onInput,this),this.onReady=bind(this.onReady,this),this.onWebSocketMessage=bind(this.onWebSocketMessage,this),this.send=bind(this.send,this),this.checkForEnter=bind(this.checkForEnter,this),this.view=bind(this.view,this),this.T=bind(this.T,this);var s;return this.options=t.extend({},this.defaults,a),this.el=t(this.view("chat")(this.options)),this.options.target.append(this.el),this.setAgentOnlineState(this.isOnline),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.el.find(".zammad-chat-input").on({keydown:this.checkForEnter,input:this.onInput}),this.session_id=void 0,e.WebSocket?(s="ws://localhost:6042",this.ws=new e.WebSocket(s),console.log("Connecting to "+s),this.ws.onopen=function(t){return function(){return console.log("ws connected"),t.send("chat_status_customer")}}(this),this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(t){return console.log("debug","close websocket connection")}}(this),this.ws.onerror=function(t){return function(t){return console.log("debug","ws:onerror",t)}}(this),void this.onReady()):void console.log("Zammad Chat: Browser not supported")}return n.prototype.defaults={invitationPhrase:"Chat with us!",agentPhrase:" is helping you",show:!0,target:t("body")},n.prototype._messageCount=0,n.prototype.isOpen=!1,n.prototype.blinkOnlineInterval=null,n.prototype.stopBlinOnlineStateTimeout=null,n.prototype.showTimeEveryXMinutes=1,n.prototype.lastTimestamp=null,n.prototype.lastAddedType=null,n.prototype.inputTimeout=null,n.prototype.isTyping=!1,n.prototype.isOnline=!0,n.prototype.strings={Online:"Online",Offline:"Offline",Connecting:"Connecting","Connection re-established":"Connection re-established",Today:"Today"},n.prototype.T=function(t){return this.strings[t]},n.prototype.view=function(t){return function(n){return function(a){return a||(a={}),a.T=n.T,e.zammadChatTemplates[t](a)}}(this)},n.prototype.checkForEnter=function(t){return t.shiftKey||13!==t.keyCode?void 0:(t.preventDefault(),this.sendMessage())},n.prototype.send=function(t,e){var n;return console.log("debug","ws:send",t,e),n=JSON.stringify({event:t,data:e}),this.ws.send(n)},n.prototype.onWebSocketMessage=function(t){var e,n,a,s;for(s=JSON.parse(t.data),console.log("debug","ws:onmessage",s),e=0,n=s.length;n>e;e++)switch(a=s[e],a.event){case"chat_session_message":if(a.data.self_written)return;this.receiveMessage(a.data);break;case"chat_session_typing":if(a.data.self_written)return;this.onAgentTypingStart();break;case"chat_session_start":switch(a.data.state){case"ok":this.onConnectionEstablished(a.data.agent)}break;case"chat_session_init":switch(a.data.state){case"ok":this.onConnectionEstablished(a.data.agent);break;case"queue":this.onQueue(a.data.position),this.session_id=a.data.session_id}break;case"chat_status_customer":switch(a.data.state){case"online":this.onReady(),console.log("Zammad Chat: ready");break;case"offline":console.log("Zammad Chat: No agent online");break;case"chat_disabled":console.log("Zammad Chat: Chat is disabled");break;case"no_seats_available":console.log("Zammad Chat: Too many clients in queue. Clients in queue: ",a.data.queue)}}},n.prototype.onReady=function(){return this.options.show?(this.show(),this.el.find(".zammad-chat-input").autoGrow({extraLine:!1})):void 0},n.prototype.onInput=function(){return this.el.find(".zammad-chat-message--unread").removeClass("zammad-chat-message--unread"),this.onTypingStart()},n.prototype.onTypingStart=function(){return this.isTypingTimeout&&clearTimeout(this.isTypingTimeout),this.isTypingTimeout=setTimeout(this.onTypingEnd,1500),this.isTyping?void 0:(this.isTyping=!0,this.send("chat_session_typing",{session_id:this.session_id}))},n.prototype.onTypingEnd=function(){return this.isTyping=!1},n.prototype.onSubmit=function(t){return t.preventDefault(),this.sendMessage()},n.prototype.sendMessage=function(){var t,e;return(t=this.el.find(".zammad-chat-input").val())?(e=this.view("message")({message:t,from:"customer",id:this._messageCount++}),this.maybeAddTimestamp(),this.el.find(".zammad-chat-message--typing").size()?(this.lastAddedType="typing-placeholder",this.el.find(".zammad-chat-message--typing").before(e)):(this.lastAddedType="message--customer",this.el.find(".zammad-chat-body").append(e)),this.el.find(".zammad-chat-input").val(""),this.scrollToBottom(),this.isTyping=!1,this.send("chat_session_message",{content:t,id:this._messageCount,session_id:this.session_id})):void 0},n.prototype.receiveMessage=function(t){var e,n;return this.onAgentTypingEnd(),this.maybeAddTimestamp(),this.lastAddedType="message--agent",n=null!=(e=document.hidden)?e:{" zammad-chat-message--unread":""},this.el.find(".zammad-chat-body").append(this.view("message")({message:t.message.content,id:t.id,from:"agent"})),this.scrollToBottom()},n.prototype.open=function(){return this.isOpen?void 0:(this.showLoader(),this.el.addClass("zammad-chat-is-open").animate({bottom:0},500,this.onOpenAnimationEnd),this.isOpen=!0)},n.prototype.onOpenAnimationEnd=function(){return this.connect()},n.prototype.close=function(t){var e;return t&&t.stopPropagation(),e=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-e},500,this.onCloseAnimationEnd)},n.prototype.onCloseAnimationEnd=function(){return this.el.removeClass("zammad-chat-is-open"),this.disconnect(),this.isOpen=!1},n.prototype.hide=function(){return this.el.removeClass("zammad-chat-is-visible")},n.prototype.show=function(){var t;return this.el.addClass("zammad-chat-is-visible"),t=this.el.outerHeight()-this.el.find(".zammad-chat-header").outerHeight(),this.el.css("bottom",-t)},n.prototype.onQueue=function(t){return console.log("onQueue",t),this.inQueue=!0,this.el.find(".zammad-chat-body").html(this.view("waiting")({position:t}))},n.prototype.onAgentTypingStart=function(){return this.stopTypingId&&clearTimeout(this.stopTypingId),this.stopTypingId=setTimeout(this.onAgentTypingEnd,3e3),this.el.find(".zammad-chat-message--typing").size()?void 0:(this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("typingIndicator")()),this.scrollToBottom())},n.prototype.onAgentTypingEnd=function(){return this.el.find(".zammad-chat-message--typing").remove()},n.prototype.maybeAddTimestamp=function(){var t,e,n;return n=Date.now(),!this.lastTimestamp||n-this.lastTimestamp>6e4*this.showTimeEveryXMinutes?(t=this.T("Today"),e=(new Date).toTimeString().substr(0,5),"timestamp"===this.lastAddedType?(this.updateLastTimestamp(t,e),this.lastTimestamp=n):(this.addStatus(t,e),this.lastTimestamp=n,this.lastAddedType="timestamp")):void 0},n.prototype.updateLastTimestamp=function(t,e){return this.el.find(".zammad-chat-body").find(".zammad-chat-status").last().replaceWith(this.view("status")({label:t,time:e}))},n.prototype.addStatus=function(t,e){return this.el.find(".zammad-chat-body").append(this.view("status")({label:t,time:e}))},n.prototype.scrollToBottom=function(){return this.el.find(".zammad-chat-body").scrollTop(t(".zammad-chat-body").prop("scrollHeight"))},n.prototype.connect=function(){return this.send("chat_session_init")},n.prototype.reconnect=function(){return this.lastAddedType="status",this.el.find(".zammad-chat-agent-status").attr("data-status","connecting").text(this.T("Connecting")),this.addStatus(this.T("Connection lost"))},n.prototype.onConnectionReestablished=function(){return this.lastAddedType="status",this.el.find(".zammad-chat-agent-status").attr("data-status","online").text(this.T("Online")),this.addStatus(this.T("Connection re-established"))},n.prototype.disconnect=function(){return this.showLoader(),this.el.find(".zammad-chat-welcome").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").addClass("zammad-chat-is-hidden")},n.prototype.onConnectionEstablished=function(t){return this.inQueue=!1,this.agent=t,this.el.find(".zammad-chat-agent").html(this.view("agent")({agent:t})),this.el.find(".zammad-chat-body").empty(),this.el.find(".zammad-chat-welcome").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-input").focus()},n.prototype.showLoader=function(){return this.el.find(".zammad-chat-body").html(this.view("loader")())},n.prototype.setAgentOnlineState=function(t){return this.isOnline=t,this.el.find(".zammad-chat-agent-status").toggleClass("zammad-chat-is-online",t).text(t?this.T("Online"):this.T("Offline"))},n}(),t(document).ready(function(){return e.zammadChat=new n})}(window.jQuery,window),window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.agent=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){this.agent.avatar&&(n.push('\n\n')),n.push('\n\n '),n.push(a(this.agent.name)),n.push(" "),n.push(this.agentPhrase),n.push("\n")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),n=function(t){return jQuery(t).after('
'),jQuery(t).next(".autogrow-textarea-mirror")[0]},a=function(t){i.innerHTML=String(t.value).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/ /g," ").replace(/\n/g,"
")+(e.extraLine?".
.":""),jQuery(t).height()!=jQuery(i).height()&&jQuery(t).height(jQuery(i).height())},s=function(){a(this)},i=n(this);i.style.display="none",i.style.wordWrap="break-word",i.style.whiteSpace="normal",i.style.padding=jQuery(this).css("paddingTop")+" "+jQuery(this).css("paddingRight")+" "+jQuery(this).css("paddingBottom")+" "+jQuery(this).css("paddingLeft"),i.style.width=jQuery(this).css("width"),i.style.fontFamily=jQuery(this).css("font-family"),i.style.fontSize=jQuery(this).css("font-size"),i.style.lineHeight=jQuery(this).css("line-height"),this.style.overflow="hidden",this.style.minHeight=this.rows+"em",this.onkeyup=s,a(this)})},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.chat=function(t){t||(t={});var e,n=[],a=t.safe,s=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},s||(s=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n
\n
\n Online\n \n \n \n \n
\n
\n \n
\n
\n \n '),n.push(this.invitationPhrase),n.push('\n
\n
\n
\n
\n \n \n
\n
')}).call(this)}.call(t),t.safe=a,t.escape=s,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.loader=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n \n \n \n \n \n '),n.push(a(this.T("Connecting"))),n.push("\n
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.message=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n '),n.push(this.message),n.push("\n
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.status=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
'),n.push(a(this.label)),n.push(" "),n.push(a(this.time)),n.push("
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.typingIndicator=function(t){t||(t={});var e,n=[],a=t.safe,s=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},s||(s=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n \n \n \n \n \n \n \n
')}).call(this)}.call(t),t.safe=a,t.escape=s,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.waiting=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n
\n \n \n \n \n \n Leider sind gerade alle Mitarbeiter belegt.
\n Warteliste-Position: '),n.push(a(this.position)),n.push("\n
\n
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")}; \ No newline at end of file +window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.agent=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){this.agent.avatar&&(n.push('\n\n')),n.push('\n\n '),n.push(a(this.agent.name)),n.push(" "),n.push(this.agentPhrase),n.push("\n")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")};var bind=function(t,e){return function(){return t.apply(e,arguments)}},slice=[].slice;!function(t,e){var n;return n=function(){function n(n,a){return this.setAgentOnlineState=bind(this.setAgentOnlineState,this),this.onConnectionEstablished=bind(this.onConnectionEstablished,this),this.onConnectionReestablished=bind(this.onConnectionReestablished,this),this.reconnect=bind(this.reconnect,this),this.connect=bind(this.connect,this),this.onAgentTypingEnd=bind(this.onAgentTypingEnd,this),this.onAgentTypingStart=bind(this.onAgentTypingStart,this),this.onQueue=bind(this.onQueue,this),this.onCloseAnimationEnd=bind(this.onCloseAnimationEnd,this),this.close=bind(this.close,this),this.onOpenAnimationEnd=bind(this.onOpenAnimationEnd,this),this.open=bind(this.open,this),this.receiveMessage=bind(this.receiveMessage,this),this.onSubmit=bind(this.onSubmit,this),this.onTypingEnd=bind(this.onTypingEnd,this),this.onInput=bind(this.onInput,this),this.onReady=bind(this.onReady,this),this.onWebSocketMessage=bind(this.onWebSocketMessage,this),this.send=bind(this.send,this),this.checkForEnter=bind(this.checkForEnter,this),this.view=bind(this.view,this),this.log=bind(this.log,this),this.T=bind(this.T,this),this.options=t.extend({},this.defaults,a),this.el=t(this.view("chat")(this.options)),this.options.target.append(this.el),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.el.find(".zammad-chat-input").on({keydown:this.checkForEnter,input:this.onInput}),this.session_id=void 0,e.WebSocket?(this.connect(),void this.onReady()):void this.log("notice","Chat: Browser not supported!")}return n.prototype.defaults={invitationPhrase:"Chat with us!",agentPhrase:" is helping you",show:!0,target:t("body")},n.prototype._messageCount=0,n.prototype.isOpen=!1,n.prototype.blinkOnlineInterval=null,n.prototype.stopBlinOnlineStateTimeout=null,n.prototype.showTimeEveryXMinutes=1,n.prototype.lastTimestamp=null,n.prototype.lastAddedType=null,n.prototype.inputTimeout=null,n.prototype.isTyping=!1,n.prototype.isOnline=!0,n.prototype.debug=!0,n.prototype.host="ws://localhost:6042",n.prototype.strings={Online:"Online",Offline:"Offline",Connecting:"Verbinden","Connection re-established":"Connection re-established",Today:"Heute",Send:"Senden","Compose your message...":"Ihre Nachricht...","All colleges are busy.":"Alle Kollegen sind belegt.","You are on waiting list position %s.":"Sie sind in der Warteliste an der Position %s.","":"","":"","":""},n.prototype.T=function(){var t,e,n,a,s,i;if(s=arguments[0],n=2<=arguments.length?slice.call(arguments,1):[],this.strings[s]||this.log("notice","Translation needed for '"+s+"'"),i=this.strings[s]||s,n)for(t=0,a=n.length;a>t;t++)e=n[t],i=i.replace(/%s/,e);return i},n.prototype.log=function(){var t,e;return t=arguments[0],e=2<=arguments.length?slice.call(arguments,1):[],this.debug||"debug"!==t?console.log(t,e):void 0},n.prototype.view=function(t){return function(n){return function(a){return a||(a={}),a.T=n.T,e.zammadChatTemplates[t](a)}}(this)},n.prototype.checkForEnter=function(t){return t.shiftKey||13!==t.keyCode?void 0:(t.preventDefault(),this.sendMessage())},n.prototype.send=function(t,e){var n;return this.log("debug","ws:send",t,e),n=JSON.stringify({event:t,data:e}),this.ws.send(n)},n.prototype.onWebSocketMessage=function(t){var e,n,a,s;for(s=JSON.parse(t.data),this.log("debug","ws:onmessage",s),e=0,n=s.length;n>e;e++)switch(a=s[e],a.event){case"chat_session_message":if(a.data.self_written)return;this.receiveMessage(a.data);break;case"chat_session_typing":if(a.data.self_written)return;this.onAgentTypingStart();break;case"chat_session_start":switch(a.data.state){case"ok":this.onConnectionEstablished(a.data.agent)}break;case"chat_session_init":switch(a.data.state){case"ok":this.onConnectionEstablished(a.data.agent);break;case"queue":this.onQueue(a.data.position),this.session_id=a.data.session_id}break;case"chat_session_closed":this.onSessionClosed(a.data);break;case"chat_session_left":this.onSessionClosed(a.data);break;case"chat_status_customer":switch(a.data.state){case"online":this.onReady(),this.log("debug","Zammad Chat: ready");break;case"offline":this.log("debug","Zammad Chat: No agent online");break;case"chat_disabled":this.log("debug","Zammad Chat: Chat is disabled");break;case"no_seats_available":this.log("debug","Zammad Chat: Too many clients in queue. Clients in queue: ",a.data.queue)}}},n.prototype.onReady=function(){return this.options.show?(this.show(),this.el.find(".zammad-chat-input").autoGrow({extraLine:!1})):void 0},n.prototype.onInput=function(){return this.el.find(".zammad-chat-message--unread").removeClass("zammad-chat-message--unread"),this.onTypingStart()},n.prototype.onTypingStart=function(){return this.isTypingTimeout&&clearTimeout(this.isTypingTimeout),this.isTypingTimeout=setTimeout(this.onTypingEnd,1500),this.isTyping?void 0:(this.isTyping=!0,this.send("chat_session_typing",{session_id:this.session_id}))},n.prototype.onTypingEnd=function(){return this.isTyping=!1},n.prototype.onSubmit=function(t){return t.preventDefault(),this.sendMessage()},n.prototype.sendMessage=function(){var t,e;return(t=this.el.find(".zammad-chat-input").val())?(e=this.view("message")({message:t,from:"customer",id:this._messageCount++}),this.maybeAddTimestamp(),this.el.find(".zammad-chat-message--typing").size()?(this.lastAddedType="typing-placeholder",this.el.find(".zammad-chat-message--typing").before(e)):(this.lastAddedType="message--customer",this.el.find(".zammad-chat-body").append(e)),this.el.find(".zammad-chat-input").val(""),this.scrollToBottom(),this.isTyping=!1,this.send("chat_session_message",{content:t,id:this._messageCount,session_id:this.session_id})):void 0},n.prototype.receiveMessage=function(t){var e,n;return this.onAgentTypingEnd(),this.maybeAddTimestamp(),this.lastAddedType="message--agent",n=null!=(e=document.hidden)?e:{" zammad-chat-message--unread":""},this.el.find(".zammad-chat-body").append(this.view("message")({message:t.message.content,id:t.id,from:"agent"})),this.scrollToBottom()},n.prototype.open=function(){return this.isOpen?void 0:(this.showLoader(),this.el.addClass("zammad-chat-is-open").animate({bottom:0},500,this.onOpenAnimationEnd),this.isOpen=!0)},n.prototype.onOpenAnimationEnd=function(){return this.session_init()},n.prototype.close=function(t){var e;return t&&t.stopPropagation(),e=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-e},500,this.onCloseAnimationEnd)},n.prototype.onCloseAnimationEnd=function(){return this.el.removeClass("zammad-chat-is-open"),this.disconnect(),this.isOpen=!1,this.send("chat_session_close",{session_id:this.session_id})},n.prototype.hide=function(){return this.el.removeClass("zammad-chat-is-visible")},n.prototype.show=function(){var t;return this.el.addClass("zammad-chat-is-visible"),t=this.el.outerHeight()-this.el.find(".zammad-chat-header").outerHeight(),this.el.css("bottom",-t)},n.prototype.disableInput=function(){return this.el.find(".zammad-chat-input").prop("disabled",!0),this.el.find(".zammad-chat-send").prop("disabled",!0)},n.prototype.enableInput=function(){return this.el.find(".zammad-chat-input").prop("disabled",!1),this.el.find(".zammad-chat-send").prop("disabled",!1)},n.prototype.onQueue=function(t){return this.log("notice","onQueue",t),this.inQueue=!0,this.el.find(".zammad-chat-body").html(this.view("waiting")({position:t}))},n.prototype.onAgentTypingStart=function(){return this.stopTypingId&&clearTimeout(this.stopTypingId),this.stopTypingId=setTimeout(this.onAgentTypingEnd,3e3),this.el.find(".zammad-chat-message--typing").size()?void 0:(this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("typingIndicator")()),this.scrollToBottom())},n.prototype.onAgentTypingEnd=function(){return this.el.find(".zammad-chat-message--typing").remove()},n.prototype.maybeAddTimestamp=function(){var t,e,n;return n=Date.now(),!this.lastTimestamp||n-this.lastTimestamp>6e4*this.showTimeEveryXMinutes?(t=this.T("Today"),e=(new Date).toTimeString().substr(0,5),"timestamp"===this.lastAddedType?(this.updateLastTimestamp(t,e),this.lastTimestamp=n):(this.addStatus(t,e),this.lastTimestamp=n,this.lastAddedType="timestamp")):void 0},n.prototype.updateLastTimestamp=function(t,e){return this.el.find(".zammad-chat-body").find(".zammad-chat-status").last().replaceWith(this.view("status")({label:t,time:e}))},n.prototype.addStatus=function(t,e){return this.el.find(".zammad-chat-body").append(this.view("status")({label:t,time:e}))},n.prototype.scrollToBottom=function(){return this.el.find(".zammad-chat-body").scrollTop(t(".zammad-chat-body").prop("scrollHeight"))},n.prototype.session_init=function(){return this.send("chat_session_init")},n.prototype.connect=function(){return this.log("notice","Connecting to "+this.host),this.ws=new e.WebSocket(this.host),this.ws.onopen=function(t){return function(){return t.log("debug","ws connected"),t.send("chat_status_customer"),t.setAgentOnlineState(!0)}}(this),this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(e){return t.log("debug","close websocket connection"),t.reconnect(),t.setAgentOnlineState(!1)}}(this),this.ws.onerror=function(t){return function(e){return t.log("debug","ws:onerror",e)}}(this)},n.prototype.reconnect=function(){return 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.addStatus(this.T("Connection lost")),this.reconnectDelayId&&clearTimeout(this.reconnectDelayId),this.reconnectDelayId=setTimeout(this.connect,5e3)},n.prototype.onConnectionReestablished=function(){return this.lastAddedType="status",this.el.find(".zammad-chat-agent-status").attr("data-status","online").text(this.T("Online")),this.addStatus(this.T("Connection re-established"))},n.prototype.onSessionClosed=function(t){return this.addStatus(this.T("Chat closed by %s",t.realname)),this.disableInput()},n.prototype.disconnect=function(){return this.showLoader(),this.el.find(".zammad-chat-welcome").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").addClass("zammad-chat-is-hidden")},n.prototype.onConnectionEstablished=function(t){return this.inQueue=!1,this.agent=t,this.el.find(".zammad-chat-agent").html(this.view("agent")({agent:t})),this.enableInput(),this.el.find(".zammad-chat-body").empty(),this.el.find(".zammad-chat-welcome").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-input").focus()},n.prototype.showLoader=function(){return this.el.find(".zammad-chat-body").html(this.view("loader")())},n.prototype.setAgentOnlineState=function(t){return this.isOnline=t,this.el.find(".zammad-chat-agent-status").toggleClass("zammad-chat-is-online",t).text(t?this.T("Online"):this.T("Offline"))},n}(),t(document).ready(function(){return e.zammadChat=new n})}(window.jQuery,window),jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),n=function(t){return jQuery(t).after('
'),jQuery(t).next(".autogrow-textarea-mirror")[0]},a=function(t){i.innerHTML=String(t.value).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/ /g," ").replace(/\n/g,"
")+(e.extraLine?".
.":""),jQuery(t).height()!=jQuery(i).height()&&jQuery(t).height(jQuery(i).height())},s=function(){a(this)},i=n(this);i.style.display="none",i.style.wordWrap="break-word",i.style.whiteSpace="normal",i.style.padding=jQuery(this).css("paddingTop")+" "+jQuery(this).css("paddingRight")+" "+jQuery(this).css("paddingBottom")+" "+jQuery(this).css("paddingLeft"),i.style.width=jQuery(this).css("width"),i.style.fontFamily=jQuery(this).css("font-family"),i.style.fontSize=jQuery(this).css("font-size"),i.style.lineHeight=jQuery(this).css("line-height"),this.style.overflow="hidden",this.style.minHeight=this.rows+"em",this.onkeyup=s,a(this)})},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.chat=function(t){t||(t={});var e,n=[],a=t.safe,s=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},s||(s=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n
\n
\n Online\n \n \n \n \n
\n
\n
\n
\n \n '),n.push(this.invitationPhrase),n.push('\n
\n
\n
\n
\n \n \n
\n
")}).call(this)}.call(t),t.safe=a,t.escape=s,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.loader=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n \n \n \n \n \n '),n.push(a(this.T("Connecting"))),n.push("\n
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.message=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n '),n.push(this.message),n.push("\n
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.status=function(t){t||(t={});var e,n=[],a=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?i(t):""},s=t.safe,i=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},i||(i=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
'),n.push(a(this.label)),n.push(" "),n.push(a(this.time)),n.push("
")}).call(this)}.call(t),t.safe=s,t.escape=i,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.typingIndicator=function(t){t||(t={});var e,n=[],a=t.safe,s=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},s||(s=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n \n \n \n \n \n \n \n
')}).call(this)}.call(t),t.safe=a,t.escape=s,n.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.waiting=function(t){t||(t={});var e,n=[],a=t.safe,s=t.escape;return e=t.safe=function(t){if(t&&t.ecoSafe)return t;("undefined"==typeof t||null==t)&&(t="");var e=new String(t);return e.ecoSafe=!0,e},s||(s=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){n.push('
\n
\n \n \n \n \n \n '),n.push(this.T("All colleges are busy.")),n.push("
\n "),n.push(this.T("You are on waiting list position %s.",this.position)),n.push("\n
\n
")}).call(this)}.call(t),t.safe=a,t.escape=s,n.join("")}; \ No newline at end of file diff --git a/public/assets/chat/views/chat.eco b/public/assets/chat/views/chat.eco index 7aa234ec7..480aef2f4 100644 --- a/public/assets/chat/views/chat.eco +++ b/public/assets/chat/views/chat.eco @@ -8,7 +8,6 @@
-
@@ -17,7 +16,7 @@
- - + +
\ No newline at end of file diff --git a/public/assets/chat/views/waiting.eco b/public/assets/chat/views/waiting.eco index b7513edc9..63170af5a 100644 --- a/public/assets/chat/views/waiting.eco +++ b/public/assets/chat/views/waiting.eco @@ -5,7 +5,7 @@ - Leider sind gerade alle Mitarbeiter belegt.
- Warteliste-Position: <%= @position %> + <%- @T('All colleges are busy.') %>
+ <%- @T('You are on waiting list position %s.', @position) %> \ No newline at end of file