From ece1546238f5b649325fdee6a465857494ed1741 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 25 Nov 2015 16:15:59 +0100 Subject: [PATCH 1/8] Fixed typing issues. --- public/assets/chat/chat.js | 147 ++++++++++++++++----------------- public/assets/chat/chat.min.js | 2 +- 2 files changed, 70 insertions(+), 79 deletions(-) diff --git a/public/assets/chat/chat.js b/public/assets/chat/chat.js index 340959935..1ee1af423 100644 --- a/public/assets/chat/chat.js +++ b/public/assets/chat/chat.js @@ -1,3 +1,64 @@ +if (!window.zammadChatTemplates) { + window.zammadChatTemplates = {}; +} +window.zammadChatTemplates["agent"] = function (__obj) { + if (!__obj) __obj = {}; + var __out = [], __capture = function(callback) { + var out = __out, result; + __out = []; + callback.call(this); + result = __out.join(''); + __out = out; + return __safe(result); + }, __sanitize = function(value) { + if (value && value.ecoSafe) { + return value; + } else if (typeof value !== 'undefined' && value != null) { + return __escape(value); + } else { + return ''; + } + }, __safe, __objSafe = __obj.safe, __escape = __obj.escape; + __safe = __obj.safe = function(value) { + if (value && value.ecoSafe) { + return value; + } else { + if (!(typeof value !== 'undefined' && value != null)) value = ''; + var result = new String(value); + result.ecoSafe = true; + return result; + } + }; + if (!__escape) { + __escape = __obj.escape = function(value) { + return ('' + value) + .replace(/&/g, '&') + .replace(//g, '>') + .replace(/"/g, '"'); + }; + } + (function() { + (function() { + if (this.agent.avatar) { + __out.push('\n\n'); + } + + __out.push('\n\n '); + + __out.push(__sanitize(this.agent.name)); + + __out.push('\n'); + + }).call(this); + + }).call(__obj); + __obj.safe = __objSafe, __obj.escape = __escape; + return __out.join(''); +}; + var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; }, slice = [].slice; @@ -123,7 +184,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.renderMessage = bind(this.renderMessage, 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.reopenSession = bind(this.reopenSession, this); this.onError = bind(this.onError, this); @@ -293,24 +353,17 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.onInput = function() { this.el.find('.zammad-chat-message--unread').removeClass('zammad-chat-message--unread'); sessionStorage.setItem('unfinished_message', this.input.val()); - return this.onTypingStart(); + return this.onTyping(); }; - ZammadChat.prototype.onTypingStart = function() { - if (this.isTypingTimeout) { - clearTimeout(this.isTypingTimeout); + ZammadChat.prototype.onTyping = function() { + if (this.isTyping && this.isTyping > new Date(new Date().getTime() - 1500)) { + return; } - this.isTypingTimeout = setTimeout(this.onTypingEnd, 1500); - if (!this.isTyping) { - this.isTyping = true; - return this.send('chat_session_typing', { - session_id: this.sessionId - }); - } - }; - - ZammadChat.prototype.onTypingEnd = function() { - return this.isTyping = false; + this.isTyping = new Date(); + return this.send('chat_session_typing', { + session_id: this.sessionId + }); }; ZammadChat.prototype.onSubmit = function(event) { @@ -340,7 +393,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); } this.input.val(''); this.scrollToBottom(); - this.isTyping = false; return this.send('chat_session_message', { content: message, id: this._messageCount, @@ -684,67 +736,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, '&') - .replace(//g, '>') - .replace(/"/g, '"'); - }; - } - (function() { - (function() { - if (this.agent.avatar) { - __out.push('\n\n'); - } - - __out.push('\n\n '); - - __out.push(__sanitize(this.agent.name)); - - __out.push('\n'); - - }).call(this); - - }).call(__obj); - __obj.safe = __objSafe, __obj.escape = __escape; - return __out.join(''); -}; - /*! * ---------------------------------------------------------------------------- * "THE BEER-WARE LICENSE" (Revision 42): diff --git a/public/assets/chat/chat.min.js b/public/assets/chat/chat.min.js index fffffd5d0..4651c28da 100644 --- a/public/assets/chat/chat.min.js +++ b/public/assets/chat/chat.min.js @@ -1 +1 @@ -var bind=function(t,e){return function(){return t.apply(e,arguments)}},slice=[].slice;!function(t,e){var s,n,i,a;return a=document.getElementsByTagName("script"),n=a[a.length-1],i=n.src.match(".*://([^:/]*).*")[1],s=function(){function s(s){return this.setAgentOnlineState=bind(this.setAgentOnlineState,this),this.onConnectionEstablished=bind(this.onConnectionEstablished,this),this.setSessionId=bind(this.setSessionId,this),this.onConnectionReestablished=bind(this.onConnectionReestablished,this),this.reconnect=bind(this.reconnect,this),this.onWebSocketOpen=bind(this.onWebSocketOpen,this),this.wsReconnect=bind(this.wsReconnect,this),this.wsClose=bind(this.wsClose,this),this.wsConnect=bind(this.wsConnect,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.onCloseAnimationEnd=bind(this.onCloseAnimationEnd,this),this.closeWindow=bind(this.closeWindow,this),this.close=bind(this.close,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.onTypingEnd=bind(this.onTypingEnd,this),this.onInput=bind(this.onInput,this),this.reopenSession=bind(this.reopenSession,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),this.view=bind(this.view,this),this.log=bind(this.log,this),this.T=bind(this.T,this),this.options=t.extend({},this.defaults,s),e.WebSocket&&sessionStorage?this.options.chatId?(this.el=t(this.view("chat")({title:this.options.title})),this.options.target.append(this.el),this.input=this.el.find(".zammad-chat-input"),t("."+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}),void this.wsConnect()):(this.state="unsupported",void this.log("error","Chat: need chatId as option!")):(this.state="unsupported",void this.log("notice","Chat: Browser not supported!"))}return s.prototype.defaults={chatId:void 0,show:!0,target:t("body"),host:"",debug:!1,flat:!1,fontSize:void 0,buttonClass:"open-zammad-chat",inactiveClass:"is-inactive",title:"Chat with us!"},s.prototype._messageCount=0,s.prototype.isOpen=!0,s.prototype.blinkOnlineInterval=null,s.prototype.stopBlinOnlineStateTimeout=null,s.prototype.showTimeEveryXMinutes=1,s.prototype.lastTimestamp=null,s.prototype.lastAddedType=null,s.prototype.inputTimeout=null,s.prototype.isTyping=!1,s.prototype.state="offline",s.prototype.initialQueueDelay=1e4,s.prototype.wsReconnectEnable=!0,s.prototype.strings={Online:"Online",Offline:"Offline",Connecting:"Verbinden","Connection re-established":"Verbindung wiederhergestellt",Today:"Heute",Send:"Senden","Compose your message...":"Ihre Nachricht...","All colleges are busy.":"Alle Kollegen sind belegt.","You are on waiting list position %s.":"Sie sind in der Warteliste an der Position %s.","Start new conversation":"Neue Konversation starten","Since you didn't respond in the last %s your conversation with %s got closed.":"Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit %s geschlossen.",minutes:"Minuten"},s.prototype.sessionId=void 0,s.prototype.T=function(){var t,e,s,n,i,a;if(i=arguments[0],s=2<=arguments.length?slice.call(arguments,1):[],this.strings[i]||this.log("notice","Translation needed for '"+i+"'"),a=this.strings[i]||i,s)for(t=0,n=s.length;n>t;t++)e=s[t],a=a.replace(/%s/,e);return a},s.prototype.log=function(){var t,e;return t=arguments[0],e=2<=arguments.length?slice.call(arguments,1):[],this.options.debug||"debug"!==t?(e.unshift(t),console.log.apply(console,e)):void 0},s.prototype.view=function(t){return function(s){return function(n){return n||(n={}),n.T=s.T,n.background=s.options.background,n.flat=s.options.flat,n.fontSize=s.options.fontSize,e.zammadChatTemplates[t](n)}}(this)},s.prototype.checkForEnter=function(t){return t.shiftKey||13!==t.keyCode?void 0:(t.preventDefault(),this.sendMessage())},s.prototype.send=function(t,e){var s;return null==e&&(e={}),e.chat_id=this.options.chatId,this.log("debug","ws:send",t,e),s=JSON.stringify({event:t,data:e}),this.ws.send(s)},s.prototype.onWebSocketMessage=function(t){var e,s,n,i;for(i=JSON.parse(t.data),e=0,s=i.length;s>e;e++)switch(n=i[e],this.log("debug","ws:onmessage",n),n.event){case"chat_error":this.log("error",n.data);break;case"chat_session_message":if(n.data.self_written)return;this.receiveMessage(n.data);break;case"chat_session_typing":if(n.data.self_written)return;this.onAgentTypingStart();break;case"chat_session_start":this.onConnectionEstablished(n.data);break;case"chat_session_queue":this.onQueueScreen(n.data);break;case"chat_session_closed":this.onSessionClosed(n.data);break;case"chat_session_left":this.onSessionClosed(n.data);break;case"chat_status_customer":switch(n.data.state){case"online":this.sessionId=void 0,this.onReady(),this.log("debug","Zammad Chat: ready");break;case"offline":this.onError("Zammad Chat: No agent online"),this.state="off",this.hide(),this.wsClose();break;case"chat_disabled":this.onError("Zammad Chat: Chat is disabled"),this.state="off",this.hide(),this.wsClose();break;case"no_seats_available":this.onError("Zammad Chat: Too many clients in queue. Clients in queue: "+n.data.queue),this.state="off",this.hide(),this.wsClose();break;case"reconnect":this.log("debug","old messages",n.data.session),this.reopenSession(n.data)}}},s.prototype.onReady=function(){return t("."+this.options.buttonClass).click(this.open).removeClass(this.inactiveClass),this.options.show?this.show():void 0},s.prototype.onError=function(e){return this.log("debug",e),t("."+this.options.buttonClass).hide()},s.prototype.reopenSession=function(t){var e,s,n,i,a;if(a=sessionStorage.getItem("unfinished_message"),t.agent){for(this.onConnectionEstablished(t),i=t.session,e=0,s=i.length;s>e;e++)n=i[e],this.renderMessage({message:n.content,id:n.id,from:n.created_by_id?"agent":"customer"});a&&this.input.val(a)}return t.position&&this.onQueue(t),this.show(),this.open(),this.scrollToBottom(),a?this.input.focus():void 0},s.prototype.onInput=function(){return this.el.find(".zammad-chat-message--unread").removeClass("zammad-chat-message--unread"),sessionStorage.setItem("unfinished_message",this.input.val()),this.onTypingStart()},s.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.sessionId}))},s.prototype.onTypingEnd=function(){return this.isTyping=!1},s.prototype.onSubmit=function(t){return t.preventDefault(),this.sendMessage()},s.prototype.sendMessage=function(){var t,e;return(t=this.input.val())?(sessionStorage.removeItem("unfinished_message"),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.input.val(""),this.scrollToBottom(),this.isTyping=!1,this.send("chat_session_message",{content:t,id:this._messageCount,session_id:this.sessionId})):void 0},s.prototype.receiveMessage=function(t){return this.onAgentTypingEnd(),this.maybeAddTimestamp(),this.renderMessage({message:t.message.content,id:t.id,from:"agent"})},s.prototype.renderMessage=function(t){var e,s;return this.lastAddedType="message--"+t.from,s=null!=(e=document.hidden)?e:{" zammad-chat-message--unread":""},this.el.find(".zammad-chat-body").append(this.view("message")(t)),this.scrollToBottom()},s.prototype.open=function(){return this.isOpen&&this.show(),this.sessionId||this.showLoader(),this.el.addClass("zammad-chat-is-open"),this.sessionId?(this.el.css("bottom",0),this.onOpenAnimationEnd()):this.el.animate({bottom:0},500,this.onOpenAnimationEnd),this.isOpen=!0,this.sessionId?void 0:this.session_init()},s.prototype.onOpenAnimationEnd=function(){},s.prototype.close=function(t){return"off"===this.state||"unsupported"===this.state?this.state:(t&&t.stopPropagation(),this.sessionId?(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.closeWindow()):void 0)},s.prototype.closeWindow=function(){var t;return this.el.removeClass("zammad-chat-is-open"),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-t},500,this.onCloseAnimationEnd)},s.prototype.onCloseAnimationEnd=function(){return this.el.removeClass("zammad-chat-is-visible"),this.disconnect(),this.isOpen=!1,this.send("chat_session_close",{session_id:this.sessionId}),this.setSessionId(void 0),sessionStorage.removeItem("unfinished_message"),this.onWebSocketOpen()},s.prototype.hide=function(){return this.el.removeClass("zammad-chat-is-shown")},s.prototype.show=function(){var t;return"off"===this.state||"unsupported"===this.state?this.state:(this.el.addClass("zammad-chat-is-shown"),this.inputInitialized||(this.inputInitialized=!0,this.input.autoGrow({extraLine:!1})),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.css("bottom",-t))},s.prototype.disableInput=function(){return this.input.prop("disabled",!0),this.el.find(".zammad-chat-send").prop("disabled",!0)},s.prototype.enableInput=function(){return this.input.prop("disabled",!1),this.el.find(".zammad-chat-send").prop("disabled",!1)},s.prototype.onQueueScreen=function(t){var e;return this.setSessionId(t.session_id),e=function(e){return function(){return e.onQueue(t)}}(this),this.initialQueueDelay&&!this.onInitialQueueDelayId?void(this.onInitialQueueDelayId=setTimeout(e,this.initialQueueDelay)):(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),e())},s.prototype.onQueue=function(t){return this.log("notice","onQueue",t.position),this.inQueue=!0,this.el.find(".zammad-chat-body").html(this.view("waiting")({position:t.position}))},s.prototype.onAgentTypingStart=function(){return this.stopTypingId&&clearTimeout(this.stopTypingId),this.stopTypingId=setTimeout(this.onAgentTypingEnd,3e3),this.el.find(".zammad-chat-message--typing").size()?void 0:(this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("typingIndicator")()),this.scrollToBottom())},s.prototype.onAgentTypingEnd=function(){return this.el.find(".zammad-chat-message--typing").remove()},s.prototype.maybeAddTimestamp=function(){var t,e,s;return s=Date.now(),!this.lastTimestamp||s-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=s):(this.el.find(".zammad-chat-body").append(this.view("timestamp")({label:t,time:e})),this.lastTimestamp=s,this.lastAddedType="timestamp",this.scrollToBottom())):void 0},s.prototype.updateLastTimestamp=function(t,e){return this.el.find(".zammad-chat-body").find(".zammad-chat-timestamp").last().replaceWith(this.view("timestamp")({label:t,time:e}))},s.prototype.addStatus=function(t){return this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("status")({status:t})),this.scrollToBottom()},s.prototype.scrollToBottom=function(){return this.el.find(".zammad-chat-body").scrollTop(t(".zammad-chat-body").prop("scrollHeight"))},s.prototype.session_init=function(){return this.send("chat_session_init")},s.prototype.detectHost=function(){var t;return t="ws://","https:"===e.location.protocol&&(t="wss://"),this.options.host=""+t+i+"/ws"},s.prototype.wsConnect=function(){return this.options.host||this.detectHost(),this.log("debug","Connecting to "+this.options.host),this.ws=new e.WebSocket(""+this.options.host),this.ws.onopen=this.onWebSocketOpen,this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(e){return t.log("debug","close websocket connection"),t.wsReconnectEnable?t.reconnect():void 0}}(this),this.ws.onerror=function(t){return function(e){return t.log("debug","ws:onerror",e)}}(this)},s.prototype.wsClose=function(){return this.wsReconnectEnable=!1,this.ws.close()},s.prototype.wsReconnect=function(){return this.reconnectDelayId&&clearTimeout(this.reconnectDelayId),this.reconnectDelayId=setTimeout(this.wsConnect,5e3)},s.prototype.onWebSocketOpen=function(){return this.sessionId=sessionStorage.getItem("sessionId"),this.log("debug","ws connected"),this.send("chat_status_customer",{session_id:this.sessionId}),this.setAgentOnlineState("online")},s.prototype.reconnect=function(){return this.log("notice","reconnecting"),this.disableInput(),this.lastAddedType="status",this.setAgentOnlineState("connecting"),this.addStatus(this.T("Connection lost")),this.wsReconnect()},s.prototype.onConnectionReestablished=function(){return this.lastAddedType="status",this.setAgentOnlineState("online"),this.addStatus(this.T("Connection re-established"))},s.prototype.onSessionClosed=function(t){return this.addStatus(this.T("Chat closed by %s",t.realname)),this.disableInput(),this.setAgentOnlineState("offline")},s.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")},s.prototype.setSessionId=function(t){return this.sessionId=t,void 0===t?sessionStorage.removeItem("sessionId"):sessionStorage.setItem("sessionId",t)},s.prototype.onConnectionEstablished=function(t){return this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.inQueue=!1,t.agent&&(this.agent=t.agent),t.session_id&&this.setSessionId(t.session_id),this.el.find(".zammad-chat-agent").html(this.view("agent")({agent:this.agent})),this.enableInput(),this.el.find(".zammad-chat-body").empty(),this.el.find(".zammad-chat-welcome").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").removeClass("zammad-chat-is-hidden"),this.input.focus(),this.setAgentOnlineState("online")},s.prototype.showTimeout=function(){return this.el.find(".zammad-chat-body").html(this.view("timeout")({agent:this.agent.name,delay:10,unit:this.T("minutes")}))},s.prototype.showLoader=function(){return this.el.find(".zammad-chat-body").html(this.view("loader")())},s.prototype.setAgentOnlineState=function(t){var e;return this.state=t,e=t.charAt(0).toUpperCase()+t.slice(1),this.el.find(".zammad-chat-agent-status").attr("data-status",t).text(this.T(e))},s}(),e.ZammadChat=s}(window.jQuery,window),window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.agent=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){this.agent.avatar&&(s.push('\n\n')),s.push('\n\n '),s.push(n(this.agent.name)),s.push("\n")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),s=function(t){return jQuery(t).after('
'),jQuery(t).next(".autogrow-textarea-mirror")[0]},n=function(t){a.innerHTML=String(t.value).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/ /g," ").replace(/\n/g,"
")+(e.extraLine?".
.":""),jQuery(t).height()!=jQuery(a).height()&&jQuery(t).height(jQuery(a).height())},i=function(){n(this)},a=s(this);a.style.display="none",a.style.wordWrap="break-word",a.style.whiteSpace="normal",a.style.padding=jQuery(this).css("paddingTop")+" "+jQuery(this).css("paddingRight")+" "+jQuery(this).css("paddingBottom")+" "+jQuery(this).css("paddingLeft"),a.style.width=jQuery(this).css("width"),a.style.fontFamily=jQuery(this).css("font-family"),a.style.fontSize=jQuery(this).css("font-size"),a.style.lineHeight=jQuery(this).css("line-height"),this.style.overflow="hidden",this.style.minHeight=this.rows+"em",this.onkeyup=i,n(this)})},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.chat=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n
\n \n \n \n \n \n
\n
\n
\n
\n \n '),s.push(this.title),s.push('\n
\n
\n
\n
\n \n \n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.loader=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n '),s.push(this.T("Connecting")),s.push("\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.message=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n "),s.push(this.message),s.push("\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.status=function(t){t||(t={});var e,s=[],n=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(){s.push('
'),s.push(this.status),s.push("
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timeout=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n '),s.push(this.T("Since you didn't respond in the last %s your conversation with %s got closed.",this.delay+" "+this.unit,this.agent)),s.push('
\n
"),s.push(this.T("Start new conversation")),s.push("
\n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timestamp=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
'),s.push(n(this.label)),s.push(" "),s.push(n(this.time)),s.push("
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.typingIndicator=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n \n \n
')}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.waiting=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n
\n \n \n \n \n \n '),s.push(this.T("All colleges are busy.")),s.push("
\n "),s.push(this.T("You are on waiting list position %s.",this.position)),s.push("\n
\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")}; \ No newline at end of file +window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.agent=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){this.agent.avatar&&(s.push('\n\n')),s.push('\n\n '),s.push(n(this.agent.name)),s.push("\n")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")};var bind=function(t,e){return function(){return t.apply(e,arguments)}},slice=[].slice;!function(t,e){var s,n,i,a;return a=document.getElementsByTagName("script"),n=a[a.length-1],i=n.src.match(".*://([^:/]*).*")[1],s=function(){function s(s){return this.setAgentOnlineState=bind(this.setAgentOnlineState,this),this.onConnectionEstablished=bind(this.onConnectionEstablished,this),this.setSessionId=bind(this.setSessionId,this),this.onConnectionReestablished=bind(this.onConnectionReestablished,this),this.reconnect=bind(this.reconnect,this),this.onWebSocketOpen=bind(this.onWebSocketOpen,this),this.wsReconnect=bind(this.wsReconnect,this),this.wsClose=bind(this.wsClose,this),this.wsConnect=bind(this.wsConnect,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.onCloseAnimationEnd=bind(this.onCloseAnimationEnd,this),this.closeWindow=bind(this.closeWindow,this),this.close=bind(this.close,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.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),this.view=bind(this.view,this),this.log=bind(this.log,this),this.T=bind(this.T,this),this.options=t.extend({},this.defaults,s),e.WebSocket&&sessionStorage?this.options.chatId?(this.el=t(this.view("chat")({title:this.options.title})),this.options.target.append(this.el),this.input=this.el.find(".zammad-chat-input"),t("."+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}),void this.wsConnect()):(this.state="unsupported",void this.log("error","Chat: need chatId as option!")):(this.state="unsupported",void this.log("notice","Chat: Browser not supported!"))}return s.prototype.defaults={chatId:void 0,show:!0,target:t("body"),host:"",debug:!1,flat:!1,fontSize:void 0,buttonClass:"open-zammad-chat",inactiveClass:"is-inactive",title:"Chat with us!"},s.prototype._messageCount=0,s.prototype.isOpen=!0,s.prototype.blinkOnlineInterval=null,s.prototype.stopBlinOnlineStateTimeout=null,s.prototype.showTimeEveryXMinutes=1,s.prototype.lastTimestamp=null,s.prototype.lastAddedType=null,s.prototype.inputTimeout=null,s.prototype.isTyping=!1,s.prototype.state="offline",s.prototype.initialQueueDelay=1e4,s.prototype.wsReconnectEnable=!0,s.prototype.strings={Online:"Online",Offline:"Offline",Connecting:"Verbinden","Connection re-established":"Verbindung wiederhergestellt",Today:"Heute",Send:"Senden","Compose your message...":"Ihre Nachricht...","All colleges are busy.":"Alle Kollegen sind belegt.","You are on waiting list position %s.":"Sie sind in der Warteliste an der Position %s.","Start new conversation":"Neue Konversation starten","Since you didn't respond in the last %s your conversation with %s got closed.":"Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit %s geschlossen.",minutes:"Minuten"},s.prototype.sessionId=void 0,s.prototype.T=function(){var t,e,s,n,i,a;if(i=arguments[0],s=2<=arguments.length?slice.call(arguments,1):[],this.strings[i]||this.log("notice","Translation needed for '"+i+"'"),a=this.strings[i]||i,s)for(t=0,n=s.length;n>t;t++)e=s[t],a=a.replace(/%s/,e);return a},s.prototype.log=function(){var t,e;return t=arguments[0],e=2<=arguments.length?slice.call(arguments,1):[],this.options.debug||"debug"!==t?(e.unshift(t),console.log.apply(console,e)):void 0},s.prototype.view=function(t){return function(s){return function(n){return n||(n={}),n.T=s.T,n.background=s.options.background,n.flat=s.options.flat,n.fontSize=s.options.fontSize,e.zammadChatTemplates[t](n)}}(this)},s.prototype.checkForEnter=function(t){return t.shiftKey||13!==t.keyCode?void 0:(t.preventDefault(),this.sendMessage())},s.prototype.send=function(t,e){var s;return null==e&&(e={}),e.chat_id=this.options.chatId,this.log("debug","ws:send",t,e),s=JSON.stringify({event:t,data:e}),this.ws.send(s)},s.prototype.onWebSocketMessage=function(t){var e,s,n,i;for(i=JSON.parse(t.data),e=0,s=i.length;s>e;e++)switch(n=i[e],this.log("debug","ws:onmessage",n),n.event){case"chat_error":this.log("error",n.data);break;case"chat_session_message":if(n.data.self_written)return;this.receiveMessage(n.data);break;case"chat_session_typing":if(n.data.self_written)return;this.onAgentTypingStart();break;case"chat_session_start":this.onConnectionEstablished(n.data);break;case"chat_session_queue":this.onQueueScreen(n.data);break;case"chat_session_closed":this.onSessionClosed(n.data);break;case"chat_session_left":this.onSessionClosed(n.data);break;case"chat_status_customer":switch(n.data.state){case"online":this.sessionId=void 0,this.onReady(),this.log("debug","Zammad Chat: ready");break;case"offline":this.onError("Zammad Chat: No agent online"),this.state="off",this.hide(),this.wsClose();break;case"chat_disabled":this.onError("Zammad Chat: Chat is disabled"),this.state="off",this.hide(),this.wsClose();break;case"no_seats_available":this.onError("Zammad Chat: Too many clients in queue. Clients in queue: "+n.data.queue),this.state="off",this.hide(),this.wsClose();break;case"reconnect":this.log("debug","old messages",n.data.session),this.reopenSession(n.data)}}},s.prototype.onReady=function(){return t("."+this.options.buttonClass).click(this.open).removeClass(this.inactiveClass),this.options.show?this.show():void 0},s.prototype.onError=function(e){return this.log("debug",e),t("."+this.options.buttonClass).hide()},s.prototype.reopenSession=function(t){var e,s,n,i,a;if(a=sessionStorage.getItem("unfinished_message"),t.agent){for(this.onConnectionEstablished(t),i=t.session,e=0,s=i.length;s>e;e++)n=i[e],this.renderMessage({message:n.content,id:n.id,from:n.created_by_id?"agent":"customer"});a&&this.input.val(a)}return t.position&&this.onQueue(t),this.show(),this.open(),this.scrollToBottom(),a?this.input.focus():void 0},s.prototype.onInput=function(){return this.el.find(".zammad-chat-message--unread").removeClass("zammad-chat-message--unread"),sessionStorage.setItem("unfinished_message",this.input.val()),this.onTyping()},s.prototype.onTyping=function(){return this.isTyping&&this.isTyping>new Date((new Date).getTime()-1500)?void 0:(this.isTyping=new Date,this.send("chat_session_typing",{session_id:this.sessionId}))},s.prototype.onSubmit=function(t){return t.preventDefault(),this.sendMessage()},s.prototype.sendMessage=function(){var t,e;return(t=this.input.val())?(sessionStorage.removeItem("unfinished_message"),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.input.val(""),this.scrollToBottom(),this.send("chat_session_message",{content:t,id:this._messageCount,session_id:this.sessionId})):void 0},s.prototype.receiveMessage=function(t){return this.onAgentTypingEnd(),this.maybeAddTimestamp(),this.renderMessage({message:t.message.content,id:t.id,from:"agent"})},s.prototype.renderMessage=function(t){var e,s;return this.lastAddedType="message--"+t.from,s=null!=(e=document.hidden)?e:{" zammad-chat-message--unread":""},this.el.find(".zammad-chat-body").append(this.view("message")(t)),this.scrollToBottom()},s.prototype.open=function(){return this.isOpen&&this.show(),this.sessionId||this.showLoader(),this.el.addClass("zammad-chat-is-open"),this.sessionId?(this.el.css("bottom",0),this.onOpenAnimationEnd()):this.el.animate({bottom:0},500,this.onOpenAnimationEnd),this.isOpen=!0,this.sessionId?void 0:this.session_init()},s.prototype.onOpenAnimationEnd=function(){},s.prototype.close=function(t){return"off"===this.state||"unsupported"===this.state?this.state:(t&&t.stopPropagation(),this.sessionId?(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.closeWindow()):void 0)},s.prototype.closeWindow=function(){var t;return this.el.removeClass("zammad-chat-is-open"),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-t},500,this.onCloseAnimationEnd)},s.prototype.onCloseAnimationEnd=function(){return this.el.removeClass("zammad-chat-is-visible"),this.disconnect(),this.isOpen=!1,this.send("chat_session_close",{session_id:this.sessionId}),this.setSessionId(void 0),sessionStorage.removeItem("unfinished_message"),this.onWebSocketOpen()},s.prototype.hide=function(){return this.el.removeClass("zammad-chat-is-shown")},s.prototype.show=function(){var t;return"off"===this.state||"unsupported"===this.state?this.state:(this.el.addClass("zammad-chat-is-shown"),this.inputInitialized||(this.inputInitialized=!0,this.input.autoGrow({extraLine:!1})),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.css("bottom",-t))},s.prototype.disableInput=function(){return this.input.prop("disabled",!0),this.el.find(".zammad-chat-send").prop("disabled",!0)},s.prototype.enableInput=function(){return this.input.prop("disabled",!1),this.el.find(".zammad-chat-send").prop("disabled",!1)},s.prototype.onQueueScreen=function(t){var e;return this.setSessionId(t.session_id),e=function(e){return function(){return e.onQueue(t)}}(this),this.initialQueueDelay&&!this.onInitialQueueDelayId?void(this.onInitialQueueDelayId=setTimeout(e,this.initialQueueDelay)):(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),e())},s.prototype.onQueue=function(t){return this.log("notice","onQueue",t.position),this.inQueue=!0,this.el.find(".zammad-chat-body").html(this.view("waiting")({position:t.position}))},s.prototype.onAgentTypingStart=function(){return this.stopTypingId&&clearTimeout(this.stopTypingId),this.stopTypingId=setTimeout(this.onAgentTypingEnd,3e3),this.el.find(".zammad-chat-message--typing").size()?void 0:(this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("typingIndicator")()),this.scrollToBottom())},s.prototype.onAgentTypingEnd=function(){return this.el.find(".zammad-chat-message--typing").remove()},s.prototype.maybeAddTimestamp=function(){var t,e,s;return s=Date.now(),!this.lastTimestamp||s-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=s):(this.el.find(".zammad-chat-body").append(this.view("timestamp")({label:t,time:e})),this.lastTimestamp=s,this.lastAddedType="timestamp",this.scrollToBottom())):void 0},s.prototype.updateLastTimestamp=function(t,e){return this.el.find(".zammad-chat-body").find(".zammad-chat-timestamp").last().replaceWith(this.view("timestamp")({label:t,time:e}))},s.prototype.addStatus=function(t){return this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("status")({status:t})),this.scrollToBottom()},s.prototype.scrollToBottom=function(){return this.el.find(".zammad-chat-body").scrollTop(t(".zammad-chat-body").prop("scrollHeight"))},s.prototype.session_init=function(){return this.send("chat_session_init")},s.prototype.detectHost=function(){var t;return t="ws://","https:"===e.location.protocol&&(t="wss://"),this.options.host=""+t+i+"/ws"},s.prototype.wsConnect=function(){return this.options.host||this.detectHost(),this.log("debug","Connecting to "+this.options.host),this.ws=new e.WebSocket(""+this.options.host),this.ws.onopen=this.onWebSocketOpen,this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(e){return t.log("debug","close websocket connection"),t.wsReconnectEnable?t.reconnect():void 0}}(this),this.ws.onerror=function(t){return function(e){return t.log("debug","ws:onerror",e)}}(this)},s.prototype.wsClose=function(){return this.wsReconnectEnable=!1,this.ws.close()},s.prototype.wsReconnect=function(){return this.reconnectDelayId&&clearTimeout(this.reconnectDelayId),this.reconnectDelayId=setTimeout(this.wsConnect,5e3)},s.prototype.onWebSocketOpen=function(){return this.sessionId=sessionStorage.getItem("sessionId"),this.log("debug","ws connected"),this.send("chat_status_customer",{session_id:this.sessionId}),this.setAgentOnlineState("online")},s.prototype.reconnect=function(){return this.log("notice","reconnecting"),this.disableInput(),this.lastAddedType="status",this.setAgentOnlineState("connecting"),this.addStatus(this.T("Connection lost")),this.wsReconnect()},s.prototype.onConnectionReestablished=function(){return this.lastAddedType="status",this.setAgentOnlineState("online"),this.addStatus(this.T("Connection re-established"))},s.prototype.onSessionClosed=function(t){return this.addStatus(this.T("Chat closed by %s",t.realname)),this.disableInput(),this.setAgentOnlineState("offline")},s.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")},s.prototype.setSessionId=function(t){return this.sessionId=t,void 0===t?sessionStorage.removeItem("sessionId"):sessionStorage.setItem("sessionId",t)},s.prototype.onConnectionEstablished=function(t){return this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.inQueue=!1,t.agent&&(this.agent=t.agent),t.session_id&&this.setSessionId(t.session_id),this.el.find(".zammad-chat-agent").html(this.view("agent")({agent:this.agent})),this.enableInput(),this.el.find(".zammad-chat-body").empty(),this.el.find(".zammad-chat-welcome").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").removeClass("zammad-chat-is-hidden"),this.input.focus(),this.setAgentOnlineState("online")},s.prototype.showTimeout=function(){return this.el.find(".zammad-chat-body").html(this.view("timeout")({agent:this.agent.name,delay:10,unit:this.T("minutes")}))},s.prototype.showLoader=function(){return this.el.find(".zammad-chat-body").html(this.view("loader")())},s.prototype.setAgentOnlineState=function(t){var e;return this.state=t,e=t.charAt(0).toUpperCase()+t.slice(1),this.el.find(".zammad-chat-agent-status").attr("data-status",t).text(this.T(e))},s}(),e.ZammadChat=s}(window.jQuery,window),jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),s=function(t){return jQuery(t).after('
'),jQuery(t).next(".autogrow-textarea-mirror")[0]},n=function(t){a.innerHTML=String(t.value).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/ /g," ").replace(/\n/g,"
")+(e.extraLine?".
.":""),jQuery(t).height()!=jQuery(a).height()&&jQuery(t).height(jQuery(a).height())},i=function(){n(this)},a=s(this);a.style.display="none",a.style.wordWrap="break-word",a.style.whiteSpace="normal",a.style.padding=jQuery(this).css("paddingTop")+" "+jQuery(this).css("paddingRight")+" "+jQuery(this).css("paddingBottom")+" "+jQuery(this).css("paddingLeft"),a.style.width=jQuery(this).css("width"),a.style.fontFamily=jQuery(this).css("font-family"),a.style.fontSize=jQuery(this).css("font-size"),a.style.lineHeight=jQuery(this).css("line-height"),this.style.overflow="hidden",this.style.minHeight=this.rows+"em",this.onkeyup=i,n(this)})},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.chat=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n
\n \n \n \n \n \n
\n
\n
\n
\n \n '),s.push(this.title),s.push('\n
\n
\n
\n
\n \n \n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.loader=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n '),s.push(this.T("Connecting")),s.push("\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.message=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n "),s.push(this.message),s.push("\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.status=function(t){t||(t={});var e,s=[],n=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(){s.push('
'),s.push(this.status),s.push("
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timeout=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n '),s.push(this.T("Since you didn't respond in the last %s your conversation with %s got closed.",this.delay+" "+this.unit,this.agent)),s.push('
\n
"),s.push(this.T("Start new conversation")),s.push("
\n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timestamp=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
'),s.push(n(this.label)),s.push(" "),s.push(n(this.time)),s.push("
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.typingIndicator=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n \n \n
')}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.waiting=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n
\n \n \n \n \n \n '),s.push(this.T("All colleges are busy.")),s.push("
\n "),s.push(this.T("You are on waiting list position %s.",this.position)),s.push("\n
\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")}; \ No newline at end of file From 069063b2c723b57922e7ff1e7abc3f9b3e877be8 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 25 Nov 2015 17:36:06 +0100 Subject: [PATCH 2/8] Use domain of users email address for preview. Added css auto load and css Url support. Added possibility to translation chat widget. --- .../app/controllers/_channel/chat.coffee | 44 ++-- .../app/views/channel/chat.jst.eco | 9 +- public/assets/chat/chat.coffee | 75 +++++-- public/assets/chat/chat.js | 202 +++++++++++------- public/assets/chat/chat.min.js | 2 +- public/assets/chat/index.html | 2 +- public/assets/chat/views/chat.eco | 2 +- public/assets/chat/znuny.html | 135 ++++++------ 8 files changed, 284 insertions(+), 187 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_channel/chat.coffee b/app/assets/javascripts/app/controllers/_channel/chat.coffee index 5fd4f65fd..5ce8032b4 100644 --- a/app/assets/javascripts/app/controllers/_channel/chat.coffee +++ b/app/assets/javascripts/app/controllers/_channel/chat.coffee @@ -30,10 +30,10 @@ class App.ChannelChat extends App.Controller apiOptions: [ { - name: 'channel' - default: "'default'" - type: 'String' - description: 'Name of the chat-channel.' + name: 'chatId' + default: '1' + type: 'Integer' + description: 'Identifier of the chat-topic.' } { name: 'show' @@ -54,18 +54,18 @@ class App.ChannelChat extends App.Controller description: "If left empty, the host gets auto-detected - in this case %s. The auto-detection reads out the host from the + .mockup { + vertical-align: bottom; + } + .settings { + position: fixed; + left: 20px; + top: 20px; + background: white; + font-size: 14px; + padding: 10px; + border-radius: 5px; + box-shadow: 0 3px 10px rgba(0,0,0,.3); + } + + .settings input { + vertical-align: middle; + } + .settings input + input { + margin-right: 3px; + } + + table td:first-child { + text-align: right; + padding-right: 0; + } + + td { + padding: 5px; + } + + h2 { + font-size: 1em; + margin: 0; + } + + @media only screen and (max-width: 768px) { + .settings { + display: none; + } + } + + .Box { + background: hsl(0,0%,91%); + width: 26px; + height: 24px; + color: hsl(0,0%,47%); + float: left; + } + .Box.Active { + background: hsl(0,0%,36%); + color: white; + } + + +
@@ -107,6 +109,7 @@ var chat = new ZammadChat({ chatId: 1, host: 'ws://localhost:6042', + cssUrl: 'http://localhost:3000/assets/chat/chat.css', debug: true, background: '#494d52', flat: true, @@ -144,4 +147,6 @@ } } }); - \ No newline at end of file + + + \ No newline at end of file From 6ce3116c33dc08649649bf4f8cefdf7866601910 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Wed, 25 Nov 2015 23:24:08 +0100 Subject: [PATCH 3/8] Small ui improvements. --- .../app/controllers/_channel/chat.coffee | 121 ++++++++++-------- .../app/controllers/_channel/form.coffee | 28 +++- .../app/views/channel/chat.jst.eco | 52 ++------ .../app/views/channel/form.jst.eco | 21 +-- .../app/views/channel/topics.jst.eco | 41 ++++++ app/controllers/chats_controller.rb | 2 + 6 files changed, 152 insertions(+), 113 deletions(-) create mode 100644 app/assets/javascripts/app/views/channel/topics.jst.eco diff --git a/app/assets/javascripts/app/controllers/_channel/chat.coffee b/app/assets/javascripts/app/controllers/_channel/chat.coffee index 5ce8032b4..e24caaa72 100644 --- a/app/assets/javascripts/app/controllers/_channel/chat.coffee +++ b/app/assets/javascripts/app/controllers/_channel/chat.coffee @@ -1,9 +1,5 @@ class App.ChannelChat extends App.Controller events: - 'click .js-add': 'new' - 'click .js-edit': 'edit' - 'click .js-remove': 'remove' - 'click .js-widget': 'widget' 'change .js-params': 'updateParams' 'input .js-params': 'updateParams' 'submit .js-demo-head': 'onUrlSubmit' @@ -11,6 +7,7 @@ class App.ChannelChat extends App.Controller 'click .js-selectBrowserWidth': 'selectBrowserWidth' 'click .js-swatch': 'usePaletteColor' 'click .js-toggle-chat': 'toggleChat' + 'click .js-chatSetting': 'toggleChatSetting' elements: '.js-browser': 'browser' @@ -27,12 +24,13 @@ class App.ChannelChat extends App.Controller '.js-code': 'code' '.js-palette': 'palette' '.js-color': 'colorField' + '.js-chatSetting': 'chatSetting' apiOptions: [ { name: 'chatId' default: '1' - type: 'Integer' + type: 'Number' description: 'Identifier of the chat-topic.' } { @@ -75,7 +73,7 @@ class App.ChannelChat extends App.Controller { name: 'flat' default: 'false' - type: 'boolean' + type: 'Boolean' description: 'Removes the shadows for a flat look.' } { @@ -93,7 +91,7 @@ class App.ChannelChat extends App.Controller { name: 'cssAutoload' default: 'true' - type: 'boolean' + type: 'Boolean' description: 'Automatically loads the chat.css file. If you want to use your own css, just set it to false.' } { @@ -110,7 +108,7 @@ class App.ChannelChat extends App.Controller constructor: -> super - + @title 'Chat' if @Session.get('email') @previewUrl = "www.#{@Session.get('email').replace(/^.+?\@/, '')}" @@ -135,16 +133,15 @@ class App.ChannelChat extends App.Controller ) render: (data = {}) => - - chats = [] - for chat_id in data.chat_ids - chats.push App.Chat.find(chat_id) - @html App.view('channel/chat')( baseurl: window.location.origin - chats: chats apiOptions: @apiOptions previewUrl: @previewUrl + chatSetting: @Config.get('chat') + ) + + new Topics( + el: @$('.js-topics') ) @code.each (i, block) -> @@ -276,47 +273,12 @@ class App.ChannelChat extends App.Controller @isOpen = @chat.hasClass('is-open') @updatePreview() - new: (e) => - new App.ControllerGenericNew( - pageData: - title: 'Chats' - object: 'Chat' - objects: 'Chats' - genericObject: 'Chat' - callback: @load - container: @el.closest('.content') - large: true - ) - - edit: (e) => - e.preventDefault() - id = $(e.target).closest('tr').data('id') - new App.ControllerGenericEdit( - id: id - genericObject: 'Chat' - pageData: - object: 'Chat' - container: @el.closest('.content') - callback: @load - ) - - remove: (e) => - e.preventDefault() - id = $(e.target).closest('tr').data('id') - item = App.Chat.find(id) - new App.ControllerGenericDestroyConfirm( - item: item - container: @el.closest('.content') - callback: @load - ) - - widget: (e) -> - e.preventDefault() - id = $(e.target).closest('.action').data('id') - new Widget( - permanent: - id: id - ) + toggleChatSetting: => + value = @chatSetting.prop('checked') + setting = App.Setting.findByAttribute('name', 'chat') + setting.state_current = { value: value } + setting.save() + @Config.set('chat', value) updateParams: => quote = (value) -> @@ -360,3 +322,52 @@ class App.ChannelChat extends App.Controller hljs.highlightBlock block App.Config.set( 'Chat', { prio: 4000, name: 'Chat', parent: '#channels', target: '#channels/chat', controller: App.ChannelChat, role: ['Admin'] }, 'NavBarAdmin' ) + +class Topics extends App.Controller + events: + 'click .js-add': 'new' + 'click .js-edit': 'edit' + 'click .js-remove': 'remove' + + constructor: -> + super + @render() + + render: => + @html App.view('channel/topics')( + chats: App.Chat.all() + ) + + new: (e) => + new App.ControllerGenericNew( + pageData: + title: 'Chats' + object: 'Chat' + objects: 'Chats' + genericObject: 'Chat' + callback: @render + container: @el.closest('.content') + large: true + ) + + edit: (e) => + e.preventDefault() + id = $(e.target).closest('tr').data('id') + new App.ControllerGenericEdit( + id: id + genericObject: 'Chat' + pageData: + object: 'Chat' + container: @el.closest('.content') + callback: @render + ) + + remove: (e) => + e.preventDefault() + id = $(e.target).closest('tr').data('id') + item = App.Chat.find(id) + new App.ControllerGenericDestroyConfirm( + item: item + container: @el.closest('.content') + callback: @render + ) diff --git a/app/assets/javascripts/app/controllers/_channel/form.coffee b/app/assets/javascripts/app/controllers/_channel/form.coffee index 381220be3..cc15bff7c 100644 --- a/app/assets/javascripts/app/controllers/_channel/form.coffee +++ b/app/assets/javascripts/app/controllers/_channel/form.coffee @@ -3,22 +3,30 @@ class App.ChannelForm extends App.Controller events: 'change form.js-params': 'updateParams' 'keyup form.js-params': 'updateParams' + 'click .js-formSetting': 'toggleFormSetting' + + elements: + '.js-paramsBlock': 'paramsBlock' + '.js-formSetting': 'formSetting' constructor: -> super @title 'Form' - @render() - @updateParams() - new App.SettingsArea( - el: @$('.js-settings') - area: 'Form::Base' - ) + @subscribeId = App.Setting.subscribe(@render, initFetch: true) - render: -> + render: => + App.Setting.unsubscribe(@subscribeId) + setting = App.Setting.findByAttribute('name', 'form_ticket_create') @html App.view('channel/form')( baseurl: window.location.origin + formSetting: setting.state_current.value ) + @paramsBlock.each (i, block) -> + hljs.highlightBlock block + + @updateParams() + updateParams: -> quote = (string) -> string = string.replace('\'', '\\\'') @@ -36,4 +44,10 @@ class App.ChannelForm extends App.Controller paramString += " #{key}: '#{quote(value)}'" @$('.js-modal-params').html(paramString) + toggleFormSetting: => + value = @formSetting.prop('checked') + setting = App.Setting.findByAttribute('name', 'form_ticket_create') + setting.state_current = { value: value } + setting.save() + App.Config.set( 'Form', { prio: 2000, name: 'Form', parent: '#channels', target: '#channels/form', controller: App.ChannelForm, role: ['Admin'] }, 'NavBarAdmin' ) diff --git a/app/assets/javascripts/app/views/channel/chat.jst.eco b/app/assets/javascripts/app/views/channel/chat.jst.eco index 8ba0c8436..c4918513c 100644 --- a/app/assets/javascripts/app/views/channel/chat.jst.eco +++ b/app/assets/javascripts/app/views/channel/chat.jst.eco @@ -6,49 +6,17 @@

<%- @T('You can create chat widgets for your webpages to allow visitors to chat with you.') %>

+

<%- @T('Enable') %>/<%- @T('Disable') %>

+
+
+ checked<% end %>> + +
+
+

<%- @T('Topics') %>

<%- @T('You can create multiple chat topics.') %>

- - - - - - - - - - - - <% for chat in @chats: %> - - - -
<%- @T('chatId') %><%- @T('Name') %><%- @T('Note') %><%- @T('Max. clients in waitlist') %><%- @T('Delete') %>
- - - - - - - - -
-
- <%- @Icon('trash') %> <%- @T('Remove') %> -
-
- <% end %> -
- <%- @Icon('plus-small') %> <%- @T('Add') %> -
+

<%- @T('Designer') %>

@@ -124,7 +92,7 @@
- +
<%- @T('Shown when the chat is closed.') %>
diff --git a/app/assets/javascripts/app/views/channel/form.jst.eco b/app/assets/javascripts/app/views/channel/form.jst.eco index 67c113585..b097aea70 100644 --- a/app/assets/javascripts/app/views/channel/form.jst.eco +++ b/app/assets/javascripts/app/views/channel/form.jst.eco @@ -6,15 +6,19 @@

<%- @T('With form you can add a formular to your web page witch directly generates a Ticket for you.') %>

-
+

<%- @T('Enable') %>/<%- @T('Disable') %>

+
+
+ checked<% end %>> + +
+
-
- -

<%- @T('Widget Designer') %>

+

<%- @T('Designer') %>

-
+
@@ -22,7 +26,7 @@
-
+
@@ -95,8 +99,7 @@

<%- @T('You need to add the following Java Script code snipped to your web page') %>: -

-<button id="feedback-form">Feedback</button>
+  
<button id="feedback-form">Feedback</button>
 
 <script id="zammad_form_script" src="<%= @baseurl %>/assets/form/form.js"></script>
 
@@ -106,5 +109,5 @@ $(function() {
 
   });
 });
-</script>
+</script>
\ No newline at end of file diff --git a/app/assets/javascripts/app/views/channel/topics.jst.eco b/app/assets/javascripts/app/views/channel/topics.jst.eco new file mode 100644 index 000000000..578a58b84 --- /dev/null +++ b/app/assets/javascripts/app/views/channel/topics.jst.eco @@ -0,0 +1,41 @@ + + + + + + + + + + + +<% for chat in @chats: %> + + + +
<%- @T('chatId') %><%- @T('Name') %><%- @T('Note') %><%- @T('Max. clients in waitlist') %><%- @T('Delete') %>
+ + + + + + + + +
+
+ <%- @Icon('trash') %> <%- @T('Remove') %> +
+
+<% end %> +
+ <%- @Icon('plus-small') %> <%- @T('Add') %> +
\ No newline at end of file diff --git a/app/controllers/chats_controller.rb b/app/controllers/chats_controller.rb index 78e4708c7..8a32d2339 100644 --- a/app/controllers/chats_controller.rb +++ b/app/controllers/chats_controller.rb @@ -11,6 +11,8 @@ class ChatsController < ApplicationController chat_ids.push chat.id assets = chat.assets(assets) } + setting = Setting.find_by(name: 'chat') + assets = setting.assets(assets) render json: { chat_ids: chat_ids, assets: assets, From d71090df6ebc853f648306bbec3c56b6ec6ee19a Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 26 Nov 2015 00:40:52 +0100 Subject: [PATCH 4/8] Added idle and inactive timeouts. --- public/assets/chat/chat.coffee | 93 +++++++++++++++----- public/assets/chat/chat.js | 122 ++++++++++++++++++++++----- public/assets/chat/chat.min.js | 2 +- public/assets/chat/views/timeout.eco | 9 +- 4 files changed, 183 insertions(+), 43 deletions(-) diff --git a/public/assets/chat/chat.coffee b/public/assets/chat/chat.coffee index 26ad26fec..239026c32 100644 --- a/public/assets/chat/chat.coffee +++ b/public/assets/chat/chat.coffee @@ -14,13 +14,15 @@ do($ = window.jQuery, window) -> host: '' debug: false flat: false - lang: undefined, - cssAutoload: true, - cssUrl: undefined, + lang: undefined + cssAutoload: true + cssUrl: undefined fontSize: undefined buttonClass: 'open-zammad-chat' inactiveClass: 'is-inactive' title: 'Chat with us!' + idleTimeout: 8 + inactiveTimeout: 20 _messageCount: 0 isOpen: true @@ -48,8 +50,8 @@ do($ = window.jQuery, window) -> 'All colleges are busy.': 'Alle Kollegen sind belegt.' 'You are on waiting list position %s.': 'Sie sind in der Warteliste an der Position %s.' 'Start new conversation': 'Neue Konversation starten' - 'Since you didn\'t respond in the last %s your conversation with %s got closed.': 'Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit %s geschlossen.' - 'minutes': 'Minuten' + 'Since you didn\'t respond in the last %s minutes your conversation with %s got closed.': 'Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit %s 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.' sessionId: undefined T: (string, items...) => @@ -86,6 +88,10 @@ do($ = window.jQuery, window) -> @options = $.extend {}, @defaults, options # check prerequisites + if !$ + @state = 'unsupported' + @log 'notice', 'Chat: no jquery found!' + return if !window.WebSocket or !sessionStorage @state = 'unsupported' @log 'notice', 'Chat: Browser not supported!' @@ -143,7 +149,9 @@ do($ = window.jQuery, window) -> @log 'debug', 'ws:onmessage', pipe switch pipe.event when 'chat_error' - @log 'error', pipe.data + @log 'notice', pipe.data + if pipe.data && pipe.data.state is 'chat_disabled' + @wsClose() when 'chat_session_message' return if pipe.data.self_written @receiveMessage pipe.data @@ -194,6 +202,8 @@ do($ = window.jQuery, window) -> $(".#{ @options.buttonClass }").hide() reopenSession: (data) => + @inactiveTimeoutStart() + unfinishedMessage = sessionStorage.getItem 'unfinished_message' # rerender chat history @@ -236,6 +246,7 @@ do($ = window.jQuery, window) -> @isTyping = new Date() @send 'chat_session_typing', session_id: @sessionId + @inactiveTimeoutStart() onSubmit: (event) => event.preventDefault() @@ -243,9 +254,10 @@ do($ = window.jQuery, window) -> sendMessage: -> message = @input.val() - return if !message + @inactiveTimeoutStart() + sessionStorage.removeItem 'unfinished_message' messageElement = @view('message') @@ -273,6 +285,8 @@ do($ = window.jQuery, window) -> session_id: @sessionId receiveMessage: (data) => + @inactiveTimeoutStart() + # hide writing indicator @onAgentTypingEnd() @@ -307,10 +321,10 @@ do($ = window.jQuery, window) -> @isOpen = true if !@sessionId - @session_init() + @sessionInit() - onOpenAnimationEnd: -> - #@showTimeout() + onOpenAnimationEnd: => + @idleTimeoutStop() close: (event) => return @state if @state is 'off' or @state is 'unsupported' @@ -319,11 +333,24 @@ do($ = window.jQuery, window) -> # only close if session_id exists return if !@sessionId + # send close + @send 'chat_session_close', + session_id: @sessionId + + # stop timer + @inactiveTimeoutStop() + + # delete input store + sessionStorage.removeItem 'unfinished_message' + # stop delay of initial queue position if @onInitialQueueDelayId clearTimeout(@onInitialQueueDelayId) - @closeWindow() + if event + @closeWindow() + + @setSessionId undefined closeWindow: => @el.removeClass('zammad-chat-is-open') @@ -335,12 +362,6 @@ do($ = window.jQuery, window) -> @disconnect() @isOpen = false - @send 'chat_session_close', - session_id: @sessionId - - @setSessionId undefined - sessionStorage.removeItem 'unfinished_message' - # restart connection @onWebSocketOpen() @@ -448,7 +469,7 @@ do($ = window.jQuery, window) -> scrollToBottom: -> @el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight')) - session_init: -> + sessionInit: -> @send('chat_session_init') detectHost: -> @@ -484,6 +505,7 @@ do($ = window.jQuery, window) -> @reconnectDelayId = setTimeout(@wsConnect, 5000) onWebSocketOpen: => + @idleTimeoutStart() @sessionId = sessionStorage.getItem('sessionId') @log 'debug', 'ws connected' @@ -511,6 +533,7 @@ do($ = window.jQuery, window) -> @addStatus @T('Chat closed by %s', data.realname) @disableInput() @setAgentOnlineState 'offline' + @inactiveTimeoutStop() disconnect: -> @showLoader() @@ -552,8 +575,11 @@ do($ = window.jQuery, window) -> showTimeout: -> @el.find('.zammad-chat-body').html @view('timeout') agent: @agent.name - delay: 10 - unit: @T('minutes') + delay: @options.inactiveTimeout + @close() + reload = -> + location.reload() + @el.find('.js-restart').click reload showLoader: -> @el.find('.zammad-chat-body').html @view('loader')() @@ -583,4 +609,31 @@ do($ = window.jQuery, window) -> newSS.href = 'data:text/css,' + escape(styles) document.getElementsByTagName('head')[0].appendChild(newSS) + inactiveTimeoutStart: => + @inactiveTimeoutStop() + delay = => + @log 'debug', "Inactive timeout of #{@options.inactiveTimeout} minutes, show timeout screen." + @state = 'off' + @setAgentOnlineState 'offline' + @showTimeout() + @wsClose() + @inactiveTimeoutStopDelayId = setTimeout(delay, @options.inactiveTimeout * 1000 * 60) + + inactiveTimeoutStop: => + return if !@inactiveTimeoutStopDelayId + clearTimeout(@inactiveTimeoutStopDelayId) + + idleTimeoutStart: => + @idleTimeoutStop() + delay = => + @log 'debug', "Idle timeout of #{@options.idleTimeout} minutes, hide widget" + @state = 'off' + @hide() + @wsClose() + @idleTimeoutStopDelayId = setTimeout(delay, @options.idleTimeout * 1000 * 60) + + idleTimeoutStop: => + return if !@idleTimeoutStopDelayId + clearTimeout(@idleTimeoutStopDelayId) + window.ZammadChat = ZammadChat diff --git a/public/assets/chat/chat.js b/public/assets/chat/chat.js index 8508350e5..c05b2f0c8 100644 --- a/public/assets/chat/chat.js +++ b/public/assets/chat/chat.js @@ -20,7 +20,9 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); fontSize: void 0, buttonClass: 'open-zammad-chat', inactiveClass: 'is-inactive', - title: 'Chat with us!' + title: 'Chat with us!', + idleTimeout: 8, + inactiveTimeout: 20 }; ZammadChat.prototype._messageCount = 0; @@ -61,8 +63,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); 'All colleges are busy.': 'Alle Kollegen sind belegt.', 'You are on waiting list position %s.': 'Sie sind in der Warteliste an der Position %s.', 'Start new conversation': 'Neue Konversation starten', - 'Since you didn\'t respond in the last %s your conversation with %s got closed.': 'Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit %s geschlossen.', - 'minutes': 'Minuten' + 'Since you didn\'t respond in the last %s minutes your conversation with %s got closed.': 'Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit %s 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.' } }; @@ -117,6 +119,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; function ZammadChat(options) { + this.idleTimeoutStop = bind(this.idleTimeoutStop, this); + this.idleTimeoutStart = bind(this.idleTimeoutStart, this); + this.inactiveTimeoutStop = bind(this.inactiveTimeoutStop, this); + this.inactiveTimeoutStart = bind(this.inactiveTimeoutStart, this); this.setAgentOnlineState = bind(this.setAgentOnlineState, this); this.onConnectionEstablished = bind(this.onConnectionEstablished, this); this.setSessionId = bind(this.setSessionId, this); @@ -133,6 +139,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.onCloseAnimationEnd = bind(this.onCloseAnimationEnd, this); this.closeWindow = bind(this.closeWindow, this); this.close = bind(this.close, 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); @@ -148,6 +155,11 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.log = bind(this.log, this); this.T = bind(this.T, this); this.options = $.extend({}, this.defaults, options); + if (!$) { + this.state = 'unsupported'; + this.log('notice', 'Chat: no jquery found!'); + return; + } if (!window.WebSocket || !sessionStorage) { this.state = 'unsupported'; this.log('notice', 'Chat: Browser not supported!'); @@ -211,7 +223,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.log('debug', 'ws:onmessage', pipe); switch (pipe.event) { case 'chat_error': - this.log('error', pipe.data); + this.log('notice', pipe.data); + if (pipe.data && pipe.data.state === 'chat_disabled') { + this.wsClose(); + } break; case 'chat_session_message': if (pipe.data.self_written) { @@ -284,6 +299,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.reopenSession = function(data) { var i, len, message, ref, unfinishedMessage; + this.inactiveTimeoutStart(); unfinishedMessage = sessionStorage.getItem('unfinished_message'); if (data.agent) { this.onConnectionEstablished(data); @@ -322,9 +338,10 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return; } this.isTyping = new Date(); - return this.send('chat_session_typing', { + this.send('chat_session_typing', { session_id: this.sessionId }); + return this.inactiveTimeoutStart(); }; ZammadChat.prototype.onSubmit = function(event) { @@ -338,6 +355,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); if (!message) { return; } + this.inactiveTimeoutStart(); sessionStorage.removeItem('unfinished_message'); messageElement = this.view('message')({ message: message, @@ -362,6 +380,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; ZammadChat.prototype.receiveMessage = function(data) { + this.inactiveTimeoutStart(); this.onAgentTypingEnd(); this.maybeAddTimestamp(); return this.renderMessage({ @@ -399,11 +418,13 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); } this.isOpen = true; if (!this.sessionId) { - return this.session_init(); + return this.sessionInit(); } }; - ZammadChat.prototype.onOpenAnimationEnd = function() {}; + ZammadChat.prototype.onOpenAnimationEnd = function() { + return this.idleTimeoutStop(); + }; ZammadChat.prototype.close = function(event) { if (this.state === 'off' || this.state === 'unsupported') { @@ -415,10 +436,18 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); if (!this.sessionId) { return; } + this.send('chat_session_close', { + session_id: this.sessionId + }); + this.inactiveTimeoutStop(); + sessionStorage.removeItem('unfinished_message'); if (this.onInitialQueueDelayId) { clearTimeout(this.onInitialQueueDelayId); } - return this.closeWindow(); + if (event) { + this.closeWindow(); + } + return this.setSessionId(void 0); }; ZammadChat.prototype.closeWindow = function() { @@ -434,11 +463,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); this.el.removeClass('zammad-chat-is-visible'); this.disconnect(); this.isOpen = false; - this.send('chat_session_close', { - session_id: this.sessionId - }); - this.setSessionId(void 0); - sessionStorage.removeItem('unfinished_message'); return this.onWebSocketOpen(); }; @@ -555,7 +579,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return this.el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight')); }; - ZammadChat.prototype.session_init = function() { + ZammadChat.prototype.sessionInit = function() { return this.send('chat_session_init'); }; @@ -604,6 +628,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; ZammadChat.prototype.onWebSocketOpen = function() { + this.idleTimeoutStart(); this.sessionId = sessionStorage.getItem('sessionId'); this.log('debug', 'ws connected'); this.send('chat_status_customer', { @@ -630,7 +655,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); ZammadChat.prototype.onSessionClosed = function(data) { this.addStatus(this.T('Chat closed by %s', data.realname)); this.disableInput(); - return this.setAgentOnlineState('offline'); + this.setAgentOnlineState('offline'); + return this.inactiveTimeoutStop(); }; ZammadChat.prototype.disconnect = function() { @@ -673,11 +699,16 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); }; ZammadChat.prototype.showTimeout = function() { - return this.el.find('.zammad-chat-body').html(this.view('timeout')({ + var reload; + this.el.find('.zammad-chat-body').html(this.view('timeout')({ agent: this.agent.name, - delay: 10, - unit: this.T('minutes') + delay: this.options.inactiveTimeout })); + this.close(); + reload = function() { + return location.reload(); + }; + return this.el.find('.js-restart').click(reload); }; ZammadChat.prototype.showLoader = function() { @@ -709,6 +740,49 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments); return document.getElementsByTagName('head')[0].appendChild(newSS); }; + ZammadChat.prototype.inactiveTimeoutStart = function() { + var delay; + this.inactiveTimeoutStop(); + delay = (function(_this) { + return function() { + _this.log('debug', "Inactive timeout of " + _this.options.inactiveTimeout + " minutes, show timeout screen."); + _this.state = 'off'; + _this.setAgentOnlineState('offline'); + _this.showTimeout(); + return _this.wsClose(); + }; + })(this); + return this.inactiveTimeoutStopDelayId = setTimeout(delay, this.options.inactiveTimeout * 1000 * 60); + }; + + ZammadChat.prototype.inactiveTimeoutStop = function() { + if (!this.inactiveTimeoutStopDelayId) { + return; + } + return clearTimeout(this.inactiveTimeoutStopDelayId); + }; + + ZammadChat.prototype.idleTimeoutStart = function() { + var delay; + this.idleTimeoutStop(); + delay = (function(_this) { + return function() { + _this.log('debug', "Idle timeout of " + _this.options.idleTimeout + " minutes, hide widget"); + _this.state = 'off'; + _this.hide(); + return _this.wsClose(); + }; + })(this); + return this.idleTimeoutStopDelayId = setTimeout(delay, this.options.idleTimeout * 1000 * 60); + }; + + ZammadChat.prototype.idleTimeoutStop = function() { + if (!this.idleTimeoutStopDelayId) { + return; + } + return clearTimeout(this.idleTimeoutStopDelayId); + }; + return ZammadChat; })(); @@ -1159,9 +1233,17 @@ window.zammadChatTemplates["timeout"] = function (__obj) { (function() { __out.push('
\n
\n '); - __out.push(this.T('Since you didn\'t respond in the last %s your conversation with %s got closed.', this.delay + " " + this.unit, this.agent)); + if (this.agent) { + __out.push('\n '); + __out.push(this.T('Since you didn\'t respond in the last %s minutes your conversation with %s got closed.', this.delay, this.agent)); + __out.push('\n '); + } else { + __out.push('\n '); + __out.push(this.T('Since you didn\'t respond in the last %s minutes your conversation got closed.', this.delay)); + __out.push('\n '); + } - __out.push('
\n
\n
Chat with us!"},s.prototype._messageCount=0,s.prototype.isOpen=!0,s.prototype.blinkOnlineInterval=null,s.prototype.stopBlinOnlineStateTimeout=null,s.prototype.showTimeEveryXMinutes=1,s.prototype.lastTimestamp=null,s.prototype.lastAddedType=null,s.prototype.inputTimeout=null,s.prototype.isTyping=!1,s.prototype.state="offline",s.prototype.initialQueueDelay=1e4,s.prototype.wsReconnectEnable=!0,s.prototype.translations={de:{"Chat with us!":"Chat mit uns!",Online:"Online",Online:"Online",Offline:"Offline",Connecting:"Verbinden","Connection re-established":"Verbindung wiederhergestellt",Today:"Heute",Send:"Senden","Compose your message...":"Ihre Nachricht...","All colleges are busy.":"Alle Kollegen sind belegt.","You are on waiting list position %s.":"Sie sind in der Warteliste an der Position %s.","Start new conversation":"Neue Konversation starten","Since you didn't respond in the last %s your conversation with %s got closed.":"Da sie in den letzten %s nichts geschrieben haben wurde ihre Konversation mit %s geschlossen.",minutes:"Minuten"}},s.prototype.sessionId=void 0,s.prototype.T=function(){var t,e,s,n,i,a;if(i=arguments[0],s=2<=arguments.length?slice.call(arguments,1):[],this.options.lang&&"en"!==this.options.lang&&(this.translations[this.options.lang]?(a=this.translations[this.options.lang],a[i]||this.log("notice","Translation needed for '"+i+"'"),i=a[i]||i):this.log("notice","Translation '"+this.options.lang+"' needed!")),s)for(t=0,n=s.length;n>t;t++)e=s[t],i=i.replace(/%s/,e);return i},s.prototype.log=function(){var t,e;return t=arguments[0],e=2<=arguments.length?slice.call(arguments,1):[],this.options.debug||"debug"!==t?(e.unshift(t),console.log.apply(console,e)):void 0},s.prototype.view=function(t){return function(s){return function(n){return n||(n={}),n.T=s.T,n.background=s.options.background,n.flat=s.options.flat,n.fontSize=s.options.fontSize,e.zammadChatTemplates[t](n)}}(this)},s.prototype.checkForEnter=function(t){return t.shiftKey||13!==t.keyCode?void 0:(t.preventDefault(),this.sendMessage())},s.prototype.send=function(t,e){var s;return null==e&&(e={}),e.chat_id=this.options.chatId,this.log("debug","ws:send",t,e),s=JSON.stringify({event:t,data:e}),this.ws.send(s)},s.prototype.onWebSocketMessage=function(t){var e,s,n,i;for(i=JSON.parse(t.data),e=0,s=i.length;s>e;e++)switch(n=i[e],this.log("debug","ws:onmessage",n),n.event){case"chat_error":this.log("error",n.data);break;case"chat_session_message":if(n.data.self_written)return;this.receiveMessage(n.data);break;case"chat_session_typing":if(n.data.self_written)return;this.onAgentTypingStart();break;case"chat_session_start":this.onConnectionEstablished(n.data);break;case"chat_session_queue":this.onQueueScreen(n.data);break;case"chat_session_closed":this.onSessionClosed(n.data);break;case"chat_session_left":this.onSessionClosed(n.data);break;case"chat_status_customer":switch(n.data.state){case"online":this.sessionId=void 0,this.onReady(),this.log("debug","Zammad Chat: ready");break;case"offline":this.onError("Zammad Chat: No agent online"),this.state="off",this.hide(),this.wsClose();break;case"chat_disabled":this.onError("Zammad Chat: Chat is disabled"),this.state="off",this.hide(),this.wsClose();break;case"no_seats_available":this.onError("Zammad Chat: Too many clients in queue. Clients in queue: "+n.data.queue),this.state="off",this.hide(),this.wsClose();break;case"reconnect":this.log("debug","old messages",n.data.session),this.reopenSession(n.data)}}},s.prototype.onReady=function(){return t("."+this.options.buttonClass).click(this.open).removeClass(this.inactiveClass),this.options.show?this.show():void 0},s.prototype.onError=function(e){return this.log("debug",e),t("."+this.options.buttonClass).hide()},s.prototype.reopenSession=function(t){var e,s,n,i,a;if(a=sessionStorage.getItem("unfinished_message"),t.agent){for(this.onConnectionEstablished(t),i=t.session,e=0,s=i.length;s>e;e++)n=i[e],this.renderMessage({message:n.content,id:n.id,from:n.created_by_id?"agent":"customer"});a&&this.input.val(a)}return t.position&&this.onQueue(t),this.show(),this.open(),this.scrollToBottom(),a?this.input.focus():void 0},s.prototype.onInput=function(){return this.el.find(".zammad-chat-message--unread").removeClass("zammad-chat-message--unread"),sessionStorage.setItem("unfinished_message",this.input.val()),this.onTyping()},s.prototype.onTyping=function(){return this.isTyping&&this.isTyping>new Date((new Date).getTime()-1500)?void 0:(this.isTyping=new Date,this.send("chat_session_typing",{session_id:this.sessionId}))},s.prototype.onSubmit=function(t){return t.preventDefault(),this.sendMessage()},s.prototype.sendMessage=function(){var t,e;return(t=this.input.val())?(sessionStorage.removeItem("unfinished_message"),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.input.val(""),this.scrollToBottom(),this.send("chat_session_message",{content:t,id:this._messageCount,session_id:this.sessionId})):void 0},s.prototype.receiveMessage=function(t){return this.onAgentTypingEnd(),this.maybeAddTimestamp(),this.renderMessage({message:t.message.content,id:t.id,from:"agent"})},s.prototype.renderMessage=function(t){var e,s;return this.lastAddedType="message--"+t.from,s=null!=(e=document.hidden)?e:{" zammad-chat-message--unread":""},this.el.find(".zammad-chat-body").append(this.view("message")(t)),this.scrollToBottom()},s.prototype.open=function(){return this.isOpen&&this.show(),this.sessionId||this.showLoader(),this.el.addClass("zammad-chat-is-open"),this.sessionId?(this.el.css("bottom",0),this.onOpenAnimationEnd()):this.el.animate({bottom:0},500,this.onOpenAnimationEnd),this.isOpen=!0,this.sessionId?void 0:this.session_init()},s.prototype.onOpenAnimationEnd=function(){},s.prototype.close=function(t){return"off"===this.state||"unsupported"===this.state?this.state:(t&&t.stopPropagation(),this.sessionId?(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.closeWindow()):void 0)},s.prototype.closeWindow=function(){var t;return this.el.removeClass("zammad-chat-is-open"),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-t},500,this.onCloseAnimationEnd)},s.prototype.onCloseAnimationEnd=function(){return this.el.removeClass("zammad-chat-is-visible"),this.disconnect(),this.isOpen=!1,this.send("chat_session_close",{session_id:this.sessionId}),this.setSessionId(void 0),sessionStorage.removeItem("unfinished_message"),this.onWebSocketOpen()},s.prototype.hide=function(){return this.el.removeClass("zammad-chat-is-shown")},s.prototype.show=function(){var t;return"off"===this.state||"unsupported"===this.state?this.state:(this.el.addClass("zammad-chat-is-shown"),this.inputInitialized||(this.inputInitialized=!0,this.input.autoGrow({extraLine:!1})),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.css("bottom",-t))},s.prototype.disableInput=function(){return this.input.prop("disabled",!0),this.el.find(".zammad-chat-send").prop("disabled",!0)},s.prototype.enableInput=function(){return this.input.prop("disabled",!1),this.el.find(".zammad-chat-send").prop("disabled",!1)},s.prototype.onQueueScreen=function(t){var e;return this.setSessionId(t.session_id),e=function(e){return function(){return e.onQueue(t)}}(this),this.initialQueueDelay&&!this.onInitialQueueDelayId?void(this.onInitialQueueDelayId=setTimeout(e,this.initialQueueDelay)):(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),e())},s.prototype.onQueue=function(t){return this.log("notice","onQueue",t.position),this.inQueue=!0,this.el.find(".zammad-chat-body").html(this.view("waiting")({position:t.position}))},s.prototype.onAgentTypingStart=function(){return this.stopTypingId&&clearTimeout(this.stopTypingId),this.stopTypingId=setTimeout(this.onAgentTypingEnd,3e3),this.el.find(".zammad-chat-message--typing").size()?void 0:(this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("typingIndicator")()),this.scrollToBottom())},s.prototype.onAgentTypingEnd=function(){return this.el.find(".zammad-chat-message--typing").remove()},s.prototype.maybeAddTimestamp=function(){var t,e,s;return s=Date.now(),!this.lastTimestamp||s-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=s):(this.el.find(".zammad-chat-body").append(this.view("timestamp")({label:t,time:e})),this.lastTimestamp=s,this.lastAddedType="timestamp",this.scrollToBottom())):void 0},s.prototype.updateLastTimestamp=function(t,e){return this.el.find(".zammad-chat-body").find(".zammad-chat-timestamp").last().replaceWith(this.view("timestamp")({label:t,time:e}))},s.prototype.addStatus=function(t){return this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("status")({status:t})),this.scrollToBottom()},s.prototype.scrollToBottom=function(){return this.el.find(".zammad-chat-body").scrollTop(t(".zammad-chat-body").prop("scrollHeight"))},s.prototype.session_init=function(){return this.send("chat_session_init")},s.prototype.detectHost=function(){var t;return t="ws://","https:"===e.location.protocol&&(t="wss://"),this.options.host=""+t+i+"/ws"},s.prototype.wsConnect=function(){return this.options.host||this.detectHost(),this.log("debug","Connecting to "+this.options.host),this.ws=new e.WebSocket(""+this.options.host),this.ws.onopen=this.onWebSocketOpen,this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(e){return t.log("debug","close websocket connection"),t.wsReconnectEnable?t.reconnect():void 0}}(this),this.ws.onerror=function(t){return function(e){return t.log("debug","ws:onerror",e)}}(this)},s.prototype.wsClose=function(){return this.wsReconnectEnable=!1,this.ws.close()},s.prototype.wsReconnect=function(){return this.reconnectDelayId&&clearTimeout(this.reconnectDelayId),this.reconnectDelayId=setTimeout(this.wsConnect,5e3)},s.prototype.onWebSocketOpen=function(){return this.sessionId=sessionStorage.getItem("sessionId"),this.log("debug","ws connected"),this.send("chat_status_customer",{session_id:this.sessionId}),this.setAgentOnlineState("online")},s.prototype.reconnect=function(){return this.log("notice","reconnecting"),this.disableInput(),this.lastAddedType="status",this.setAgentOnlineState("connecting"),this.addStatus(this.T("Connection lost")),this.wsReconnect()},s.prototype.onConnectionReestablished=function(){return this.lastAddedType="status",this.setAgentOnlineState("online"),this.addStatus(this.T("Connection re-established"))},s.prototype.onSessionClosed=function(t){return this.addStatus(this.T("Chat closed by %s",t.realname)),this.disableInput(),this.setAgentOnlineState("offline")},s.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")},s.prototype.setSessionId=function(t){return this.sessionId=t,void 0===t?sessionStorage.removeItem("sessionId"):sessionStorage.setItem("sessionId",t)},s.prototype.onConnectionEstablished=function(t){return this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.inQueue=!1,t.agent&&(this.agent=t.agent),t.session_id&&this.setSessionId(t.session_id),this.el.find(".zammad-chat-agent").html(this.view("agent")({agent:this.agent})),this.enableInput(),this.el.find(".zammad-chat-body").empty(),this.el.find(".zammad-chat-welcome").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").removeClass("zammad-chat-is-hidden"),this.input.focus(),this.setAgentOnlineState("online")},s.prototype.showTimeout=function(){return this.el.find(".zammad-chat-body").html(this.view("timeout")({agent:this.agent.name,delay:10,unit:this.T("minutes")}))},s.prototype.showLoader=function(){return this.el.find(".zammad-chat-body").html(this.view("loader")())},s.prototype.setAgentOnlineState=function(t){var e;return this.state=t,e=t.charAt(0).toUpperCase()+t.slice(1),this.el.find(".zammad-chat-agent-status").attr("data-status",t).text(this.T(e))},s.prototype.loadCss=function(){var t,e,s;if(this.options.cssAutoload)return s=this.options.cssUrl,s||(s=this.options.host.replace(/^wss/i,"https").replace(/^ws/i,"http").replace(/\/ws/i,""),s+="/assets/chat/chat.css"),this.log("debug","load css from '"+s+"'"),e="@import url('"+s+"');",t=document.createElement("link"),t.rel="stylesheet",t.href="data:text/css,"+escape(e),document.getElementsByTagName("head")[0].appendChild(t)},s}(),e.ZammadChat=s}(window.jQuery,window),window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.agent=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){this.agent.avatar&&(s.push('\n\n')),s.push('\n\n '),s.push(n(this.agent.name)),s.push("\n")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),s=function(t){return jQuery(t).after('
'),jQuery(t).next(".autogrow-textarea-mirror")[0]},n=function(t){a.innerHTML=String(t.value).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/ /g," ").replace(/\n/g,"
")+(e.extraLine?".
.":""),jQuery(t).height()!=jQuery(a).height()&&jQuery(t).height(jQuery(a).height())},i=function(){n(this)},a=s(this);a.style.display="none",a.style.wordWrap="break-word",a.style.whiteSpace="normal",a.style.padding=jQuery(this).css("paddingTop")+" "+jQuery(this).css("paddingRight")+" "+jQuery(this).css("paddingBottom")+" "+jQuery(this).css("paddingLeft"),a.style.width=jQuery(this).css("width"),a.style.fontFamily=jQuery(this).css("font-family"),a.style.fontSize=jQuery(this).css("font-size"),a.style.lineHeight=jQuery(this).css("line-height"),this.style.overflow="hidden",this.style.minHeight=this.rows+"em",this.onkeyup=i,n(this)})},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.chat=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n
\n \n \n \n \n \n
\n
\n
\n
\n \n '),s.push(this.T(this.title)),s.push('\n
\n
\n
\n \n \n \n \n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.loader=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n '),s.push(this.T("Connecting")),s.push("\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.message=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n "),s.push(this.message),s.push("\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.status=function(t){t||(t={});var e,s=[],n=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(){s.push('
'),s.push(this.status),s.push("
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timeout=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n '),s.push(this.T("Since you didn't respond in the last %s your conversation with %s got closed.",this.delay+" "+this.unit,this.agent)),s.push('
\n
"),s.push(this.T("Start new conversation")),s.push("
\n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timestamp=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
'),s.push(n(this.label)),s.push(" "),s.push(n(this.time)),s.push("
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.typingIndicator=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n \n \n
')}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.waiting=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n
\n \n \n \n \n \n '),s.push(this.T("All colleges are busy.")),s.push("
\n "),s.push(this.T("You are on waiting list position %s.",this.position)),s.push("\n
\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")}; \ No newline at end of file +var bind=function(t,e){return function(){return t.apply(e,arguments)}},slice=[].slice;!function(t,e){var s,n,i,a;return a=document.getElementsByTagName("script"),n=a[a.length-1],i=n.src.match(".*://([^:/]*).*")[1],s=function(){function s(s){return this.idleTimeoutStop=bind(this.idleTimeoutStop,this),this.idleTimeoutStart=bind(this.idleTimeoutStart,this),this.inactiveTimeoutStop=bind(this.inactiveTimeoutStop,this),this.inactiveTimeoutStart=bind(this.inactiveTimeoutStart,this),this.setAgentOnlineState=bind(this.setAgentOnlineState,this),this.onConnectionEstablished=bind(this.onConnectionEstablished,this),this.setSessionId=bind(this.setSessionId,this),this.onConnectionReestablished=bind(this.onConnectionReestablished,this),this.reconnect=bind(this.reconnect,this),this.onWebSocketOpen=bind(this.onWebSocketOpen,this),this.wsReconnect=bind(this.wsReconnect,this),this.wsClose=bind(this.wsClose,this),this.wsConnect=bind(this.wsConnect,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.onCloseAnimationEnd=bind(this.onCloseAnimationEnd,this),this.closeWindow=bind(this.closeWindow,this),this.close=bind(this.close,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.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),this.view=bind(this.view,this),this.log=bind(this.log,this),this.T=bind(this.T,this),this.options=t.extend({},this.defaults,s),t?e.WebSocket&&sessionStorage?this.options.chatId?(this.options.lang||(this.options.lang=t("html").attr("lang")),this.options.lang&&(this.options.lang=this.options.lang.replace(/-.+?$/,""),this.log("debug","lang: "+this.options.lang)),this.el=t(this.view("chat")({title:this.options.title})),this.options.target.append(this.el),this.input=this.el.find(".zammad-chat-input"),t("."+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}),this.wsConnect(),void this.loadCss()):(this.state="unsupported",void this.log("error","Chat: need chatId as option!")):(this.state="unsupported",void this.log("notice","Chat: Browser not supported!")):(this.state="unsupported",void this.log("notice","Chat: no jquery found!"))}return s.prototype.defaults={chatId:void 0,show:!0,target:t("body"),host:"",debug:!1,flat:!1,lang:void 0,cssAutoload:!0,cssUrl:void 0,fontSize:void 0,buttonClass:"open-zammad-chat",inactiveClass:"is-inactive",title:"Chat with us!",idleTimeout:8,inactiveTimeout:20},s.prototype._messageCount=0,s.prototype.isOpen=!0,s.prototype.blinkOnlineInterval=null,s.prototype.stopBlinOnlineStateTimeout=null,s.prototype.showTimeEveryXMinutes=1,s.prototype.lastTimestamp=null,s.prototype.lastAddedType=null,s.prototype.inputTimeout=null,s.prototype.isTyping=!1,s.prototype.state="offline",s.prototype.initialQueueDelay=1e4,s.prototype.wsReconnectEnable=!0,s.prototype.translations={de:{"Chat with us!":"Chat mit uns!",Online:"Online",Online:"Online",Offline:"Offline",Connecting:"Verbinden","Connection re-established":"Verbindung wiederhergestellt",Today:"Heute",Send:"Senden","Compose your message...":"Ihre Nachricht...","All colleges are busy.":"Alle Kollegen sind belegt.","You are on waiting list position %s.":"Sie sind in der Warteliste an der Position %s.","Start new conversation":"Neue Konversation starten","Since you didn't respond in the last %s minutes your conversation with %s got closed.":"Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit %s 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."}},s.prototype.sessionId=void 0,s.prototype.T=function(){var t,e,s,n,i,a;if(i=arguments[0],s=2<=arguments.length?slice.call(arguments,1):[],this.options.lang&&"en"!==this.options.lang&&(this.translations[this.options.lang]?(a=this.translations[this.options.lang],a[i]||this.log("notice","Translation needed for '"+i+"'"),i=a[i]||i):this.log("notice","Translation '"+this.options.lang+"' needed!")),s)for(t=0,n=s.length;n>t;t++)e=s[t],i=i.replace(/%s/,e);return i},s.prototype.log=function(){var t,e;return t=arguments[0],e=2<=arguments.length?slice.call(arguments,1):[],this.options.debug||"debug"!==t?(e.unshift(t),console.log.apply(console,e)):void 0},s.prototype.view=function(t){return function(s){return function(n){return n||(n={}),n.T=s.T,n.background=s.options.background,n.flat=s.options.flat,n.fontSize=s.options.fontSize,e.zammadChatTemplates[t](n)}}(this)},s.prototype.checkForEnter=function(t){return t.shiftKey||13!==t.keyCode?void 0:(t.preventDefault(),this.sendMessage())},s.prototype.send=function(t,e){var s;return null==e&&(e={}),e.chat_id=this.options.chatId,this.log("debug","ws:send",t,e),s=JSON.stringify({event:t,data:e}),this.ws.send(s)},s.prototype.onWebSocketMessage=function(t){var e,s,n,i;for(i=JSON.parse(t.data),e=0,s=i.length;s>e;e++)switch(n=i[e],this.log("debug","ws:onmessage",n),n.event){case"chat_error":this.log("notice",n.data),n.data&&"chat_disabled"===n.data.state&&this.wsClose();break;case"chat_session_message":if(n.data.self_written)return;this.receiveMessage(n.data);break;case"chat_session_typing":if(n.data.self_written)return;this.onAgentTypingStart();break;case"chat_session_start":this.onConnectionEstablished(n.data);break;case"chat_session_queue":this.onQueueScreen(n.data);break;case"chat_session_closed":this.onSessionClosed(n.data);break;case"chat_session_left":this.onSessionClosed(n.data);break;case"chat_status_customer":switch(n.data.state){case"online":this.sessionId=void 0,this.onReady(),this.log("debug","Zammad Chat: ready");break;case"offline":this.onError("Zammad Chat: No agent online"),this.state="off",this.hide(),this.wsClose();break;case"chat_disabled":this.onError("Zammad Chat: Chat is disabled"),this.state="off",this.hide(),this.wsClose();break;case"no_seats_available":this.onError("Zammad Chat: Too many clients in queue. Clients in queue: "+n.data.queue),this.state="off",this.hide(),this.wsClose();break;case"reconnect":this.log("debug","old messages",n.data.session),this.reopenSession(n.data)}}},s.prototype.onReady=function(){return t("."+this.options.buttonClass).click(this.open).removeClass(this.inactiveClass),this.options.show?this.show():void 0},s.prototype.onError=function(e){return this.log("debug",e),t("."+this.options.buttonClass).hide()},s.prototype.reopenSession=function(t){var e,s,n,i,a;if(this.inactiveTimeoutStart(),a=sessionStorage.getItem("unfinished_message"),t.agent){for(this.onConnectionEstablished(t),i=t.session,e=0,s=i.length;s>e;e++)n=i[e],this.renderMessage({message:n.content,id:n.id,from:n.created_by_id?"agent":"customer"});a&&this.input.val(a)}return t.position&&this.onQueue(t),this.show(),this.open(),this.scrollToBottom(),a?this.input.focus():void 0},s.prototype.onInput=function(){return this.el.find(".zammad-chat-message--unread").removeClass("zammad-chat-message--unread"),sessionStorage.setItem("unfinished_message",this.input.val()),this.onTyping()},s.prototype.onTyping=function(){return this.isTyping&&this.isTyping>new Date((new Date).getTime()-1500)?void 0:(this.isTyping=new Date,this.send("chat_session_typing",{session_id:this.sessionId}),this.inactiveTimeoutStart())},s.prototype.onSubmit=function(t){return t.preventDefault(),this.sendMessage()},s.prototype.sendMessage=function(){var t,e;return(t=this.input.val())?(this.inactiveTimeoutStart(),sessionStorage.removeItem("unfinished_message"),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.input.val(""),this.scrollToBottom(),this.send("chat_session_message",{content:t,id:this._messageCount,session_id:this.sessionId})):void 0},s.prototype.receiveMessage=function(t){return this.inactiveTimeoutStart(),this.onAgentTypingEnd(),this.maybeAddTimestamp(),this.renderMessage({message:t.message.content,id:t.id,from:"agent"})},s.prototype.renderMessage=function(t){var e,s;return this.lastAddedType="message--"+t.from,s=null!=(e=document.hidden)?e:{" zammad-chat-message--unread":""},this.el.find(".zammad-chat-body").append(this.view("message")(t)),this.scrollToBottom()},s.prototype.open=function(){return this.isOpen&&this.show(),this.sessionId||this.showLoader(),this.el.addClass("zammad-chat-is-open"),this.sessionId?(this.el.css("bottom",0),this.onOpenAnimationEnd()):this.el.animate({bottom:0},500,this.onOpenAnimationEnd),this.isOpen=!0,this.sessionId?void 0:this.sessionInit()},s.prototype.onOpenAnimationEnd=function(){return this.idleTimeoutStop()},s.prototype.close=function(t){return"off"===this.state||"unsupported"===this.state?this.state:(t&&t.stopPropagation(),this.sessionId?(this.send("chat_session_close",{session_id:this.sessionId}),this.inactiveTimeoutStop(),sessionStorage.removeItem("unfinished_message"),this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),t&&this.closeWindow(),this.setSessionId(void 0)):void 0)},s.prototype.closeWindow=function(){var t;return this.el.removeClass("zammad-chat-is-open"),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.animate({bottom:-t},500,this.onCloseAnimationEnd)},s.prototype.onCloseAnimationEnd=function(){return this.el.removeClass("zammad-chat-is-visible"),this.disconnect(),this.isOpen=!1,this.onWebSocketOpen()},s.prototype.hide=function(){return this.el.removeClass("zammad-chat-is-shown")},s.prototype.show=function(){var t;return"off"===this.state||"unsupported"===this.state?this.state:(this.el.addClass("zammad-chat-is-shown"),this.inputInitialized||(this.inputInitialized=!0,this.input.autoGrow({extraLine:!1})),t=this.el.height()-this.el.find(".zammad-chat-header").outerHeight(),this.el.css("bottom",-t))},s.prototype.disableInput=function(){return this.input.prop("disabled",!0),this.el.find(".zammad-chat-send").prop("disabled",!0)},s.prototype.enableInput=function(){return this.input.prop("disabled",!1),this.el.find(".zammad-chat-send").prop("disabled",!1)},s.prototype.onQueueScreen=function(t){var e;return this.setSessionId(t.session_id),e=function(e){return function(){return e.onQueue(t)}}(this),this.initialQueueDelay&&!this.onInitialQueueDelayId?void(this.onInitialQueueDelayId=setTimeout(e,this.initialQueueDelay)):(this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),e())},s.prototype.onQueue=function(t){return this.log("notice","onQueue",t.position),this.inQueue=!0,this.el.find(".zammad-chat-body").html(this.view("waiting")({position:t.position}))},s.prototype.onAgentTypingStart=function(){return this.stopTypingId&&clearTimeout(this.stopTypingId),this.stopTypingId=setTimeout(this.onAgentTypingEnd,3e3),this.el.find(".zammad-chat-message--typing").size()?void 0:(this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("typingIndicator")()),this.scrollToBottom())},s.prototype.onAgentTypingEnd=function(){return this.el.find(".zammad-chat-message--typing").remove()},s.prototype.maybeAddTimestamp=function(){var t,e,s;return s=Date.now(),!this.lastTimestamp||s-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=s):(this.el.find(".zammad-chat-body").append(this.view("timestamp")({label:t,time:e})),this.lastTimestamp=s,this.lastAddedType="timestamp",this.scrollToBottom())):void 0},s.prototype.updateLastTimestamp=function(t,e){return this.el.find(".zammad-chat-body").find(".zammad-chat-timestamp").last().replaceWith(this.view("timestamp")({label:t,time:e}))},s.prototype.addStatus=function(t){return this.maybeAddTimestamp(),this.el.find(".zammad-chat-body").append(this.view("status")({status:t})),this.scrollToBottom()},s.prototype.scrollToBottom=function(){return this.el.find(".zammad-chat-body").scrollTop(t(".zammad-chat-body").prop("scrollHeight"))},s.prototype.sessionInit=function(){return this.send("chat_session_init")},s.prototype.detectHost=function(){var t;return t="ws://","https:"===e.location.protocol&&(t="wss://"),this.options.host=""+t+i+"/ws"},s.prototype.wsConnect=function(){return this.options.host||this.detectHost(),this.log("debug","Connecting to "+this.options.host),this.ws=new e.WebSocket(""+this.options.host),this.ws.onopen=this.onWebSocketOpen,this.ws.onmessage=this.onWebSocketMessage,this.ws.onclose=function(t){return function(e){return t.log("debug","close websocket connection"),t.wsReconnectEnable?t.reconnect():void 0}}(this),this.ws.onerror=function(t){return function(e){return t.log("debug","ws:onerror",e)}}(this)},s.prototype.wsClose=function(){return this.wsReconnectEnable=!1,this.ws.close()},s.prototype.wsReconnect=function(){return this.reconnectDelayId&&clearTimeout(this.reconnectDelayId),this.reconnectDelayId=setTimeout(this.wsConnect,5e3)},s.prototype.onWebSocketOpen=function(){return this.idleTimeoutStart(),this.sessionId=sessionStorage.getItem("sessionId"),this.log("debug","ws connected"),this.send("chat_status_customer",{session_id:this.sessionId}),this.setAgentOnlineState("online")},s.prototype.reconnect=function(){return this.log("notice","reconnecting"),this.disableInput(),this.lastAddedType="status",this.setAgentOnlineState("connecting"),this.addStatus(this.T("Connection lost")),this.wsReconnect()},s.prototype.onConnectionReestablished=function(){return this.lastAddedType="status",this.setAgentOnlineState("online"),this.addStatus(this.T("Connection re-established"))},s.prototype.onSessionClosed=function(t){return this.addStatus(this.T("Chat closed by %s",t.realname)),this.disableInput(),this.setAgentOnlineState("offline"),this.inactiveTimeoutStop()},s.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")},s.prototype.setSessionId=function(t){return this.sessionId=t,void 0===t?sessionStorage.removeItem("sessionId"):sessionStorage.setItem("sessionId",t)},s.prototype.onConnectionEstablished=function(t){return this.onInitialQueueDelayId&&clearTimeout(this.onInitialQueueDelayId),this.inQueue=!1,t.agent&&(this.agent=t.agent),t.session_id&&this.setSessionId(t.session_id),this.el.find(".zammad-chat-agent").html(this.view("agent")({agent:this.agent})),this.enableInput(),this.el.find(".zammad-chat-body").empty(),this.el.find(".zammad-chat-welcome").addClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent").removeClass("zammad-chat-is-hidden"),this.el.find(".zammad-chat-agent-status").removeClass("zammad-chat-is-hidden"),this.input.focus(),this.setAgentOnlineState("online")},s.prototype.showTimeout=function(){var t;return this.el.find(".zammad-chat-body").html(this.view("timeout")({agent:this.agent.name,delay:this.options.inactiveTimeout})),this.close(),t=function(){return location.reload()},this.el.find(".js-restart").click(t)},s.prototype.showLoader=function(){return this.el.find(".zammad-chat-body").html(this.view("loader")())},s.prototype.setAgentOnlineState=function(t){var e;return this.state=t,e=t.charAt(0).toUpperCase()+t.slice(1),this.el.find(".zammad-chat-agent-status").attr("data-status",t).text(this.T(e))},s.prototype.loadCss=function(){var t,e,s;if(this.options.cssAutoload)return s=this.options.cssUrl,s||(s=this.options.host.replace(/^wss/i,"https").replace(/^ws/i,"http").replace(/\/ws/i,""),s+="/assets/chat/chat.css"),this.log("debug","load css from '"+s+"'"),e="@import url('"+s+"');",t=document.createElement("link"),t.rel="stylesheet",t.href="data:text/css,"+escape(e),document.getElementsByTagName("head")[0].appendChild(t)},s.prototype.inactiveTimeoutStart=function(){var t;return this.inactiveTimeoutStop(),t=function(t){return function(){return t.log("debug","Inactive timeout of "+t.options.inactiveTimeout+" minutes, show timeout screen."),t.state="off",t.setAgentOnlineState("offline"),t.showTimeout(),t.wsClose()}}(this),this.inactiveTimeoutStopDelayId=setTimeout(t,1e3*this.options.inactiveTimeout*60)},s.prototype.inactiveTimeoutStop=function(){return this.inactiveTimeoutStopDelayId?clearTimeout(this.inactiveTimeoutStopDelayId):void 0},s.prototype.idleTimeoutStart=function(){var t;return this.idleTimeoutStop(),t=function(t){return function(){return t.log("debug","Idle timeout of "+t.options.idleTimeout+" minutes, hide widget"),t.state="off",t.hide(),t.wsClose()}}(this),this.idleTimeoutStopDelayId=setTimeout(t,1e3*this.options.idleTimeout*60)},s.prototype.idleTimeoutStop=function(){return this.idleTimeoutStopDelayId?clearTimeout(this.idleTimeoutStopDelayId):void 0},s}(),e.ZammadChat=s}(window.jQuery,window),window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.agent=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){this.agent.avatar&&(s.push('\n\n')),s.push('\n\n '),s.push(n(this.agent.name)),s.push("\n")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},jQuery.fn.autoGrow=function(t){return this.each(function(){var e=jQuery.extend({extraLine:!0},t),s=function(t){return jQuery(t).after('
'),jQuery(t).next(".autogrow-textarea-mirror")[0]},n=function(t){a.innerHTML=String(t.value).replace(/&/g,"&").replace(/"/g,""").replace(/'/g,"'").replace(//g,">").replace(/ /g," ").replace(/\n/g,"
")+(e.extraLine?".
.":""),jQuery(t).height()!=jQuery(a).height()&&jQuery(t).height(jQuery(a).height())},i=function(){n(this)},a=s(this);a.style.display="none",a.style.wordWrap="break-word",a.style.whiteSpace="normal",a.style.padding=jQuery(this).css("paddingTop")+" "+jQuery(this).css("paddingRight")+" "+jQuery(this).css("paddingBottom")+" "+jQuery(this).css("paddingLeft"),a.style.width=jQuery(this).css("width"),a.style.fontFamily=jQuery(this).css("font-family"),a.style.fontSize=jQuery(this).css("font-size"),a.style.lineHeight=jQuery(this).css("line-height"),this.style.overflow="hidden",this.style.minHeight=this.rows+"em",this.onkeyup=i,n(this)})},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.chat=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n
\n \n \n \n \n \n
\n
\n
\n
\n \n '),s.push(this.T(this.title)),s.push('\n
\n
\n
\n
\n \n \n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.loader=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n '),s.push(this.T("Connecting")),s.push("\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.message=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n "),s.push(this.message),s.push("\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.status=function(t){t||(t={});var e,s=[],n=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(){s.push('
'),s.push(this.status),s.push("
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timeout=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
\n
\n '),this.agent?(s.push("\n "),s.push(this.T("Since you didn't respond in the last %s minutes your conversation with %s got closed.",this.delay,this.agent)),s.push("\n ")):(s.push("\n "),s.push(this.T("Since you didn't respond in the last %s minutes your conversation got closed.",this.delay)),s.push("\n ")),s.push('\n
\n
"),s.push(this.T("Start new conversation")),s.push("
\n
\n
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.timestamp=function(t){t||(t={});var e,s=[],n=function(t){return t&&t.ecoSafe?t:"undefined"!=typeof t&&null!=t?a(t):""},i=t.safe,a=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},a||(a=t.escape=function(t){return(""+t).replace(/&/g,"&").replace(//g,">").replace(/"/g,""")}),function(){(function(){s.push('
'),s.push(n(this.label)),s.push(" "),s.push(n(this.time)),s.push("
")}).call(this)}.call(t),t.safe=i,t.escape=a,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.typingIndicator=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n \n \n \n \n \n \n \n
')}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")},window.zammadChatTemplates||(window.zammadChatTemplates={}),window.zammadChatTemplates.waiting=function(t){t||(t={});var e,s=[],n=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(){s.push('
\n
\n \n \n \n \n \n '),s.push(this.T("All colleges are busy.")),s.push("
\n "),s.push(this.T("You are on waiting list position %s.",this.position)),s.push("\n
\n
")}).call(this)}.call(t),t.safe=n,t.escape=i,s.join("")}; \ No newline at end of file diff --git a/public/assets/chat/views/timeout.eco b/public/assets/chat/views/timeout.eco index 7c6a45dda..b66dbc836 100644 --- a/public/assets/chat/views/timeout.eco +++ b/public/assets/chat/views/timeout.eco @@ -1,6 +1,11 @@
- <%- @T('Since you didn\'t respond in the last %s your conversation with %s got closed.', "#{ @delay } #{ @unit }", @agent) %>
-
><%- @T('Start new conversation') %>
+ <% if @agent: %> + <%- @T('Since you didn\'t respond in the last %s minutes your conversation with %s got closed.', @delay, @agent) %> + <% else: %> + <%- @T('Since you didn\'t respond in the last %s minutes your conversation got closed.', @delay) %> + <% end %> +
+
><%- @T('Start new conversation') %>
\ No newline at end of file From 0de7b74ae66a6fad526ff490d9476e273b1cb7ed Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 26 Nov 2015 00:55:56 +0100 Subject: [PATCH 5/8] Removed use of session store to prevent store overflow. --- .../controllers/_application_controller.coffee | 16 +--------------- .../app/controllers/widget/tag.coffee | 17 +++-------------- 2 files changed, 4 insertions(+), 29 deletions(-) diff --git a/app/assets/javascripts/app/controllers/_application_controller.coffee b/app/assets/javascripts/app/controllers/_application_controller.coffee index 2f1833f85..34c765d8a 100644 --- a/app/assets/javascripts/app/controllers/_application_controller.coffee +++ b/app/assets/javascripts/app/controllers/_application_controller.coffee @@ -443,26 +443,12 @@ class App.Controller extends Spine.Controller } processData: true, success: (data, status, xhr) -> - App.SessionStorage.set( "user-ticket-popover::#{params.user_id}", data ) - - # load assets App.Collection.loadAssets( data.assets ) - show( params, { open: data.ticket_ids_open, closed: data.ticket_ids_closed } ) ) # get data - data = App.SessionStorage.get( "user-ticket-popover::#{params.user_id}" ) - if data - show( params, { open: data.ticket_ids_open, closed: data.ticket_ids_closed } ) - @delay( - -> - fetch(params) - 1000 - 'fetch' - ) - else - fetch(params) + fetch(params) userTicketPopupsDestroy: => if @userTicketPopupsList diff --git a/app/assets/javascripts/app/controllers/widget/tag.coffee b/app/assets/javascripts/app/controllers/widget/tag.coffee index dab945d36..ca43c441e 100644 --- a/app/assets/javascripts/app/controllers/widget/tag.coffee +++ b/app/assets/javascripts/app/controllers/widget/tag.coffee @@ -13,27 +13,17 @@ class App.WidgetTag extends App.Controller constructor: -> super - @cacheKey = "tags::#{@object_type}::#{@object.id}" + @key = "tags::#{@object_type}::#{@object.id}" if @tags @render() return - @tags = App.SessionStorage.get( @cacheKey ) || [] - if !_.isEmpty(@tags) - @render() - @delay( - => - @fetch() - 1000 - 'fetch' - ) - else - @fetch() + @fetch() fetch: => @ajax( - id: @cacheKey + id: @key type: 'GET' url: @apiPath + '/tags' data: @@ -42,7 +32,6 @@ class App.WidgetTag extends App.Controller processData: true success: (data, status, xhr) => @tags = data.tags - App.SessionStorage.set( @cacheKey, @tags ) @render() ) From 2728074f41cc8023a026fb4aafdea91e3669016d Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 26 Nov 2015 01:09:46 +0100 Subject: [PATCH 6/8] A little bit more robust. --- public/assets/chat/chat.css | 1 + 1 file changed, 1 insertion(+) diff --git a/public/assets/chat/chat.css b/public/assets/chat/chat.css index 8d14d78ea..104b61721 100644 --- a/public/assets/chat/chat.css +++ b/public/assets/chat/chat.css @@ -296,6 +296,7 @@ align-items: flex-start; border-top: 1px solid #ededed; padding: 0; + margin: 0; line-height: 1.4em; box-shadow: 0 1px rgba(0, 0, 0, 0.01), 0 -1px rgba(0, 0, 0, 0.02); position: relative; From 8a770cff94d6d65fa05e5264c2ff36ca4033b562 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 26 Nov 2015 01:25:53 +0100 Subject: [PATCH 7/8] A little bit more robust. --- public/assets/chat/chat.css | 5 ++--- public/assets/chat/chat.scss | 12 ++++++------ 2 files changed, 8 insertions(+), 9 deletions(-) diff --git a/public/assets/chat/chat.css b/public/assets/chat/chat.css index 104b61721..3d33aba23 100644 --- a/public/assets/chat/chat.css +++ b/public/assets/chat/chat.css @@ -105,7 +105,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; } @@ -316,6 +316,7 @@ box-shadow: none; padding: 1em 2em; outline: none; + box-shadow: none; resize: none; -webkit-flex: 1; -ms-flex: 1; @@ -352,9 +353,7 @@ display: block; } /* - # Flat Design - */ .zammad-chat--flat .zammad-chat-header, .zammad-chat--flat .zammad-chat-body { diff --git a/public/assets/chat/chat.scss b/public/assets/chat/chat.scss index 8fd6ef72c..8b2178e05 100644 --- a/public/assets/chat/chat.scss +++ b/public/assets/chat/chat.scss @@ -11,7 +11,7 @@ will-change: bottom; display: none; flex-direction: column; - + &.is-fullscreen { right: 0; width: 100%; @@ -43,7 +43,7 @@ background: hsl(203,67%,53%); color: white; line-height: 2.5em; - box-shadow: + box-shadow: 0 -1px rgba(0,0,0,.1), 0 1px rgba(255,255,255,.3) inset, 0 -1px rgba(0,0,0,.1) inset, @@ -52,7 +52,7 @@ border-radius: 5px 5px 0 0; overflow: hidden; cursor: pointer; - + @media only screen and (max-width: 768px) { border-radius: 0 !important; } @@ -294,8 +294,9 @@ align-items: flex-start; border-top: 1px solid hsl(0,0%,93%); padding: 0; + margin: 0; line-height: 1.4em; - box-shadow: + box-shadow: 0 1px rgba(0,0,0,.01), 0 -1px rgba(0,0,0,.02); position: relative; @@ -314,6 +315,7 @@ box-shadow: none; padding: 1em 2em; outline: none; + box-shadow: none; resize: none; flex: 1; max-height: 6em; @@ -355,9 +357,7 @@ } /* - # Flat Design - */ .zammad-chat--flat .zammad-chat-header, From b3aa055c0eec6a361d0f1eac128d8385de3b0b01 Mon Sep 17 00:00:00 2001 From: Martin Edenhofer Date: Thu, 26 Nov 2015 09:29:58 +0100 Subject: [PATCH 8/8] Check if in text/plain is content - if not, use text/html. --- app/models/channel/email_parser.rb | 3 ++- test/fixtures/mail34.box | 29 +++++++++++++++++++++++++++++ test/unit/email_parser_test.rb | 21 ++++++++++++++++++--- 3 files changed, 49 insertions(+), 4 deletions(-) create mode 100644 test/fixtures/mail34.box diff --git a/app/models/channel/email_parser.rb b/app/models/channel/email_parser.rb index 7c87953c5..fb3fe64aa 100644 --- a/app/models/channel/email_parser.rb +++ b/app/models/channel/email_parser.rb @@ -134,9 +134,10 @@ class Channel::EmailParser if !data[:body].valid_encoding? data[:body] = data[:body].encode('utf-8', 'binary', invalid: :replace, undef: :replace, replace: '?') end + end # html attachment/body may exists and will be converted to text - else + if !mail.text_part || !data[:body] || data[:body] == '' filename = '-no name-' if mail.html_part && mail.html_part.body filename = 'message.html' diff --git a/test/fixtures/mail34.box b/test/fixtures/mail34.box new file mode 100644 index 000000000..36b39af43 --- /dev/null +++ b/test/fixtures/mail34.box @@ -0,0 +1,29 @@ +From: "Bay" +Subject: strange email with empty text/plain +To: bay@example.com +Date: 11 Nov 2015 12:07:51 +0000 +Priority: normal +X-Priority: 3 (Normal) +Importance: normal +X-David-SYM: 0 +X-David-Flags: 0 +Message-ID: <7ca10659-214c-4bd0-8438-b935a01c7601@stange> +MIME-Version: 1.0 +Content-Type: multipart/alternative; boundary="----_=_NextPart_000_06409CFC.56433DA7" + +This message is in MIME format. Since your mail reader does not understand +this format, some or all of this message may not be legible. + +------_=_NextPart_000_06409CFC.56433DA7 +Content-Type: text/plain; charset=iso-8859-1 +Content-Transfer-Encoding: quoted-printable + + + +------_=_NextPart_000_06409CFC.56433DA7 +Content-Type: text/html; charset=utf-8 +Content-Transfer-Encoding: quoted-printable + +some html text + +------_=_NextPart_000_06409CFC.56433DA7 diff --git a/test/unit/email_parser_test.rb b/test/unit/email_parser_test.rb index 7b3c723cf..5f01bef5a 100644 --- a/test/unit/email_parser_test.rb +++ b/test/unit/email_parser_test.rb @@ -308,7 +308,7 @@ Hof # spam email { data: IO.read('test/fixtures/mail15.box'), - body_md5: 'd41d8cd98f00b204e9800998ecf8427e', + body_md5: '5872ddcdfdf6bfe40f36cd0408fca667', attachments: [ # :preferences=>{"Message-ID"=>"", "Content-Type"=>"application/octet-stream; name=\"\xBC\xA8\xD0\xA7\xB9\xDC\xC0\xED,\xBE\xBF\xBE\xB9\xCB\xAD\xB4\xED\xC1\xCB.xls\"", "Mime-Type"=>"application/octet-stream", "Charset"=>"UTF-8"}} # mutt c1abb5fb77a9d2ab2017749a7987c074 @@ -466,14 +466,14 @@ Freemont and pulling out several minutes. }, { data: IO.read('test/fixtures/mail24.box'), - body_md5: 'd41d8cd98f00b204e9800998ecf8427e', + body_md5: '5872ddcdfdf6bfe40f36cd0408fca667', params: { from: 'oracle@IG0-1-DB01.example.com', from_email: 'oracle@IG0-1-DB01.example.com', from_display_name: '', subject: 'Regelsets im Test-Status gefunden: 1', to: 'support@example.com', - body: '', + body: 'no visible content', }, attachments: [ { @@ -672,9 +672,24 @@ Weil wir die Echtheit oder Vollständigkeit der in dieserNachricht enthaltenen I to: 'info@znuny.inc', }, }, + { + data: IO.read('test/fixtures/mail34.box'), + body_md5: 'b6e46176404ec81b3ab412fe71dff0f0', + params: { + from: 'Bay ', + from_email: 'memberbay+12345@members.somewhat', + from_display_name: 'Bay', + subject: 'strange email with empty text/plain', + to: 'bay@example.com', + body: 'some html text', + }, + }, ] + count = 0 files.each { |file| + count += 1 + #p "Count: #{count}" parser = Channel::EmailParser.new data = parser.parse( file[:data] )