Browse Source

chat: add waiting screen, outsource agent view, add basic socket communication

Felix Niklas 9 years ago
parent
commit
806ffb70c7

+ 116 - 44
public/assets/chat/chat.coffee

@@ -5,10 +5,11 @@ do($ = window.jQuery, window) ->
 
     defaults:
       invitationPhrase: '<strong>Chat</strong> with us!'
-      agentPhrase: '%%agent%% is helping you'
+      agentPhrase: ' is helping you'
       show: true
       target: $('body')
 
+    _messageCount: 0
     isOpen: false
     blinkOnlineInterval: null
     stopBlinOnlineStateTimeout: null
@@ -25,15 +26,20 @@ do($ = window.jQuery, window) ->
       'Connection re-established': 'Connection re-established'
       'Today': 'Today'
 
-    T: (string) ->
+    T: (string) =>
       return @strings[string]
 
-    view: (name) ->
-      return window.zammadChatTemplates[name]
+    view: (name) =>
+      return (options) =>
+        if !options
+          options = {}
+
+        options.T = @T
+        return window.zammadChatTemplates[name](options)
 
     constructor: (el, options) ->
       @options = $.extend {}, @defaults, options
-      @el = $(@view('chat')())
+      @el = $(@view('chat')(@options))
       @options.target.append @el
 
       @setAgentOnlineState @isOnline
@@ -46,19 +52,18 @@ do($ = window.jQuery, window) ->
       ).autoGrow { extraLine: false }
 
       if !window.WebSocket
-        console.log('no websockets available')
+        console.log('Zammad Chat: Browser not supported')
         return
 
       zammad_host = 'ws://localhost:6042'
       @ws = new window.WebSocket(zammad_host)
-      console.log("connecting ws #{zammad_host}")
+      console.log("Connecting to #{zammad_host}")
 
       @ws.onopen = =>
         console.log('ws connected')
+        @send "chat_status"
 
-      @ws.onmessage = (e) =>
-        pipe = JSON.parse( e.data )
-        console.log 'debug', 'ws:onmessage', pipe
+      @ws.onmessage = @onWebSocketMessage
 
       @ws.onclose = (e) =>
         console.log 'debug', 'close websocket connection'
@@ -71,6 +76,44 @@ do($ = window.jQuery, window) ->
         event.preventDefault()
         @sendMessage()
 
+    send: (action, data) =>
+      pipe = JSON.stringify
+        action: action
+        data: data
+
+      @ws.send pipe
+
+    onWebSocketMessage: (e) =>
+      pipe = JSON.parse( e.data )
+      console.log 'debug', 'ws:onmessage', pipe
+
+      switch pipe.action
+        when 'message'
+          @receiveMessage pipe.data
+        when 'typing_start'
+          @onAgentTypingStart()
+        when 'typing_end'
+          @onAgentTypingEnd()
+        when 'chat_init'
+          switch pipe.data.state
+            when 'ok'
+              @onConnectionEstablished pipe.data.agent
+            when 'queue'
+              @onQueue pipe.data.position
+        when 'chat_status'
+          switch pipe.data.state 
+            when 'ok'
+              @onReady()
+            when 'offline'
+              console.log 'Zammad Chat: No agent online'
+            when 'chat_disabled'
+              console.log 'Zammad Chat: Chat is disabled'
+            when 'no_seats_available'
+              console.log 'Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue
+
+    onReady: =>
+      @show() if @options.show
+
     onInput: =>
       # remove unread-state from messages
       @el.find('.zammad-chat-message--unread')
@@ -86,10 +129,12 @@ do($ = window.jQuery, window) ->
     onTypingStart: ->
       # send typing start event
       @isTyping = true
+      @send 'typing_start'
 
     onTypingEnd: =>
       # send typing end event
       @isTyping = false
+      @send 'typing_end'
 
     onSubmit: (event) =>
       event.preventDefault()
@@ -98,28 +143,35 @@ do($ = window.jQuery, window) ->
     sendMessage: ->
       message = @el.find('.zammad-chat-input').val()
 
-      if message
-        messageElement = @view('message')
-          message: message
-          from: 'customer'
+      if !message
+        return
 
-        @maybeAddTimestamp()
+      messageElement = @view('message')
+        message: message
+        from: 'customer'
+        id: @_messageCount++
 
-        # add message before message typing loader
-        if @el.find('.zammad-chat-message--typing').size()
-          @lastAddedType = 'typing-placeholder'
-          @el.find('.zammad-chat-message--typing').before messageElement 
-        else
-          @lastAddedType = 'message--customer'
-          @el.find('.zammad-chat-body').append messageElement
+      @maybeAddTimestamp()
 
-        @el.find('.zammad-chat-input').val('')
-        @scrollToBottom()
+      # add message before message typing loader
+      if @el.find('.zammad-chat-message--typing').size()
+        @lastAddedType = 'typing-placeholder'
+        @el.find('.zammad-chat-message--typing').before messageElement 
+      else
+        @lastAddedType = 'message--customer'
+        @el.find('.zammad-chat-body').append messageElement
 
-        @isTyping = false
-        # send message event
+      @el.find('.zammad-chat-input').val('')
+      @scrollToBottom()
 
-    receiveMessage: (message) =>
+      @isTyping = false
+
+      # send message event
+      @send 'message',
+        body: message
+        id: @_messageCount
+
+    receiveMessage: (data) =>
       # hide writing indicator
       @onAgentTypingEnd()
 
@@ -128,7 +180,8 @@ do($ = window.jQuery, window) ->
       @lastAddedType = 'message--agent'
       unread = document.hidden ? " zammad-chat-message--unread" : ""
       @el.find('.zammad-chat-body').append @view('message')
-        message: message
+        message: data.body
+        id: data.id
         from: 'agent'
       @scrollToBottom()
 
@@ -136,15 +189,18 @@ do($ = window.jQuery, window) ->
       if @isOpen then @close() else @open()
 
     open: ->
+      @showLoader()
+
       @el
         .addClass('zammad-chat-is-open')
         .animate { bottom: 0 }, 500, @onOpenAnimationEnd
 
     onOpenAnimationEnd: =>
       @isOpen = true
-      setTimeout @onConnectionEstablished, 1180
-      setTimeout @onAgentTypingStart, 2000
-      setTimeout @receiveMessage, 5000, "Hello! How can I help you?"
+      #setTimeout @onQueue, 1180
+      # setTimeout @onConnectionEstablished, 1180
+      # setTimeout @onAgentTypingStart, 2000
+      # setTimeout @receiveMessage, 5000, "Hello! How can I help you?"
       @connect()
 
     close: ->
@@ -166,8 +222,15 @@ do($ = window.jQuery, window) ->
 
       @el.css 'bottom', -remainerHeight
 
+    onQueue: (position) =>
+      console.log "onQueue", position
+      @inQueue = true
+
+      @el.find('.zammad-chat-body').html @view('waiting')
+        position: position
+
     onAgentTypingStart: =>
-      # never display two loaders
+      # never display two typing indicators
       return if @el.find('.zammad-chat-message--typing').size()
 
       @maybeAddTimestamp()
@@ -212,7 +275,7 @@ do($ = window.jQuery, window) ->
       @el.find('.zammad-chat-body').scrollTop($('.zammad-chat-body').prop('scrollHeight'))
 
     connect: ->
-
+      @send('chat_init')
 
     reconnect: =>
       # set status to connecting
@@ -227,17 +290,26 @@ do($ = window.jQuery, window) ->
       @addStatus @T('Connection re-established')
 
     disconnect: ->
-      @el.find('.zammad-chat-loader').removeClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden');
-
-    onConnectionEstablished: =>
-      @el.find('.zammad-chat-loader').addClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-agent-status').removeClass('zammad-chat-is-hidden');
-      @el.find('.zammad-chat-input').focus();
+      @showLoader()
+      @el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden')
+      @el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden')
+      @el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden')
+
+    onConnectionEstablished: (agent) =>
+      @inQueue = false
+      @agent = agent
+
+      @el.find('.zammad-chat-agent').html @view('agent')
+        agent: agent
+
+      @el.find('.zammad-chat-body').empty()
+      @el.find('.zammad-chat-welcome').addClass('zammad-chat-is-hidden')
+      @el.find('.zammad-chat-agent').removeClass('zammad-chat-is-hidden')
+      @el.find('.zammad-chat-agent-status').removeClass('zammad-chat-is-hidden')
+      @el.find('.zammad-chat-input').focus()
+
+    showLoader: ->
+      @el.find('.zammad-chat-body').html @view('loader')()
 
     setAgentOnlineState: (state) =>
       @isOnline = state

+ 324 - 32
public/assets/chat/chat.js

@@ -1,7 +1,7 @@
 if (!window.zammadChatTemplates) {
   window.zammadChatTemplates = {};
 }
-window.zammadChatTemplates["chat"] = function (__obj) {
+window.zammadChatTemplates["agent"] = function (__obj) {
   if (!__obj) __obj = {};
   var __out = [], __capture = function(callback) {
     var out = __out, result;
@@ -40,7 +40,19 @@ window.zammadChatTemplates["chat"] = function (__obj) {
   }
   (function() {
     (function() {
-      __out.push('<div class="zammad-chat">\n  <div class="zammad-chat-header">\n    <div class="zammad-chat-header-controls">\n      <span class="zammad-chat-agent-status zammad-chat-is-hidden" data-status="online">Online</span>\n      <span class="zammad-chat-toggle">\n        <svg class="zammad-chat-toggle-icon-open" viewBox="0 0 13 7"><path d="M10.807 7l1.4-1.428-5-4.9L6.5-.02l-.7.7-4.9 4.9 1.414 1.413L6.5 2.886 10.807 7z" fill-rule="evenodd"/></svg>\n        <svg class="zammad-chat-toggle-icon-close" viewBox="0 0 13 7"><path d="M6.554 4.214L2.246 0l-1.4 1.428 5 4.9.708.693.7-.7 4.9-4.9L10.74.008 6.553 4.214z" fill-rule="evenodd"/></svg>\n      </span>\n    </div>\n    <div class="zammad-chat-agent zammad-chat-is-hidden">\n      <img class="zammad-chat-agent-avatar" src="https://s3.amazonaws.com/uifaces/faces/twitter/joshaustin/128.jpg">\n      <span class="zammad-chat-agent-sentence">\n        <span class="zammad-chat-agent-name">Adam</span> is helping you.\n      </span>\n    </div>\n    <div class="zammad-chat-welcome">\n      <svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg>\n      <span class="zammad-chat-welcome-text"><strong>Chat</strong> with us!</span>\n    </div>\n  </div>\n  <div class="zammad-chat-loader">\n    <span class="zammad-chat-loading-animation">\n      <span class="zammad-chat-loading-circle"></span>\n      <span class="zammad-chat-loading-circle"></span>\n      <span class="zammad-chat-loading-circle"></span>\n    </span>\n    <span class="zammad-chat-loader-text">Connecting</span>\n  </div>\n  <div class="zammad-chat-body"></div>\n  <form class="zammad-chat-controls">\n    <textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea>\n    <button type="submit" class="zammad-chat-send">Send</button>\n  </form>\n</div>');
+      __out.push('<img class="zammad-chat-agent-avatar" src="');
+    
+      __out.push(__sanitize(this.agent.avatar));
+    
+      __out.push('">\n<span class="zammad-chat-agent-sentence">\n  <span class="zammad-chat-agent-name">');
+    
+      __out.push(__sanitize(this.agent.name));
+    
+      __out.push('</span> ');
+    
+      __out.push(this.agentPhrase);
+    
+      __out.push('\n</span>');
     
     }).call(this);
     
@@ -56,11 +68,13 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
   ZammadChat = (function() {
     ZammadChat.prototype.defaults = {
       invitationPhrase: '<strong>Chat</strong> with us!',
-      agentPhrase: '%%agent%% is helping you',
+      agentPhrase: ' is helping you',
       show: true,
       target: $('body')
     };
 
+    ZammadChat.prototype._messageCount = 0;
+
     ZammadChat.prototype.isOpen = false;
 
     ZammadChat.prototype.blinkOnlineInterval = null;
@@ -92,7 +106,15 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
     };
 
     ZammadChat.prototype.view = function(name) {
-      return window.zammadChatTemplates[name];
+      return (function(_this) {
+        return function(options) {
+          if (!options) {
+            options = {};
+          }
+          options.T = _this.T;
+          return window.zammadChatTemplates[name](options);
+        };
+      })(this);
     };
 
     function ZammadChat(el, options) {
@@ -102,6 +124,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
       this.reconnect = bind(this.reconnect, this);
       this.onAgentTypingEnd = bind(this.onAgentTypingEnd, this);
       this.onAgentTypingStart = bind(this.onAgentTypingStart, this);
+      this.onQueue = bind(this.onQueue, this);
       this.onCloseAnimationEnd = bind(this.onCloseAnimationEnd, this);
       this.onOpenAnimationEnd = bind(this.onOpenAnimationEnd, this);
       this.toggle = bind(this.toggle, this);
@@ -109,9 +132,15 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
       this.onSubmit = bind(this.onSubmit, this);
       this.onTypingEnd = bind(this.onTypingEnd, this);
       this.onInput = bind(this.onInput, this);
+      this.onReady = bind(this.onReady, this);
+      this.onWebSocketMessage = bind(this.onWebSocketMessage, this);
+      this.send = bind(this.send, this);
       this.checkForEnter = bind(this.checkForEnter, this);
+      this.view = bind(this.view, this);
+      this.T = bind(this.T, this);
+      var zammad_host;
       this.options = $.extend({}, this.defaults, options);
-      this.el = $(this.view('chat')());
+      this.el = $(this.view('chat')(this.options));
       this.options.target.append(this.el);
       this.setAgentOnlineState(this.isOnline);
       this.el.find('.zammad-chat-header').click(this.toggle);
@@ -122,6 +151,30 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
       }).autoGrow({
         extraLine: false
       });
+      if (!window.WebSocket) {
+        console.log('Zammad Chat: Browser not supported');
+        return;
+      }
+      zammad_host = 'ws://localhost:6042';
+      this.ws = new window.WebSocket(zammad_host);
+      console.log("Connecting to " + zammad_host);
+      this.ws.onopen = (function(_this) {
+        return function() {
+          console.log('ws connected');
+          return _this.send("chat_status");
+        };
+      })(this);
+      this.ws.onmessage = this.onWebSocketMessage;
+      this.ws.onclose = (function(_this) {
+        return function(e) {
+          return console.log('debug', 'close websocket connection');
+        };
+      })(this);
+      this.ws.onerror = (function(_this) {
+        return function(e) {
+          return console.log('debug', 'ws:onerror', e);
+        };
+      })(this);
     }
 
     ZammadChat.prototype.checkForEnter = function(event) {
@@ -131,6 +184,54 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
       }
     };
 
+    ZammadChat.prototype.send = function(action, data) {
+      var pipe;
+      pipe = JSON.stringify({
+        action: action,
+        data: data
+      });
+      return this.ws.send(pipe);
+    };
+
+    ZammadChat.prototype.onWebSocketMessage = function(e) {
+      var pipe;
+      pipe = JSON.parse(e.data);
+      console.log('debug', 'ws:onmessage', pipe);
+      switch (pipe.action) {
+        case 'message':
+          return this.receiveMessage(pipe.data);
+        case 'typing_start':
+          return this.onAgentTypingStart();
+        case 'typing_end':
+          return this.onAgentTypingEnd();
+        case 'chat_init':
+          switch (pipe.data.state) {
+            case 'ok':
+              return this.onConnectionEstablished(pipe.data.agent);
+            case 'queue':
+              return this.onQueue(pipe.data.position);
+          }
+          break;
+        case 'chat_status':
+          switch (pipe.data.state) {
+            case 'ok':
+              return this.onReady();
+            case 'offline':
+              return console.log('Zammad Chat: No agent online');
+            case 'chat_disabled':
+              return console.log('Zammad Chat: Chat is disabled');
+            case 'no_seats_available':
+              return console.log('Zammad Chat: Too many clients in queue. Clients in queue: ', pipe.data.queue);
+          }
+      }
+    };
+
+    ZammadChat.prototype.onReady = function() {
+      if (this.options.show) {
+        return this.show();
+      }
+    };
+
     ZammadChat.prototype.onInput = function() {
       this.el.find('.zammad-chat-message--unread').removeClass('zammad-chat-message--unread');
       if (this.inputTimeout) {
@@ -143,11 +244,13 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
     };
 
     ZammadChat.prototype.onTypingStart = function() {
-      return this.isTyping = true;
+      this.isTyping = true;
+      return this.send('typing_start');
     };
 
     ZammadChat.prototype.onTypingEnd = function() {
-      return this.isTyping = false;
+      this.isTyping = false;
+      return this.send('typing_end');
     };
 
     ZammadChat.prototype.onSubmit = function(event) {
@@ -158,26 +261,32 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
     ZammadChat.prototype.sendMessage = function() {
       var message, messageElement;
       message = this.el.find('.zammad-chat-input').val();
-      if (message) {
-        messageElement = this.view('message')({
-          message: message,
-          from: 'customer'
-        });
-        this.maybeAddTimestamp();
-        if (this.el.find('.zammad-chat-message--typing').size()) {
-          this.lastAddedType = 'typing-placeholder';
-          this.el.find('.zammad-chat-message--typing').before(messageElement);
-        } else {
-          this.lastAddedType = 'message--customer';
-          this.el.find('.zammad-chat-body').append(messageElement);
-        }
-        this.el.find('.zammad-chat-input').val('');
-        this.scrollToBottom();
-        return this.isTyping = false;
+      if (!message) {
+        return;
+      }
+      messageElement = this.view('message')({
+        message: message,
+        from: 'customer',
+        id: this._messageCount++
+      });
+      this.maybeAddTimestamp();
+      if (this.el.find('.zammad-chat-message--typing').size()) {
+        this.lastAddedType = 'typing-placeholder';
+        this.el.find('.zammad-chat-message--typing').before(messageElement);
+      } else {
+        this.lastAddedType = 'message--customer';
+        this.el.find('.zammad-chat-body').append(messageElement);
       }
+      this.el.find('.zammad-chat-input').val('');
+      this.scrollToBottom();
+      this.isTyping = false;
+      return this.send('message', {
+        body: message,
+        id: this._messageCount
+      });
     };
 
-    ZammadChat.prototype.receiveMessage = function(message) {
+    ZammadChat.prototype.receiveMessage = function(data) {
       var ref, unread;
       this.onAgentTypingEnd();
       this.maybeAddTimestamp();
@@ -186,7 +295,8 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
         " zammad-chat-message--unread": ""
       };
       this.el.find('.zammad-chat-body').append(this.view('message')({
-        message: message,
+        message: data.body,
+        id: data.id,
         from: 'agent'
       }));
       return this.scrollToBottom();
@@ -201,6 +311,7 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
     };
 
     ZammadChat.prototype.open = function() {
+      this.showLoader();
       return this.el.addClass('zammad-chat-is-open').animate({
         bottom: 0
       }, 500, this.onOpenAnimationEnd);
@@ -208,9 +319,6 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
 
     ZammadChat.prototype.onOpenAnimationEnd = function() {
       this.isOpen = true;
-      setTimeout(this.onConnectionEstablished, 1180);
-      setTimeout(this.onAgentTypingStart, 2000);
-      setTimeout(this.receiveMessage, 5000, "Hello! How can I help you?");
       return this.connect();
     };
 
@@ -239,6 +347,14 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
       return this.el.css('bottom', -remainerHeight);
     };
 
+    ZammadChat.prototype.onQueue = function(position) {
+      console.log("onQueue", position);
+      this.inQueue = true;
+      return this.el.find('.zammad-chat-body').html(this.view('waiting')({
+        position: position
+      }));
+    };
+
     ZammadChat.prototype.onAgentTypingStart = function() {
       if (this.el.find('.zammad-chat-message--typing').size()) {
         return;
@@ -287,7 +403,9 @@ 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.connect = function() {};
+    ZammadChat.prototype.connect = function() {
+      return this.send('chat_init');
+    };
 
     ZammadChat.prototype.reconnect = function() {
       this.lastAddedType = 'status';
@@ -302,20 +420,29 @@ var bind = function(fn, me){ return function(){ return fn.apply(me, arguments);
     };
 
     ZammadChat.prototype.disconnect = function() {
-      this.el.find('.zammad-chat-loader').removeClass('zammad-chat-is-hidden');
+      this.showLoader();
       this.el.find('.zammad-chat-welcome').removeClass('zammad-chat-is-hidden');
       this.el.find('.zammad-chat-agent').addClass('zammad-chat-is-hidden');
       return this.el.find('.zammad-chat-agent-status').addClass('zammad-chat-is-hidden');
     };
 
-    ZammadChat.prototype.onConnectionEstablished = function() {
-      this.el.find('.zammad-chat-loader').addClass('zammad-chat-is-hidden');
+    ZammadChat.prototype.onConnectionEstablished = function(agent) {
+      this.inQueue = false;
+      this.agent = agent;
+      this.el.find('.zammad-chat-agent').html(this.view('agent')({
+        agent: agent
+      }));
+      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');
       return this.el.find('.zammad-chat-input').focus();
     };
 
+    ZammadChat.prototype.showLoader = function() {
+      return this.el.find('.zammad-chat-body').html(this.view('loader')());
+    };
+
     ZammadChat.prototype.setAgentOnlineState = function(state) {
       this.isOnline = state;
       return this.el.find('.zammad-chat-agent-status').toggleClass('zammad-chat-is-online', state).text(state ? this.T('Online') : this.T('Offline'));
@@ -406,6 +533,116 @@ jQuery.fn.autoGrow = function(options) {
 
   });
 };
+if (!window.zammadChatTemplates) {
+  window.zammadChatTemplates = {};
+}
+window.zammadChatTemplates["chat"] = function (__obj) {
+  if (!__obj) __obj = {};
+  var __out = [], __capture = function(callback) {
+    var out = __out, result;
+    __out = [];
+    callback.call(this);
+    result = __out.join('');
+    __out = out;
+    return __safe(result);
+  }, __sanitize = function(value) {
+    if (value && value.ecoSafe) {
+      return value;
+    } else if (typeof value !== 'undefined' && value != null) {
+      return __escape(value);
+    } else {
+      return '';
+    }
+  }, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
+  __safe = __obj.safe = function(value) {
+    if (value && value.ecoSafe) {
+      return value;
+    } else {
+      if (!(typeof value !== 'undefined' && value != null)) value = '';
+      var result = new String(value);
+      result.ecoSafe = true;
+      return result;
+    }
+  };
+  if (!__escape) {
+    __escape = __obj.escape = function(value) {
+      return ('' + value)
+        .replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/"/g, '&quot;');
+    };
+  }
+  (function() {
+    (function() {
+      __out.push('<div class="zammad-chat">\n  <div class="zammad-chat-header">\n    <div class="zammad-chat-header-controls">\n      <span class="zammad-chat-agent-status zammad-chat-is-hidden" data-status="online">Online</span>\n      <span class="zammad-chat-toggle">\n        <svg class="zammad-chat-toggle-icon-open" viewBox="0 0 13 7"><path d="M10.807 7l1.4-1.428-5-4.9L6.5-.02l-.7.7-4.9 4.9 1.414 1.413L6.5 2.886 10.807 7z" fill-rule="evenodd"/></svg>\n        <svg class="zammad-chat-toggle-icon-close" viewBox="0 0 13 7"><path d="M6.554 4.214L2.246 0l-1.4 1.428 5 4.9.708.693.7-.7 4.9-4.9L10.74.008 6.553 4.214z" fill-rule="evenodd"/></svg>\n      </span>\n    </div>\n    <div class="zammad-chat-agent zammad-chat-is-hidden">\n      \n    </div>\n    <div class="zammad-chat-welcome">\n      <svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg>\n      <span class="zammad-chat-welcome-text">');
+    
+      __out.push(this.invitationPhrase);
+    
+      __out.push('</span>\n    </div>\n  </div>\n  <div class="zammad-chat-body"></div>\n  <form class="zammad-chat-controls">\n    <textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea>\n    <button type="submit" class="zammad-chat-send">Send</button>\n  </form>\n</div>');
+    
+    }).call(this);
+    
+  }).call(__obj);
+  __obj.safe = __objSafe, __obj.escape = __escape;
+  return __out.join('');
+};
+
+if (!window.zammadChatTemplates) {
+  window.zammadChatTemplates = {};
+}
+window.zammadChatTemplates["loader"] = function (__obj) {
+  if (!__obj) __obj = {};
+  var __out = [], __capture = function(callback) {
+    var out = __out, result;
+    __out = [];
+    callback.call(this);
+    result = __out.join('');
+    __out = out;
+    return __safe(result);
+  }, __sanitize = function(value) {
+    if (value && value.ecoSafe) {
+      return value;
+    } else if (typeof value !== 'undefined' && value != null) {
+      return __escape(value);
+    } else {
+      return '';
+    }
+  }, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
+  __safe = __obj.safe = function(value) {
+    if (value && value.ecoSafe) {
+      return value;
+    } else {
+      if (!(typeof value !== 'undefined' && value != null)) value = '';
+      var result = new String(value);
+      result.ecoSafe = true;
+      return result;
+    }
+  };
+  if (!__escape) {
+    __escape = __obj.escape = function(value) {
+      return ('' + value)
+        .replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/"/g, '&quot;');
+    };
+  }
+  (function() {
+    (function() {
+      __out.push('<div class="zammad-chat-modal">\n  <span class="zammad-chat-loading-animation">\n    <span class="zammad-chat-loading-circle"></span>\n    <span class="zammad-chat-loading-circle"></span>\n    <span class="zammad-chat-loading-circle"></span>\n  </span>\n  <span class="zammad-chat-modal-text">');
+    
+      __out.push(__sanitize(this.T('Connecting')));
+    
+      __out.push('</span>\n</div>');
+    
+    }).call(this);
+    
+  }).call(__obj);
+  __obj.safe = __objSafe, __obj.escape = __escape;
+  return __out.join('');
+};
+
 if (!window.zammadChatTemplates) {
   window.zammadChatTemplates = {};
 }
@@ -574,3 +811,58 @@ window.zammadChatTemplates["typingIndicator"] = function (__obj) {
   __obj.safe = __objSafe, __obj.escape = __escape;
   return __out.join('');
 };
+
+if (!window.zammadChatTemplates) {
+  window.zammadChatTemplates = {};
+}
+window.zammadChatTemplates["waiting"] = function (__obj) {
+  if (!__obj) __obj = {};
+  var __out = [], __capture = function(callback) {
+    var out = __out, result;
+    __out = [];
+    callback.call(this);
+    result = __out.join('');
+    __out = out;
+    return __safe(result);
+  }, __sanitize = function(value) {
+    if (value && value.ecoSafe) {
+      return value;
+    } else if (typeof value !== 'undefined' && value != null) {
+      return __escape(value);
+    } else {
+      return '';
+    }
+  }, __safe, __objSafe = __obj.safe, __escape = __obj.escape;
+  __safe = __obj.safe = function(value) {
+    if (value && value.ecoSafe) {
+      return value;
+    } else {
+      if (!(typeof value !== 'undefined' && value != null)) value = '';
+      var result = new String(value);
+      result.ecoSafe = true;
+      return result;
+    }
+  };
+  if (!__escape) {
+    __escape = __obj.escape = function(value) {
+      return ('' + value)
+        .replace(/&/g, '&amp;')
+        .replace(/</g, '&lt;')
+        .replace(/>/g, '&gt;')
+        .replace(/"/g, '&quot;');
+    };
+  }
+  (function() {
+    (function() {
+      __out.push('<div class="zammad-chat-modal">\n  <div class="zammad-chat-modal-text">\n    <span class="zammad-chat-loading-animation">\n      <span class="zammad-chat-loading-circle"></span>\n      <span class="zammad-chat-loading-circle"></span>\n      <span class="zammad-chat-loading-circle"></span>\n    </span>\n    Leider sind gerade alle Mitarbeiter belegt.<br>\n    Warteliste-Position: <strong>');
+    
+      __out.push(__sanitize(this.position));
+    
+      __out.push('</strong>\n  </div>\n</div>');
+    
+    }).call(this);
+    
+  }).call(__obj);
+  __obj.safe = __objSafe, __obj.escape = __escape;
+  return __out.join('');
+};

File diff suppressed because it is too large
+ 0 - 0
public/assets/chat/chat.min.js


+ 11 - 5
public/assets/chat/style.css

@@ -123,7 +123,7 @@
     -webkit-transform: scale(1);
     transform: scale(1); } }
 
-.zammad-chat-loader {
+.zammad-chat-modal {
   position: absolute;
   left: 0;
   right: 0;
@@ -144,12 +144,15 @@
   -ms-flex-pack: center;
   justify-content: center; }
 
-.zammad-chat-loader-text {
-  font-size: 1.3em; }
+.zammad-chat-modal-text {
+  font-size: 1.3em;
+  line-height: 1.45; }
+  .zammad-chat-modal-text .zammad-chat-loading-animation {
+    font-size: 0.7em; }
 
-.zammad-chat-loader .zammad-chat-loading-animation {
+.zammad-chat-modal .zammad-chat-loading-animation {
   margin-right: 8px;
-  margin-left: -19px; }
+  vertical-align: middle; }
 
 .zammad-chat-body {
   padding: 0.5em 1em;
@@ -192,6 +195,9 @@
 .zammad-chat-message--typing .zammad-chat-message-body {
   white-space: normal; }
 
+.zammad-chat-loading-animation {
+  display: inline-block; }
+
 .zammad-chat-loading-circle {
   background: #c5dded;
   border-radius: 100%;

+ 13 - 4
public/assets/chat/style.scss

@@ -154,7 +154,7 @@ $baseTextColor: if($luminance < 0.2, white, black);
   to { opacity: 1; transform: scale(1); }
 }
 
-.zammad-chat-loader {
+.zammad-chat-modal {
   position: absolute;
   left: 0;
   right: 0;
@@ -170,13 +170,18 @@ $baseTextColor: if($luminance < 0.2, white, black);
   justify-content: center;
 }
 
-.zammad-chat-loader-text {
+.zammad-chat-modal-text {
   font-size: 1.3em;
+  line-height: 1.45;
+  
+  .zammad-chat-loading-animation {
+    font-size: 0.7em;
+  }
 }
 
-.zammad-chat-loader .zammad-chat-loading-animation {
+.zammad-chat-modal .zammad-chat-loading-animation {
   margin-right: 8px;
-  margin-left: -19px;
+  vertical-align: middle;
 }
 
 .zammad-chat-body {
@@ -232,6 +237,10 @@ $baseTextColor: if($luminance < 0.2, white, black);
   white-space: normal;
 }
 
+.zammad-chat-loading-animation {
+  display: inline-block;
+}
+
 .zammad-chat-loading-circle {
   background: desaturate(lightenMax($themeColor, 50%, 85%), 15%);
   border-radius: 100%;

+ 4 - 0
public/assets/chat/views/agent.eco

@@ -0,0 +1,4 @@
+<img class="zammad-chat-agent-avatar" src="<%= @agent.avatar %>">
+<span class="zammad-chat-agent-sentence">
+  <span class="zammad-chat-agent-name"><%= @agent.name %></span> <%- @agentPhrase %>
+</span>

+ 2 - 13
public/assets/chat/views/chat.eco

@@ -8,24 +8,13 @@
       </span>
     </div>
     <div class="zammad-chat-agent zammad-chat-is-hidden">
-      <img class="zammad-chat-agent-avatar" src="https://s3.amazonaws.com/uifaces/faces/twitter/joshaustin/128.jpg">
-      <span class="zammad-chat-agent-sentence">
-        <span class="zammad-chat-agent-name">Adam</span> is helping you.
-      </span>
+      
     </div>
     <div class="zammad-chat-welcome">
       <svg class="zammad-chat-icon" viewBox="0 0 24 24"><path d="M2 5C2 4 3 3 4 3h16c1 0 2 1 2 2v10C22 16 21 17 20 17H4C3 17 2 16 2 15V5zM12 17l6 4v-4h-6z" fill-rule="evenodd"/></svg>
-      <span class="zammad-chat-welcome-text"><strong>Chat</strong> with us!</span>
+      <span class="zammad-chat-welcome-text"><%- @invitationPhrase %></span>
     </div>
   </div>
-  <div class="zammad-chat-loader">
-    <span class="zammad-chat-loading-animation">
-      <span class="zammad-chat-loading-circle"></span>
-      <span class="zammad-chat-loading-circle"></span>
-      <span class="zammad-chat-loading-circle"></span>
-    </span>
-    <span class="zammad-chat-loader-text">Connecting</span>
-  </div>
   <div class="zammad-chat-body"></div>
   <form class="zammad-chat-controls">
     <textarea class="zammad-chat-input" rows="1" placeholder="Compose your message..."></textarea>

+ 8 - 0
public/assets/chat/views/loader.eco

@@ -0,0 +1,8 @@
+<div class="zammad-chat-modal">
+  <span class="zammad-chat-loading-animation">
+    <span class="zammad-chat-loading-circle"></span>
+    <span class="zammad-chat-loading-circle"></span>
+    <span class="zammad-chat-loading-circle"></span>
+  </span>
+  <span class="zammad-chat-modal-text"><%= @T('Connecting') %></span>
+</div>

+ 11 - 0
public/assets/chat/views/waiting.eco

@@ -0,0 +1,11 @@
+<div class="zammad-chat-modal">
+  <div class="zammad-chat-modal-text">
+    <span class="zammad-chat-loading-animation">
+      <span class="zammad-chat-loading-circle"></span>
+      <span class="zammad-chat-loading-circle"></span>
+      <span class="zammad-chat-loading-circle"></span>
+    </span>
+    Leider sind gerade alle Mitarbeiter belegt.<br>
+    Warteliste-Position: <strong><%= @position %></strong>
+  </div>
+</div>

Some files were not shown because too many files changed in this diff