app/assets/javascripts/app/controllers/_channel/chat.coffee
class ChannelChat extends App.ControllerSubContent
@requiredPermission: 'admin.channel_chat'
header: __('Chat')
events:
'change .js-params': 'updateParams'
'input .js-params': 'updateParams'
'submit .js-demo-head': 'onUrlSubmit'
'click .js-selectBrowserSize': 'selectBrowserSize'
'click .js-swatch': 'usePaletteColor'
'click .js-toggle-chat': 'toggleChat'
'change .js-chatSetting input': 'toggleChatSetting'
'click .js-eyedropper': 'pickColor'
'change .js-chat-jquery-widget': 'switchScriptPreview'
elements:
'.js-browser': 'browser'
'.js-browserBody': 'browserBody'
'.js-screenshot': 'screenshot'
'.js-website': 'website'
'.js-chat': 'chat'
'.js-chatHeader': 'chatHeader'
'.js-chat-welcome': 'chatWelcome'
'.js-testurl-input': 'urlInput'
'.js-backgroundColor': 'chatBackground'
'.js-code': 'code'
'.js-palette': 'palette'
'.js-color': 'colorField'
'.js-chatSetting input': 'chatSetting'
'.js-eyedropper': 'eyedropper'
apiOptions: [
{
name: 'chatId'
default: '1'
type: 'Number'
description: __('Identifier of the chat topic.')
}
{
name: 'show'
default: true
type: 'Boolean'
description: __('Show the chat when ready.')
}
{
name: 'target'
default: "$('body')"
type: 'jQuery Object'
description: __('Where to append the chat to.')
}
{
name: 'host'
default: '(Empty)'
type: 'String'
description: __("If left empty, the host gets auto-detected - in this case %s. The auto-detection reads out the host from the <script> tag. If you don't include it via a <script> tag you need to specify the host.")
descriptionSubstitute: window.location.origin
}
{
name: 'debug'
default: false
type: 'Boolean'
description: __('Enables console logging.')
}
{
name: 'title'
default: "'<strong>Chat</strong> with us!'"
type: 'String'
description: __('Welcome Title shown on the closed chat. Can contain HTML.')
}
{
name: 'fontSize'
default: 'undefined'
type: 'String'
description: __('CSS font-size with a unit like 12px, 1.5em. If left undefined it inherits the font-size of the website.')
}
{
name: 'flat'
default: 'false'
type: 'Boolean'
description: __('Removes the shadows for a flat look.')
}
{
name: 'buttonClass'
default: "'open-zammad-chat'"
type: 'String'
description: __('Add this class to a button on your page that should open the chat.')
}
{
name: 'inactiveClass'
default: "'is-inactive'"
type: 'String'
description: __('This class gets added to the button on initialization and will be removed once the chat connection is established.')
}
{
name: 'cssAutoload'
default: 'true'
type: 'Boolean'
description: __('Automatically loads the chat.css file. If you want to use your own css, just set it to false.')
}
{
name: 'cssUrl'
default: 'undefined'
type: 'String'
description: __('Location of an external chat.css file.')
}
]
isOpen: true
browserSize: 'desktop'
previewUrl: ''
previewScale: 1
constructor: ->
super
if @Session.get('email')
@previewUrl = "www.#{@Session.get('email').replace(/^.+?\@/, '')}"
@load()
@permanent =
chatId: 1
@widgetDesignerPermanentParams =
id: 'id'
$(window).on 'resize.chat-designer', @resizeDemo
load: =>
@startLoading()
@ajax(
id: 'chat_index'
type: 'GET'
url: @apiPath + '/chats'
processData: true
success: (data, status, xhr) =>
App.Collection.loadAssets(data.assets)
firstChat = App.Chat.first()
if firstChat
@permanent =
chatId: firstChat.id
@stopLoading()
@render(data)
)
render: (data = {}) =>
@html App.view('channel/chat')(
baseurl: window.location.origin
apiOptions: @apiOptions
previewUrl: @previewUrl
chatSetting: @Config.get('chat')
)
new Topics(
el: @$('.js-topics')
)
@code.each (i, block) ->
hljs.highlightBlock block
@updatePreview()
@updateParams()
@changeDemoWebsite()
# bind updatePreview with parameter animate = false
$(window).on 'resize.chat-designer', => @updatePreview false
release: ->
$(window).off 'resize.chat-designer'
@website.off('click.eyedropper')
selectBrowserSize: (event) =>
tab = $(event.target).closest('[data-size]')
# select tab
tab.addClass('active').siblings().removeClass('active')
@browserSize = tab.attr('data-size')
@updatePreview()
updatePreview: (animate = true) =>
# reset zoom
@chat
.removeClass('is-fullscreen')
.toggleClass('no-transition', !animate)
.css 'transform', "translateY(#{ @getChatOffset() }px)"
@browser.attr('data-size', @browserSize)
@previewScale = 1
switch @browserSize
when 'mobile'
@chat.addClass('is-fullscreen').css 'transform', "translateY(#{ @getChatOffset(true) }px)"
when '1:1'
@previewScale = Math.max(1, 1280/@el.width())
@website.css 'transform', "scale(#{ @previewScale })"
when 'desktop'
scale = Math.min(1, @el.width()/1280) # don't use it for the previewScale (used for the color picker)
@website.css 'transform', ''
@chat.css 'transform', "translateY(#{ @getChatOffset() * scale }px) scale(#{ scale })"
getChatOffset: (fullscreen) ->
return 0 if @isOpen
if fullscreen
return @browserBody.height() - @chatHeader.outerHeight()
else
return @chat.height() - @chatHeader.outerHeight()
onUrlSubmit: (event) ->
event.preventDefault() if event
@urlInput.trigger('focus')
@changeDemoWebsite()
changeDemoWebsite: ->
return if @urlInput.val() is '' or @urlInput.val() is @urlCache
@urlCache = @urlInput.val()
@url = @urlCache
if !@url.startsWith('http')
@url = "http://#{ @url }"
@urlInput.addClass('is-loading')
@palette.empty()
@screenshot.attr('src', '')
$.ajax
url: 'https://images.zammad.com/api/v1/webpage/combined'
data:
url: @url
count: 20
success: @renderDemoWebsite
dataType: 'json'
renderDemoWebsite: (data) =>
@_screenshotSource = data['data_url']
@screenshot.attr 'src', @_screenshotSource
@renderPalette data['palette']
@urlInput.removeClass('is-loading')
renderPalette: (palette) ->
palette = _.map palette, tinycolor
# filter white
palette = _.filter palette, (color) ->
color.getLuminance() < 0.85
htmlString = ''
max = 8
for color, i in palette
htmlString += App.view('channel/color_swatch')
color: color.toHexString()
break if i is max
@palette.html htmlString
# auto use first color
if palette[0]
@usePaletteColor undefined, palette[0].toHexString()
usePaletteColor: (event, code) ->
if event
code = $(event.currentTarget).attr('data-color')
@colorField.val code
@updateParams()
pickColor: ->
return if !@_screenshotSource
if @_pickingColor
@_pickingColor = false
@website
.off('click.eyedropper')
.removeClass('is-picking')
@eyedropper.removeClass('is-active')
else
@_pickingColor = true
@website
.on('click.eyedropper', @onColorPicked)
.addClass('is-picking')
@eyedropper.addClass('is-active')
onColorPicked: (event) =>
website_x = @website.position().left
website_y = @website.position().top
relative_x = event.pageX - @browserBody.offset().left
relative_y = event.pageY - @browserBody.offset().top
image = new Image()
image.src = @_screenshotSource
canvas = document.createElement('canvas')
ctx = canvas.getContext('2d')
canvas.width = @browserBody.width()
canvas.height = @browserBody.height()
ctx.drawImage(image, website_x, website_y, @website.width() * @previewScale, @website.width() * @previewScale)
pixels = ctx.getImageData(relative_x, relative_y, 1, 1).data
@colorField.val("rgb(#{pixels.slice(0,3).join(',')})").trigger('change')
switchScriptPreview: (e) ->
@$('.js-chat-jquery-widget-jquery').toggleClass('hide', !e.currentTarget.checked)
@$('.js-chat-jquery-widget-vanilla').toggleClass('hide', e.currentTarget.checked)
toggleChat: =>
@chat.toggleClass('is-open')
@isOpen = @chat.hasClass('is-open')
@updatePreview()
toggleChatSetting: =>
value = @chatSetting.prop('checked')
App.Setting.set('chat', value)
updateParams: =>
quote = (value) ->
if value.replace
value = value.replace('\'', '\\\'')
.replace(/\</g, '<')
.replace(/\>/g, '>')
value
params = @formParam(@$('.js-params'))
if parseInt(params.fontSize, 10) > 2
@chat.css('font-size', params.fontSize)
@chatBackground.css('background', params.background)
if params.flat is 'on'
@chat.addClass('zammad-chat--flat')
params.flat = true
else
@chat.removeClass('zammad-chat--flat')
@chatWelcome.html params.title
@updatePreview false
if @permanent
for key, value of @permanent
params[key] = value
paramString = ''
for key, value of params
if _.isNumber(value) || _.isBoolean(value) || !_.isEmpty(value)
if paramString != ''
# coffeelint: disable=no_unnecessary_double_quotes
paramString += ",\n"
# coffeelint: enable=no_unnecessary_double_quotes
if value == true || value == false || _.isNumber(value)
paramString += " #{key}: #{value}"
else
paramString += " #{key}: '#{quote(value)}'"
@$('.js-modal-params').html(paramString)
# highlight
@code.each (i, block) ->
hljs.highlightBlock block
App.Config.set('Chat', { prio: 4000, name: __('Chat'), parent: '#channels', target: '#channels/chat', controller: ChannelChat, permission: ['admin.channel_chat'] }, 'NavBarAdmin')
class Topics extends App.Controller
events:
'click .js-add': 'new'
'click .js-edit': 'edit'
'click .js-remove': 'remove'
constructor: ->
super
@render()
render: =>
@html App.view('channel/topics')(
chats: App.Chat.all()
)
new: (e) =>
new App.ControllerGenericNew(
pageData:
title: __('Chats')
object: __('Chat')
objects: __('Chats')
genericObject: 'Chat'
callback: @render
container: @el.closest('.content')
large: true
)
edit: (e) =>
e.preventDefault()
id = $(e.target).closest('tr').data('id')
new App.ControllerGenericEdit(
id: id
genericObject: 'Chat'
pageData:
object: __('Chat')
container: @el.closest('.content')
callback: @render
)
remove: (e) =>
e.preventDefault()
id = $(e.target).closest('tr').data('id')
item = App.Chat.find(id)
new App.ControllerGenericDestroyConfirm(
item: item
container: @el.closest('.content')
callback: @render
)