2015-10-15 09:14:19 +00:00
do ($ = window.jQuery, window) ->
2015-11-13 12:59:15 +00:00
scripts = document . getElementsByTagName ( ' script ' )
myScript = scripts [ scripts . length - 1 ]
2015-11-25 09:35:57 +00:00
scriptHost = myScript . src . match ( ' .*://([^:/]*).* ' ) [ 1 ]
2015-11-13 12:59:15 +00:00
2015-10-15 09:14:19 +00:00
# Define the plugin class
2015-12-05 19:41:14 +00:00
class Base
defaults:
debug: false
constructor: (options) ->
@options = $ . extend { } , @ defaults , options
@log = new Log ( debug: @ options . debug , logPrefix: @ options . logPrefix || @ logPrefix )
class Log
defaults:
debug: false
constructor: (options) ->
@options = $ . extend { } , @ defaults , options
debug: (items...) =>
2015-12-06 15:40:47 +00:00
return if ! @ options . debug
2015-12-05 19:41:14 +00:00
@ log ( ' debug ' , items )
notice: (items...) =>
@ log ( ' notice ' , items )
error: (items...) =>
@ log ( ' error ' , items )
log: (level, items) =>
items . unshift ( ' || ' )
items . unshift ( level )
items . unshift ( @ options . logPrefix )
console . log . apply console , items
return if ! @ options . debug
logString = ' '
for item in items
logString += ' '
if typeof item is ' object '
logString += JSON . stringify ( item )
else if item && item . toString
logString += item . toString ( )
else
logString += item
$ ( ' .js-chatLogDisplay ' ) . prepend ( ' <div> ' + logString + ' </div> ' )
class Timeout extends Base
timeoutStartedAt: null
logPrefix: ' timeout '
defaults:
debug: false
timeout: 4
timeoutIntervallCheck: 0.5
constructor: (options) ->
super ( options )
start: =>
@ stop ( )
timeoutStartedAt = new Date
check = =>
timeLeft = new Date - new Date ( timeoutStartedAt . getTime ( ) + @ options . timeout * 1000 * 60 )
@ log . debug " Timeout check for #{ @ options . timeout } minutes (left #{ timeLeft / 1000 } sec.) " #, new Date
return if timeLeft < 0
@ stop ( )
@ options . callback ( )
@ log . debug " Start timeout in #{ @ options . timeout } minutes " #, new Date
@intervallId = setInterval ( check , @ options . timeoutIntervallCheck * 1000 * 60 )
stop: =>
return if ! @ intervallId
2015-12-08 11:32:19 +00:00
@ log . debug " Stop timeout of #{ @ options . timeout } minutes " #, new Date
2015-12-05 19:41:14 +00:00
clearInterval ( @ intervallId )
class Io extends Base
logPrefix: ' io '
constructor: (options) ->
super ( options )
set: (params) =>
for key , value of params
@ options [ key ] = value
connect: =>
@ log . debug " Connecting to #{ @ options . host } "
@ws = new window . WebSocket ( " #{ @ options . host } " )
@ws.onopen = (e) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' onOpen ' , e
2015-12-05 19:41:14 +00:00
@ options . onOpen ( e )
2015-12-09 14:11:29 +00:00
@ ping ( )
2015-12-05 19:41:14 +00:00
@ws.onmessage = (e) =>
pipes = JSON . parse ( e . data )
2015-12-08 11:32:19 +00:00
@ log . debug ' onMessage ' , e . data
2015-12-09 14:11:29 +00:00
for pipe in pipes
if pipe . event is ' pong '
@ ping ( )
2015-12-05 19:41:14 +00:00
if @ options . onMessage
@ options . onMessage ( pipes )
@ws.onclose = (e) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' close websocket connection ' , e
2015-12-09 14:11:29 +00:00
if @ pingDelayId
clearTimeout ( @ pingDelayId )
2015-12-08 11:32:19 +00:00
if @ manualClose
@ log . debug ' manual close, onClose callback '
@manualClose = false
if @ options . onClose
@ options . onClose ( e )
else
@ log . debug ' error close, onError callback '
if @ options . onError
@ options . onError ( ' Connection lost... ' )
2015-12-05 19:41:14 +00:00
@ws.onerror = (e) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' onError ' , e
2015-12-05 19:41:14 +00:00
if @ options . onError
@ options . onError ( e )
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
close: =>
2015-12-08 11:32:19 +00:00
@ log . debug ' close websocket manually '
@manualClose = true
2015-12-05 19:41:14 +00:00
@ ws . close ( )
reconnect: =>
2015-12-08 11:32:19 +00:00
@ log . debug ' reconnect '
@ close ( )
2015-12-05 19:41:14 +00:00
@ connect ( )
send: (event, data = {}) =>
@ log . debug ' send ' , event , data
msg = JSON . stringify
event: event
data: data
@ ws . send msg
2015-12-09 14:11:29 +00:00
ping: =>
localPing = =>
@ send ( ' ping ' )
@pingDelayId = setTimeout ( localPing , 29000 )
2015-12-05 19:41:14 +00:00
class ZammadChat extends Base
2015-10-15 09:14:19 +00:00
defaults:
2015-11-25 11:44:26 +00:00
chatId: undefined
2015-11-13 14:15:44 +00:00
show: true
2015-10-15 09:14:19 +00:00
target: $ ( ' body ' )
2015-11-13 12:59:15 +00:00
host: ' '
2015-11-13 14:15:44 +00:00
debug: false
2015-11-24 12:04:31 +00:00
flat: false
2015-11-25 23:40:52 +00:00
lang: undefined
cssAutoload: true
cssUrl: undefined
2015-11-16 10:46:42 +00:00
fontSize: undefined
2015-11-20 14:43:23 +00:00
buttonClass: ' open-zammad-chat '
inactiveClass: ' is-inactive '
2015-11-17 00:43:36 +00:00
title: ' <strong>Chat</strong> with us! '
2015-12-08 11:32:19 +00:00
idleTimeout: 6
2015-12-05 19:41:14 +00:00
idleTimeoutIntervallCheck: 0.5
inactiveTimeout: 8
inactiveTimeoutIntervallCheck: 0.5
waitingListTimeout: 4
waitingListTimeoutIntervallCheck: 0.5
logPrefix: ' chat '
2015-11-02 15:48:16 +00:00
_messageCount: 0
2015-12-08 11:32:19 +00:00
isOpen: false
2015-10-15 09:14:19 +00:00
blinkOnlineInterval: null
stopBlinOnlineStateTimeout: null
2016-01-05 12:46:43 +00:00
showTimeEveryXMinutes: 2
2015-10-15 09:14:19 +00:00
lastTimestamp: null
lastAddedType: null
inputTimeout: null
isTyping: false
2015-11-13 14:58:44 +00:00
state: ' offline '
2015-11-13 09:45:40 +00:00
initialQueueDelay: 10000
2015-11-25 16:36:06 +00:00
translations:
de:
2016-01-20 18:09:53 +00:00
' <strong>Chat</strong> with us! ' : ' <strong>Chatte</strong> mit uns! '
2015-11-25 16:36:06 +00:00
' Online ' : ' Online '
' Online ' : ' Online '
' Offline ' : ' Offline '
' Connecting ' : ' Verbinden '
' Connection re-established ' : ' Verbindung wiederhergestellt '
' Today ' : ' Heute '
' Send ' : ' Senden '
' Compose your message... ' : ' Ihre Nachricht... '
2016-01-05 12:46:43 +00:00
' All colleagues are busy. ' : ' Alle Kollegen sind belegt. '
2015-11-25 16:36:06 +00:00
' You are on waiting list position <strong>%s</strong>. ' : ' Sie sind in der Warteliste an der Position <strong>%s</strong>. '
' Start new conversation ' : ' Neue Konversation starten '
2015-11-25 23:40:52 +00:00
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit <strong>%s</strong> geschlossen. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation geschlossen. '
2015-11-12 10:44:37 +00:00
sessionId: undefined
2015-11-11 20:44:54 +00:00
T: (string, items...) =>
2015-11-25 16:36:06 +00:00
if @ options . lang && @ options . lang isnt ' en '
if ! @ translations [ @ options . lang ]
2015-12-05 19:41:14 +00:00
@ log . notice " Translation ' #{ @ options . lang } ' needed! "
2015-11-25 16:36:06 +00:00
else
translations = @ translations [ @ options . lang ]
if ! translations [ string ]
2015-12-05 19:41:14 +00:00
@ log . notice " Translation needed for ' #{ string } ' "
2015-11-25 16:36:06 +00:00
string = translations [ string ] || string
2015-11-11 20:44:54 +00:00
if items
for item in items
2015-11-25 16:36:06 +00:00
string = string . replace ( /%s/ , item )
string
2015-11-11 20:44:54 +00:00
2015-11-02 15:48:16 +00:00
view: (name) =>
return (options) =>
if ! options
options = { }
options.T = @ T
2015-11-13 14:58:44 +00:00
options.background = @ options . background
options.flat = @ options . flat
2015-11-16 10:46:42 +00:00
options.fontSize = @ options . fontSize
2015-11-02 15:48:16 +00:00
return window . zammadChatTemplates [ name ] ( options )
2015-10-15 09:14:19 +00:00
2015-11-13 12:59:15 +00:00
constructor: (options) ->
2015-11-25 11:55:02 +00:00
@options = $ . extend { } , @ defaults , options
2015-12-05 19:41:14 +00:00
super ( @ options )
2015-11-13 14:15:44 +00:00
2016-02-08 15:06:56 +00:00
# fullscreen
@isFullscreen = ( window . matchMedia and window . matchMedia ( ' (max-width: 768px) ' ) . matches )
@scrollRoot = $ ( @ getScrollRoot ( ) )
2015-11-13 14:15:44 +00:00
# check prerequisites
2015-11-25 23:40:52 +00:00
if ! $
@state = ' unsupported '
2015-12-05 19:41:14 +00:00
@ log . notice ' Chat: no jquery found! '
2015-11-25 23:40:52 +00:00
return
2015-11-13 14:15:44 +00:00
if ! window . WebSocket or ! sessionStorage
2015-11-16 12:08:32 +00:00
@state = ' unsupported '
2015-12-05 19:41:14 +00:00
@ log . notice ' Chat: Browser not supported! '
2015-11-13 14:15:44 +00:00
return
2015-11-25 11:55:02 +00:00
if ! @ options . chatId
2015-11-25 09:35:57 +00:00
@state = ' unsupported '
2015-12-05 19:41:14 +00:00
@ log . error ' Chat: need chatId as option! '
2015-11-25 09:35:57 +00:00
return
2015-11-25 16:36:06 +00:00
# detect language
if ! @ options . lang
@options.lang = $ ( ' html ' ) . attr ( ' lang ' )
if @ options . lang
@options.lang = @ options . lang . replace ( /-.+?$/ , ' ' ) # replace "-xx" of xx-xx
2015-12-05 19:41:14 +00:00
@ log . debug " lang: #{ @ options . lang } "
2015-11-25 16:36:06 +00:00
2015-12-07 10:41:12 +00:00
# detect host
@ detectHost ( ) if ! @ options . host
2015-12-05 19:41:14 +00:00
@ loadCss ( )
@io = new Io ( @ options )
@ io . set (
onOpen: @ render
2015-12-08 11:32:19 +00:00
onClose: @ onWebSocketClose
2015-12-05 19:41:14 +00:00
onMessage: @ onWebSocketMessage
2015-12-08 11:32:19 +00:00
onError: @ onError
2015-12-05 19:41:14 +00:00
)
@ io . connect ( )
2016-02-08 15:06:56 +00:00
getScrollRoot: ->
return document . scrollingElement if ' scrollingElement ' of document
html = document . documentElement
start = html . scrollTop
html.scrollTop = start + 1
end = html . scrollTop
html.scrollTop = start
return if end > start then html else document . body
2015-12-05 19:41:14 +00:00
render: =>
2015-12-08 11:32:19 +00:00
if ! @ el || ! $ ( ' .zammad-chat ' ) . get ( 0 )
2016-02-08 15:06:56 +00:00
@ renderBase ( )
2015-11-12 13:56:47 +00:00
2015-11-20 14:43:23 +00:00
# disable open button
$ ( " . #{ @ options . buttonClass } " ) . addClass @ inactiveClass
2015-12-05 19:41:14 +00:00
@ setAgentOnlineState ' online '
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
@ log . debug ' widget rendered '
2015-10-15 15:07:18 +00:00
2015-12-05 19:41:14 +00:00
@ startTimeoutObservers ( )
@ idleTimeout . start ( )
# get current chat status
@sessionId = sessionStorage . getItem ( ' sessionId ' )
@ send ' chat_status_customer ' ,
session_id: @ sessionId
2015-11-25 16:36:06 +00:00
2016-02-08 15:06:56 +00:00
renderBase: ->
@el = $ ( @ view ( ' chat ' ) (
title: @ options . title
) )
@ options . target . append @ el
@input = @ el . find ( ' .zammad-chat-input ' )
# start bindings
@ el . find ( ' .js-chat-open ' ) . click @ open
@ el . find ( ' .js-chat-toggle ' ) . click @ toggle
@ el . find ( ' .zammad-chat-controls ' ) . on ' submit ' , @ onSubmit
@ input . on
keydown: @ checkForEnter
input: @ onInput
$ ( window ) . on ( ' beforeunload ' , =>
@ onLeaveTemporary ( )
)
$ ( window ) . bind ( ' hashchange ' , =>
return if @ isOpen
@ idleTimeout . start ( )
)
if @ isFullscreen
@ input . on
focus: @ onFocus
focusout: @ onFocusOut
2015-10-15 09:14:19 +00:00
checkForEnter: (event) =>
if not event . shiftKey and event . keyCode is 13
event . preventDefault ( )
@ sendMessage ( )
2015-11-25 09:35:57 +00:00
send: (event, data = {}) =>
2015-11-25 11:44:26 +00:00
data.chat_id = @ options . chatId
2015-12-05 19:41:14 +00:00
@ io . send ( event , data )
2015-11-10 14:01:04 +00:00
2015-12-05 19:41:14 +00:00
onWebSocketMessage: (pipes) =>
2015-11-10 14:01:04 +00:00
for pipe in pipes
2015-12-05 19:41:14 +00:00
@ log . debug ' ws:onmessage ' , pipe
2015-11-10 14:01:04 +00:00
switch pipe . event
2015-11-25 09:35:57 +00:00
when ' chat_error '
2015-12-05 19:41:14 +00:00
@ log . notice pipe . data
2015-11-25 23:40:52 +00:00
if pipe . data && pipe . data . state is ' chat_disabled '
2015-12-08 11:32:19 +00:00
@ destroy ( remove: true )
2015-11-10 14:01:04 +00:00
when ' chat_session_message '
return if pipe . data . self_written
@ receiveMessage pipe . data
when ' chat_session_typing '
return if pipe . data . self_written
@ onAgentTypingStart ( )
when ' chat_session_start '
2015-11-12 13:56:47 +00:00
@ onConnectionEstablished pipe . data
2015-11-12 10:44:37 +00:00
when ' chat_session_queue '
2015-11-12 15:16:01 +00:00
@ onQueueScreen pipe . data
2015-11-11 20:44:54 +00:00
when ' chat_session_closed '
@ onSessionClosed pipe . data
when ' chat_session_left '
@ onSessionClosed pipe . data
2015-11-11 10:24:19 +00:00
when ' chat_status_customer '
2015-11-10 14:01:04 +00:00
switch pipe . data . state
when ' online '
2015-11-12 23:00:13 +00:00
@sessionId = undefined
2015-11-10 14:01:04 +00:00
@ onReady ( )
when ' offline '
2015-11-13 14:58:44 +00:00
@ onError ' Zammad Chat: No agent online '
2015-11-10 14:01:04 +00:00
when ' chat_disabled '
2015-11-13 14:58:44 +00:00
@ onError ' Zammad Chat: Chat is disabled '
2015-11-10 14:01:04 +00:00
when ' no_seats_available '
2015-11-25 09:35:57 +00:00
@ onError " Zammad Chat: Too many clients in queue. Clients in queue: #{ pipe . data . queue } "
2015-11-12 09:39:14 +00:00
when ' reconnect '
2015-12-08 11:32:19 +00:00
@ onReopenSession pipe . data
2015-11-02 15:48:16 +00:00
2015-12-08 11:32:19 +00:00
onReady: ->
2015-12-05 19:41:14 +00:00
@ log . debug ' widget ready for use '
2015-11-20 14:43:23 +00:00
$ ( " . #{ @ options . buttonClass } " ) . click ( @ open ) . removeClass ( @ inactiveClass )
2015-11-16 12:08:32 +00:00
2015-11-11 09:48:54 +00:00
if @ options . show
@ show ( )
2015-11-12 13:56:47 +00:00
2015-11-13 14:58:44 +00:00
onError: (message) =>
2015-12-05 19:41:14 +00:00
@ log . debug message
2015-12-08 11:32:19 +00:00
@ addStatus ( message )
2015-11-20 14:43:23 +00:00
$ ( " . #{ @ options . buttonClass } " ) . hide ( )
2015-12-08 11:32:19 +00:00
if @ isOpen
@ disableInput ( )
@ destroy ( remove: false )
else
@ destroy ( remove: true )
2015-11-13 14:58:44 +00:00
2015-12-08 11:32:19 +00:00
onReopenSession: (data) =>
@ log . debug ' old messages ' , data . session
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-11-25 23:40:52 +00:00
2015-11-12 14:05:43 +00:00
unfinishedMessage = sessionStorage . getItem ' unfinished_message '
2015-11-12 15:16:01 +00:00
2015-11-13 14:15:44 +00:00
# rerender chat history
if data . agent
@ onConnectionEstablished ( data )
2015-11-12 15:16:01 +00:00
2015-11-13 14:15:44 +00:00
for message in data . session
@ renderMessage
message: message . content
id: message . id
from: if message . created_by_id then ' agent ' else ' customer '
2015-11-12 13:56:47 +00:00
2015-11-13 14:15:44 +00:00
if unfinishedMessage
@ input . val unfinishedMessage
# show wait list
if data . position
@ onQueue data
2015-11-12 13:56:47 +00:00
@ show ( )
2015-11-12 14:30:44 +00:00
@ open ( )
2015-11-12 16:15:58 +00:00
@ scrollToBottom ( )
2015-11-02 15:48:16 +00:00
2015-11-12 14:05:43 +00:00
if unfinishedMessage
@ input . focus ( )
2015-10-15 09:14:19 +00:00
onInput: =>
# remove unread-state from messages
@ el . find ( ' .zammad-chat-message--unread ' )
. removeClass ' zammad-chat-message--unread '
2015-11-12 13:56:47 +00:00
sessionStorage . setItem ' unfinished_message ' , @ input . val ( )
2015-11-25 14:08:55 +00:00
@ onTyping ( )
2015-10-15 09:14:19 +00:00
2016-02-08 15:06:56 +00:00
onFocus: =>
$ ( window ) . scrollTop ( 10 )
keyboardShown = $ ( window ) . scrollTop ( ) > 0
$ ( window ) . scrollTop ( 0 )
if keyboardShown
@ log . notice ' virtual keyboard shown '
# on keyboard shown
# can't measure visible area height :(
onFocusOut: ->
# on keyboard hidden
2015-11-25 14:08:55 +00:00
onTyping: ->
2015-10-15 09:14:19 +00:00
2015-11-25 14:08:55 +00:00
# send typing start event only every 1.5 seconds
return if @ isTyping && @ isTyping > new Date ( new Date ( ) . getTime ( ) - 1500 )
@isTyping = new Date ( )
@ send ' chat_session_typing ' ,
session_id: @ sessionId
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-10-15 09:14:19 +00:00
onSubmit: (event) =>
event . preventDefault ( )
@ sendMessage ( )
sendMessage: ->
2015-11-12 13:56:47 +00:00
message = @ input . val ( )
2015-11-11 20:44:54 +00:00
return if ! message
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-11-25 23:40:52 +00:00
2015-11-12 13:56:47 +00:00
sessionStorage . removeItem ' unfinished_message '
2015-11-02 15:48:16 +00:00
messageElement = @ view ( ' message ' )
message: message
from: ' customer '
id: @ _messageCount ++
2015-11-26 09:52:10 +00:00
unreadClass: ' '
2015-10-15 09:14:19 +00:00
2015-11-02 15:48:16 +00:00
@ maybeAddTimestamp ( )
2015-10-15 09:14:19 +00:00
2015-11-02 15:48:16 +00:00
# add message before message typing loader
if @ el . find ( ' .zammad-chat-message--typing ' ) . size ( )
@lastAddedType = ' typing-placeholder '
2015-11-11 10:24:19 +00:00
@ el . find ( ' .zammad-chat-message--typing ' ) . before messageElement
2015-11-02 15:48:16 +00:00
else
@lastAddedType = ' message--customer '
@ el . find ( ' .zammad-chat-body ' ) . append messageElement
2015-10-15 09:14:19 +00:00
2015-11-12 14:05:43 +00:00
@ input . val ( ' ' )
2015-11-02 15:48:16 +00:00
@ scrollToBottom ( )
2015-10-15 09:14:19 +00:00
2015-11-02 15:48:16 +00:00
# send message event
2015-11-10 14:01:04 +00:00
@ send ' chat_session_message ' ,
content: message
2015-11-02 15:48:16 +00:00
id: @ _messageCount
2015-11-12 10:44:37 +00:00
session_id: @ sessionId
2015-11-02 15:48:16 +00:00
receiveMessage: (data) =>
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-11-25 23:40:52 +00:00
2015-10-15 09:14:19 +00:00
# hide writing indicator
@ onAgentTypingEnd ( )
@ maybeAddTimestamp ( )
2015-11-12 13:56:47 +00:00
@ renderMessage
2015-11-10 14:01:04 +00:00
message: data . message . content
2015-11-02 15:48:16 +00:00
id: data . id
2015-10-15 09:26:56 +00:00
from: ' agent '
2015-11-12 13:56:47 +00:00
renderMessage: (data) =>
@lastAddedType = " message-- #{ data . from } "
2015-11-26 09:52:10 +00:00
data.unreadClass = if document . hidden then ' zammad-chat-message--unread ' else ' '
2015-11-12 13:56:47 +00:00
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' message ' ) ( data )
2015-10-15 09:14:19 +00:00
@ scrollToBottom ( )
2015-11-12 14:30:44 +00:00
open: =>
2015-11-12 16:31:20 +00:00
if @ isOpen
2015-12-08 11:32:19 +00:00
@ log . debug ' widget already open, block '
return
@isOpen = true
@ log . debug ' open widget '
2015-10-15 09:14:19 +00:00
2015-11-12 14:30:44 +00:00
if ! @ sessionId
2015-11-12 13:56:47 +00:00
@ showLoader ( )
2015-11-02 15:48:16 +00:00
2015-11-25 13:47:02 +00:00
@ el . addClass ( ' zammad-chat-is-open ' )
2015-11-12 13:56:47 +00:00
2015-11-12 14:30:44 +00:00
if ! @ sessionId
2015-11-12 13:56:47 +00:00
@ el . animate { bottom: 0 } , 500 , @ onOpenAnimationEnd
2016-02-08 20:49:00 +00:00
sendDelayedInit = =>
@ send ( ' chat_session_init ' )
@initDelayId = setTimeout ( sendDelayedInit , 1000 )
2015-11-12 13:56:47 +00:00
else
@ el . css ' bottom ' , 0
@ onOpenAnimationEnd ( )
2015-10-15 09:14:19 +00:00
2015-11-25 23:40:52 +00:00
onOpenAnimationEnd: =>
2015-12-05 19:41:14 +00:00
@ idleTimeout . stop ( )
2015-11-12 23:00:13 +00:00
2016-02-08 15:06:56 +00:00
if @ isFullscreen
@ disableScrollOnRoot ( )
2015-12-08 11:32:19 +00:00
sessionClose: =>
2015-11-25 23:40:52 +00:00
# send close
@ send ' chat_session_close ' ,
session_id: @ sessionId
# stop timer
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . stop ( )
@ waitingListTimeout . stop ( )
2015-11-25 23:40:52 +00:00
# delete input store
sessionStorage . removeItem ' unfinished_message '
2015-11-12 23:00:13 +00:00
# stop delay of initial queue position
if @ onInitialQueueDelayId
clearTimeout ( @ onInitialQueueDelayId )
2015-11-12 13:56:47 +00:00
2015-11-25 23:40:52 +00:00
@ setSessionId undefined
2015-11-12 13:56:47 +00:00
2016-02-08 15:06:56 +00:00
toggle: (event) =>
if @ isOpen
@ close ( event )
else
@ open ( event )
2015-12-08 11:32:19 +00:00
close: (event) =>
if ! @ isOpen
@ log . debug ' can \' t close widget, it \' s not open '
return
2016-02-08 20:49:00 +00:00
if @ initDelayId
clearTimeout ( @ initDelayId )
2015-12-08 11:32:19 +00:00
if ! @ sessionId
@ log . debug ' can \' t close widget without sessionId '
return
@ log . debug ' close widget '
event . stopPropagation ( ) if event
@ sessionClose ( )
2016-02-08 15:06:56 +00:00
if @ isFullscreen
@ enableScrollOnRoot ( )
2015-12-08 11:32:19 +00:00
# close window
2015-11-25 13:47:02 +00:00
@ el . removeClass ( ' zammad-chat-is-open ' )
2015-10-15 09:14:19 +00:00
remainerHeight = @ el . height ( ) - @ el . find ( ' .zammad-chat-header ' ) . outerHeight ( )
2015-10-15 13:28:30 +00:00
@ el . animate { bottom: - remainerHeight } , 500 , @ onCloseAnimationEnd
2015-10-15 09:14:19 +00:00
onCloseAnimationEnd: =>
2015-11-24 14:20:38 +00:00
@ el . removeClass ( ' zammad-chat-is-visible ' )
2015-12-05 19:41:14 +00:00
@ showLoader ( )
@ el . find ( ' .zammad-chat-welcome ' ) . removeClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent ' ) . addClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent-status ' ) . addClass ( ' zammad-chat-is-hidden ' )
2015-10-15 09:14:19 +00:00
@isOpen = false
2015-12-05 19:41:14 +00:00
@ io . reconnect ( )
2015-11-12 16:15:58 +00:00
2015-12-08 11:32:19 +00:00
onWebSocketClose: =>
return if @ isOpen
2015-12-05 19:41:14 +00:00
if @ el
@ el . removeClass ( ' zammad-chat-is-shown ' )
2015-10-15 09:14:19 +00:00
show: ->
2015-12-08 11:32:19 +00:00
return if @ state is ' offline '
2015-11-16 12:08:32 +00:00
2015-11-25 13:47:02 +00:00
@ el . addClass ( ' zammad-chat-is-shown ' )
2015-10-15 09:14:19 +00:00
2015-11-16 13:54:11 +00:00
if ! @ inputInitialized
@inputInitialized = true
@ input . autoGrow
extraLine: false
2015-11-12 13:56:47 +00:00
2015-11-13 10:06:47 +00:00
remainerHeight = @ el . height ( ) - @ el . find ( ' .zammad-chat-header ' ) . outerHeight ( )
@ el . css ' bottom ' , - remainerHeight
2015-11-11 20:44:54 +00:00
disableInput: ->
2015-11-12 14:05:43 +00:00
@ input . prop ( ' disabled ' , true )
2015-11-11 20:44:54 +00:00
@ el . find ( ' .zammad-chat-send ' ) . prop ( ' disabled ' , true )
enableInput: ->
2015-11-12 14:05:43 +00:00
@ input . prop ( ' disabled ' , false )
2015-11-11 20:44:54 +00:00
@ el . find ( ' .zammad-chat-send ' ) . prop ( ' disabled ' , false )
2016-02-08 15:06:56 +00:00
hideModal: ->
@ el . find ( ' .zammad-chat-modal ' ) . html ' '
2015-11-12 13:19:41 +00:00
onQueueScreen: (data) =>
2015-11-12 23:00:13 +00:00
@ setSessionId data . session_id
2015-11-12 13:19:41 +00:00
# delay initial queue position, show connecting first
show = =>
2015-11-12 14:21:04 +00:00
@ onQueue data
2015-12-05 19:41:14 +00:00
@ waitingListTimeout . start ( )
2015-11-12 13:19:41 +00:00
if @ initialQueueDelay && ! @ onInitialQueueDelayId
@onInitialQueueDelayId = setTimeout ( show , @ initialQueueDelay )
return
# stop delay of initial queue position
if @ onInitialQueueDelayId
clearTimeout ( @ onInitialQueueDelayId )
# show queue position
show ( )
2015-11-12 14:20:07 +00:00
onQueue: (data) =>
2015-12-05 19:41:14 +00:00
@ log . notice ' onQueue ' , data . position
2015-11-02 15:48:16 +00:00
@inQueue = true
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' waiting ' )
2015-11-12 13:56:47 +00:00
position: data . position
2015-11-02 15:48:16 +00:00
2015-10-15 09:14:19 +00:00
onAgentTypingStart: =>
2015-11-11 10:24:19 +00:00
if @ stopTypingId
clearTimeout ( @ stopTypingId )
@stopTypingId = setTimeout ( @ onAgentTypingEnd , 3000 )
2015-11-02 15:48:16 +00:00
# never display two typing indicators
2015-10-15 09:14:19 +00:00
return if @ el . find ( ' .zammad-chat-message--typing ' ) . size ( )
@ maybeAddTimestamp ( )
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' typingIndicator ' ) ( )
@ scrollToBottom ( )
onAgentTypingEnd: =>
@ el . find ( ' .zammad-chat-message--typing ' ) . remove ( )
2015-12-05 19:41:14 +00:00
onLeaveTemporary: =>
return if ! @ sessionId
@ send ' chat_session_leave_temporary ' ,
session_id: @ sessionId
2015-10-15 09:14:19 +00:00
maybeAddTimestamp: ->
timestamp = Date . now ( )
2015-10-15 09:26:56 +00:00
if ! @ lastTimestamp or ( timestamp - @ lastTimestamp ) > @ showTimeEveryXMinutes * 60000
2015-10-15 09:14:19 +00:00
label = @ T ( ' Today ' )
time = new Date ( ) . toTimeString ( ) . substr 0 , 5
if @ lastAddedType is ' timestamp '
# update last time
@ updateLastTimestamp label , time
@lastTimestamp = timestamp
else
# add new timestamp
2015-11-12 15:19:47 +00:00
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' timestamp ' )
label: label
time: time
2015-10-15 09:14:19 +00:00
@lastTimestamp = timestamp
@lastAddedType = ' timestamp '
2015-11-12 15:19:47 +00:00
@ scrollToBottom ( )
2015-10-15 09:14:19 +00:00
updateLastTimestamp: (label, time) ->
2015-12-08 11:32:19 +00:00
return if ! @ el
2015-10-15 09:14:19 +00:00
@ el . find ( ' .zammad-chat-body ' )
2015-11-12 15:19:47 +00:00
. find ( ' .zammad-chat-timestamp ' )
2015-10-15 09:14:19 +00:00
. last ( )
2015-11-12 15:19:47 +00:00
. replaceWith @ view ( ' timestamp ' )
2015-10-15 09:14:19 +00:00
label: label
time: time
2015-11-12 15:19:47 +00:00
addStatus: (status) ->
2015-12-08 11:32:19 +00:00
return if ! @ el
2015-11-12 16:31:20 +00:00
@ maybeAddTimestamp ( )
2015-10-15 09:26:56 +00:00
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' status ' )
2015-11-12 15:19:47 +00:00
status: status
@ scrollToBottom ( )
2015-10-15 09:14:19 +00:00
scrollToBottom: ->
@ el . find ( ' .zammad-chat-body ' ) . scrollTop ( $ ( ' .zammad-chat-body ' ) . prop ( ' scrollHeight ' ) )
2015-12-05 19:41:14 +00:00
destroy: (params = {}) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' destroy widget ' , params
@ setAgentOnlineState ' offline '
if params . remove && @ el
@ el . remove ( )
2015-12-07 10:41:12 +00:00
# stop all timer
2015-12-08 11:32:19 +00:00
if @ waitingListTimeout
@ waitingListTimeout . stop ( )
if @ inactiveTimeout
@ inactiveTimeout . stop ( )
if @ idleTimeout
@ idleTimeout . stop ( )
2015-12-07 10:41:12 +00:00
# stop ws connection
2015-12-05 19:41:14 +00:00
@ io . close ( )
2015-10-15 09:14:19 +00:00
reconnect: =>
# set status to connecting
2015-12-05 19:41:14 +00:00
@ log . notice ' reconnecting '
2015-11-11 20:44:54 +00:00
@ disableInput ( )
2015-10-15 09:14:19 +00:00
@lastAddedType = ' status '
2015-11-13 14:58:44 +00:00
@ setAgentOnlineState ' connecting '
2015-10-15 09:14:19 +00:00
@ addStatus @ T ( ' Connection lost ' )
2015-11-11 20:44:54 +00:00
2015-10-15 09:14:19 +00:00
onConnectionReestablished: =>
# set status back to online
@lastAddedType = ' status '
2015-11-13 14:58:44 +00:00
@ setAgentOnlineState ' online '
2015-10-15 09:14:19 +00:00
@ addStatus @ T ( ' Connection re-established ' )
2015-11-11 20:44:54 +00:00
onSessionClosed: (data) ->
@ addStatus @ T ( ' Chat closed by %s ' , data . realname )
@ disableInput ( )
2015-11-25 09:35:57 +00:00
@ setAgentOnlineState ' offline '
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . stop ( )
2015-11-02 15:48:16 +00:00
2015-11-12 14:05:43 +00:00
setSessionId: (id) =>
@sessionId = id
2015-11-12 16:15:58 +00:00
if id is undefined
sessionStorage . removeItem ' sessionId '
else
sessionStorage . setItem ' sessionId ' , id
2015-11-12 14:05:43 +00:00
onConnectionEstablished: (data) =>
2015-11-12 13:19:41 +00:00
# stop delay of initial queue position
if @ onInitialQueueDelayId
2015-11-12 14:05:43 +00:00
clearTimeout @ onInitialQueueDelayId
2015-11-12 13:56:47 +00:00
2015-11-02 15:48:16 +00:00
@inQueue = false
2015-11-12 15:16:01 +00:00
if data . agent
@agent = data . agent
if data . session_id
@ setSessionId data . session_id
2015-11-02 15:48:16 +00:00
@ el . find ( ' .zammad-chat-agent ' ) . html @ view ( ' agent ' )
2015-11-12 13:56:47 +00:00
agent: @ agent
2015-11-02 15:48:16 +00:00
2015-11-11 20:44:54 +00:00
@ enableInput ( )
2016-02-08 15:06:56 +00:00
@ hideModal ( )
2015-11-02 15:48:16 +00:00
@ el . find ( ' .zammad-chat-welcome ' ) . addClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent ' ) . removeClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent-status ' ) . removeClass ( ' zammad-chat-is-hidden ' )
2016-02-08 15:06:56 +00:00
@ input . focus ( ) if not @ isFullscreen
2015-11-02 15:48:16 +00:00
2015-11-25 09:35:57 +00:00
@ setAgentOnlineState ' online '
2015-12-05 19:41:14 +00:00
@ waitingListTimeout . stop ( )
@ idleTimeout . stop ( )
@ inactiveTimeout . start ( )
showCustomerTimeout: ->
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' customer_timeout ' )
2015-11-12 16:31:20 +00:00
agent: @ agent . name
2015-11-25 23:40:52 +00:00
delay: @ options . inactiveTimeout
reload = ->
location . reload ( )
@ el . find ( ' .js-restart ' ) . click reload
2015-12-08 11:32:19 +00:00
@ sessionClose ( )
2015-11-12 16:31:20 +00:00
2015-12-05 19:41:14 +00:00
showWaitingListTimeout: ->
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' waiting_list_timeout ' )
2015-12-05 19:41:14 +00:00
delay: @ options . watingListTimeout
reload = ->
location . reload ( )
@ el . find ( ' .js-restart ' ) . click reload
2015-12-08 11:32:19 +00:00
@ sessionClose ( )
2015-12-05 19:41:14 +00:00
2015-11-02 15:48:16 +00:00
showLoader: ->
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' loader ' ) ( )
2015-10-15 09:14:19 +00:00
setAgentOnlineState: (state) =>
2015-11-13 14:58:44 +00:00
@state = state
2015-12-08 11:32:19 +00:00
return if ! @ el
2015-11-13 14:58:44 +00:00
capitalizedState = state . charAt ( 0 ) . toUpperCase ( ) + state . slice ( 1 )
2015-10-15 09:14:19 +00:00
@ el
. find ( ' .zammad-chat-agent-status ' )
2015-11-13 14:58:44 +00:00
. attr ( ' data-status ' , state )
. text @ T ( capitalizedState )
2015-10-15 09:14:19 +00:00
2015-12-07 10:41:12 +00:00
detectHost: ->
protocol = ' ws:// '
if window . location . protocol is ' https: '
protocol = ' wss:// '
@options.host = " #{ protocol } #{ scriptHost } /ws "
2015-11-25 16:36:06 +00:00
loadCss: ->
return if ! @ options . cssAutoload
url = @ options . cssUrl
if ! url
url = @ options . host
. replace ( /^wss/i , ' https ' )
. replace ( /^ws/i , ' http ' )
. replace ( /\/ws/i , ' ' )
url += ' /assets/chat/chat.css '
2015-12-05 19:41:14 +00:00
@ log . debug " load css from ' #{ url } ' "
2015-11-25 16:36:06 +00:00
styles = " @import url( ' #{ url } ' ); "
newSS = document . createElement ( ' link ' )
newSS.rel = ' stylesheet '
newSS.href = ' data:text/css, ' + escape ( styles )
document . getElementsByTagName ( ' head ' ) [ 0 ] . appendChild ( newSS )
2015-12-05 19:41:14 +00:00
startTimeoutObservers: =>
@idleTimeout = new Timeout (
logPrefix: ' idleTimeout '
debug: @ options . debug
timeout: @ options . idleTimeout
timeoutIntervallCheck: @ options . idleTimeoutIntervallCheck
callback: =>
@ log . debug ' Idle timeout reached, hide widget ' , new Date
2015-12-08 11:32:19 +00:00
@ destroy ( remove: true )
2015-12-05 19:41:14 +00:00
)
@inactiveTimeout = new Timeout (
logPrefix: ' inactiveTimeout '
debug: @ options . debug
timeout: @ options . inactiveTimeout
timeoutIntervallCheck: @ options . inactiveTimeoutIntervallCheck
callback: =>
@ log . debug ' Inactive timeout reached, show timeout screen. ' , new Date
@ showCustomerTimeout ( )
2015-12-08 11:32:19 +00:00
@ destroy ( remove: false )
2015-12-05 19:41:14 +00:00
)
@waitingListTimeout = new Timeout (
logPrefix: ' waitingListTimeout '
debug: @ options . debug
timeout: @ options . waitingListTimeout
timeoutIntervallCheck: @ options . waitingListTimeoutIntervallCheck
callback: =>
@ log . debug ' Waiting list timeout reached, show timeout screen. ' , new Date
@ showWaitingListTimeout ( )
2015-12-08 11:32:19 +00:00
@ destroy ( remove: false )
2015-12-05 19:41:14 +00:00
)
2015-11-25 23:40:52 +00:00
2016-02-08 15:06:56 +00:00
disableScrollOnRoot: ->
@rootScrollOffset = @ scrollRoot . scrollTop ( )
@ scrollRoot . css
overflow: ' hidden '
position: ' fixed '
enableScrollOnRoot: ->
@ scrollRoot . scrollTop @ rootScrollOffset
@ scrollRoot . css
overflow: ' '
position: ' '
2015-11-13 12:59:15 +00:00
window . ZammadChat = ZammadChat