Fixed timing issues, improved tests.

This commit is contained in:
Martin Edenhofer 2015-12-08 12:32:19 +01:00
parent 465ab3e6be
commit f06348e843
7 changed files with 409 additions and 274 deletions

View file

@ -291,7 +291,7 @@ class ChatWindow extends App.Controller
phrasesArray = phrases.split(';')
phrase = phrasesArray[_.random(0, phrasesArray.length-1)]
@input.html(phrase)
@sendMessage()
@sendMessage(1600)
focus: =>
@input.focus()
@ -372,16 +372,27 @@ class ChatWindow extends App.Controller
event.preventDefault()
@sendMessage()
sendMessage: =>
sendMessage: (delay) =>
content = @input.html()
return if !content
App.WebSocket.send(
event:'chat_session_message'
data:
content: content
session_id: @session.session_id
)
send = =>
App.WebSocket.send(
event:'chat_session_message'
data:
content: content
session_id: @session.session_id
)
if !delay
send()
else
# show key enter and send phrase
App.WebSocket.send(
event:'chat_session_typing'
data:
session_id: @session.session_id
)
@delay(send, delay)
@addMessage content, 'agent'
@input.html('')

View file

@ -73,7 +73,7 @@ do($ = window.jQuery, window) ->
stop: =>
return if !@intervallId
@log.debug "Stop timeout of #{@options.timeout} minutes at"#, new Date
@log.debug "Stop timeout of #{@options.timeout} minutes"#, new Date
clearInterval(@intervallId)
class Io extends Base
@ -89,30 +89,40 @@ do($ = window.jQuery, window) ->
@log.debug "Connecting to #{@options.host}"
@ws = new window.WebSocket("#{@options.host}")
@ws.onopen = (e) =>
@log.debug 'on open', e
@log.debug 'onOpen', e
@options.onOpen(e)
@ws.onmessage = (e) =>
pipes = JSON.parse(e.data)
@log.debug 'on message', e.data
@log.debug 'onMessage', e.data
if @options.onMessage
@options.onMessage(pipes)
@ws.onclose = (e) =>
@log.debug 'close websocket connection'
if @options.onClose
@options.onClose(e)
@log.debug 'close websocket connection', e
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...')
@ws.onerror = (e) =>
@log.debug 'onerror', e
@log.debug 'onError', e
if @options.onError
@options.onError(e)
close: =>
@log.debug 'close websocket manually'
@manualClose = true
@ws.close()
reconnect: =>
@ws.close()
@log.debug 'reconnect'
@close()
@connect()
send: (event, data = {}) =>
@ -137,7 +147,7 @@ do($ = window.jQuery, window) ->
buttonClass: 'open-zammad-chat'
inactiveClass: 'is-inactive'
title: '<strong>Chat</strong> with us!'
idleTimeout: 8
idleTimeout: 6
idleTimeoutIntervallCheck: 0.5
inactiveTimeout: 8
inactiveTimeoutIntervallCheck: 0.5
@ -146,7 +156,7 @@ do($ = window.jQuery, window) ->
logPrefix: 'chat'
_messageCount: 0
isOpen: true
isOpen: false
blinkOnlineInterval: null
stopBlinOnlineStateTimeout: null
showTimeEveryXMinutes: 1
@ -232,31 +242,39 @@ do($ = window.jQuery, window) ->
@io = new Io(@options)
@io.set(
onOpen: @render
onClose: @hide
onClose: @onWebSocketClose
onMessage: @onWebSocketMessage
onError: @onError
)
@io.connect()
render: =>
@el = $(@view('chat')(
title: @options.title
))
@options.target.append @el
if !@el || !$('.zammad-chat').get(0)
@el = $(@view('chat')(
title: @options.title
))
@options.target.append @el
@input = @el.find('.zammad-chat-input')
@input = @el.find('.zammad-chat-input')
# start bindings
@el.find('.js-chat-open').click @open
@el.find('.js-chat-close').click @close
@el.find('.zammad-chat-controls').on 'submit', @onSubmit
@input.on
keydown: @checkForEnter
input: @onInput
$(window).on('beforeunload', =>
@onLeaveTemporary()
)
$(window).bind('hashchange', =>
return if @isOpen
@idleTimeout.start()
)
# disable open button
$(".#{ @options.buttonClass }").addClass @inactiveClass
@el.find('.js-chat-open').click @open
@el.find('.js-chat-close').click @close
@el.find('.zammad-chat-controls').on 'submit', @onSubmit
@input.on
keydown: @checkForEnter
input: @onInput
$(window).on('beforeunload', =>
@onLeaveTemporary()
)
@setAgentOnlineState 'online'
@log.debug 'widget rendered'
@ -285,7 +303,7 @@ do($ = window.jQuery, window) ->
when 'chat_error'
@log.notice pipe.data
if pipe.data && pipe.data.state is 'chat_disabled'
@destroy(hide: true)
@destroy(remove: true)
when 'chat_session_message'
return if pipe.data.self_written
@receiveMessage pipe.data
@ -307,21 +325,14 @@ do($ = window.jQuery, window) ->
@onReady()
when 'offline'
@onError 'Zammad Chat: No agent online'
@state = 'off'
@destroy(hide: true)
when 'chat_disabled'
@onError 'Zammad Chat: Chat is disabled'
@state = 'off'
@destroy(hide: true)
when 'no_seats_available'
@onError "Zammad Chat: Too many clients in queue. Clients in queue: #{pipe.data.queue}"
@state = 'off'
@destroy(hide: true)
when 'reconnect'
@log.debug 'old messages', pipe.data.session
@reopenSession pipe.data
@onReopenSession pipe.data
onReady: =>
onReady: ->
@log.debug 'widget ready for use'
$(".#{ @options.buttonClass }").click(@open).removeClass(@inactiveClass)
@ -330,9 +341,16 @@ do($ = window.jQuery, window) ->
onError: (message) =>
@log.debug message
@addStatus(message)
$(".#{ @options.buttonClass }").hide()
if @isOpen
@disableInput()
@destroy(remove: false)
else
@destroy(remove: true)
reopenSession: (data) =>
onReopenSession: (data) =>
@log.debug 'old messages', data.session
@inactiveTimeout.start()
unfinishedMessage = sessionStorage.getItem 'unfinished_message'
@ -436,9 +454,12 @@ do($ = window.jQuery, window) ->
@scrollToBottom()
open: =>
@log.debug 'open widget'
if @isOpen
@show()
@log.debug 'widget already open, block'
return
@isOpen = true
@log.debug 'open widget'
if !@sessionId
@showLoader()
@ -447,27 +468,15 @@ do($ = window.jQuery, window) ->
if !@sessionId
@el.animate { bottom: 0 }, 500, @onOpenAnimationEnd
@send('chat_session_init')
else
@el.css 'bottom', 0
@onOpenAnimationEnd()
@isOpen = true
if !@sessionId
@send('chat_session_init')
onOpenAnimationEnd: =>
@idleTimeout.stop()
close: (event) =>
@log.debug 'close widget'
return @state if @state is 'off' or @state is 'unsupported'
event.stopPropagation() if event
# only close if session_id exists
return if !@sessionId
sessionClose: =>
# send close
@send 'chat_session_close',
session_id: @sessionId
@ -483,12 +492,23 @@ do($ = window.jQuery, window) ->
if @onInitialQueueDelayId
clearTimeout(@onInitialQueueDelayId)
if event
@closeWindow()
@setSessionId undefined
closeWindow: =>
close: (event) =>
if !@isOpen
@log.debug 'can\'t close widget, it\'s not open'
return
if !@sessionId
@log.debug 'can\'t close widget without sessionId'
return
@log.debug 'close widget'
event.stopPropagation() if event
@sessionClose()
# close window
@el.removeClass('zammad-chat-is-open')
remainerHeight = @el.height() - @el.find('.zammad-chat-header').outerHeight()
@el.animate { bottom: -remainerHeight }, 500, @onCloseAnimationEnd
@ -503,15 +523,15 @@ do($ = window.jQuery, window) ->
@isOpen = false
# restart connection
@io.reconnect()
hide: ->
onWebSocketClose: =>
return if @isOpen
if @el
@el.removeClass('zammad-chat-is-shown')
show: ->
return @state if @state is 'off' or @state is 'unsupported'
return if @state is 'offline'
@el.addClass('zammad-chat-is-shown')
@ -600,6 +620,7 @@ do($ = window.jQuery, window) ->
@scrollToBottom()
updateLastTimestamp: (label, time) ->
return if !@el
@el.find('.zammad-chat-body')
.find('.zammad-chat-timestamp')
.last()
@ -608,6 +629,7 @@ do($ = window.jQuery, window) ->
time: time
addStatus: (status) ->
return if !@el
@maybeAddTimestamp()
@el.find('.zammad-chat-body').append @view('status')
@ -619,30 +641,24 @@ do($ = window.jQuery, window) ->
@el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'))
destroy: (params = {}) =>
@log.debug 'destroy widget'
if params.hide
if @el
@el.remove()
@log.debug 'destroy widget', params
@setAgentOnlineState 'offline'
if params.remove && @el
@el.remove()
# stop all timer
@waitingListTimeout.stop()
@inactiveTimeout.stop()
@idleTimeout.stop()
@wsReconnectStop()
if @waitingListTimeout
@waitingListTimeout.stop()
if @inactiveTimeout
@inactiveTimeout.stop()
if @idleTimeout
@idleTimeout.stop()
# stop ws connection
@io.close()
wsReconnectStart: =>
@wsReconnectStop()
if @reconnectDelayId
clearTimeout(@reconnectDelayId)
@reconnectDelayId = setTimeout(@io.connect(), 5000)
wsReconnectStop: =>
if @reconnectDelayId
clearTimeout(@reconnectDelayId)
reconnect: =>
# set status to connecting
@log.notice 'reconnecting'
@ -702,24 +718,25 @@ do($ = window.jQuery, window) ->
@el.find('.zammad-chat-body').html @view('customer_timeout')
agent: @agent.name
delay: @options.inactiveTimeout
@close()
reload = ->
location.reload()
@el.find('.js-restart').click reload
@sessionClose()
showWaitingListTimeout: ->
@el.find('.zammad-chat-body').html @view('waiting_list_timeout')
delay: @options.watingListTimeout
@close()
reload = ->
location.reload()
@el.find('.js-restart').click reload
@sessionClose()
showLoader: ->
@el.find('.zammad-chat-body').html @view('loader')()
setAgentOnlineState: (state) =>
@state = state
return if !@el
capitalizedState = state.charAt(0).toUpperCase() + state.slice(1)
@el
.find('.zammad-chat-agent-status')
@ -757,8 +774,7 @@ do($ = window.jQuery, window) ->
timeoutIntervallCheck: @options.idleTimeoutIntervallCheck
callback: =>
@log.debug 'Idle timeout reached, hide widget', new Date
@state = 'off'
@destroy(hide: true)
@destroy(remove: true)
)
@inactiveTimeout = new Timeout(
logPrefix: 'inactiveTimeout'
@ -767,10 +783,8 @@ do($ = window.jQuery, window) ->
timeoutIntervallCheck: @options.inactiveTimeoutIntervallCheck
callback: =>
@log.debug 'Inactive timeout reached, show timeout screen.', new Date
@state = 'off'
@setAgentOnlineState 'offline'
@showCustomerTimeout()
@destroy(hide:false)
@destroy(remove: false)
)
@waitingListTimeout = new Timeout(
logPrefix: 'waitingListTimeout'
@ -779,10 +793,8 @@ do($ = window.jQuery, window) ->
timeoutIntervallCheck: @options.waitingListTimeoutIntervallCheck
callback: =>
@log.debug 'Waiting list timeout reached, show timeout screen.', new Date
@state = 'off'
@setAgentOnlineState 'offline'
@showWaitingListTimeout()
@destroy(hide:false)
@destroy(remove: false)
)
window.ZammadChat = ZammadChat

View file

@ -12,7 +12,8 @@
display: none;
-webkit-flex-direction: column;
-ms-flex-direction: column;
flex-direction: column; }
flex-direction: column;
z-index: 999; }
.zammad-chat.is-fullscreen {
right: 0;
width: 100%;
@ -106,7 +107,7 @@
margin: 0 1em;
display: inline-block;
line-height: 2em;
padding: 0 0.7em;
padding: 0 .7em;
border-radius: 1em;
background: rgba(0, 0, 0, 0.1);
box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.04) inset; }
@ -349,6 +350,10 @@
.zammad-chat-send {
float: right; }
.zammad-chat-button:disabled,
.zammad-chat-input:disabled {
opacity: 0.3; }
.zammad-chat-is-hidden {
display: none; }

View file

@ -128,7 +128,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
if (!this.intervallId) {
return;
}
this.log.debug("Stop timeout of " + this.options.timeout + " minutes at");
this.log.debug("Stop timeout of " + this.options.timeout + " minutes");
return clearInterval(this.intervallId);
};
@ -164,7 +164,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.ws = new window.WebSocket("" + this.options.host);
this.ws.onopen = (function(_this) {
return function(e) {
_this.log.debug('on open', e);
_this.log.debug('onOpen', e);
return _this.options.onOpen(e);
};
})(this);
@ -172,7 +172,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return function(e) {
var pipes;
pipes = JSON.parse(e.data);
_this.log.debug('on message', e.data);
_this.log.debug('onMessage', e.data);
if (_this.options.onMessage) {
return _this.options.onMessage(pipes);
}
@ -180,15 +180,24 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
})(this);
this.ws.onclose = (function(_this) {
return function(e) {
_this.log.debug('close websocket connection');
if (_this.options.onClose) {
return _this.options.onClose(e);
_this.log.debug('close websocket connection', e);
if (_this.manualClose) {
_this.log.debug('manual close, onClose callback');
_this.manualClose = false;
if (_this.options.onClose) {
return _this.options.onClose(e);
}
} else {
_this.log.debug('error close, onError callback');
if (_this.options.onError) {
return _this.options.onError('Connection lost...');
}
}
};
})(this);
return this.ws.onerror = (function(_this) {
return function(e) {
_this.log.debug('onerror', e);
_this.log.debug('onError', e);
if (_this.options.onError) {
return _this.options.onError(e);
}
@ -197,11 +206,14 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
Io.prototype.close = function() {
this.log.debug('close websocket manually');
this.manualClose = true;
return this.ws.close();
};
Io.prototype.reconnect = function() {
this.ws.close();
this.log.debug('reconnect');
this.close();
return this.connect();
};
@ -238,7 +250,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
buttonClass: 'open-zammad-chat',
inactiveClass: 'is-inactive',
title: '<strong>Chat</strong> with us!',
idleTimeout: 8,
idleTimeout: 6,
idleTimeoutIntervallCheck: 0.5,
inactiveTimeout: 8,
inactiveTimeoutIntervallCheck: 0.5,
@ -250,7 +262,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype._messageCount = 0;
ZammadChat.prototype.isOpen = true;
ZammadChat.prototype.isOpen = false;
ZammadChat.prototype.blinkOnlineInterval = null;
@ -336,26 +348,24 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.setSessionId = bind(this.setSessionId, this);
this.onConnectionReestablished = bind(this.onConnectionReestablished, this);
this.reconnect = bind(this.reconnect, this);
this.wsReconnectStop = bind(this.wsReconnectStop, this);
this.wsReconnectStart = bind(this.wsReconnectStart, this);
this.destroy = bind(this.destroy, this);
this.onLeaveTemporary = bind(this.onLeaveTemporary, this);
this.onAgentTypingEnd = bind(this.onAgentTypingEnd, this);
this.onAgentTypingStart = bind(this.onAgentTypingStart, this);
this.onQueue = bind(this.onQueue, this);
this.onQueueScreen = bind(this.onQueueScreen, this);
this.onWebSocketClose = bind(this.onWebSocketClose, this);
this.onCloseAnimationEnd = bind(this.onCloseAnimationEnd, this);
this.closeWindow = bind(this.closeWindow, this);
this.close = bind(this.close, this);
this.sessionClose = bind(this.sessionClose, this);
this.onOpenAnimationEnd = bind(this.onOpenAnimationEnd, this);
this.open = bind(this.open, this);
this.renderMessage = bind(this.renderMessage, this);
this.receiveMessage = bind(this.receiveMessage, this);
this.onSubmit = bind(this.onSubmit, this);
this.onInput = bind(this.onInput, this);
this.reopenSession = bind(this.reopenSession, this);
this.onReopenSession = bind(this.onReopenSession, this);
this.onError = bind(this.onError, this);
this.onReady = bind(this.onReady, this);
this.onWebSocketMessage = bind(this.onWebSocketMessage, this);
this.send = bind(this.send, this);
this.checkForEnter = bind(this.checkForEnter, this);
@ -393,31 +403,42 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.io = new Io(this.options);
this.io.set({
onOpen: this.render,
onClose: this.hide,
onMessage: this.onWebSocketMessage
onClose: this.onWebSocketClose,
onMessage: this.onWebSocketMessage,
onError: this.onError
});
this.io.connect();
}
ZammadChat.prototype.render = function() {
this.el = $(this.view('chat')({
title: this.options.title
}));
this.options.target.append(this.el);
this.input = this.el.find('.zammad-chat-input');
if (!this.el || !$('.zammad-chat').get(0)) {
this.el = $(this.view('chat')({
title: this.options.title
}));
this.options.target.append(this.el);
this.input = this.el.find('.zammad-chat-input');
this.el.find('.js-chat-open').click(this.open);
this.el.find('.js-chat-close').click(this.close);
this.el.find('.zammad-chat-controls').on('submit', this.onSubmit);
this.input.on({
keydown: this.checkForEnter,
input: this.onInput
});
$(window).on('beforeunload', (function(_this) {
return function() {
return _this.onLeaveTemporary();
};
})(this));
$(window).bind('hashchange', (function(_this) {
return function() {
if (_this.isOpen) {
return;
}
return _this.idleTimeout.start();
};
})(this));
}
$("." + this.options.buttonClass).addClass(this.inactiveClass);
this.el.find('.js-chat-open').click(this.open);
this.el.find('.js-chat-close').click(this.close);
this.el.find('.zammad-chat-controls').on('submit', this.onSubmit);
this.input.on({
keydown: this.checkForEnter,
input: this.onInput
});
$(window).on('beforeunload', (function(_this) {
return function() {
return _this.onLeaveTemporary();
};
})(this));
this.setAgentOnlineState('online');
this.log.debug('widget rendered');
this.startTimeoutObservers();
@ -453,7 +474,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.log.notice(pipe.data);
if (pipe.data && pipe.data.state === 'chat_disabled') {
this.destroy({
hide: true
remove: true
});
}
break;
@ -489,28 +510,15 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
break;
case 'offline':
this.onError('Zammad Chat: No agent online');
this.state = 'off';
this.destroy({
hide: true
});
break;
case 'chat_disabled':
this.onError('Zammad Chat: Chat is disabled');
this.state = 'off';
this.destroy({
hide: true
});
break;
case 'no_seats_available':
this.onError("Zammad Chat: Too many clients in queue. Clients in queue: " + pipe.data.queue);
this.state = 'off';
this.destroy({
hide: true
});
break;
case 'reconnect':
this.log.debug('old messages', pipe.data.session);
this.reopenSession(pipe.data);
this.onReopenSession(pipe.data);
}
}
}
@ -526,11 +534,23 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.onError = function(message) {
this.log.debug(message);
return $("." + this.options.buttonClass).hide();
this.addStatus(message);
$("." + this.options.buttonClass).hide();
if (this.isOpen) {
this.disableInput();
return this.destroy({
remove: false
});
} else {
return this.destroy({
remove: true
});
}
};
ZammadChat.prototype.reopenSession = function(data) {
ZammadChat.prototype.onReopenSession = function(data) {
var i, len, message, ref, unfinishedMessage;
this.log.debug('old messages', data.session);
this.inactiveTimeout.start();
unfinishedMessage = sessionStorage.getItem('unfinished_message');
if (data.agent) {
@ -631,10 +651,12 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.open = function() {
this.log.debug('open widget');
if (this.isOpen) {
this.show();
this.log.debug('widget already open, block');
return;
}
this.isOpen = true;
this.log.debug('open widget');
if (!this.sessionId) {
this.showLoader();
}
@ -643,13 +665,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.el.animate({
bottom: 0
}, 500, this.onOpenAnimationEnd);
return this.send('chat_session_init');
} else {
this.el.css('bottom', 0);
this.onOpenAnimationEnd();
}
this.isOpen = true;
if (!this.sessionId) {
return this.send('chat_session_init');
return this.onOpenAnimationEnd();
}
};
@ -657,17 +676,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return this.idleTimeout.stop();
};
ZammadChat.prototype.close = function(event) {
this.log.debug('close widget');
if (this.state === 'off' || this.state === 'unsupported') {
return this.state;
}
if (event) {
event.stopPropagation();
}
if (!this.sessionId) {
return;
}
ZammadChat.prototype.sessionClose = function() {
this.send('chat_session_close', {
session_id: this.sessionId
});
@ -677,14 +686,24 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
if (this.onInitialQueueDelayId) {
clearTimeout(this.onInitialQueueDelayId);
}
if (event) {
this.closeWindow();
}
return this.setSessionId(void 0);
};
ZammadChat.prototype.closeWindow = function() {
ZammadChat.prototype.close = function(event) {
var remainerHeight;
if (!this.isOpen) {
this.log.debug('can\'t close widget, it\'s not open');
return;
}
if (!this.sessionId) {
this.log.debug('can\'t close widget without sessionId');
return;
}
this.log.debug('close widget');
if (event) {
event.stopPropagation();
}
this.sessionClose();
this.el.removeClass('zammad-chat-is-open');
remainerHeight = this.el.height() - this.el.find('.zammad-chat-header').outerHeight();
return this.el.animate({
@ -702,7 +721,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return this.io.reconnect();
};
ZammadChat.prototype.hide = function() {
ZammadChat.prototype.onWebSocketClose = function() {
if (this.isOpen) {
return;
}
if (this.el) {
return this.el.removeClass('zammad-chat-is-shown');
}
@ -710,8 +732,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.show = function() {
var remainerHeight;
if (this.state === 'off' || this.state === 'unsupported') {
return this.state;
if (this.state === 'offline') {
return;
}
this.el.addClass('zammad-chat-is-shown');
if (!this.inputInitialized) {
@ -809,6 +831,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.updateLastTimestamp = function(label, time) {
if (!this.el) {
return;
}
return this.el.find('.zammad-chat-body').find('.zammad-chat-timestamp').last().replaceWith(this.view('timestamp')({
label: label,
time: time
@ -816,6 +841,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
};
ZammadChat.prototype.addStatus = function(status) {
if (!this.el) {
return;
}
this.maybeAddTimestamp();
this.el.find('.zammad-chat-body').append(this.view('status')({
status: status
@ -831,33 +859,23 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
if (params == null) {
params = {};
}
this.log.debug('destroy widget');
if (params.hide) {
if (this.el) {
this.el.remove();
}
this.log.debug('destroy widget', params);
this.setAgentOnlineState('offline');
if (params.remove && this.el) {
this.el.remove();
}
if (this.waitingListTimeout) {
this.waitingListTimeout.stop();
}
if (this.inactiveTimeout) {
this.inactiveTimeout.stop();
}
if (this.idleTimeout) {
this.idleTimeout.stop();
}
this.waitingListTimeout.stop();
this.inactiveTimeout.stop();
this.idleTimeout.stop();
this.wsReconnectStop();
return this.io.close();
};
ZammadChat.prototype.wsReconnectStart = function() {
this.wsReconnectStop();
if (this.reconnectDelayId) {
clearTimeout(this.reconnectDelayId);
}
return this.reconnectDelayId = setTimeout(this.io.connect(), 5000);
};
ZammadChat.prototype.wsReconnectStop = function() {
if (this.reconnectDelayId) {
return clearTimeout(this.reconnectDelayId);
}
};
ZammadChat.prototype.reconnect = function() {
this.log.notice('reconnecting');
this.disableInput();
@ -920,11 +938,11 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
agent: this.agent.name,
delay: this.options.inactiveTimeout
}));
this.close();
reload = function() {
return location.reload();
};
return this.el.find('.js-restart').click(reload);
this.el.find('.js-restart').click(reload);
return this.sessionClose();
};
ZammadChat.prototype.showWaitingListTimeout = function() {
@ -932,11 +950,11 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
this.el.find('.zammad-chat-body').html(this.view('waiting_list_timeout')({
delay: this.options.watingListTimeout
}));
this.close();
reload = function() {
return location.reload();
};
return this.el.find('.js-restart').click(reload);
this.el.find('.js-restart').click(reload);
return this.sessionClose();
};
ZammadChat.prototype.showLoader = function() {
@ -946,6 +964,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
ZammadChat.prototype.setAgentOnlineState = function(state) {
var capitalizedState;
this.state = state;
if (!this.el) {
return;
}
capitalizedState = state.charAt(0).toUpperCase() + state.slice(1);
return this.el.find('.zammad-chat-agent-status').attr('data-status', state).text(this.T(capitalizedState));
};
@ -986,9 +1007,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
callback: (function(_this) {
return function() {
_this.log.debug('Idle timeout reached, hide widget', new Date);
_this.state = 'off';
return _this.destroy({
hide: true
remove: true
});
};
})(this)
@ -1001,11 +1021,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
callback: (function(_this) {
return function() {
_this.log.debug('Inactive timeout reached, show timeout screen.', new Date);
_this.state = 'off';
_this.setAgentOnlineState('offline');
_this.showCustomerTimeout();
return _this.destroy({
hide: false
remove: false
});
};
})(this)
@ -1018,11 +1036,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
callback: (function(_this) {
return function() {
_this.log.debug('Waiting list timeout reached, show timeout screen.', new Date);
_this.state = 'off';
_this.setAgentOnlineState('offline');
_this.showWaitingListTimeout();
return _this.destroy({
hide: false
remove: false
});
};
})(this)
@ -1035,67 +1051,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
return window.ZammadChat = ZammadChat;
})(window.jQuery, window);
if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {};
}
window.zammadChatTemplates["agent"] = function (__obj) {
if (!__obj) __obj = {};
var __out = [], __capture = function(callback) {
var out = __out, result;
__out = [];
callback.call(this);
result = __out.join('');
__out = out;
return __safe(result);
}, __sanitize = function(value) {
if (value && value.ecoSafe) {
return value;
} else if (typeof value !== 'undefined' && value != null) {
return __escape(value);
} else {
return '';
}
}, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
__safe = __obj.safe = function(value) {
if (value && value.ecoSafe) {
return value;
} else {
if (!(typeof value !== 'undefined' && value != null)) value = '';
var result = new String(value);
result.ecoSafe = true;
return result;
}
};
if (!__escape) {
__escape = __obj.escape = function(value) {
return ('' + value)
.replace(/&/g, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}
(function() {
(function() {
if (this.agent.avatar) {
__out.push('\n<img class="zammad-chat-agent-avatar" src="');
__out.push(__sanitize(this.agent.avatar));
__out.push('">\n');
}
__out.push('\n<span class="zammad-chat-agent-sentence">\n <span class="zammad-chat-agent-name">');
__out.push(__sanitize(this.agent.name));
__out.push('</span>\n</span>');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};
/*!
* ----------------------------------------------------------------------------
* "THE BEER-WARE LICENSE" (Revision 42):
@ -1181,6 +1136,67 @@ 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, '&amp;')
.replace(/</g, '&lt;')
.replace(/>/g, '&gt;')
.replace(/"/g, '&quot;');
};
}
(function() {
(function() {
if (this.agent.avatar) {
__out.push('\n<img class="zammad-chat-agent-avatar" src="');
__out.push(__sanitize(this.agent.avatar));
__out.push('">\n');
}
__out.push('\n<span class="zammad-chat-agent-sentence">\n <span class="zammad-chat-agent-name">');
__out.push(__sanitize(this.agent.name));
__out.push('</span>\n</span>');
}).call(this);
}).call(__obj);
__obj.safe = __objSafe, __obj.escape = __escape;
return __out.join('');
};
if (!window.zammadChatTemplates) {
window.zammadChatTemplates = {};
}

File diff suppressed because one or more lines are too long

View file

@ -11,6 +11,7 @@
will-change: bottom;
display: none;
flex-direction: column;
z-index: 999;
&.is-fullscreen {
right: 0;
@ -177,7 +178,7 @@
.zammad-chat-modal-text {
font-size: 1.3em;
line-height: 1.45;
.zammad-chat-loading-animation {
font-size: 0.7em;
}
@ -198,7 +199,7 @@
overflow: auto;
background: white;
flex: 1;
@media only screen and (max-width: 768px) {
height: auto;
flex: 1;
@ -351,6 +352,11 @@
float: right;
}
.zammad-chat-button:disabled,
.zammad-chat-input:disabled {
opacity: 0.3;
}
.zammad-chat-is-hidden {
display: none;
}

View file

@ -387,6 +387,42 @@ class ChatTest < TestCase
css: '.zammad-chat',
value: 'Chat closed by',
)
click(
browser: customer,
css: '.zammad-chat .js-chat-close',
)
watch_for_disappear(
browser: customer,
css: '.zammad-chat-is-open',
)
agent.find_elements( { css: '.active .chat-window .js-close' } ).each(&:click)
sleep 2
click(
browser: customer,
css: '.js-chat-open',
)
exists(
browser: customer,
css: '.zammad-chat-is-shown',
)
watch_for(
browser: customer,
css: '.zammad-chat',
value: '(waiting|warte)',
)
click(
browser: agent,
css: '.active .js-acceptChat',
)
sleep 2
exists_not(
browser: agent,
css: '.active .chat-window .chat-status.is-modified',
)
exists(
browser: agent,
css: '.active .chat-window .chat-status',
)
end
def test_basic_usecase3
@ -579,7 +615,7 @@ class ChatTest < TestCase
browser: customer,
css: '.zammad-chat',
value: '(takes longer|dauert länger)',
timeout: 90,
timeout: 120,
)
# no customer action, show sorry screen
@ -625,6 +661,55 @@ class ChatTest < TestCase
timeout: 150,
)
agent.find_elements( { css: '.active .chat-window .js-close' } ).each(&:click)
sleep 2
click(
browser: customer,
css: '.js-restart',
)
sleep 5
click(
browser: customer,
css: '.js-chat-open',
)
exists(
browser: customer,
css: '.zammad-chat-is-shown',
)
watch_for(
browser: customer,
css: '.zammad-chat',
value: '(waiting|warte)',
)
click(
browser: agent,
css: '.active .js-acceptChat',
)
sleep 2
exists(
browser: agent,
css: '.active .chat-window .chat-status',
)
set(
browser: agent,
css: '.active .chat-window .js-customerChatInput',
value: 'my name is me',
)
click(
browser: agent,
css: '.active .chat-window .js-send',
)
watch_for(
browser: customer,
css: '.zammad-chat .zammad-chat-agent-status',
value: 'online',
)
watch_for(
browser: customer,
css: '.zammad-chat',
value: 'my name is me',
)
end
end