-
-
Notifications
You must be signed in to change notification settings - Fork 311
/
alchemy.dialog.js.coffee
267 lines (247 loc) · 7.91 KB
/
alchemy.dialog.js.coffee
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
# Dialog windows
#
class window.Alchemy.Dialog
DEFAULTS:
header_height: 36
size: '400x300'
padding: true
title: ''
modal: true
overflow: 'visible'
ready: ->
closed: ->
# Arguments:
# - url: The url to load the content from via ajax
# - options: A object holding options
# - size: The maximum size of the Dialog
# - title: The title of the Dialog
constructor: (@url, @options = {}) ->
@options = $.extend({}, @DEFAULTS, @options)
@$document = $(document)
@$window = $(window)
@$body = $('body')
size = @options.size.split('x')
@width = parseInt(size[0], 10)
@height = parseInt(size[1], 10)
@build()
# Opens the Dialog and loads the content via ajax.
open: ->
@dialog.trigger 'Alchemy.DialogOpen'
@bind_close_events()
window.requestAnimationFrame =>
@dialog_container.addClass('open')
@overlay.addClass('open') if @overlay?
@$body.addClass('prevent-scrolling')
Alchemy.currentDialogs.push(this)
@load()
true
# Closes the Dialog and removes it from the DOM
close: ->
@dialog.trigger 'DialogClose.Alchemy'
@$document.off 'keydown'
@dialog_container.removeClass('open')
@overlay.removeClass('open') if @overlay?
@$document.on 'webkitTransitionEnd transitionend oTransitionEnd', =>
@$document.off 'webkitTransitionEnd transitionend oTransitionEnd'
@dialog_container.remove()
@overlay.remove() if @overlay?
@$body.removeClass('prevent-scrolling')
Alchemy.currentDialogs.pop(this)
if @options.closed?
@options.closed()
true
# Loads the content via ajax and replaces the Dialog body with server response.
load: ->
@show_spinner()
$.get @url, (data) =>
@replace(data)
.fail (xhr) =>
@show_error(xhr)
true
# Reloads the Dialog content
reload: ->
@dialog_body.empty()
@load()
# Replaces the dialog body with given content and initializes it.
replace: (data) ->
@remove_spinner()
@dialog_body.hide()
@dialog_body.html(data)
@init()
@dialog[0].dispatchEvent(new CustomEvent(
"DialogReady.Alchemy",
bubbles: true
detail:
body: @dialog_body[0]
))
if @options.ready?
@options.ready(@dialog_body)
@dialog_body.show()
true
# Adds a spinner into Dialog body
show_spinner: ->
@spinner = new Alchemy.Spinner('medium')
@spinner.spin(@dialog_body[0])
# Removes the spinner from Dialog body
remove_spinner: ->
@spinner.stop()
# Initializes the Dialog body
init: ->
Alchemy.GUI.init(@dialog_body)
@watch_remote_forms()
# Watches ajax requests inside of dialog body and replaces the content accordingly
watch_remote_forms: ->
form = $('[data-remote="true"]', @dialog_body)
form.bind "ajax:success", (event) =>
xhr = event.detail[2]
content_type = xhr.getResponseHeader('Content-Type')
if content_type.match(/javascript/)
return
else
@dialog_body.html(xhr.responseText)
@init()
return
form.bind "ajax:error", (event, b, c) =>
statusText = event.detail[1]
xhr = event.detail[2]
@show_error(xhr, statusText)
return
# Displays an error message
show_error: (xhr, status_message, $container = @dialog_body) ->
error_type = "warning"
switch xhr.status
when 0
error_header = "The server does not respond."
error_body = "Please check server and try again."
when 403
error_header = "You are not authorized!"
error_body = "Please close this window."
else
error_type = "error"
if status_message
error_header = status_message
console.error(xhr.responseText)
else
error_header = "#{xhr.statusText} (#{xhr.status})"
error_body = "Please check log and try again."
$errorDiv = $("<alchemy-message type=\"#{error_type}\">
<h1>#{error_header}</h1>
<p>#{error_body}</p>
</alchemy-message>")
$container.html $errorDiv
# Binds close events on:
# - Close button
# - Overlay (if the Dialog is a modal)
# - ESC Key
bind_close_events: ->
@close_button.on "click", =>
@close()
false
@dialog_container.addClass('closable').on "click", (e) =>
return true if e.target != @dialog_container.get(0)
@close()
false
@$document.keydown (e) =>
if e.which == 27
@close()
false
else
true
# Builds the html structure of the Dialog
build: ->
@dialog_container = $('<div class="alchemy-dialog-container" />')
@dialog = $('<div class="alchemy-dialog" />')
@dialog_body = $('<div class="alchemy-dialog-body" />')
@dialog_header = $('<div class="alchemy-dialog-header" />')
@dialog_title = $('<div class="alchemy-dialog-title" />')
@close_button = $('<a class="alchemy-dialog-close"><alchemy-icon name="close"></alchemy-icon></a>')
@dialog_title.text(@options.title)
@dialog_header.append(@dialog_title)
@dialog_header.append(@close_button)
@dialog.append(@dialog_header)
@dialog.append(@dialog_body)
@dialog_container.append(@dialog)
@dialog.addClass('modal') if @options.modal
@dialog_body.addClass('padded') if @options.padding
if @options.modal
@overlay = $('<div class="alchemy-dialog-overlay" />')
@$body.append(@overlay)
@$body.append(@dialog_container)
@resize()
@dialog
# Sets the correct size of the dialog
# It normalizes the given size, so that it never acceeds the window size.
resize: ->
padding = 16
$doc_width = @$window.width()
$doc_height = @$window.height()
if @options.size == 'fullscreen'
[@width, @height] = [$doc_width, $doc_height]
if @width >= $doc_width
@width = $doc_width - padding
if @height >= $doc_height
@height = $doc_height - padding - @DEFAULTS.header_height
@dialog.css
'width': @width
'min-height': @height
overflow: @options.overflow
if @options.overflow == 'hidden'
@dialog_body.css
height: @height
overflow: 'auto'
else
@dialog_body.css
'min-height': @height
overflow: 'visible'
return
# Collection of all current dialog instances
window.Alchemy.currentDialogs = []
# Gets the last dialog instantiated, which is the current one.
window.Alchemy.currentDialog = ->
length = Alchemy.currentDialogs.length
return if length == 0
Alchemy.currentDialogs[length - 1]
# Utility function to close the current Dialog
#
# You can pass a callback function, that gets triggered after the Dialog gets closed.
#
window.Alchemy.closeCurrentDialog = (callback) ->
dialog = Alchemy.currentDialog()
if dialog?
dialog.options.closed = callback
dialog.close()
# Utility function to open a new Dialog
window.Alchemy.openDialog = (url, options) ->
if !url
throw('No url given! Please provide an url.')
dialog = new Alchemy.Dialog(url, options)
dialog.open()
# Watches elements for Alchemy Dialogs
#
# Links having a data-alchemy-confirm-delete
# and input/buttons having a data-alchemy-confirm attribute get watched.
#
# You can pass a scope so that only elements inside this scope are queried.
#
# The href attribute of the link is the url for the overlay window.
#
# See Alchemy.Dialog for further options you can add to the data attribute
#
window.Alchemy.watchForDialogs = (scope = '#alchemy') ->
$(scope).on 'click', '[data-alchemy-confirm-delete]', (event) ->
$this = $(this)
options = $this.data('alchemy-confirm-delete')
Alchemy.confirmToDeleteDialog($this.attr('href'), options)
event.preventDefault()
return
$(scope).on 'click', '[data-alchemy-confirm]', (event) ->
options = $(this).data('alchemy-confirm')
Alchemy.openConfirmDialog options.message, $.extend options,
ok_label: options.ok_label
cancel_label: options.cancel_label
on_ok: =>
Alchemy.pleaseWaitOverlay()
@form.submit()
return
event.preventDefault()
return