trabajo-afectivo/public/assets/chat/chat.coffee

890 lines
24 KiB
CoffeeScript
Raw Normal View History

2015-10-15 09:14:19 +00:00
do($ = window.jQuery, window) ->
scripts = document.getElementsByTagName('script')
myScript = scripts[scripts.length - 1]
2015-11-25 09:35:57 +00:00
scriptHost = myScript.src.match('.*://([^:/]*).*')[1]
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)
@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
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
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
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:
chatId: undefined
2015-11-13 14:15:44 +00:00
show: true
2015-10-15 09:14:19 +00:00
target: $('body')
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
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'
_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
state: 'offline'
initialQueueDelay: 10000
translations:
de:
'<strong>Chat</strong> with us!': '<strong>Chatte</strong> mit uns!'
'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.'
'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
T: (string, items...) =>
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!"
else
translations = @translations[@options.lang]
if !translations[string]
2015-12-05 19:41:14 +00:00
@log.notice "Translation needed for '#{string}'"
string = translations[string] || string
if items
for item in items
string = string.replace(/%s/, item)
string
view: (name) =>
return (options) =>
if !options
options = {}
options.T = @T
options.background = @options.background
options.flat = @options.flat
2015-11-16 10:46:42 +00:00
options.fontSize = @options.fontSize
return window.zammadChatTemplates[name](options)
2015-10-15 09:14:19 +00:00
constructor: (options) ->
@options = $.extend {}, @defaults, options
2015-12-05 19:41:14 +00:00
super(@options)
2015-11-13 14:15:44 +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
@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
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
# 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}"
# 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
)
2015-12-05 19:41:14 +00:00
@io.connect()
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)
@renderBase()
# 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
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 = {}) =>
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'
@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
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'
@sessionId = undefined
if !@options.cssAutoload || @cssLoaded
@onReady()
else
@socketReady = true
2015-11-10 14:01:04 +00:00
when 'offline'
@onError 'Zammad Chat: No agent online'
2015-11-10 14:01:04 +00:00
when 'chat_disabled'
@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}"
when 'reconnect'
2015-12-08 11:32:19 +00:00
@onReopenSession pipe.data
2015-12-08 11:32:19 +00:00
onReady: ->
2015-12-05 19:41:14 +00:00
@log.debug 'widget ready for use'
$(".#{ @options.buttonClass }").click(@open).removeClass(@inactiveClass)
2015-11-11 09:48:54 +00:00
if @options.show
@show()
onError: (message) =>
2015-12-05 19:41:14 +00:00
@log.debug message
2015-12-08 11:32:19 +00:00
@addStatus(message)
$(".#{ @options.buttonClass }").hide()
2015-12-08 11:32:19 +00:00
if @isOpen
@disableInput()
@destroy(remove: false)
else
@destroy(remove: true)
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-13 14:15:44 +00:00
if unfinishedMessage
@input.val unfinishedMessage
# show wait list
if data.position
@onQueue data
@show()
@open()
@scrollToBottom()
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'
sessionStorage.setItem 'unfinished_message', @input.val()
2015-11-25 14:08:55 +00:00
@onTyping()
2015-10-15 09:14:19 +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: ->
message = @input.val()
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
sessionStorage.removeItem 'unfinished_message'
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
@maybeAddTimestamp()
2015-10-15 09:14:19 +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
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('')
@scrollToBottom()
2015-10-15 09:14:19 +00:00
# send message event
2015-11-10 14:01:04 +00:00
@send 'chat_session_message',
content: message
id: @_messageCount
2015-11-12 10:44:37 +00:00
session_id: @sessionId
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()
@renderMessage
2015-11-10 14:01:04 +00:00
message: data.message.content
id: data.id
2015-10-15 09:26:56 +00:00
from: 'agent'
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 ''
@el.find('.zammad-chat-body').append @view('message')(data)
2015-10-15 09:14:19 +00:00
@scrollToBottom()
open: =>
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
if !@sessionId
@showLoader()
2015-11-25 13:47:02 +00:00
@el.addClass('zammad-chat-is-open')
if !@sessionId
@el.animate { bottom: 0 }, 500, @onOpenAnimationEnd
@send('chat_session_init')
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()
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'
# stop delay of initial queue position
if @onInitialQueueDelayId
clearTimeout(@onInitialQueueDelayId)
2015-11-25 23:40:52 +00:00
@setSessionId undefined
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
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()
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: =>
@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-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')
@el.removeClass('zammad-chat-is-loaded')
2015-10-15 09:14:19 +00:00
show: ->
2015-12-08 11:32:19 +00:00
return if @state is 'offline'
@el.addClass('zammad-chat-is-loaded')
2015-10-15 09:14:19 +00:00
if !@inputInitialized
@inputInitialized = true
@input.autoGrow
extraLine: false
remainerHeight = @el.height() - @el.find('.zammad-chat-header').outerHeight()
@el.css 'bottom', -remainerHeight
@el.addClass('zammad-chat-is-shown')
disableInput: ->
2015-11-12 14:05:43 +00:00
@input.prop('disabled', true)
@el.find('.zammad-chat-send').prop('disabled', true)
enableInput: ->
2015-11-12 14:05:43 +00:00
@input.prop('disabled', false)
@el.find('.zammad-chat-send').prop('disabled', false)
hideModal: ->
@el.find('.zammad-chat-modal').html ''
onQueueScreen: (data) =>
@setSessionId data.session_id
# 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()
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
@inQueue = true
@el.find('.zammad-chat-modal').html @view('waiting')
position: data.position
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)
# 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
@el.find('.zammad-chat-body').append @view('timestamp')
label: label
time: time
2015-10-15 09:14:19 +00:00
@lastTimestamp = timestamp
@lastAddedType = 'timestamp'
@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')
.find('.zammad-chat-timestamp')
2015-10-15 09:14:19 +00:00
.last()
.replaceWith @view('timestamp')
2015-10-15 09:14:19 +00:00
label: label
time: time
addStatus: (status) ->
2015-12-08 11:32:19 +00:00
return if !@el
@maybeAddTimestamp()
2015-10-15 09:26:56 +00:00
@el.find('.zammad-chat-body').append @view('status')
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()
# stop all timer
2015-12-08 11:32:19 +00:00
if @waitingListTimeout
@waitingListTimeout.stop()
if @inactiveTimeout
@inactiveTimeout.stop()
if @idleTimeout
@idleTimeout.stop()
# 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'
@disableInput()
2015-10-15 09:14:19 +00:00
@lastAddedType = 'status'
@setAgentOnlineState 'connecting'
2015-10-15 09:14:19 +00:00
@addStatus @T('Connection lost')
2015-10-15 09:14:19 +00:00
onConnectionReestablished: =>
# set status back to online
@lastAddedType = 'status'
@setAgentOnlineState 'online'
2015-10-15 09:14:19 +00:00
@addStatus @T('Connection re-established')
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-12 14:05:43 +00:00
setSessionId: (id) =>
@sessionId = id
if id is undefined
sessionStorage.removeItem 'sessionId'
else
sessionStorage.setItem 'sessionId', id
2015-11-12 14:05:43 +00:00
onConnectionEstablished: (data) =>
# stop delay of initial queue position
if @onInitialQueueDelayId
2015-11-12 14:05:43 +00:00
clearTimeout @onInitialQueueDelayId
@inQueue = false
2015-11-12 15:16:01 +00:00
if data.agent
@agent = data.agent
if data.session_id
@setSessionId data.session_id
@el.find('.zammad-chat-agent').html @view('agent')
agent: @agent
@enableInput()
@hideModal()
@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')
@input.focus() if not @isFullscreen
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: ->
@el.find('.zammad-chat-modal').html @view('customer_timeout')
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-12-05 19:41:14 +00:00
showWaitingListTimeout: ->
@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
showLoader: ->
@el.find('.zammad-chat-modal').html @view('loader')()
2015-10-15 09:14:19 +00:00
setAgentOnlineState: (state) =>
@state = state
2015-12-08 11:32:19 +00:00
return if !@el
capitalizedState = state.charAt(0).toUpperCase() + state.slice(1)
2015-10-15 09:14:19 +00:00
@el
.find('.zammad-chat-agent-status')
.attr('data-status', state)
.text @T(capitalizedState)
2015-10-15 09:14:19 +00:00
detectHost: ->
protocol = 'ws://'
if window.location.protocol is 'https:'
protocol = 'wss://'
@options.host = "#{ protocol }#{ scriptHost }/ws"
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}'"
styles = "@import url('#{url}');"
newSS = document.createElement('link')
newSS.onload = @onCssLoaded
newSS.rel = 'stylesheet'
newSS.href = 'data:text/css,' + escape(styles)
document.getElementsByTagName('head')[0].appendChild(newSS)
onCssLoaded: =>
if @socketReady
@onReady()
else
@cssLoaded = true
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
disableScrollOnRoot: ->
@rootScrollOffset = @scrollRoot.scrollTop()
@scrollRoot.css
overflow: 'hidden'
position: 'fixed'
enableScrollOnRoot: ->
@scrollRoot.scrollTop @rootScrollOffset
@scrollRoot.css
overflow: ''
position: ''
window.ZammadChat = ZammadChat