2015-10-15 09:14:19 +00:00
do ($ = window.jQuery, window) ->
2015-11-13 12:59:15 +00:00
scripts = document . getElementsByTagName ( ' script ' )
2019-01-19 15:20:31 +00:00
# search for script to get protocol and hostname for ws connection
2015-11-13 12:59:15 +00:00
myScript = scripts [ scripts . length - 1 ]
2019-01-19 15:20:31 +00:00
scriptProtocol = window . location . protocol . replace ( ' : ' , ' ' ) # set default protocol
if myScript && myScript . src
scriptHost = myScript . src . match ( ' .*://([^:/]*).* ' ) [ 1 ]
scriptProtocol = myScript . src . match ( ' (.*)://[^:/]*.* ' ) [ 1 ]
2015-11-13 12:59:15 +00:00
2015-10-15 09:14:19 +00:00
# Define the plugin class
2015-12-05 19:41:14 +00:00
class Base
defaults:
debug: false
constructor: (options) ->
@options = $ . extend { } , @ defaults , options
@log = new Log ( debug: @ options . debug , logPrefix: @ options . logPrefix || @ logPrefix )
class Log
defaults:
debug: false
constructor: (options) ->
@options = $ . extend { } , @ defaults , options
debug: (items...) =>
2015-12-06 15:40:47 +00:00
return if ! @ options . debug
2015-12-05 19:41:14 +00:00
@ log ( ' debug ' , items )
notice: (items...) =>
@ log ( ' notice ' , items )
error: (items...) =>
@ log ( ' error ' , items )
log: (level, items) =>
items . unshift ( ' || ' )
items . unshift ( level )
items . unshift ( @ options . logPrefix )
console . log . apply console , items
return if ! @ options . debug
logString = ' '
for item in items
logString += ' '
if typeof item is ' object '
logString += JSON . stringify ( item )
else if item && item . toString
logString += item . toString ( )
else
logString += item
$ ( ' .js-chatLogDisplay ' ) . prepend ( ' <div> ' + logString + ' </div> ' )
class Timeout extends Base
timeoutStartedAt: null
logPrefix: ' timeout '
defaults:
debug: false
timeout: 4
timeoutIntervallCheck: 0.5
constructor: (options) ->
super ( options )
start: =>
@ stop ( )
timeoutStartedAt = new Date
check = =>
timeLeft = new Date - new Date ( timeoutStartedAt . getTime ( ) + @ options . timeout * 1000 * 60 )
@ log . debug " Timeout check for #{ @ options . timeout } minutes (left #{ timeLeft / 1000 } sec.) " #, new Date
return if timeLeft < 0
@ stop ( )
@ options . callback ( )
@ log . debug " Start timeout in #{ @ options . timeout } minutes " #, new Date
@intervallId = setInterval ( check , @ options . timeoutIntervallCheck * 1000 * 60 )
stop: =>
return if ! @ intervallId
2015-12-08 11:32:19 +00:00
@ log . debug " Stop timeout of #{ @ options . timeout } minutes " #, new Date
2015-12-05 19:41:14 +00:00
clearInterval ( @ intervallId )
class Io extends Base
logPrefix: ' io '
constructor: (options) ->
super ( options )
set: (params) =>
for key , value of params
@ options [ key ] = value
connect: =>
@ log . debug " Connecting to #{ @ options . host } "
@ws = new window . WebSocket ( " #{ @ options . host } " )
@ws.onopen = (e) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' onOpen ' , e
2015-12-05 19:41:14 +00:00
@ options . onOpen ( e )
2015-12-09 14:11:29 +00:00
@ ping ( )
2015-12-05 19:41:14 +00:00
@ws.onmessage = (e) =>
pipes = JSON . parse ( e . data )
2015-12-08 11:32:19 +00:00
@ log . debug ' onMessage ' , e . data
2015-12-09 14:11:29 +00:00
for pipe in pipes
if pipe . event is ' pong '
@ ping ( )
2015-12-05 19:41:14 +00:00
if @ options . onMessage
@ options . onMessage ( pipes )
@ws.onclose = (e) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' close websocket connection ' , e
2015-12-09 14:11:29 +00:00
if @ pingDelayId
clearTimeout ( @ pingDelayId )
2015-12-08 11:32:19 +00:00
if @ manualClose
@ log . debug ' manual close, onClose callback '
@manualClose = false
if @ options . onClose
@ options . onClose ( e )
else
@ log . debug ' error close, onError callback '
if @ options . onError
@ options . onError ( ' Connection lost... ' )
2015-12-05 19:41:14 +00:00
@ws.onerror = (e) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' onError ' , e
2015-12-05 19:41:14 +00:00
if @ options . onError
@ options . onError ( e )
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
close: =>
2015-12-08 11:32:19 +00:00
@ log . debug ' close websocket manually '
@manualClose = true
2015-12-05 19:41:14 +00:00
@ ws . close ( )
reconnect: =>
2015-12-08 11:32:19 +00:00
@ log . debug ' reconnect '
@ close ( )
2015-12-05 19:41:14 +00:00
@ connect ( )
send: (event, data = {}) =>
@ log . debug ' send ' , event , data
msg = JSON . stringify
event: event
data: data
@ ws . send msg
2015-12-09 14:11:29 +00:00
ping: =>
localPing = =>
@ send ( ' ping ' )
@pingDelayId = setTimeout ( localPing , 29000 )
2015-12-05 19:41:14 +00:00
class ZammadChat extends Base
2015-10-15 09:14:19 +00:00
defaults:
2015-11-25 11:44:26 +00:00
chatId: undefined
2015-11-13 14:15:44 +00:00
show: true
2015-10-15 09:14:19 +00:00
target: $ ( ' body ' )
2015-11-13 12:59:15 +00:00
host: ' '
2015-11-13 14:15:44 +00:00
debug: false
2015-11-24 12:04:31 +00:00
flat: false
2015-11-25 23:40:52 +00:00
lang: undefined
cssAutoload: true
cssUrl: undefined
2015-11-16 10:46:42 +00:00
fontSize: undefined
2015-11-20 14:43:23 +00:00
buttonClass: ' open-zammad-chat '
inactiveClass: ' is-inactive '
2015-11-17 00:43:36 +00:00
title: ' <strong>Chat</strong> with us! '
2017-07-28 12:04:12 +00:00
scrollHint: ' Scroll down to see new messages '
2015-12-08 11:32:19 +00:00
idleTimeout: 6
2015-12-05 19:41:14 +00:00
idleTimeoutIntervallCheck: 0.5
inactiveTimeout: 8
inactiveTimeoutIntervallCheck: 0.5
waitingListTimeout: 4
waitingListTimeoutIntervallCheck: 0.5
2020-01-18 16:51:20 +00:00
# Callbacks
onReady: undefined
onCloseAnimationEnd: undefined
onError: undefined
onOpenAnimationEnd: undefined
onConnectionReestablished: undefined
onSessionClosed: undefined
onConnectionEstablished: undefined
onCssLoaded: undefined
2015-12-05 19:41:14 +00:00
logPrefix: ' chat '
2015-11-02 15:48:16 +00:00
_messageCount: 0
2015-12-08 11:32:19 +00:00
isOpen: false
2015-10-15 09:14:19 +00:00
blinkOnlineInterval: null
stopBlinOnlineStateTimeout: null
2016-01-05 12:46:43 +00:00
showTimeEveryXMinutes: 2
2015-10-15 09:14:19 +00:00
lastTimestamp: null
lastAddedType: null
inputTimeout: null
isTyping: false
2015-11-13 14:58:44 +00:00
state: ' offline '
2015-11-13 09:45:40 +00:00
initialQueueDelay: 10000
2015-11-25 16:36:06 +00:00
translations:
2019-01-25 18:53:01 +00:00
' da ' :
' <strong>Chat</strong> with us! ' : ' <strong>Chat</strong> med os! '
' Scroll down to see new messages ' : ' Scroll ned for at se nye beskeder '
' Online ' : ' Online '
' Offline ' : ' Offline '
' Connecting ' : ' Forbinder '
' Connection re-established ' : ' Forbindelse genoprettet '
' Today ' : ' I dag '
' Send ' : ' Send '
' Chat closed by %s ' : ' Chat lukket af %s '
' Compose your message... ' : ' Skriv en besked... '
' All colleagues are busy. ' : ' Alle kollegaer er optaget. '
' You are on waiting list position <strong>%s</strong>. ' : ' Du er i venteliste som nummer <strong>%s</strong>. '
' Start new conversation ' : ' Start en ny samtale '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Da du ikke har svaret i de sidste %s minutter er din samtale med <strong>%s</strong> blevet lukket. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Da du ikke har svaret i de sidste %s minutter er din samtale blevet lukket. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Vi beklager, det tager længere end forventet at få en ledig plads. Prøv venligst igen senere eller send os en e-mail. På forhånd tak! '
2017-01-09 13:02:10 +00:00
' de ' :
2016-01-20 18:09:53 +00:00
' <strong>Chat</strong> with us! ' : ' <strong>Chatte</strong> mit uns! '
2016-07-05 11:00:49 +00:00
' Scroll down to see new messages ' : ' Scrolle nach unten um neue Nachrichten zu sehen '
2015-11-25 16:36:06 +00:00
' Online ' : ' Online '
' Offline ' : ' Offline '
' Connecting ' : ' Verbinden '
' Connection re-established ' : ' Verbindung wiederhergestellt '
' Today ' : ' Heute '
' Send ' : ' Senden '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Chat beendet von %s '
2015-11-25 16:36:06 +00:00
' Compose your message... ' : ' Ihre Nachricht... '
2016-01-05 12:46:43 +00:00
' All colleagues are busy. ' : ' Alle Kollegen sind belegt. '
2015-11-25 16:36:06 +00:00
' You are on waiting list position <strong>%s</strong>. ' : ' Sie sind in der Warteliste an der Position <strong>%s</strong>. '
' Start new conversation ' : ' Neue Konversation starten '
2015-11-25 23:40:52 +00:00
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation mit <strong>%s</strong> geschlossen. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Da Sie in den letzten %s Minuten nichts geschrieben haben wurde Ihre Konversation geschlossen. '
2017-01-05 09:20:29 +00:00
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Es tut uns leid, es dauert länger als erwartet, um einen freien Platz zu erhalten. Bitte versuchen Sie es zu einem späteren Zeitpunkt noch einmal oder schicken Sie uns eine E-Mail. Vielen Dank! '
2017-07-27 14:22:50 +00:00
' es ' :
2017-07-21 12:30:13 +00:00
' <strong>Chat</strong> with us! ' : ' <strong>Chatee</strong> con nosotros! '
' Scroll down to see new messages ' : ' Haga scroll hacia abajo para ver nuevos mensajes '
' Online ' : ' En linea '
' Offline ' : ' Desconectado '
' Connecting ' : ' Conectando '
' Connection re-established ' : ' Conexión restablecida '
' Today ' : ' Hoy '
' Send ' : ' Enviar '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Chat cerrado por %s '
2017-07-21 12:30:13 +00:00
' Compose your message... ' : ' Escriba su mensaje... '
' All colleagues are busy. ' : ' Todos los agentes están ocupados. '
' You are on waiting list position <strong>%s</strong>. ' : ' Usted está en la posición <strong>%s</strong> de la lista de espera. '
' Start new conversation ' : ' Iniciar nueva conversación '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Puesto que usted no respondió en los últimos %s minutos su conversación con <strong>%s</strong> se ha cerrado. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Puesto que usted no respondió en los últimos %s minutos su conversación se ha cerrado. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Lo sentimos, se tarda más tiempo de lo esperado para ser atendido por un agente. Inténtelo de nuevo más tarde o envíenos un correo electrónico. ¡Gracias! '
2019-02-25 14:53:02 +00:00
' fi ' :
' <strong>Chat</strong> with us! ' : ' <strong>Keskustele</strong> kanssamme! '
' Scroll down to see new messages ' : ' Rullaa alas nähdäksesi uudet viestit '
' Online ' : ' Paikalla '
' Offline ' : ' Poissa '
' Connecting ' : ' Yhdistetään '
' Connection re-established ' : ' Yhteys muodostettu uudelleen '
' Today ' : ' Tänään '
' Send ' : ' Lähetä '
' Chat closed by %s ' : ' %s sulki keskustelun '
' Compose your message... ' : ' Luo viestisi... '
' All colleagues are busy. ' : ' Kaikki kollegat ovat varattuja. '
' You are on waiting list position <strong>%s</strong>. ' : ' Olet odotuslistalla sijalla <strong>%s</strong>. '
' Start new conversation ' : ' Aloita uusi keskustelu '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Koska et vastannut viimeiseen %s minuuttiin, keskustelusi <strong>%s</strong> kanssa suljettiin. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Koska et vastannut viimeiseen %s minuuttiin, keskustelusi suljettiin. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Olemme pahoillamme, tyhjän paikan vapautumisessa kestää odotettua pidempään. Ole hyvä ja yritä myöhemmin uudestaan tai lähetä meille sähköpostia. Kiitos! '
2017-01-09 13:02:10 +00:00
' fr ' :
2017-01-05 09:20:29 +00:00
' <strong>Chat</strong> with us! ' : ' <strong>Chattez</strong> avec nous! '
' Scroll down to see new messages ' : ' Faites défiler pour lire les nouveaux messages '
' Online ' : ' En-ligne '
' Offline ' : ' Hors-ligne '
' Connecting ' : ' Connexion en cours '
' Connection re-established ' : ' Connexion rétablie '
' Today ' : ' Aujourdhui '
' Send ' : ' Envoyer '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Chat fermé par %s '
2017-01-05 09:20:29 +00:00
' Compose your message... ' : ' Composez votre message... '
' All colleagues are busy. ' : ' Tous les collègues sont actuellement occupés. '
' You are on waiting list position <strong>%s</strong>. ' : ' Vous êtes actuellement en <strong>%s</strong> position dans la file d \' attente. '
' Start new conversation ' : ' Démarrer une nouvelle conversation '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Si vous ne répondez pas dans les <strong>%s</strong> minutes, votre conversation avec %s va être fermée. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Si vous ne répondez pas dans les %s minutes, votre conversation va être fermée. '
2017-01-05 15:42:11 +00:00
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Nous sommes désolés, il faut plus de temps que prévu pour obtenir un emplacement vide. Veuillez réessayer ultérieurement ou nous envoyer un courriel. Je vous remercie! '
2017-11-07 13:42:51 +00:00
' nl ' :
' <strong>Chat</strong> with us! ' : ' <strong>Chat</strong> met ons! '
' Scroll down to see new messages ' : ' Scrol naar beneden om nieuwe berichten te zien '
' Online ' : ' Online '
' Offline ' : ' Offline '
' Connecting ' : ' Verbinden '
' Connection re-established ' : ' Verbinding herstelt '
' Today ' : ' Vandaag '
' Send ' : ' Verzenden '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Chat gesloten door %s '
2017-11-07 13:42:51 +00:00
' Compose your message... ' : ' Typ uw bericht... '
' All colleagues are busy. ' : ' Alle medewerkers zijn bezet. '
' You are on waiting list position <strong>%s</strong>. ' : ' U bent <strong>%s</strong> in de wachtrij. '
' Start new conversation ' : ' Nieuwe conversatie starten '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Omdat u in de laatste %s minuten niets geschreven heeft wordt de conversatie met <strong>%s</strong> gesloten. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Omdat u in de laatste %s minuten niets geschreven heeft is de conversatie gesloten. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Het spijt ons, het duurt langer dan verwacht om te antwoorden. Alstublieft probeer het later nogmaals of stuur ons een email. Hartelijk dank! '
2017-11-23 21:13:02 +00:00
' it ' :
' <strong>Chat</strong> with us! ' : ' <strong>Chatta</strong> con noi! '
2018-11-16 20:08:56 +00:00
' Scroll down to see new messages ' : ' Scorri verso il basso per vedere i nuovi messaggi '
2017-11-23 21:13:02 +00:00
' Online ' : ' Online '
' Offline ' : ' Offline '
2018-11-16 20:08:56 +00:00
' Connecting ' : ' Collegamento in corso '
2017-11-23 21:13:02 +00:00
' Connection re-established ' : ' Collegamento ristabilito '
' Today ' : ' Oggi '
' Send ' : ' Invio '
2018-11-16 20:08:56 +00:00
' Chat closed by %s ' : ' Chat chiusa da %s '
' Compose your message... ' : ' Componi il tuo messaggio... '
' All colleagues are busy. ' : ' Tutti gli operatori sono occupati. '
' You are on waiting list position <strong>%s</strong>. ' : ' Sei in posizione <strong>%s</strong> nella lista d \' attesa. '
' Start new conversation ' : ' Avvia una nuova chat '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Dal momento che non hai risposto negli ultimi %s minuti la tua chat con <strong>%s</strong> è stata chiusa. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Dal momento che non hai risposto negli ultimi %s minuti la tua chat è stata chiusa. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Ci dispiace, ci vuole più tempo del previsto per arrivare al tuo turno. Per favore riprova più tardi o inviaci un \' email. Grazie! '
2017-11-23 21:13:02 +00:00
' pl ' :
' <strong>Chat</strong> with us! ' : ' <strong>Czatuj</strong> z nami! '
' Scroll down to see new messages ' : ' Przewiń w dół, aby wyświetlić nowe wiadomości '
' Online ' : ' Online '
' Offline ' : ' Offline '
' Connecting ' : ' Łączenie '
' Connection re-established ' : ' Ponowne nawiązanie połączenia '
' Today ' : ' dzisiejszy '
' Send ' : ' Wyślij '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Czat zamknięty przez %s '
2017-11-23 21:13:02 +00:00
' Compose your message... ' : ' Utwórz swoją wiadomość... '
' All colleagues are busy. ' : ' Wszyscy koledzy są zajęci. '
' You are on waiting list position <strong>%s</strong>. ' : ' Na liście oczekujących znajduje się pozycja <strong>%s</strong>. '
' Start new conversation ' : ' Rozpoczęcie nowej konwersacji '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Ponieważ w ciągu ostatnich %s minut nie odpowiedziałeś, Twoja rozmowa z <strong>%s</strong> została zamknięta. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Ponieważ nie odpowiedziałeś w ciągu ostatnich %s minut, Twoja rozmowa została zamknięta. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Przykro nam, ale to trwa dłużej niż się spodziewamy. Spróbuj ponownie później lub wyślij nam wiadomość e-mail. Dziękuję! '
2017-01-09 13:02:10 +00:00
' zh-cn ' :
2017-01-09 12:47:12 +00:00
' <strong>Chat</strong> with us! ' : ' 发起<strong>即时对话</strong>! '
' Scroll down to see new messages ' : ' 向下滚动以查看新消息 '
' Online ' : ' 在线 '
' Offline ' : ' 离线 '
' Connecting ' : ' 连接中 '
' Connection re-established ' : ' 正在重新建立连接 '
' Today ' : ' 今天 '
' Send ' : ' 发送 '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Chat closed by %s '
2017-01-09 12:47:12 +00:00
' Compose your message... ' : ' 正在输入信息... '
' All colleagues are busy. ' : ' 所有工作人员都在忙碌中. '
' You are on waiting list position <strong>%s</strong>. ' : ' 您目前的等候位置是第 <strong>%s</strong> 位. '
' Start new conversation ' : ' 开始新的会话 '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' 由于您超过 %s 分钟没有回复, 您与 <strong>%s</strong> 的会话已被关闭. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' 由于您超过 %s 分钟没有任何回复, 该对话已被关闭. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' 非常抱歉, 目前需要等候更长的时间才能接入对话, 请稍后重试或向我们发送电子邮件. 谢谢! '
2017-01-09 13:02:10 +00:00
' zh-tw ' :
2017-01-09 12:47:12 +00:00
' <strong>Chat</strong> with us! ' : ' 開始<strong>即時對话</strong>! '
' Scroll down to see new messages ' : ' 向下滑動以查看新訊息 '
' Online ' : ' 線上 '
' Offline ' : ' 离线 '
' Connecting ' : ' 連線中 '
' Connection re-established ' : ' 正在重新建立連線中 '
' Today ' : ' 今天 '
' Send ' : ' 發送 '
2018-08-07 10:38:20 +00:00
' Chat closed by %s ' : ' Chat closed by %s '
2017-01-09 12:47:12 +00:00
' Compose your message... ' : ' 正在輸入訊息... '
' All colleagues are busy. ' : ' 所有服務人員都在忙碌中. '
' You are on waiting list position <strong>%s</strong>. ' : ' 你目前的等候位置是第 <strong>%s</strong> 順位. '
' Start new conversation ' : ' 開始新的對話 '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' 由於你超過 %s 分鐘沒有回應, 你與 <strong>%s</strong> 的對話已被關閉. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' 由於你超過 %s 分鐘沒有任何回應, 該對話已被關閉. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' 非常抱歉, 當前需要等候更長的時間方可排入對話程序, 請稍後重試或向我們寄送電子郵件. 謝謝! '
2018-08-07 10:38:20 +00:00
' ru ' :
' <strong>Chat</strong> with us! ' : ' Напишите нам! '
' Scroll down to see new messages ' : ' Прокрутите, чтобы увидеть новые сообщения '
' Online ' : ' Онлайн '
' Offline ' : ' Оффлайн '
' Connecting ' : ' Подключение '
' Connection re-established ' : ' Подключение восстановлено '
' Today ' : ' Сегодня '
' Send ' : ' Отправить '
' Chat closed by %s ' : ' %s закрыл чат '
' Compose your message... ' : ' Напишите сообщение... '
' All colleagues are busy. ' : ' В с е сотрудники заняты'
' You are on waiting list position %s. ' : ' Вы в списке ожидания под номером %s '
' Start new conversation ' : ' Начать новую переписку. '
' Since you didn \' t respond in the last %s minutes your conversation with %s got closed. ' : ' Поскольку вы не отвечали в течение последних %s минут, ваш разговор с %s был закрыт. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Поскольку вы не отвечали в течение последних %s минут, ваш разговор был закрыт. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' К сожалению, ожидание свободного места требует больше времени. Повторите попытку позже или отправьте нам электронное письмо. Спасибо!'
2019-12-18 16:12:20 +00:00
' sv ' :
' <strong>Chat</strong> with us! ' : ' <strong>Chatta</strong> med oss! '
' Scroll down to see new messages ' : ' Rulla ner för att se nya meddelanden '
' Online ' : ' Online '
' Offline ' : ' Offline '
' Connecting ' : ' Ansluter '
' Connection re-established ' : ' Anslutningen återupprättas '
' Today ' : ' I dag '
' Send ' : ' Skicka '
' Chat closed by %s ' : ' Chatt stängd av %s '
' Compose your message... ' : ' Skriv ditt meddelande... '
' All colleagues are busy. ' : ' Alla kollegor är upptagna. '
' You are on waiting list position <strong>%s</strong>. ' : ' Du är på väntelistan som position <strong>%s</strong>. '
' Start new conversation ' : ' Starta ny konversation '
' Since you didn \' t respond in the last %s minutes your conversation with <strong>%s</strong> got closed. ' : ' Eftersom du inte svarat inom %s minuterna i din konversation med <strong>%s</strong> så stängdes chatten. '
' Since you didn \' t respond in the last %s minutes your conversation got closed. ' : ' Då du inte svarat inom de senaste %s minuterna så avslutades din chatt. '
' We are sorry, it takes longer as expected to get an empty slot. Please try again later or send us an email. Thank you! ' : ' Vi är ledsna, det tar längre tid som förväntat att få en ledig plats. Försök igen senare eller skicka ett e-postmeddelande till oss. Tack! '
2015-11-12 10:44:37 +00:00
sessionId: undefined
2016-07-05 11:00:49 +00:00
scrolledToBottom: true
scrollSnapTolerance: 10
2017-07-18 13:43:04 +00:00
richTextFormatKey:
66 : true # b
73 : true # i
85 : true # u
83 : true # s
2015-11-11 20:44:54 +00:00
T: (string, items...) =>
2015-11-25 16:36:06 +00:00
if @ options . lang && @ options . lang isnt ' en '
if ! @ translations [ @ options . lang ]
2015-12-05 19:41:14 +00:00
@ log . notice " Translation ' #{ @ options . lang } ' needed! "
2015-11-25 16:36:06 +00:00
else
translations = @ translations [ @ options . lang ]
if ! translations [ string ]
2015-12-05 19:41:14 +00:00
@ log . notice " Translation needed for ' #{ string } ' "
2015-11-25 16:36:06 +00:00
string = translations [ string ] || string
2015-11-11 20:44:54 +00:00
if items
for item in items
2015-11-25 16:36:06 +00:00
string = string . replace ( /%s/ , item )
string
2015-11-11 20:44:54 +00:00
2015-11-02 15:48:16 +00:00
view: (name) =>
return (options) =>
if ! options
options = { }
options.T = @ T
2015-11-13 14:58:44 +00:00
options.background = @ options . background
options.flat = @ options . flat
2015-11-16 10:46:42 +00:00
options.fontSize = @ options . fontSize
2015-11-02 15:48:16 +00:00
return window . zammadChatTemplates [ name ] ( options )
2015-10-15 09:14:19 +00:00
2015-11-13 12:59:15 +00:00
constructor: (options) ->
2015-11-25 11:55:02 +00:00
@options = $ . extend { } , @ defaults , options
2015-12-05 19:41:14 +00:00
super ( @ options )
2015-11-13 14:15:44 +00:00
2016-02-08 15:06:56 +00:00
# fullscreen
@isFullscreen = ( window . matchMedia and window . matchMedia ( ' (max-width: 768px) ' ) . matches )
@scrollRoot = $ ( @ getScrollRoot ( ) )
2015-11-13 14:15:44 +00:00
# check prerequisites
2015-11-25 23:40:52 +00:00
if ! $
@state = ' unsupported '
2015-12-05 19:41:14 +00:00
@ log . notice ' Chat: no jquery found! '
2015-11-25 23:40:52 +00:00
return
2015-11-13 14:15:44 +00:00
if ! window . WebSocket or ! sessionStorage
2015-11-16 12:08:32 +00:00
@state = ' unsupported '
2015-12-05 19:41:14 +00:00
@ log . notice ' Chat: Browser not supported! '
2015-11-13 14:15:44 +00:00
return
2015-11-25 11:55:02 +00:00
if ! @ options . chatId
2015-11-25 09:35:57 +00:00
@state = ' unsupported '
2015-12-05 19:41:14 +00:00
@ log . error ' Chat: need chatId as option! '
2015-11-25 09:35:57 +00:00
return
2015-11-25 16:36:06 +00:00
# detect language
if ! @ options . lang
@options.lang = $ ( ' html ' ) . attr ( ' lang ' )
if @ options . lang
2017-01-09 13:02:10 +00:00
if ! @ translations [ @ options . lang ]
@ log . debug " lang: No #{ @ options . lang } found, try first two letters "
@options.lang = @ options . lang . replace ( /-.+?$/ , ' ' ) # replace "-xx" of xx-xx
2015-12-05 19:41:14 +00:00
@ log . debug " lang: #{ @ options . lang } "
2015-11-25 16:36:06 +00:00
2015-12-07 10:41:12 +00:00
# detect host
@ detectHost ( ) if ! @ options . host
2015-12-05 19:41:14 +00:00
@ loadCss ( )
@io = new Io ( @ options )
@ io . set (
onOpen: @ render
2015-12-08 11:32:19 +00:00
onClose: @ onWebSocketClose
2015-12-05 19:41:14 +00:00
onMessage: @ onWebSocketMessage
2015-12-08 11:32:19 +00:00
onError: @ onError
2015-12-05 19:41:14 +00:00
)
2016-03-03 14:12:53 +00:00
2015-12-05 19:41:14 +00:00
@ io . connect ( )
2016-02-08 15:06:56 +00:00
getScrollRoot: ->
return document . scrollingElement if ' scrollingElement ' of document
html = document . documentElement
start = html . scrollTop
html.scrollTop = start + 1
end = html . scrollTop
html.scrollTop = start
return if end > start then html else document . body
2015-12-05 19:41:14 +00:00
render: =>
2015-12-08 11:32:19 +00:00
if ! @ el || ! $ ( ' .zammad-chat ' ) . get ( 0 )
2016-02-08 15:06:56 +00:00
@ renderBase ( )
2015-11-12 13:56:47 +00:00
2015-11-20 14:43:23 +00:00
# disable open button
$ ( " . #{ @ options . buttonClass } " ) . addClass @ inactiveClass
2015-12-05 19:41:14 +00:00
@ setAgentOnlineState ' online '
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
@ log . debug ' widget rendered '
2015-10-15 15:07:18 +00:00
2015-12-05 19:41:14 +00:00
@ startTimeoutObservers ( )
@ idleTimeout . start ( )
# get current chat status
@sessionId = sessionStorage . getItem ( ' sessionId ' )
@ send ' chat_status_customer ' ,
session_id: @ sessionId
2016-03-25 07:45:21 +00:00
url: window . location . href
2015-11-25 16:36:06 +00:00
2016-02-08 15:06:56 +00:00
renderBase: ->
@el = $ ( @ view ( ' chat ' ) (
2016-07-05 11:00:49 +00:00
title: @ options . title ,
scrollHint: @ options . scrollHint
2016-02-08 15:06:56 +00:00
) )
@ options . target . append @ el
@input = @ el . find ( ' .zammad-chat-input ' )
# start bindings
@ el . find ( ' .js-chat-open ' ) . click @ open
@ el . find ( ' .js-chat-toggle ' ) . click @ toggle
2018-04-13 11:22:15 +00:00
@ el . find ( ' .js-chat-status ' ) . click @ stopPropagation
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-controls ' ) . on ' submit ' , @ onSubmit
2016-07-05 11:00:49 +00:00
@ el . find ( ' .zammad-chat-body ' ) . on ' scroll ' , @ detectScrolledtoBottom
@ el . find ( ' .zammad-scroll-hint ' ) . click @ onScrollHintClick
2017-07-18 13:43:04 +00:00
@ input . on (
2016-02-08 15:06:56 +00:00
keydown: @ checkForEnter
input: @ onInput
2017-07-18 13:43:04 +00:00
)
@ input . on ( ' keydown ' , (e) =>
richtTextControl = false
if ! e . altKey && ! e . ctrlKey && e . metaKey
richtTextControl = true
else if ! e . altKey && e . ctrlKey && ! e . metaKey
richtTextControl = true
if richtTextControl && @ richTextFormatKey [ e . keyCode ]
e . preventDefault ( )
if e . keyCode is 66
document . execCommand ( ' bold ' )
return true
if e . keyCode is 73
document . execCommand ( ' italic ' )
return true
if e . keyCode is 85
document . execCommand ( ' underline ' )
return true
if e . keyCode is 83
document . execCommand ( ' strikeThrough ' )
return true
)
@ input . on ( ' paste ' , (e) =>
e . stopPropagation ( )
e . preventDefault ( )
clipboardData
if e . clipboardData
clipboardData = e . clipboardData
else if window . clipboardData
clipboardData = window . clipboardData
else if e . originalEvent . clipboardData
clipboardData = e . originalEvent . clipboardData
else
throw ' No clipboardData support '
imageInserted = false
if clipboardData && clipboardData . items && clipboardData . items [ 0 ]
item = clipboardData . items [ 0 ]
if item . kind == ' file ' && ( item . type == ' image/png ' || item . type == ' image/jpeg ' )
imageFile = item . getAsFile ( )
reader = new FileReader ( )
reader.onload = (e) =>
result = e . target . result
img = document . createElement ( ' img ' )
img.src = result
insert = (dataUrl, width, height, isRetina) =>
# adapt image if we are on retina devices
if @ isRetina ( )
width = width / 2
height = height / 2
result = dataUrl
img = " <img style= \" width: 100%; max-width: #{ width } px; \" src= \" #{ result } \" > "
document . execCommand ( ' insertHTML ' , false , img )
# resize if to big
@ resizeImage ( img . src , 460 , ' auto ' , 2 , ' image/jpeg ' , ' auto ' , insert )
reader . readAsDataURL ( imageFile )
imageInserted = true
return if imageInserted
# check existing + paste text for limit
text = undefined
docType = undefined
try
text = clipboardData . getData ( ' text/html ' )
docType = ' html '
if ! text || text . length is 0
docType = ' text '
text = clipboardData . getData ( ' text/plain ' )
if ! text || text . length is 0
docType = ' text2 '
text = clipboardData . getData ( ' text ' )
catch e
console . log ( ' Sorry, can \' t insert markup because browser is not supporting it. ' )
docType = ' text3 '
text = clipboardData . getData ( ' text ' )
if docType is ' text ' || docType is ' text2 ' || docType is ' text3 '
text = ' <div> ' + text . replace ( /\n/g , ' </div><div> ' ) + ' </div> '
text = text . replace ( /<div><\/div>/g , ' <div><br></div> ' )
console . log ( ' p ' , docType , text )
if docType is ' html '
html = $ ( " <div> #{ text } </div> " )
match = false
htmlTmp = text
regex = new RegExp ( ' <(/w|w) \: [A-Za-z] ' )
if htmlTmp . match ( regex )
match = true
htmlTmp = htmlTmp . replace ( regex , ' ' )
regex = new RegExp ( ' <(/o|o) \: [A-Za-z] ' )
if htmlTmp . match ( regex )
match = true
htmlTmp = htmlTmp . replace ( regex , ' ' )
if match
html = @ wordFilter ( html )
#html
html = $ ( html )
html . contents ( ) . each ( ->
if @ nodeType == 8
$ ( @ ) . remove ( )
)
# remove tags, keep content
html . find ( ' a, font, small, time, form, label ' ) . replaceWith ( ->
$ ( @ ) . contents ( )
)
# replace tags with generic div
# New type of the tag
replacementTag = ' div ' ;
# Replace all x tags with the type of replacementTag
html . find ( ' textarea ' ) . each ( ->
outer = @ outerHTML
# Replace opening tag
regex = new RegExp ( ' < ' + @ tagName , ' i ' )
newTag = outer . replace ( regex , ' < ' + replacementTag )
# Replace closing tag
regex = new RegExp ( ' </ ' + @ tagName , ' i ' )
newTag = newTag . replace ( regex , ' </ ' + replacementTag )
$ ( @ ) . replaceWith ( newTag )
)
# remove tags & content
html . find ( ' font, img, svg, input, select, button, style, applet, embed, noframes, canvas, script, frame, iframe, meta, link, title, head, fieldset ' ) . remove ( )
@ removeAttributes ( html )
text = html . html ( )
# as fallback, insert html via pasteHtmlAtCaret (for IE 11 and lower)
if docType is ' text3 '
@ pasteHtmlAtCaret ( text )
else
document . execCommand ( ' insertHTML ' , false , text )
true
)
@ input . on ( ' drop ' , (e) =>
e . stopPropagation ( )
e . preventDefault ( )
dataTransfer
if window . dataTransfer # ie
dataTransfer = window . dataTransfer
else if e . originalEvent . dataTransfer # other browsers
dataTransfer = e . originalEvent . dataTransfer
else
throw ' No clipboardData support '
x = e . clientX
y = e . clientY
file = dataTransfer . files [ 0 ]
# look for images
if file . type . match ( ' image.* ' )
reader = new FileReader ( )
reader.onload = (e) =>
result = e . target . result
img = document . createElement ( ' img ' )
img.src = result
# Insert the image at the carat
insert = (dataUrl, width, height, isRetina) =>
# adapt image if we are on retina devices
if @ isRetina ( )
width = width / 2
height = height / 2
result = dataUrl
img = $ ( " <img style= \" width: 100%; max-width: #{ width } px; \" src= \" #{ result } \" > " )
img = img . get ( 0 )
if document . caretPositionFromPoint
pos = document . caretPositionFromPoint ( x , y )
range = document . createRange ( )
range . setStart ( pos . offsetNode , pos . offset )
range . collapse ( )
range . insertNode ( img )
else if document . caretRangeFromPoint
range = document . caretRangeFromPoint ( x , y )
range . insertNode ( img )
else
console . log ( ' could not find carat ' )
# resize if to big
@ resizeImage ( img . src , 460 , ' auto ' , 2 , ' image/jpeg ' , ' auto ' , insert )
reader . readAsDataURL ( file )
)
2016-02-08 15:06:56 +00:00
$ ( window ) . on ( ' beforeunload ' , =>
@ onLeaveTemporary ( )
)
$ ( window ) . bind ( ' hashchange ' , =>
2016-03-25 07:45:21 +00:00
if @ isOpen
if @ sessionId
@ send ' chat_session_notice ' ,
session_id: @ sessionId
message: window . location . href
return
2016-02-08 15:06:56 +00:00
@ idleTimeout . start ( )
)
if @ isFullscreen
@ input . on
focus: @ onFocus
focusout: @ onFocusOut
2018-04-13 11:22:15 +00:00
stopPropagation: (event) ->
event . stopPropagation ( )
2015-10-15 09:14:19 +00:00
checkForEnter: (event) =>
if not event . shiftKey and event . keyCode is 13
event . preventDefault ( )
@ sendMessage ( )
2015-11-25 09:35:57 +00:00
send: (event, data = {}) =>
2015-11-25 11:44:26 +00:00
data.chat_id = @ options . chatId
2015-12-05 19:41:14 +00:00
@ io . send ( event , data )
2015-11-10 14:01:04 +00:00
2015-12-05 19:41:14 +00:00
onWebSocketMessage: (pipes) =>
2015-11-10 14:01:04 +00:00
for pipe in pipes
2015-12-05 19:41:14 +00:00
@ log . debug ' ws:onmessage ' , pipe
2015-11-10 14:01:04 +00:00
switch pipe . event
2015-11-25 09:35:57 +00:00
when ' chat_error '
2015-12-05 19:41:14 +00:00
@ log . notice pipe . data
2015-11-25 23:40:52 +00:00
if pipe . data && pipe . data . state is ' chat_disabled '
2015-12-08 11:32:19 +00:00
@ destroy ( remove: true )
2015-11-10 14:01:04 +00:00
when ' chat_session_message '
return if pipe . data . self_written
@ receiveMessage pipe . data
when ' chat_session_typing '
return if pipe . data . self_written
@ onAgentTypingStart ( )
when ' chat_session_start '
2015-11-12 13:56:47 +00:00
@ onConnectionEstablished pipe . data
2015-11-12 10:44:37 +00:00
when ' chat_session_queue '
2015-11-12 15:16:01 +00:00
@ onQueueScreen pipe . data
2015-11-11 20:44:54 +00:00
when ' chat_session_closed '
@ onSessionClosed pipe . data
when ' chat_session_left '
@ onSessionClosed pipe . data
2019-12-03 06:29:02 +00:00
when ' chat_session_notice '
@ addStatus @ T ( pipe . data . message )
2015-11-11 10:24:19 +00:00
when ' chat_status_customer '
2015-11-10 14:01:04 +00:00
switch pipe . data . state
when ' online '
2015-11-12 23:00:13 +00:00
@sessionId = undefined
2016-03-03 14:12:53 +00:00
if ! @ options . cssAutoload || @ cssLoaded
@ onReady ( )
else
@socketReady = true
2015-11-10 14:01:04 +00:00
when ' offline '
2015-11-13 14:58:44 +00:00
@ onError ' Zammad Chat: No agent online '
2015-11-10 14:01:04 +00:00
when ' chat_disabled '
2015-11-13 14:58:44 +00:00
@ onError ' Zammad Chat: Chat is disabled '
2015-11-10 14:01:04 +00:00
when ' no_seats_available '
2015-11-25 09:35:57 +00:00
@ onError " Zammad Chat: Too many clients in queue. Clients in queue: #{ pipe . data . queue } "
2015-11-12 09:39:14 +00:00
when ' reconnect '
2015-12-08 11:32:19 +00:00
@ onReopenSession pipe . data
2015-11-02 15:48:16 +00:00
2015-12-08 11:32:19 +00:00
onReady: ->
2015-12-05 19:41:14 +00:00
@ log . debug ' widget ready for use '
2015-11-20 14:43:23 +00:00
$ ( " . #{ @ options . buttonClass } " ) . click ( @ open ) . removeClass ( @ inactiveClass )
2015-11-16 12:08:32 +00:00
2020-01-18 16:51:20 +00:00
@ options . onReady ? ( )
2015-11-11 09:48:54 +00:00
if @ options . show
@ show ( )
2015-11-12 13:56:47 +00:00
2015-11-13 14:58:44 +00:00
onError: (message) =>
2015-12-05 19:41:14 +00:00
@ log . debug message
2015-12-08 11:32:19 +00:00
@ addStatus ( message )
2015-11-20 14:43:23 +00:00
$ ( " . #{ @ options . buttonClass } " ) . hide ( )
2015-12-08 11:32:19 +00:00
if @ isOpen
@ disableInput ( )
@ destroy ( remove: false )
else
@ destroy ( remove: true )
2015-11-13 14:58:44 +00:00
2020-01-18 16:51:20 +00:00
@ options . onError ? ( message )
2015-12-08 11:32:19 +00:00
onReopenSession: (data) =>
@ log . debug ' old messages ' , data . session
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-11-25 23:40:52 +00:00
2015-11-12 14:05:43 +00:00
unfinishedMessage = sessionStorage . getItem ' unfinished_message '
2015-11-12 15:16:01 +00:00
2015-11-13 14:15:44 +00:00
# rerender chat history
if data . agent
@ onConnectionEstablished ( data )
2015-11-12 15:16:01 +00:00
2015-11-13 14:15:44 +00:00
for message in data . session
@ renderMessage
message: message . content
id: message . id
from: if message . created_by_id then ' agent ' else ' customer '
2015-11-12 13:56:47 +00:00
2015-11-13 14:15:44 +00:00
if unfinishedMessage
2017-07-14 13:49:16 +00:00
@ input . html ( unfinishedMessage )
2015-11-13 14:15:44 +00:00
# show wait list
if data . position
@ onQueue data
2015-11-12 13:56:47 +00:00
@ show ( )
2015-11-12 14:30:44 +00:00
@ open ( )
2015-11-12 16:15:58 +00:00
@ scrollToBottom ( )
2015-11-02 15:48:16 +00:00
2015-11-12 14:05:43 +00:00
if unfinishedMessage
@ input . focus ( )
2015-10-15 09:14:19 +00:00
onInput: =>
# remove unread-state from messages
@ el . find ( ' .zammad-chat-message--unread ' )
. removeClass ' zammad-chat-message--unread '
2017-07-14 13:49:16 +00:00
sessionStorage . setItem ' unfinished_message ' , @ input . html ( )
2015-11-12 13:56:47 +00:00
2015-11-25 14:08:55 +00:00
@ onTyping ( )
2015-10-15 09:14:19 +00:00
2016-02-08 15:06:56 +00:00
onFocus: =>
$ ( window ) . scrollTop ( 10 )
keyboardShown = $ ( window ) . scrollTop ( ) > 0
$ ( window ) . scrollTop ( 0 )
if keyboardShown
@ log . notice ' virtual keyboard shown '
# on keyboard shown
# can't measure visible area height :(
onFocusOut: ->
# on keyboard hidden
2015-11-25 14:08:55 +00:00
onTyping: ->
2015-10-15 09:14:19 +00:00
2015-11-25 14:08:55 +00:00
# send typing start event only every 1.5 seconds
return if @ isTyping && @ isTyping > new Date ( new Date ( ) . getTime ( ) - 1500 )
@isTyping = new Date ( )
@ send ' chat_session_typing ' ,
session_id: @ sessionId
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-10-15 09:14:19 +00:00
onSubmit: (event) =>
event . preventDefault ( )
@ sendMessage ( )
sendMessage: ->
2017-07-14 13:49:16 +00:00
message = @ input . html ( )
2015-11-11 20:44:54 +00:00
return if ! message
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-11-25 23:40:52 +00:00
2015-11-12 13:56:47 +00:00
sessionStorage . removeItem ' unfinished_message '
2015-11-02 15:48:16 +00:00
messageElement = @ view ( ' message ' )
message: message
from: ' customer '
id: @ _messageCount ++
2015-11-26 09:52:10 +00:00
unreadClass: ' '
2015-10-15 09:14:19 +00:00
2015-11-02 15:48:16 +00:00
@ maybeAddTimestamp ( )
2015-10-15 09:14:19 +00:00
2015-11-02 15:48:16 +00:00
# add message before message typing loader
2017-07-03 12:12:42 +00:00
if @ el . find ( ' .zammad-chat-message--typing ' ) . get ( 0 )
2015-11-02 15:48:16 +00:00
@lastAddedType = ' typing-placeholder '
2015-11-11 10:24:19 +00:00
@ el . find ( ' .zammad-chat-message--typing ' ) . before messageElement
2015-11-02 15:48:16 +00:00
else
@lastAddedType = ' message--customer '
@ el . find ( ' .zammad-chat-body ' ) . append messageElement
2015-10-15 09:14:19 +00:00
2017-07-14 13:49:16 +00:00
@ input . html ( ' ' )
2015-11-02 15:48:16 +00:00
@ scrollToBottom ( )
2015-10-15 09:14:19 +00:00
2015-11-02 15:48:16 +00:00
# send message event
2015-11-10 14:01:04 +00:00
@ send ' chat_session_message ' ,
content: message
2015-11-02 15:48:16 +00:00
id: @ _messageCount
2015-11-12 10:44:37 +00:00
session_id: @ sessionId
2015-11-02 15:48:16 +00:00
receiveMessage: (data) =>
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . start ( )
2015-11-25 23:40:52 +00:00
2015-10-15 09:14:19 +00:00
# hide writing indicator
@ onAgentTypingEnd ( )
@ maybeAddTimestamp ( )
2015-11-12 13:56:47 +00:00
@ renderMessage
2015-11-10 14:01:04 +00:00
message: data . message . content
2015-11-02 15:48:16 +00:00
id: data . id
2015-10-15 09:26:56 +00:00
from: ' agent '
2015-11-12 13:56:47 +00:00
2016-07-05 11:00:49 +00:00
@ scrollToBottom showHint: true
2015-11-12 13:56:47 +00:00
renderMessage: (data) =>
@lastAddedType = " message-- #{ data . from } "
2015-11-26 09:52:10 +00:00
data.unreadClass = if document . hidden then ' zammad-chat-message--unread ' else ' '
2015-11-12 13:56:47 +00:00
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' message ' ) ( data )
2015-10-15 09:14:19 +00:00
2015-11-12 14:30:44 +00:00
open: =>
2015-11-12 16:31:20 +00:00
if @ isOpen
2015-12-08 11:32:19 +00:00
@ log . debug ' widget already open, block '
return
@isOpen = true
@ log . debug ' open widget '
2019-01-19 15:20:31 +00:00
@ show ( )
2015-10-15 09:14:19 +00:00
2015-11-12 14:30:44 +00:00
if ! @ sessionId
2015-11-12 13:56:47 +00:00
@ showLoader ( )
2015-11-02 15:48:16 +00:00
2015-11-25 13:47:02 +00:00
@ el . addClass ( ' zammad-chat-is-open ' )
2015-11-12 13:56:47 +00:00
2016-03-24 10:03:20 +00:00
remainerHeight = @ el . height ( ) - @ el . find ( ' .zammad-chat-header ' ) . outerHeight ( )
@ el . css ' bottom ' , - remainerHeight
2015-11-12 14:30:44 +00:00
if ! @ sessionId
2015-11-12 13:56:47 +00:00
@ el . animate { bottom: 0 } , 500 , @ onOpenAnimationEnd
2016-03-25 07:45:21 +00:00
@ send ( ' chat_session_init '
url: window . location . href
)
2015-11-12 13:56:47 +00:00
else
@ el . css ' bottom ' , 0
@ onOpenAnimationEnd ( )
2015-10-15 09:14:19 +00:00
2015-11-25 23:40:52 +00:00
onOpenAnimationEnd: =>
2015-12-05 19:41:14 +00:00
@ idleTimeout . stop ( )
2015-11-12 23:00:13 +00:00
2016-02-08 15:06:56 +00:00
if @ isFullscreen
@ disableScrollOnRoot ( )
2020-01-18 16:51:20 +00:00
@ options . onOpenAnimationEnd ? ( )
2016-02-08 15:06:56 +00:00
2015-12-08 11:32:19 +00:00
sessionClose: =>
2015-11-25 23:40:52 +00:00
# send close
@ send ' chat_session_close ' ,
session_id: @ sessionId
# stop timer
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . stop ( )
@ waitingListTimeout . stop ( )
2015-11-25 23:40:52 +00:00
# delete input store
sessionStorage . removeItem ' unfinished_message '
2015-11-12 23:00:13 +00:00
# stop delay of initial queue position
if @ onInitialQueueDelayId
clearTimeout ( @ onInitialQueueDelayId )
2015-11-12 13:56:47 +00:00
2015-11-25 23:40:52 +00:00
@ setSessionId undefined
2015-11-12 13:56:47 +00:00
2016-02-08 15:06:56 +00:00
toggle: (event) =>
if @ isOpen
@ close ( event )
else
@ open ( event )
2015-12-08 11:32:19 +00:00
close: (event) =>
if ! @ isOpen
@ log . debug ' can \' t close widget, it \' s not open '
return
2016-02-08 20:49:00 +00:00
if @ initDelayId
clearTimeout ( @ initDelayId )
2015-12-08 11:32:19 +00:00
if ! @ sessionId
@ log . debug ' can \' t close widget without sessionId '
return
@ log . debug ' close widget '
event . stopPropagation ( ) if event
@ sessionClose ( )
2016-02-08 15:06:56 +00:00
if @ isFullscreen
@ enableScrollOnRoot ( )
2015-12-08 11:32:19 +00:00
# close window
2015-10-15 09:14:19 +00:00
remainerHeight = @ el . height ( ) - @ el . find ( ' .zammad-chat-header ' ) . outerHeight ( )
2015-10-15 13:28:30 +00:00
@ el . animate { bottom: - remainerHeight } , 500 , @ onCloseAnimationEnd
2015-10-15 09:14:19 +00:00
onCloseAnimationEnd: =>
2016-03-24 10:03:20 +00:00
@ el . css ' bottom ' , ' '
@ el . removeClass ( ' zammad-chat-is-open ' )
2015-12-05 19:41:14 +00:00
@ showLoader ( )
@ el . find ( ' .zammad-chat-welcome ' ) . removeClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent ' ) . addClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent-status ' ) . addClass ( ' zammad-chat-is-hidden ' )
2015-10-15 09:14:19 +00:00
@isOpen = false
2020-01-18 16:51:20 +00:00
@ options . onCloseAnimationEnd ? ( )
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
@ io . reconnect ( )
2015-11-12 16:15:58 +00:00
2015-12-08 11:32:19 +00:00
onWebSocketClose: =>
return if @ isOpen
2015-12-05 19:41:14 +00:00
if @ el
@ el . removeClass ( ' zammad-chat-is-shown ' )
2016-03-03 14:12:53 +00:00
@ el . removeClass ( ' zammad-chat-is-loaded ' )
2015-10-15 09:14:19 +00:00
show: ->
2015-12-08 11:32:19 +00:00
return if @ state is ' offline '
2015-11-16 12:08:32 +00:00
2016-03-03 14:12:53 +00:00
@ el . addClass ( ' zammad-chat-is-loaded ' )
2015-10-15 09:14:19 +00:00
2016-03-03 14:12:53 +00:00
@ el . addClass ( ' zammad-chat-is-shown ' )
2015-11-13 10:06:47 +00:00
2015-11-11 20:44:54 +00:00
disableInput: ->
2015-11-12 14:05:43 +00:00
@ input . prop ( ' disabled ' , true )
2015-11-11 20:44:54 +00:00
@ el . find ( ' .zammad-chat-send ' ) . prop ( ' disabled ' , true )
enableInput: ->
2015-11-12 14:05:43 +00:00
@ input . prop ( ' disabled ' , false )
2015-11-11 20:44:54 +00:00
@ el . find ( ' .zammad-chat-send ' ) . prop ( ' disabled ' , false )
2016-02-08 15:06:56 +00:00
hideModal: ->
@ el . find ( ' .zammad-chat-modal ' ) . html ' '
2015-11-12 13:19:41 +00:00
onQueueScreen: (data) =>
2015-11-12 23:00:13 +00:00
@ setSessionId data . session_id
2015-11-12 13:19:41 +00:00
# delay initial queue position, show connecting first
show = =>
2015-11-12 14:21:04 +00:00
@ onQueue data
2015-12-05 19:41:14 +00:00
@ waitingListTimeout . start ( )
2015-11-12 13:19:41 +00:00
if @ initialQueueDelay && ! @ onInitialQueueDelayId
@onInitialQueueDelayId = setTimeout ( show , @ initialQueueDelay )
return
# stop delay of initial queue position
if @ onInitialQueueDelayId
clearTimeout ( @ onInitialQueueDelayId )
# show queue position
show ( )
2015-11-12 14:20:07 +00:00
onQueue: (data) =>
2015-12-05 19:41:14 +00:00
@ log . notice ' onQueue ' , data . position
2015-11-02 15:48:16 +00:00
@inQueue = true
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' waiting ' )
2015-11-12 13:56:47 +00:00
position: data . position
2015-11-02 15:48:16 +00:00
2015-10-15 09:14:19 +00:00
onAgentTypingStart: =>
2015-11-11 10:24:19 +00:00
if @ stopTypingId
clearTimeout ( @ stopTypingId )
@stopTypingId = setTimeout ( @ onAgentTypingEnd , 3000 )
2015-11-02 15:48:16 +00:00
# never display two typing indicators
2017-07-03 12:12:42 +00:00
return if @ el . find ( ' .zammad-chat-message--typing ' ) . get ( 0 )
2015-10-15 09:14:19 +00:00
@ maybeAddTimestamp ( )
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' typingIndicator ' ) ( )
2016-06-28 09:34:57 +00:00
# only if typing indicator is shown
return if ! @ isVisible ( @ el . find ( ' .zammad-chat-message--typing ' ) , true )
2015-10-15 09:14:19 +00:00
@ scrollToBottom ( )
onAgentTypingEnd: =>
@ el . find ( ' .zammad-chat-message--typing ' ) . remove ( )
2015-12-05 19:41:14 +00:00
onLeaveTemporary: =>
return if ! @ sessionId
@ send ' chat_session_leave_temporary ' ,
session_id: @ sessionId
2015-10-15 09:14:19 +00:00
maybeAddTimestamp: ->
timestamp = Date . now ( )
2015-10-15 09:26:56 +00:00
if ! @ lastTimestamp or ( timestamp - @ lastTimestamp ) > @ showTimeEveryXMinutes * 60000
2015-10-15 09:14:19 +00:00
label = @ T ( ' Today ' )
time = new Date ( ) . toTimeString ( ) . substr 0 , 5
if @ lastAddedType is ' timestamp '
# update last time
@ updateLastTimestamp label , time
@lastTimestamp = timestamp
else
# add new timestamp
2015-11-12 15:19:47 +00:00
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' timestamp ' )
label: label
time: time
2015-10-15 09:14:19 +00:00
@lastTimestamp = timestamp
@lastAddedType = ' timestamp '
2015-11-12 15:19:47 +00:00
@ scrollToBottom ( )
2015-10-15 09:14:19 +00:00
updateLastTimestamp: (label, time) ->
2015-12-08 11:32:19 +00:00
return if ! @ el
2015-10-15 09:14:19 +00:00
@ el . find ( ' .zammad-chat-body ' )
2015-11-12 15:19:47 +00:00
. find ( ' .zammad-chat-timestamp ' )
2015-10-15 09:14:19 +00:00
. last ( )
2015-11-12 15:19:47 +00:00
. replaceWith @ view ( ' timestamp ' )
2015-10-15 09:14:19 +00:00
label: label
time: time
2015-11-12 15:19:47 +00:00
addStatus: (status) ->
2015-12-08 11:32:19 +00:00
return if ! @ el
2015-11-12 16:31:20 +00:00
@ maybeAddTimestamp ( )
2015-10-15 09:26:56 +00:00
@ el . find ( ' .zammad-chat-body ' ) . append @ view ( ' status ' )
2015-11-12 15:19:47 +00:00
status: status
@ scrollToBottom ( )
2015-10-15 09:14:19 +00:00
2016-07-05 11:00:49 +00:00
detectScrolledtoBottom: =>
scrollBottom = @ el . find ( ' .zammad-chat-body ' ) . scrollTop ( ) + @ el . find ( ' .zammad-chat-body ' ) . outerHeight ( )
@scrolledToBottom = Math . abs ( scrollBottom - @ el . find ( ' .zammad-chat-body ' ) . prop ( ' scrollHeight ' ) ) <= @ scrollSnapTolerance
@ el . find ( ' .zammad-scroll-hint ' ) . addClass ( ' is-hidden ' ) if @ scrolledToBottom
showScrollHint: ->
@ el . find ( ' .zammad-scroll-hint ' ) . removeClass ( ' is-hidden ' )
# compensate scroll
@ el . find ( ' .zammad-chat-body ' ) . scrollTop ( @ el . find ( ' .zammad-chat-body ' ) . scrollTop ( ) + @ el . find ( ' .zammad-scroll-hint ' ) . outerHeight ( ) )
onScrollHintClick: =>
# animate scroll
@ el . find ( ' .zammad-chat-body ' ) . animate ( { scrollTop: @ el . find ( ' .zammad-chat-body ' ) . prop ( ' scrollHeight ' ) } , 300 )
scrollToBottom: ({ showHint } = { showHint: false }) ->
if @ scrolledToBottom
@ el . find ( ' .zammad-chat-body ' ) . scrollTop ( $ ( ' .zammad-chat-body ' ) . prop ( ' scrollHeight ' ) )
else if showHint
@ showScrollHint ( )
2015-10-15 09:14:19 +00:00
2015-12-05 19:41:14 +00:00
destroy: (params = {}) =>
2015-12-08 11:32:19 +00:00
@ log . debug ' destroy widget ' , params
@ setAgentOnlineState ' offline '
if params . remove && @ el
@ el . remove ( )
2015-12-07 10:41:12 +00:00
# stop all timer
2015-12-08 11:32:19 +00:00
if @ waitingListTimeout
@ waitingListTimeout . stop ( )
if @ inactiveTimeout
@ inactiveTimeout . stop ( )
if @ idleTimeout
@ idleTimeout . stop ( )
2015-12-07 10:41:12 +00:00
# stop ws connection
2015-12-05 19:41:14 +00:00
@ io . close ( )
2015-10-15 09:14:19 +00:00
reconnect: =>
# set status to connecting
2015-12-05 19:41:14 +00:00
@ log . notice ' reconnecting '
2015-11-11 20:44:54 +00:00
@ disableInput ( )
2015-10-15 09:14:19 +00:00
@lastAddedType = ' status '
2015-11-13 14:58:44 +00:00
@ setAgentOnlineState ' connecting '
2015-10-15 09:14:19 +00:00
@ addStatus @ T ( ' Connection lost ' )
2015-11-11 20:44:54 +00:00
2015-10-15 09:14:19 +00:00
onConnectionReestablished: =>
# set status back to online
@lastAddedType = ' status '
2015-11-13 14:58:44 +00:00
@ setAgentOnlineState ' online '
2015-10-15 09:14:19 +00:00
@ addStatus @ T ( ' Connection re-established ' )
2020-01-18 16:51:20 +00:00
@ options . onConnectionReestablished ? ( )
2015-10-15 09:14:19 +00:00
2015-11-11 20:44:54 +00:00
onSessionClosed: (data) ->
@ addStatus @ T ( ' Chat closed by %s ' , data . realname )
@ disableInput ( )
2015-11-25 09:35:57 +00:00
@ setAgentOnlineState ' offline '
2015-12-05 19:41:14 +00:00
@ inactiveTimeout . stop ( )
2020-01-18 16:51:20 +00:00
@ options . onSessionClosed ? ( data )
2015-11-02 15:48:16 +00:00
2015-11-12 14:05:43 +00:00
setSessionId: (id) =>
@sessionId = id
2015-11-12 16:15:58 +00:00
if id is undefined
sessionStorage . removeItem ' sessionId '
else
sessionStorage . setItem ' sessionId ' , id
2015-11-12 14:05:43 +00:00
onConnectionEstablished: (data) =>
2015-11-12 13:19:41 +00:00
# stop delay of initial queue position
if @ onInitialQueueDelayId
2015-11-12 14:05:43 +00:00
clearTimeout @ onInitialQueueDelayId
2015-11-12 13:56:47 +00:00
2015-11-02 15:48:16 +00:00
@inQueue = false
2015-11-12 15:16:01 +00:00
if data . agent
@agent = data . agent
if data . session_id
@ setSessionId data . session_id
2015-11-02 15:48:16 +00:00
2016-03-25 15:15:37 +00:00
# empty old messages
@ el . find ( ' .zammad-chat-body ' ) . html ( ' ' )
2015-11-02 15:48:16 +00:00
@ el . find ( ' .zammad-chat-agent ' ) . html @ view ( ' agent ' )
2015-11-12 13:56:47 +00:00
agent: @ agent
2015-11-02 15:48:16 +00:00
2015-11-11 20:44:54 +00:00
@ enableInput ( )
2016-02-08 15:06:56 +00:00
@ hideModal ( )
2015-11-02 15:48:16 +00:00
@ el . find ( ' .zammad-chat-welcome ' ) . addClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent ' ) . removeClass ( ' zammad-chat-is-hidden ' )
@ el . find ( ' .zammad-chat-agent-status ' ) . removeClass ( ' zammad-chat-is-hidden ' )
2016-02-08 15:06:56 +00:00
@ input . focus ( ) if not @ isFullscreen
2015-11-02 15:48:16 +00:00
2015-11-25 09:35:57 +00:00
@ setAgentOnlineState ' online '
2015-12-05 19:41:14 +00:00
@ waitingListTimeout . stop ( )
@ idleTimeout . stop ( )
@ inactiveTimeout . start ( )
2020-01-18 16:51:20 +00:00
@ options . onConnectionEstablished ? ( data )
2015-12-05 19:41:14 +00:00
showCustomerTimeout: ->
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' customer_timeout ' )
2015-11-12 16:31:20 +00:00
agent: @ agent . name
2015-11-25 23:40:52 +00:00
delay: @ options . inactiveTimeout
reload = ->
location . reload ( )
@ el . find ( ' .js-restart ' ) . click reload
2015-12-08 11:32:19 +00:00
@ sessionClose ( )
2015-11-12 16:31:20 +00:00
2015-12-05 19:41:14 +00:00
showWaitingListTimeout: ->
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' waiting_list_timeout ' )
2015-12-05 19:41:14 +00:00
delay: @ options . watingListTimeout
reload = ->
location . reload ( )
@ el . find ( ' .js-restart ' ) . click reload
2015-12-08 11:32:19 +00:00
@ sessionClose ( )
2015-12-05 19:41:14 +00:00
2015-11-02 15:48:16 +00:00
showLoader: ->
2016-02-08 15:06:56 +00:00
@ el . find ( ' .zammad-chat-modal ' ) . html @ view ( ' loader ' ) ( )
2015-10-15 09:14:19 +00:00
setAgentOnlineState: (state) =>
2015-11-13 14:58:44 +00:00
@state = state
2015-12-08 11:32:19 +00:00
return if ! @ el
2015-11-13 14:58:44 +00:00
capitalizedState = state . charAt ( 0 ) . toUpperCase ( ) + state . slice ( 1 )
2015-10-15 09:14:19 +00:00
@ el
. find ( ' .zammad-chat-agent-status ' )
2015-11-13 14:58:44 +00:00
. attr ( ' data-status ' , state )
. text @ T ( capitalizedState )
2015-10-15 09:14:19 +00:00
2015-12-07 10:41:12 +00:00
detectHost: ->
protocol = ' ws:// '
2017-02-10 15:29:44 +00:00
if scriptProtocol is ' https '
2015-12-07 10:41:12 +00:00
protocol = ' wss:// '
@options.host = " #{ protocol } #{ scriptHost } /ws "
2015-11-25 16:36:06 +00:00
loadCss: ->
return if ! @ options . cssAutoload
url = @ options . cssUrl
if ! url
url = @ options . host
. replace ( /^wss/i , ' https ' )
. replace ( /^ws/i , ' http ' )
. replace ( /\/ws/i , ' ' )
url += ' /assets/chat/chat.css '
2015-12-05 19:41:14 +00:00
@ log . debug " load css from ' #{ url } ' "
2015-11-25 16:36:06 +00:00
styles = " @import url( ' #{ url } ' ); "
newSS = document . createElement ( ' link ' )
2016-03-03 14:12:53 +00:00
newSS.onload = @ onCssLoaded
2015-11-25 16:36:06 +00:00
newSS.rel = ' stylesheet '
newSS.href = ' data:text/css, ' + escape ( styles )
document . getElementsByTagName ( ' head ' ) [ 0 ] . appendChild ( newSS )
2016-03-03 14:12:53 +00:00
onCssLoaded: =>
2018-08-23 13:54:50 +00:00
@cssLoaded = true
2016-03-03 14:12:53 +00:00
if @ socketReady
@ onReady ( )
2020-01-18 16:51:20 +00:00
@ options . onCssLoaded ? ( )
2016-03-03 14:12:53 +00:00
2015-12-05 19:41:14 +00:00
startTimeoutObservers: =>
@idleTimeout = new Timeout (
logPrefix: ' idleTimeout '
debug: @ options . debug
timeout: @ options . idleTimeout
timeoutIntervallCheck: @ options . idleTimeoutIntervallCheck
callback: =>
@ log . debug ' Idle timeout reached, hide widget ' , new Date
2015-12-08 11:32:19 +00:00
@ destroy ( remove: true )
2015-12-05 19:41:14 +00:00
)
@inactiveTimeout = new Timeout (
logPrefix: ' inactiveTimeout '
debug: @ options . debug
timeout: @ options . inactiveTimeout
timeoutIntervallCheck: @ options . inactiveTimeoutIntervallCheck
callback: =>
@ log . debug ' Inactive timeout reached, show timeout screen. ' , new Date
@ showCustomerTimeout ( )
2015-12-08 11:32:19 +00:00
@ destroy ( remove: false )
2015-12-05 19:41:14 +00:00
)
@waitingListTimeout = new Timeout (
logPrefix: ' waitingListTimeout '
debug: @ options . debug
timeout: @ options . waitingListTimeout
timeoutIntervallCheck: @ options . waitingListTimeoutIntervallCheck
callback: =>
@ log . debug ' Waiting list timeout reached, show timeout screen. ' , new Date
@ showWaitingListTimeout ( )
2015-12-08 11:32:19 +00:00
@ destroy ( remove: false )
2015-12-05 19:41:14 +00:00
)
2015-11-25 23:40:52 +00:00
2016-02-08 15:06:56 +00:00
disableScrollOnRoot: ->
@rootScrollOffset = @ scrollRoot . scrollTop ( )
@ scrollRoot . css
overflow: ' hidden '
position: ' fixed '
enableScrollOnRoot: ->
@ scrollRoot . scrollTop @ rootScrollOffset
@ scrollRoot . css
overflow: ' '
position: ' '
2016-06-28 09:34:57 +00:00
# based on https://github.com/customd/jquery-visible/blob/master/jquery.visible.js
# to have not dependency, port to coffeescript
isVisible: (el, partial, hidden, direction) ->
return if el . length < 1
$w = $ ( window )
$t = if el . length > 1 then el . eq ( 0 ) else el
t = $t . get ( 0 )
vpWidth = $w . width ( )
vpHeight = $w . height ( )
direction = if direction then direction else ' both '
clientSize = if hidden is true then t . offsetWidth * t . offsetHeight else true
if typeof t . getBoundingClientRect is ' function '
# Use this native browser method, if available.
rec = t . getBoundingClientRect ( )
tViz = rec . top >= 0 && rec . top < vpHeight
bViz = rec . bottom > 0 && rec . bottom <= vpHeight
lViz = rec . left >= 0 && rec . left < vpWidth
rViz = rec . right > 0 && rec . right <= vpWidth
vVisible = if partial then tViz || bViz else tViz && bViz
hVisible = if partial then lViz || rViz else lViz && rViz
if direction is ' both '
return clientSize && vVisible && hVisible
else if direction is ' vertical '
return clientSize && vVisible
else if direction is ' horizontal '
return clientSize && hVisible
else
viewTop = $w . scrollTop ( )
viewBottom = viewTop + vpHeight
viewLeft = $w . scrollLeft ( )
viewRight = viewLeft + vpWidth
offset = $t . offset ( )
_top = offset . top
_bottom = _top + $t . height ( )
_left = offset . left
_right = _left + $t . width ( )
compareTop = if partial is true then _bottom else _top
compareBottom = if partial is true then _top else _bottom
compareLeft = if partial is true then _right else _left
compareRight = if partial is true then _left else _right
if direction is ' both '
return ! ! clientSize && ( ( compareBottom <= viewBottom ) && ( compareTop >= viewTop ) ) && ( ( compareRight <= viewRight ) && ( compareLeft >= viewLeft ) )
else if direction is ' vertical '
return ! ! clientSize && ( ( compareBottom <= viewBottom ) && ( compareTop >= viewTop ) )
else if direction is ' horizontal '
return ! ! clientSize && ( ( compareRight <= viewRight ) && ( compareLeft >= viewLeft ) )
2017-07-18 13:43:04 +00:00
isRetina: ->
if window . matchMedia
mq = window . matchMedia ( ' only screen and (min--moz-device-pixel-ratio: 1.3), only screen and (-o-min-device-pixel-ratio: 2.6/2), only screen and (-webkit-min-device-pixel-ratio: 1.3), only screen and (min-device-pixel-ratio: 1.3), only screen and (min-resolution: 1.3dppx) ' )
return ( mq && mq . matches || ( window . devicePixelRatio > 1 ) )
false
resizeImage: (dataURL, x = 'auto', y = 'auto', sizeFactor = 1, type, quallity, callback, force = true) ->
# load image from data url
imageObject = new Image ( )
imageObject.onload = ->
imageWidth = imageObject . width
imageHeight = imageObject . height
console . log ( ' ImageService ' , ' current size ' , imageWidth , imageHeight )
if y is ' auto ' && x is ' auto '
x = imageWidth
y = imageHeight
# get auto dimensions
if y is ' auto '
factor = imageWidth / x
y = imageHeight / factor
if x is ' auto '
factor = imageWidth / y
x = imageHeight / factor
# check if resize is needed
resize = false
if x < imageWidth || y < imageHeight
resize = true
x = x * sizeFactor
y = y * sizeFactor
else
x = imageWidth
y = imageHeight
# create canvas and set dimensions
canvas = document . createElement ( ' canvas ' )
canvas.width = x
canvas.height = y
# draw image on canvas and set image dimensions
context = canvas . getContext ( ' 2d ' )
context . drawImage ( imageObject , 0 , 0 , x , y )
# set quallity based on image size
if quallity == ' auto '
if x < 200 && y < 200
quallity = 1
else if x < 400 && y < 400
quallity = 0.9
else if x < 600 && y < 600
quallity = 0.8
else if x < 900 && y < 900
quallity = 0.7
else
quallity = 0.6
# execute callback with resized image
newDataUrl = canvas . toDataURL ( type , quallity )
if resize
console . log ( ' ImageService ' , ' resize ' , x / sizeFactor , y / sizeFactor , quallity , ( newDataUrl . length * 0.75 ) / 1024 / 1024 , ' in mb ' )
callback ( newDataUrl , x / sizeFactor , y / sizeFactor , true )
return
console . log ( ' ImageService ' , ' no resize ' , x , y , quallity , ( newDataUrl . length * 0.75 ) / 1024 / 1024 , ' in mb ' )
callback ( newDataUrl , x , y , false )
# load image from data url
imageObject.src = dataURL
# taken from https://stackoverflow.com/questions/6690752/insert-html-at-caret-in-a-contenteditable-div/6691294#6691294
pasteHtmlAtCaret: (html) ->
sel = undefined
range = undefined
if window . getSelection
sel = window . getSelection ( )
if sel . getRangeAt && sel . rangeCount
range = sel . getRangeAt ( 0 )
range . deleteContents ( )
el = document . createElement ( ' div ' )
el.innerHTML = html
frag = document . createDocumentFragment ( node , lastNode )
while node = el . firstChild
lastNode = frag . appendChild ( node )
range . insertNode ( frag )
if lastNode
range = range . cloneRange ( )
range . setStartAfter ( lastNode )
range . collapse ( true )
sel . removeAllRanges ( )
sel . addRange ( range )
else if document . selection && document . selection . type != ' Control '
document . selection . createRange ( ) . pasteHTML ( html )
# (C) sbrin - https://github.com/sbrin
# https://gist.github.com/sbrin/6801034
wordFilter: (editor) ->
content = editor . html ( )
# Word comments like conditional comments etc
content = content . replace ( /<!--[\s\S]+?-->/gi , ' ' )
# Remove comments, scripts (e.g., msoShowComment), XML tag, VML content,
# MS Office namespaced tags, and a few other tags
content = content . replace ( /<(!|script[^>]*>.*?<\/script(?=[>\s])|\/?(\?xml(:\w+)?|img|meta|link|style|\w:\w+)(?=[\s\/>]))[^>]*>/gi , ' ' )
# Convert <s> into <strike> for line-though
content = content . replace ( /<(\/?)s>/gi , ' <$1strike> ' )
# Replace nbsp entites to char since it's easier to handle
# content = content.replace(/ /gi, "\u00a0")
content = content . replace ( / /gi , ' ' )
# Convert <span style="mso-spacerun:yes">___</span> to string of alternating
# breaking/non-breaking spaces of same length
#content = content.replace(/<span\s+style\s*=\s*"\s*mso-spacerun\s*:\s*yes\s*;?\s*"\s*>([\s\u00a0]*)<\/span>/gi, (str, spaces) ->
# return (spaces.length > 0) ? spaces.replace(/./, " ").slice(Math.floor(spaces.length/2)).split("").join("\u00a0") : ''
#)
editor . html ( content )
# Parse out list indent level for lists
$ ( ' p ' , editor ) . each ( ->
str = $ ( @ ) . attr ( ' style ' )
matches = /mso-list:\w+ \w+([0-9]+)/ . exec ( str )
if matches
$ ( @ ) . data ( ' _listLevel ' , parseInt ( matches [ 1 ] , 10 ) )
)
# Parse Lists
last_level = 0
pnt = null
$ ( ' p ' , editor ) . each ( ->
cur_level = $ ( @ ) . data ( ' _listLevel ' )
if cur_level != undefined
txt = $ ( @ ) . text ( )
list_tag = ' <ul></ul> '
if ( /^\s*\w+\./ . test ( txt ) )
matches = /([0-9])\./ . exec ( txt )
if matches
start = parseInt ( matches [ 1 ] , 10 )
list_tag = start > 1 ? ' <ol start= " ' + start + ' " ></ol> ' : ' <ol></ol> '
else
list_tag = ' <ol></ol> '
if cur_level > last_level
if last_level == 0
$ ( @ ) . before ( list_tag )
pnt = $ ( @ ) . prev ( )
else
pnt = $ ( list_tag ) . appendTo ( pnt )
if cur_level < last_level
for i in [ i . . last_level - cur_level ]
pnt = pnt . parent ( )
$ ( ' span:first ' , @ ) . remove ( )
pnt . append ( ' <li> ' + $ ( @ ) . html ( ) + ' </li> ' )
$ ( @ ) . remove ( )
last_level = cur_level
else
last_level = 0
)
$ ( ' [style] ' , editor ) . removeAttr ( ' style ' )
$ ( ' [align] ' , editor ) . removeAttr ( ' align ' )
$ ( ' span ' , editor ) . replaceWith ( ->
$ ( @ ) . contents ( )
)
$ ( ' span:empty ' , editor ) . remove ( )
$ ( " [class^= ' Mso ' ] " , editor ) . removeAttr ( ' class ' )
$ ( ' p:empty ' , editor ) . remove ( )
editor
removeAttribute: (element) ->
return if ! element
$element = $ ( element )
for att in element . attributes
if att && att . name
element . removeAttribute ( att . name )
#$element.removeAttr(att.name)
$element . removeAttr ( ' style ' )
. removeAttr ( ' class ' )
. removeAttr ( ' lang ' )
. removeAttr ( ' type ' )
. removeAttr ( ' align ' )
. removeAttr ( ' id ' )
. removeAttr ( ' wrap ' )
. removeAttr ( ' title ' )
removeAttributes: (html, parent = true) =>
if parent
html . each ( (index, element) => @ removeAttribute ( element ) )
html . find ( ' * ' ) . each ( (index, element) => @ removeAttribute ( element ) )
html
2015-11-13 12:59:15 +00:00
window . ZammadChat = ZammadChat