diff --git a/app/assets/javascripts/app/controllers/chat.coffee b/app/assets/javascripts/app/controllers/chat.coffee
index 9eb72f8ee..34cf3bd8c 100644
--- a/app/assets/javascripts/app/controllers/chat.coffee
+++ b/app/assets/javascripts/app/controllers/chat.coffee
@@ -32,6 +32,7 @@ class App.CustomerChat extends App.Controller
(data) =>
@meta = data
@updateMeta()
+ @interval(@pushState, 20000, 'pushState')
)
App.Event.bind(
'chat_session_start'
@@ -43,8 +44,6 @@ class App.CustomerChat extends App.Controller
App.WebSocket.send(event:'chat_status_agent')
- @interval(@pushState, 16000)
-
pushState: =>
App.WebSocket.send(
event:'chat_agent_state'
@@ -59,13 +58,23 @@ class App.CustomerChat extends App.Controller
@navupdate '#customer_chat'
counter: =>
- if @meta.waiting_chat_count
- if @waitingChatCountLast isnt @meta.waiting_chat_count
- @sounds.chat_new.play()
- @waitingChatCountLast = @meta.waiting_chat_count
+ counter = 0
- return @messageCounter + @meta.waiting_chat_count
- @messageCounter
+ # get count of controller messages
+ if @meta.waiting_chat_count
+ counter += @meta.waiting_chat_count
+
+ # play on changes
+ if @lastWaitingChatCount isnt counter
+ @sounds.chat_new.play()
+ @lastWaitingChatCount = counter
+
+ # collect chat window messages
+ for key, value of @chatWindows
+ if value
+ counter += value.unreadMessages()
+
+ @messageCounter = counter
switch: (state = undefined) =>
@@ -85,7 +94,7 @@ class App.CustomerChat extends App.Controller
updateNavMenu: =>
delay = ->
App.Event.trigger('menu:render')
- @delay(delay, 200)
+ @delay(delay, 200, 'updateNavMenu')
updateMeta: =>
if @meta.waiting_chat_count
@@ -107,7 +116,8 @@ class App.CustomerChat extends App.Controller
chat = new chatWindow
name: "#{session.created_at}"
session: session
- callback: @removeChat
+ removeCallback: @removeChat
+ messageCallback: @updateNavMenu
@on 'layout-has-changed', @propagateLayoutChange
@@ -115,7 +125,6 @@ class App.CustomerChat extends App.Controller
@chatWindows[session.session_id] = chat
removeChat: (session_id) =>
- console.log('removeChat', session_id, @chatWindows[session_id])
delete @chatWindows[session_id]
propagateLayoutChange: (event) =>
@@ -177,6 +186,7 @@ class chatWindow extends App.Controller
@lastAddedType
@isTyping = false
@render()
+ @resetUnreadMessages()
@on 'layout-change', @scrollToBottom
@@ -231,8 +241,8 @@ class chatWindow extends App.Controller
data:
session_id: @session.session_id
)
- if @callback
- @callback(@session.session_id)
+ if @removeCallback
+ @removeCallback(@session.session_id)
release: =>
@trigger 'closed'
@@ -241,6 +251,7 @@ class chatWindow extends App.Controller
clearUnread: =>
@$('.chat-message--new').removeClass('chat-message--new')
@updateModified(false)
+ @resetUnreadMessages()
onKeydown: (event) =>
TABKEY = 9;
@@ -304,9 +315,23 @@ class chatWindow extends App.Controller
@addMessage(message, 'customer', !isFocused)
if !isFocused
+ @addUnreadMessages()
@updateModified(true)
@sounds.message.play()
+ unreadMessages: =>
+ @unreadMessagesCounter
+
+ addUnreadMessages: =>
+ if @messageCallback
+ @messageCallback(@session.session_id)
+ @unreadMessagesCounter += 1
+
+ resetUnreadMessages: =>
+ if @messageCallback
+ @messageCallback(@session.session_id)
+ @unreadMessagesCounter = 0
+
addMessage: (message, sender, isNew) =>
@maybeAddTimestamp()
diff --git a/app/models/chat.rb b/app/models/chat.rb
index 8ec1536a9..afaca3180 100644
--- a/app/models/chat.rb
+++ b/app/models/chat.rb
@@ -24,7 +24,7 @@ class Chat < ApplicationModel
def self.agent_state(user_id)
return { state: 'chat_disabled' } if !Setting.get('chat')
actice_sessions = []
- Chat::Session.where(state: 'running').order('created_at ASC').each {|session|
+ Chat::Session.where(state: 'running', user_id: user_id).order('created_at ASC').each {|session|
session_attributes = session.attributes
session_attributes['messages'] = []
Chat::Message.where(chat_session_id: session.id).each { |message|
@@ -127,6 +127,26 @@ class Chat::Session < ApplicationModel
def generate_session_id
self.session_id = Digest::MD5.hexdigest(Time.zone.now.to_s + rand(99_999_999_999_999).to_s)
end
+
+ def add_recipient(client_id, store = false)
+ if !self.preferences[:participants]
+ self.preferences[:participants] = []
+ end
+ return self.preferences[:participants] if self.preferences[:participants].include?(client_id)
+ self.preferences[:participants].push client_id
+ if store
+ self.save
+ end
+ self.preferences[:participants]
+ end
+
+ def send_to_recipients(message, ignore_client_id)
+ self.preferences[:participants].each {|local_client_id|
+ next if local_client_id == ignore_client_id
+ Sessions.send(local_client_id, message)
+ }
+ true
+ end
end
class Chat::Message < ApplicationModel
diff --git a/db/migrate/20151109000001_create_chat.rb b/db/migrate/20151109000001_create_chat.rb
index d4658ceae..e4d3e6670 100644
--- a/db/migrate/20151109000001_create_chat.rb
+++ b/db/migrate/20151109000001_create_chat.rb
@@ -1,12 +1,5 @@
class CreateChat < ActiveRecord::Migration
def up
-=begin
- ActiveRecord::Migration.drop_table :chats
- ActiveRecord::Migration.drop_table :chat_topics
- ActiveRecord::Migration.drop_table :chat_sessions
- ActiveRecord::Migration.drop_table :chat_messages
- ActiveRecord::Migration.drop_table :chat_agents
-=end
create_table :chats do |t|
t.string :name, limit: 250, null: true
t.integer :max_queue, null: false, default: 5
diff --git a/lib/sessions/event.rb b/lib/sessions/event.rb
index 732e248e6..fcb06fffb 100644
--- a/lib/sessions/event.rb
+++ b/lib/sessions/event.rb
@@ -9,8 +9,11 @@ class Sessions::Event
return { error: "No such event #{event}" }
end
+ instance = backend.new(data, session, client_id)
+ result = instance.pre_check
+ return result if result
ActiveRecord::Base.establish_connection
- result = backend.run(data, session, client_id)
+ result = instance.run
ActiveRecord::Base.remove_connection
result
end
diff --git a/lib/sessions/event/chat_agent_state.rb b/lib/sessions/event/chat_agent_state.rb
index f63900a90..a201b9c92 100644
--- a/lib/sessions/event/chat_agent_state.rb
+++ b/lib/sessions/event/chat_agent_state.rb
@@ -1,21 +1,11 @@
-class Sessions::Event::ChatAgentState
+class Sessions::Event::ChatAgentState < Sessions::Event::ChatBase
- def self.run(data, session, _client_id)
-
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_agent_state',
- data: {
- state: 'chat_disabled',
- },
- }
- end
+ def run
# only agents can do this
chat_id = 1
chat = Chat.find_by(id: chat_id)
- if !session['id']
+ if !@session['id']
return {
event: 'chat_agent_state',
data: {
@@ -25,13 +15,13 @@ class Sessions::Event::ChatAgentState
}
end
- Chat::Agent.state(session['id'], data['data']['active'])
+ Chat::Agent.state(@session['id'], @data['data']['active'])
{
event: 'chat_agent_state',
data: {
state: 'ok',
- active: data['data']['active'],
+ active: @data['data']['active'],
},
}
end
diff --git a/lib/sessions/event/chat_base.rb b/lib/sessions/event/chat_base.rb
new file mode 100644
index 000000000..6df4a7dcd
--- /dev/null
+++ b/lib/sessions/event/chat_base.rb
@@ -0,0 +1,25 @@
+class Sessions::Event::ChatBase
+
+ def initialize(data, session, client_id)
+ @data = data
+ @session = session
+ @client_id = client_id
+
+ end
+
+ def pre_check
+
+ # check if feature is enabled
+ if !Setting.get('chat')
+ return {
+ event: 'chat_error',
+ data: {
+ state: 'chat_disabled',
+ },
+ }
+ end
+
+ false
+ end
+
+end
diff --git a/lib/sessions/event/chat_session_close.rb b/lib/sessions/event/chat_session_close.rb
index 6a5a0a535..99cafd22f 100644
--- a/lib/sessions/event/chat_session_close.rb
+++ b/lib/sessions/event/chat_session_close.rb
@@ -1,18 +1,8 @@
-class Sessions::Event::ChatSessionClose
+class Sessions::Event::ChatSessionClose < Sessions::Event::ChatBase
- def self.run(data, _session, _client_id)
+ def run
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_status_close',
- data: {
- state: 'chat_disabled',
- },
- }
- end
-
- if !data['data'] || !data['data']['session_id']
+ if !@data['data'] || !@data['data']['session_id']
return {
event: 'chat_status_close',
data: {
@@ -21,7 +11,7 @@ class Sessions::Event::ChatSessionClose
}
end
- chat_session = Chat::Session.find_by(session_id: data['data']['session_id'])
+ chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
if !chat_session
return {
event: 'chat_status_close',
diff --git a/lib/sessions/event/chat_session_init.rb b/lib/sessions/event/chat_session_init.rb
index f2bd37068..298cbcd3d 100644
--- a/lib/sessions/event/chat_session_init.rb
+++ b/lib/sessions/event/chat_session_init.rb
@@ -1,16 +1,6 @@
-class Sessions::Event::ChatSessionInit
+class Sessions::Event::ChatSessionInit < Sessions::Event::ChatBase
- def self.run(data, _session, client_id)
-
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_session_init',
- data: {
- state: 'chat_disabled',
- },
- }
- end
+ def run
chat_id = 1
chat = Chat.find_by(id: chat_id)
@@ -29,17 +19,17 @@ class Sessions::Event::ChatSessionInit
name: '',
state: 'waiting',
preferences: {
- participants: [client_id],
+ participants: [@client_id],
},
)
- # send update to agents
- User.where(active: true).each {|user|
+ # send broadcast to agents
+ Chat::Agent.where(active: true).each {|item|
data = {
event: 'chat_status_agent',
- data: Chat.agent_state(user.id),
+ data: Chat.agent_state(item.updated_by_id),
}
- Sessions.send_to(user.id, data)
+ Sessions.send_to(item.updated_by_id, data)
}
# return new session
diff --git a/lib/sessions/event/chat_session_message.rb b/lib/sessions/event/chat_session_message.rb
index f410943cd..dc3071d1b 100644
--- a/lib/sessions/event/chat_session_message.rb
+++ b/lib/sessions/event/chat_session_message.rb
@@ -1,18 +1,8 @@
-class Sessions::Event::ChatSessionMessage
+class Sessions::Event::ChatSessionMessage < Sessions::Event::ChatBase
- def self.run(data, session, client_id)
+ def run
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_session_message',
- data: {
- state: 'chat_disabled',
- },
- }
- end
-
- if !data['data'] || !data['data']['session_id']
+ if !@data['data'] || !@data['data']['session_id']
return {
event: 'chat_session_message',
data: {
@@ -21,7 +11,7 @@ class Sessions::Event::ChatSessionMessage
}
end
- chat_session = Chat::Session.find_by(session_id: data['data']['session_id'])
+ chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
if !chat_session
return {
event: 'chat_session_message',
@@ -32,12 +22,12 @@ class Sessions::Event::ChatSessionMessage
end
user_id = nil
- if session
- user_id = session['id']
+ if @session
+ user_id = @session['id']
end
chat_message = Chat::Message.create(
chat_session_id: chat_session.id,
- content: data['data']['content'],
+ content: @data['data']['content'],
created_by_id: user_id,
)
message = {
@@ -49,10 +39,7 @@ class Sessions::Event::ChatSessionMessage
}
# send to participents
- chat_session.preferences[:participants].each {|local_client_id|
- next if local_client_id == client_id
- Sessions.send(local_client_id, message)
- }
+ chat_session.send_to_recipients(message, @client_id)
# send chat_session_init to agent
{
diff --git a/lib/sessions/event/chat_session_start.rb b/lib/sessions/event/chat_session_start.rb
index c7880e259..c1b75e46e 100644
--- a/lib/sessions/event/chat_session_start.rb
+++ b/lib/sessions/event/chat_session_start.rb
@@ -1,16 +1,6 @@
-class Sessions::Event::ChatSessionStart
+class Sessions::Event::ChatSessionStart < Sessions::Event::ChatBase
- def self.run(data, session, client_id)
-
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_session_start',
- data: {
- state: 'chat_disabled',
- },
- }
- end
+ def run
# find first in waiting list
chat_session = Chat::Session.where(state: 'waiting').order('created_at ASC').first
@@ -23,9 +13,9 @@ class Sessions::Event::ChatSessionStart
},
}
end
- chat_session.user_id = session['id']
+ chat_session.user_id = @session['id']
chat_session.state = 'running'
- chat_session.preferences[:participants].push client_id
+ chat_session.preferences[:participants] = chat_session.add_recipient(@client_id)
chat_session.save
# send chat_session_init to client
@@ -42,11 +32,7 @@ class Sessions::Event::ChatSessionStart
session_id: chat_session.session_id,
},
}
-
- chat_session.preferences[:participants].each {|local_client_id|
- next if local_client_id == client_id
- Sessions.send(local_client_id, data)
- }
+ chat_session.send_to_recipients(data, @client_id)
# send chat_session_init to agent
{
diff --git a/lib/sessions/event/chat_session_typing.rb b/lib/sessions/event/chat_session_typing.rb
index 660818c61..f8e72fa09 100644
--- a/lib/sessions/event/chat_session_typing.rb
+++ b/lib/sessions/event/chat_session_typing.rb
@@ -1,18 +1,8 @@
-class Sessions::Event::ChatSessionTyping
+class Sessions::Event::ChatSessionTyping < Sessions::Event::ChatBase
- def self.run(data, session, client_id)
+ def run
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_session_typing',
- data: {
- state: 'chat_disabled',
- },
- }
- end
-
- if !data['data'] || !data['data']['session_id']
+ if !@data['data'] || !@data['data']['session_id']
return {
event: 'chat_session_typing',
data: {
@@ -21,7 +11,7 @@ class Sessions::Event::ChatSessionTyping
}
end
- chat_session = Chat::Session.find_by(session_id: data['data']['session_id'])
+ chat_session = Chat::Session.find_by(session_id: @data['data']['session_id'])
if !chat_session
return {
event: 'chat_session_typing',
@@ -32,8 +22,8 @@ class Sessions::Event::ChatSessionTyping
end
user_id = nil
- if session
- user_id = session['id']
+ if @session
+ user_id = @session['id']
end
message = {
event: 'chat_session_typing',
@@ -44,10 +34,7 @@ class Sessions::Event::ChatSessionTyping
}
# send to participents
- chat_session.preferences[:participants].each {|local_client_id|
- next if local_client_id == client_id
- Sessions.send(local_client_id, message)
- }
+ chat_session.send_to_recipients(message, @client_id)
# send chat_session_init to agent
{
diff --git a/lib/sessions/event/chat_status.rb b/lib/sessions/event/chat_status.rb
deleted file mode 100644
index 40576a53c..000000000
--- a/lib/sessions/event/chat_status.rb
+++ /dev/null
@@ -1,31 +0,0 @@
-class Sessions::Event::ChatStatus
-
- def self.run(_data, _session, _client_id)
-
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_status',
- data: {
- state: 'chat_disabled',
- },
- }
- end
-
- chat_id = 1
- chat = Chat.find_by(id: chat_id)
- if !chat
- return {
- event: 'chat_status',
- data: {
- state: 'no_such_chat',
- },
- }
- end
-
- {
- event: 'chat_status',
- data: chat.state,
- }
- end
-end
diff --git a/lib/sessions/event/chat_status_agent.rb b/lib/sessions/event/chat_status_agent.rb
index 8890fddd7..884b47103 100644
--- a/lib/sessions/event/chat_status_agent.rb
+++ b/lib/sessions/event/chat_status_agent.rb
@@ -1,26 +1,22 @@
-class Sessions::Event::ChatStatusAgent
+class Sessions::Event::ChatStatusAgent < Sessions::Event::ChatBase
- def self.run(_data, session, _client_id)
+ def run
# check if user has permissions
- # check if feature is enabled
- if !Setting.get('chat')
- return {
- event: 'chat_status_agent',
- data: {
- state: 'chat_disabled',
- },
- }
- end
# renew timestamps
- state = Chat::Agent.state(session['id'])
- Chat::Agent.state(session['id'], state)
+ 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)
+ }
{
event: 'chat_status_agent',
- data: Chat.agent_state(session['id']),
+ data: Chat.agent_state(@session['id']),
}
end
diff --git a/lib/sessions/event/chat_status_customer.rb b/lib/sessions/event/chat_status_customer.rb
new file mode 100644
index 000000000..8dbfb54d1
--- /dev/null
+++ b/lib/sessions/event/chat_status_customer.rb
@@ -0,0 +1,21 @@
+class Sessions::Event::ChatStatusCustomer < Sessions::Event::ChatBase
+
+ def run
+
+ chat_id = 1
+ chat = Chat.find_by(id: chat_id)
+ if !chat
+ return {
+ event: 'chat_status_customer',
+ data: {
+ state: 'no_such_chat',
+ },
+ }
+ end
+
+ {
+ event: 'chat_status_customer',
+ data: chat.state,
+ }
+ end
+end
diff --git a/public/assets/chat/chat.coffee b/public/assets/chat/chat.coffee
index e61cd36fd..0beb91613 100644
--- a/public/assets/chat/chat.coffee
+++ b/public/assets/chat/chat.coffee
@@ -64,7 +64,6 @@ do($ = window.jQuery, window) ->
console.log('ws connected')
@send 'chat_status_customer'
-
@ws.onmessage = @onWebSocketMessage
@ws.onclose = (e) =>
diff --git a/public/assets/chat/chat.js b/public/assets/chat/chat.js
index 443cee98b..282cad7e7 100644
--- a/public/assets/chat/chat.js
+++ b/public/assets/chat/chat.js
@@ -162,7 +162,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.ws.onopen = (function(_this) {
return function() {
console.log('ws connected');
- return _this.send('chat_status');
+ return _this.send('chat_status_customer');
};
})(this);
this.ws.onmessage = this.onWebSocketMessage;
@@ -196,7 +196,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.onWebSocketMessage = function(e) {
- var delay, i, len, pipe, pipes;
+ var i, len, pipe, pipes;
pipes = JSON.parse(e.data);
console.log('debug', 'ws:onmessage', pipes);
for (i = 0, len = pipes.length; i < len; i++) {
@@ -213,15 +213,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return;
}
this.onAgentTypingStart();
- if (this.stopTypingId) {
- clearTimeout(this.stopTypingId);
- }
- delay = (function(_this) {
- return function() {
- return _this.onAgentTypingEnd();
- };
- })(this);
- this.stopTypingId = setTimeout(delay, 3000);
break;
case 'chat_session_start':
switch (pipe.data.state) {
@@ -239,7 +230,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.session_id = pipe.data.session_id;
}
break;
- case 'chat_status':
+ case 'chat_status_customer':
switch (pipe.data.state) {
case 'online':
this.onReady();
@@ -393,6 +384,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.onAgentTypingStart = function() {
+ if (this.stopTypingId) {
+ clearTimeout(this.stopTypingId);
+ }
+ this.stopTypingId = setTimeout(this.onAgentTypingEnd, 3000);
if (this.el.find('.zammad-chat-message--typing').size()) {
return;
}
diff --git a/public/assets/chat/chat.min.js b/public/assets/chat/chat.min.js
index 38c5ed0ac..c67880a11 100644
--- a/public/assets/chat/chat.min.js
+++ b/public/assets/chat/chat.min.js
@@ -1 +1 @@
-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)}};!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.onOpenAnimationEnd=bind(this.onOpenAnimationEnd,this),this.toggle=bind(this.toggle,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(".zammad-chat-header").click(this.toggle),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")}}(this),this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(t){return console.log("debug","close websocket connection")}}(this),void(this.ws.onerror=function(t){return function(t){return console.log("debug","ws:onerror",t)}}(this))):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,i;for(i=JSON.parse(t.data),console.log("debug","ws:onmessage",i),n=0,a=i.length;a>n;n++)switch(s=i[n],s.event){case"chat_session_message":if(s.data.self_written)return;this.receiveMessage(s.data);break;case"chat_session_typing":if(s.data.self_written)return;this.onAgentTypingStart(),this.stopTypingId&&clearTimeout(this.stopTypingId),e=function(t){return function(){return t.onAgentTypingEnd()}}(this),this.stopTypingId=setTimeout(e,3e3);break;case"chat_session_start":switch(s.data.state){case"ok":this.onConnectionEstablished(s.data.agent)}break;case"chat_session_init":switch(s.data.state){case"ok":this.onConnectionEstablished(s.data.agent);break;case"queue":this.onQueue(s.data.position),this.session_id=s.data.session_id}break;case"chat_status":switch(s.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: ",s.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.toggle=function(){return this.isOpen?this.close():this.open()},n.prototype.open=function(){return this.showLoader(),this.el.addClass("zammad-chat-is-open").animate({bottom:0},500,this.onOpenAnimationEnd)},n.prototype.onOpenAnimationEnd=function(){return this.isOpen=!0,this.connect()},n.prototype.close=function(){var t;return t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-t},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.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),jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),n=function(t){return jQuery(t).after('