unpoly-rails 0.24.1 → 0.25.0
Sign up to get free protection for your applications and to get access to all the features.
Potentially problematic release.
This version of unpoly-rails might be problematic. Click here for more details.
- checksums.yaml +4 -4
- data/CHANGELOG.md +31 -0
- data/README_RAILS.md +8 -1
- data/dist/unpoly.css +22 -10
- data/dist/unpoly.js +406 -196
- data/dist/unpoly.min.css +1 -1
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly/flow.js.coffee +36 -19
- data/lib/assets/javascripts/unpoly/form.js.coffee +1 -2
- data/lib/assets/javascripts/unpoly/link.js.coffee +2 -2
- data/lib/assets/javascripts/unpoly/modal.js.coffee +174 -81
- data/lib/assets/javascripts/unpoly/navigation.js.coffee +3 -1
- data/lib/assets/javascripts/unpoly/popup.js.coffee +62 -37
- data/lib/assets/javascripts/unpoly/proxy.js.coffee +1 -0
- data/lib/assets/javascripts/unpoly/syntax.js.coffee +12 -4
- data/lib/assets/javascripts/unpoly/util.js.coffee +55 -13
- data/lib/assets/stylesheets/unpoly/modal.css.sass +28 -12
- data/lib/unpoly/rails/inspector.rb +26 -0
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/controllers/binding_test_controller.rb +6 -0
- data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -11
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +21 -7
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +15 -0
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +11 -10
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +232 -30
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +33 -27
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +72 -0
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +51 -13
- metadata +2 -2
@@ -6,7 +6,7 @@ Instead of [linking to a page fragment](/up.link), you can choose
|
|
6
6
|
to show a fragment in a modal dialog. The existing page will remain
|
7
7
|
open in the background and reappear once the modal is closed.
|
8
8
|
|
9
|
-
To open a modal, add an [`up-modal` attribute](/
|
9
|
+
To open a modal, add an [`up-modal` attribute](/up-modal) to a link,
|
10
10
|
or call the Javascript functions [`up.modal.follow`](/up.modal.follow)
|
11
11
|
and [`up.modal.visit`](/up.modal.visit).
|
12
12
|
|
@@ -46,7 +46,7 @@ configure Unpoly to [use a different HTML structure](/up.modal.config).
|
|
46
46
|
\#\#\#\# Closing behavior
|
47
47
|
|
48
48
|
By default the dialog automatically closes
|
49
|
-
*
|
49
|
+
*when a link inside a modal changes a fragment behind the modal*.
|
50
50
|
This is useful to have the dialog interact with the page that
|
51
51
|
opened it, e.g. by updating parts of a larger form or by signing in a user
|
52
52
|
and revealing additional information.
|
@@ -109,6 +109,9 @@ up.modal = (($) ->
|
|
109
109
|
The timing function controlling the acceleration of the opening animation.
|
110
110
|
@param {String} [config.closeEasing]
|
111
111
|
The timing function controlling the acceleration of the closing animation.
|
112
|
+
@param {Boolean} [options.sticky=false]
|
113
|
+
If set to `true`, the modal remains
|
114
|
+
open even it changes the page in the background.
|
112
115
|
@stable
|
113
116
|
###
|
114
117
|
config = u.config
|
@@ -119,13 +122,14 @@ up.modal = (($) ->
|
|
119
122
|
history: true
|
120
123
|
openAnimation: 'fade-in'
|
121
124
|
closeAnimation: 'fade-out'
|
122
|
-
closeDuration: null
|
123
|
-
closeEasing: null
|
124
125
|
openDuration: null
|
126
|
+
closeDuration: null
|
125
127
|
openEasing: null
|
128
|
+
closeEasing: null
|
126
129
|
backdropOpenAnimation: 'fade-in'
|
127
130
|
backdropCloseAnimation: 'fade-out'
|
128
131
|
closeLabel: '×'
|
132
|
+
flavors: { default: {} }
|
129
133
|
|
130
134
|
template: (config) ->
|
131
135
|
"""
|
@@ -133,8 +137,8 @@ up.modal = (($) ->
|
|
133
137
|
<div class="up-modal-backdrop"></div>
|
134
138
|
<div class="up-modal-viewport">
|
135
139
|
<div class="up-modal-dialog">
|
136
|
-
<div class="up-modal-close" up-close>#{config.closeLabel}</div>
|
137
140
|
<div class="up-modal-content"></div>
|
141
|
+
<div class="up-modal-close" up-close>#{flavorDefault('closeLabel')}</div>
|
138
142
|
</div>
|
139
143
|
</div>
|
140
144
|
</div>
|
@@ -151,6 +155,8 @@ up.modal = (($) ->
|
|
151
155
|
###
|
152
156
|
currentUrl = undefined
|
153
157
|
|
158
|
+
currentFlavor = undefined
|
159
|
+
|
154
160
|
###*
|
155
161
|
Returns the URL of the page behind the modal overlay.
|
156
162
|
|
@@ -165,10 +171,11 @@ up.modal = (($) ->
|
|
165
171
|
# Destroy the modal container regardless whether it's currently in a closing animation
|
166
172
|
close(animation: false)
|
167
173
|
currentUrl = undefined
|
174
|
+
currentFlavor = undefined
|
168
175
|
config.reset()
|
169
176
|
|
170
177
|
templateHtml = ->
|
171
|
-
template =
|
178
|
+
template = flavorDefault('template')
|
172
179
|
if u.isFunction(template)
|
173
180
|
template(config)
|
174
181
|
else
|
@@ -180,20 +187,27 @@ up.modal = (($) ->
|
|
180
187
|
$modal.removeAttr('up-covered-title')
|
181
188
|
|
182
189
|
createFrame = (target, options) ->
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
190
|
+
promise = u.resolvedPromise()
|
191
|
+
if isOpen()
|
192
|
+
promise = promise.then -> close()
|
193
|
+
promise = promise.then ->
|
194
|
+
currentFlavor = options.flavor
|
195
|
+
$modal = $(templateHtml())
|
196
|
+
$modal.attr('up-flavor', currentFlavor)
|
197
|
+
$modal.attr('up-sticky', '') if options.sticky
|
198
|
+
$modal.attr('up-covered-url', up.browser.url())
|
199
|
+
$modal.attr('up-covered-title', document.title)
|
200
|
+
$dialog = $modal.find('.up-modal-dialog')
|
201
|
+
$dialog.css('width', options.width) if u.isPresent(options.width)
|
202
|
+
$dialog.css('max-width', options.maxWidth) if u.isPresent(options.maxWidth)
|
203
|
+
$dialog.css('height', options.height) if u.isPresent(options.height)
|
204
|
+
$content = $modal.find('.up-modal-content')
|
205
|
+
# Create an empty element that will match the
|
206
|
+
# selector that is being replaced.
|
207
|
+
u.$createPlaceholder(target, $content)
|
208
|
+
$modal.appendTo(document.body)
|
209
|
+
return promise
|
210
|
+
|
197
211
|
unshifters = []
|
198
212
|
|
199
213
|
# Gives `<body>` a right padding in the width of a scrollbar.
|
@@ -204,32 +218,35 @@ up.modal = (($) ->
|
|
204
218
|
# modal overlay, which has its own scroll bar.
|
205
219
|
# This is screwed up, but Bootstrap does the same.
|
206
220
|
shiftElements = ->
|
207
|
-
if unshifters.length
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
221
|
+
return if unshifters.length > 0
|
222
|
+
|
223
|
+
if u.documentHasVerticalScrollbar()
|
224
|
+
$body = $('body')
|
225
|
+
scrollbarWidth = u.scrollbarWidth()
|
226
|
+
bodyRightPadding = parseInt($body.css('padding-right'))
|
227
|
+
bodyRightShift = scrollbarWidth + bodyRightPadding
|
228
|
+
unshiftBody = u.temporaryCss($body,
|
229
|
+
'padding-right': "#{bodyRightShift}px",
|
230
|
+
'overflow-y': 'hidden'
|
231
|
+
)
|
232
|
+
unshifters.push(unshiftBody)
|
233
|
+
up.layout.anchoredRight().each ->
|
234
|
+
$element = $(this)
|
235
|
+
elementRight = parseInt($element.css('right'))
|
236
|
+
elementRightShift = scrollbarWidth + elementRight
|
237
|
+
unshifter = u.temporaryCss($element, 'right': elementRightShift)
|
238
|
+
unshifters.push(unshifter)
|
239
|
+
|
224
240
|
|
225
241
|
# Reverts the effects of `shiftElements`.
|
226
242
|
unshiftElements = ->
|
227
|
-
$('.up-modal').removeClass('up-modal-ready')
|
228
243
|
unshifter() while unshifter = unshifters.pop()
|
229
244
|
|
230
245
|
###*
|
231
246
|
Returns whether a modal is currently open.
|
232
247
|
|
248
|
+
This also returns `true` if the modal is in an opening or closing animation.
|
249
|
+
|
233
250
|
@function up.modal.isOpen
|
234
251
|
@stable
|
235
252
|
###
|
@@ -259,7 +276,7 @@ up.modal = (($) ->
|
|
259
276
|
By [default](/up.modal.config) the dialog will grow to fit its contents.
|
260
277
|
@param {Boolean} [options.sticky=false]
|
261
278
|
If set to `true`, the modal remains
|
262
|
-
open even
|
279
|
+
open even it changes the page in the background.
|
263
280
|
@param {String} [options.confirm]
|
264
281
|
A message that will be displayed in a cancelable confirmation dialog
|
265
282
|
before the modal is being opened.
|
@@ -353,42 +370,38 @@ up.modal = (($) ->
|
|
353
370
|
options = u.options(options)
|
354
371
|
$link = u.option(u.pluckKey(options, '$link'), u.nullJQuery())
|
355
372
|
url = u.option(u.pluckKey(options, 'url'), $link.attr('up-href'), $link.attr('href'))
|
356
|
-
html = u.pluckKey(options, 'html')
|
373
|
+
html = u.option(u.pluckKey(options, 'html'))
|
357
374
|
target = u.option(u.pluckKey(options, 'target'), $link.attr('up-modal'), 'body')
|
358
|
-
options.
|
359
|
-
options.
|
360
|
-
options.
|
361
|
-
options.
|
362
|
-
options.
|
363
|
-
options.
|
375
|
+
options.flavor = u.option(options.flavor, $link.attr('up-flavor'))
|
376
|
+
options.width = u.option(options.width, $link.attr('up-width'), flavorDefault('width', options.flavor))
|
377
|
+
options.maxWidth = u.option(options.maxWidth, $link.attr('up-max-width'), flavorDefault('maxWidth', options.flavor))
|
378
|
+
options.height = u.option(options.height, $link.attr('up-height'), flavorDefault('height'))
|
379
|
+
options.animation = u.option(options.animation, $link.attr('up-animation'), flavorDefault('openAnimation', options.flavor))
|
380
|
+
options.backdropAnimation = u.option(options.backdropAnimation, $link.attr('up-backdrop-animation'), flavorDefault('backdropOpenAnimation', options.flavor))
|
381
|
+
options.sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'), flavorDefault('sticky', options.flavor))
|
364
382
|
options.confirm = u.option(options.confirm, $link.attr('up-confirm'))
|
365
|
-
animateOptions = up.motion.animateOptions(options, $link,
|
383
|
+
animateOptions = up.motion.animateOptions(options, $link, duration: flavorDefault('openDuration', options.flavor), easing: flavorDefault('openEasing', options.flavor))
|
366
384
|
|
367
385
|
# Although we usually fall back to full page loads if a browser doesn't support pushState,
|
368
386
|
# in the case of modals we assume that the developer would rather see a dialog
|
369
387
|
# without an URL update.
|
370
|
-
options.history = u.option(options.history, u.castedAttr($link, 'up-history'),
|
388
|
+
options.history = u.option(options.history, u.castedAttr($link, 'up-history'), flavorDefault('history', options.flavor))
|
371
389
|
options.history = false unless up.browser.canPushState()
|
372
390
|
|
373
391
|
up.browser.confirm(options).then ->
|
374
392
|
if up.bus.nobodyPrevents('up:modal:open', url: url, message: 'Opening modal')
|
375
|
-
wasOpen = isOpen()
|
376
|
-
close(animation: false) if wasOpen
|
377
393
|
options.beforeSwap = -> createFrame(target, options)
|
378
394
|
extractOptions = u.merge(options, animation: false)
|
379
|
-
if
|
380
|
-
promise = up.replace(target, url, extractOptions)
|
381
|
-
else
|
395
|
+
if html
|
382
396
|
promise = up.extract(target, html, extractOptions)
|
383
|
-
|
384
|
-
|
385
|
-
promise = promise.then ->
|
386
|
-
$.when(
|
387
|
-
up.animate($('.up-modal-backdrop'), options.backdropAnimation, animateOptions),
|
388
|
-
up.animate($('.up-modal-viewport'), options.animation, animateOptions)
|
389
|
-
)
|
397
|
+
else
|
398
|
+
promise = up.replace(target, url, extractOptions)
|
390
399
|
promise = promise.then ->
|
391
400
|
shiftElements()
|
401
|
+
|
402
|
+
promise = promise.then -> animate(options.animation, options.backdropAnimation, animateOptions)
|
403
|
+
|
404
|
+
promise = promise.then ->
|
392
405
|
up.emit('up:modal:opened', message: 'Modal opened')
|
393
406
|
promise
|
394
407
|
else
|
@@ -432,18 +445,15 @@ up.modal = (($) ->
|
|
432
445
|
$modal = $('.up-modal')
|
433
446
|
if $modal.length
|
434
447
|
if up.bus.nobodyPrevents('up:modal:close', $element: $modal, message: 'Closing modal')
|
435
|
-
|
436
|
-
|
437
|
-
|
438
|
-
|
439
|
-
|
440
|
-
|
441
|
-
|
442
|
-
|
443
|
-
|
444
|
-
up.animate($('.up-modal-viewport'), viewportCloseAnimation, animateOptions),
|
445
|
-
up.animate($('.up-modal-backdrop'), backdropCloseAnimation, animateOptions)
|
446
|
-
)
|
448
|
+
viewportCloseAnimation = u.option(options.animation, flavorDefault('closeAnimation'))
|
449
|
+
backdropCloseAnimation = u.option(options.backdropAnimation, flavorDefault('backdropCloseAnimation'))
|
450
|
+
animateOptions = up.motion.animateOptions(options, duration: flavorDefault('closeDuration'), easing: flavorDefault('closeEasing'))
|
451
|
+
|
452
|
+
promise = u.resolvedPromise()
|
453
|
+
|
454
|
+
promise = promise.then ->
|
455
|
+
animate(viewportCloseAnimation, backdropCloseAnimation, animateOptions)
|
456
|
+
|
447
457
|
promise = promise.then ->
|
448
458
|
destroyOptions = u.options(
|
449
459
|
u.except(options, 'animation', 'duration', 'easing', 'delay'),
|
@@ -454,16 +464,37 @@ up.modal = (($) ->
|
|
454
464
|
# since up.navigation listens to up:fragment:destroyed and then
|
455
465
|
# re-assigns .up-current classes.
|
456
466
|
currentUrl = undefined
|
457
|
-
|
467
|
+
|
468
|
+
return up.destroy($modal, destroyOptions)
|
469
|
+
|
470
|
+
promise = promise.then ->
|
471
|
+
unshiftElements()
|
472
|
+
currentFlavor = undefined
|
458
473
|
up.emit('up:modal:closed', message: 'Modal closed')
|
474
|
+
|
459
475
|
promise
|
460
476
|
else
|
461
|
-
# Although someone prevented the destruction,
|
462
|
-
#
|
463
|
-
|
464
|
-
|
477
|
+
# Although someone prevented the destruction, keep a uniform API
|
478
|
+
# for callers by returning a promise that will never be resolved.
|
479
|
+
u.unresolvablePromise()
|
480
|
+
else
|
481
|
+
u.resolvedPromise()
|
482
|
+
|
483
|
+
markAsAnimating = (state = true) ->
|
484
|
+
$('.up-modal').toggleClass('up-modal-animating', state)
|
485
|
+
|
486
|
+
animate = (viewportAnimation, backdropAnimation, animateOptions) ->
|
487
|
+
# If we're not animating the dialog, don't animate the backdrop either
|
488
|
+
if up.motion.isNone(viewportAnimation)
|
489
|
+
u.resolvedPromise()
|
465
490
|
else
|
466
|
-
|
491
|
+
markAsAnimating()
|
492
|
+
promise = $.when(
|
493
|
+
up.animate($('.up-modal-viewport'), viewportAnimation, animateOptions),
|
494
|
+
up.animate($('.up-modal-backdrop'), backdropAnimation, animateOptions)
|
495
|
+
)
|
496
|
+
promise = promise.then -> markAsAnimating(false)
|
497
|
+
promise
|
467
498
|
|
468
499
|
###*
|
469
500
|
This event is [emitted](/up.emit) when a modal dialog
|
@@ -500,6 +531,66 @@ up.modal = (($) ->
|
|
500
531
|
$element = $(elementOrSelector)
|
501
532
|
$element.closest('.up-modal').length > 0
|
502
533
|
|
534
|
+
###*
|
535
|
+
Register a new modal variant with its own default configuration, CSS or HTML template.
|
536
|
+
|
537
|
+
\#\#\#\# Example
|
538
|
+
|
539
|
+
Let's implement a drawer that slides in from the right:
|
540
|
+
|
541
|
+
up.modal.flavor('drawer', {
|
542
|
+
openAnimation: 'move-from-right',
|
543
|
+
closeAnimation: 'move-to-right',
|
544
|
+
maxWidth: 400
|
545
|
+
}
|
546
|
+
|
547
|
+
Modals with that flavor will have a container `<div class='up-modal' up-flavor='drawer'>...</div>`.
|
548
|
+
We can target the `up-flavor` attribute override the default dialog styles:
|
549
|
+
|
550
|
+
.up-modal[up-flavor='drawer'] {
|
551
|
+
|
552
|
+
// Align drawer on the right
|
553
|
+
.up-modal-viewport { text-align: right; }
|
554
|
+
|
555
|
+
// Remove margin so the drawer starts at the screen edge
|
556
|
+
.up-modal-dialog { margin: 0; }
|
557
|
+
|
558
|
+
// Stretch drawer background to full window height
|
559
|
+
.up-modal-content { min-height: 100vh; }
|
560
|
+
}
|
561
|
+
|
562
|
+
@function up.modal.flavor
|
563
|
+
@param {String} name
|
564
|
+
The name of the new flavor.
|
565
|
+
@param {Object} [overrideConfig]
|
566
|
+
An object whose properties override the defaults in [`/up.modal.config`](/up.modal.config).
|
567
|
+
@experimental
|
568
|
+
###
|
569
|
+
flavor = (name, overrideConfig = {}) ->
|
570
|
+
u.extend(flavorOverrides(name), overrideConfig)
|
571
|
+
|
572
|
+
###*
|
573
|
+
Returns a config object for the given flavor.
|
574
|
+
Properties in that config should be preferred to the defaults in
|
575
|
+
[`/up.modal.config`](/up.modal.config).
|
576
|
+
|
577
|
+
@function flavorOverrides
|
578
|
+
@internal
|
579
|
+
###
|
580
|
+
flavorOverrides = (flavor) ->
|
581
|
+
config.flavors[flavor] ||= {}
|
582
|
+
|
583
|
+
###*
|
584
|
+
Returns the config option for the current flavor.
|
585
|
+
|
586
|
+
@function flavorDefault
|
587
|
+
@internal
|
588
|
+
###
|
589
|
+
flavorDefault = (key, flavorName = currentFlavor) ->
|
590
|
+
value = flavorOverrides(flavorName)[key] if flavorName
|
591
|
+
value = config[key] if u.isMissing(value)
|
592
|
+
value
|
593
|
+
|
503
594
|
###*
|
504
595
|
Clicking this link will load the destination via AJAX and open
|
505
596
|
the given selector in a modal dialog.
|
@@ -513,7 +604,7 @@ up.modal = (($) ->
|
|
513
604
|
and place the matching `.blog-list` tag will be placed in
|
514
605
|
a modal dialog.
|
515
606
|
|
516
|
-
@selector
|
607
|
+
@selector [up-modal]
|
517
608
|
@param {String} [up-confirm]
|
518
609
|
A message that will be displayed in a cancelable confirmation dialog
|
519
610
|
before the modal is opened.
|
@@ -527,11 +618,12 @@ up.modal = (($) ->
|
|
527
618
|
@param {String} [up-height]
|
528
619
|
The width of the dialog in pixels.
|
529
620
|
By [default](/up.modal.config) the dialog will grow to fit its contents.
|
530
|
-
@param [up-width]
|
621
|
+
@param {String} [up-width]
|
531
622
|
The width of the dialog in pixels.
|
532
623
|
By [default](/up.modal.config) the dialog will grow to fit its contents.
|
533
|
-
@param [up-history="true"]
|
624
|
+
@param {String} [up-history="true"]
|
534
625
|
Whether to add a browser history entry for the modal's source URL.
|
626
|
+
|
535
627
|
@stable
|
536
628
|
###
|
537
629
|
up.link.onAction '[up-modal]', ($link) ->
|
@@ -594,5 +686,6 @@ up.modal = (($) ->
|
|
594
686
|
contains: contains
|
595
687
|
source: -> up.error('up.modal.source no longer exists. Please use up.popup.url instead.')
|
596
688
|
isOpen: isOpen
|
689
|
+
flavor: flavor
|
597
690
|
|
598
691
|
)(jQuery)
|
@@ -150,7 +150,9 @@ up.navigation = (($) ->
|
|
150
150
|
$element = findClickArea(elementOrSelector, options)
|
151
151
|
$element.removeClass(CLASS_ACTIVE)
|
152
152
|
|
153
|
-
withActiveMark = (elementOrSelector,
|
153
|
+
withActiveMark = (elementOrSelector, args...) ->
|
154
|
+
block = args.pop()
|
155
|
+
options = u.options(args.pop())
|
154
156
|
$element = $(elementOrSelector)
|
155
157
|
markActive($element, options)
|
156
158
|
promise = block()
|
@@ -5,7 +5,7 @@ Pop-up overlays
|
|
5
5
|
Instead of [linking to a page fragment](/up.link), you can choose
|
6
6
|
to show a fragment in a popup overlay that rolls down from an anchoring element.
|
7
7
|
|
8
|
-
To open a popup, add an [`up-popup` attribute](/
|
8
|
+
To open a popup, add an [`up-popup` attribute](/up-popup) to a link,
|
9
9
|
or call the Javascript function [`up.popup.attach`](/up.popup.attach).
|
10
10
|
|
11
11
|
For modal dialogs see [up.modal](/up.modal) instead.
|
@@ -31,7 +31,7 @@ By default the popup uses the following DOM structure:
|
|
31
31
|
The popup closes when the user clicks anywhere outside the popup area.
|
32
32
|
|
33
33
|
By default the popup also closes
|
34
|
-
*
|
34
|
+
*when a link within the popup changes a fragment behind the popup*.
|
35
35
|
This is useful to have the popup interact with the page that
|
36
36
|
opened it, e.g. by updating parts of a larger form or by signing in a user
|
37
37
|
and revealing additional information.
|
@@ -71,21 +71,36 @@ up.popup = (($) ->
|
|
71
71
|
Sets default options for future popups.
|
72
72
|
|
73
73
|
@property up.popup.config
|
74
|
-
@param {String} [config.openAnimation='fade-in']
|
75
|
-
The animation used to open a popup.
|
76
|
-
@param {String} [config.closeAnimation='fade-out']
|
77
|
-
The animation used to close a popup.
|
78
74
|
@param {String} [config.position='bottom-right']
|
79
75
|
Defines where the popup is attached to the opening element.
|
80
76
|
|
81
77
|
Valid values are `bottom-right`, `bottom-left`, `top-right` and `top-left`.
|
82
78
|
@param {String} [config.history=false]
|
83
79
|
Whether opening a popup will add a browser history entry.
|
80
|
+
@param {String} [config.openAnimation='fade-in']
|
81
|
+
The animation used to open a popup.
|
82
|
+
@param {String} [config.closeAnimation='fade-out']
|
83
|
+
The animation used to close a popup.
|
84
|
+
@param {String} [config.openDuration]
|
85
|
+
The duration of the open animation (in milliseconds).
|
86
|
+
@param {String} [config.closeDuration]
|
87
|
+
The duration of the close animation (in milliseconds).
|
88
|
+
@param {String} [config.openEasing]
|
89
|
+
The timing function controlling the acceleration of the opening animation.
|
90
|
+
@param {String} [config.closeEasing]
|
91
|
+
The timing function controlling the acceleration of the closing animation.
|
92
|
+
@param {Boolean} [options.sticky=false]
|
93
|
+
If set to `true`, the popup remains
|
94
|
+
open even it changes the page in the background.
|
84
95
|
@stable
|
85
96
|
###
|
86
97
|
config = u.config
|
87
98
|
openAnimation: 'fade-in'
|
88
99
|
closeAnimation: 'fade-out'
|
100
|
+
openDuration: null
|
101
|
+
closeDuration: null
|
102
|
+
openEasing: null
|
103
|
+
closeEasing: null
|
89
104
|
position: 'bottom-right'
|
90
105
|
history: false
|
91
106
|
|
@@ -147,15 +162,20 @@ up.popup = (($) ->
|
|
147
162
|
$popup.removeAttr('up-covered-title')
|
148
163
|
|
149
164
|
createFrame = (target, options) ->
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
165
|
+
promise = u.resolvedPromise()
|
166
|
+
if isOpen()
|
167
|
+
promise = promise.then -> close()
|
168
|
+
promise = promise.then ->
|
169
|
+
$popup = u.$createElementFromSelector('.up-popup')
|
170
|
+
$popup.attr('up-sticky', '') if options.sticky
|
171
|
+
$popup.attr('up-covered-url', up.browser.url())
|
172
|
+
$popup.attr('up-covered-title', document.title)
|
173
|
+
# Create an empty element that will match the
|
174
|
+
# selector that is being replaced.
|
175
|
+
u.$createPlaceholder(target, $popup)
|
176
|
+
$popup.appendTo(document.body)
|
177
|
+
$popup
|
178
|
+
return promise
|
159
179
|
|
160
180
|
###*
|
161
181
|
Returns whether popup modal is currently open.
|
@@ -174,6 +194,8 @@ up.popup = (($) ->
|
|
174
194
|
@function up.popup.attach
|
175
195
|
@param {Element|jQuery|String} elementOrSelector
|
176
196
|
@param {String} [options.url]
|
197
|
+
@param {String} [options.target]
|
198
|
+
A CSS selector that will be extracted from the response and placed into the popup.
|
177
199
|
@param {String} [options.position='bottom-right']
|
178
200
|
Defines where the popup is attached to the opening element.
|
179
201
|
|
@@ -203,33 +225,35 @@ up.popup = (($) ->
|
|
203
225
|
$link.length or u.error('Cannot attach popup to non-existing element %o', linkOrSelector)
|
204
226
|
|
205
227
|
options = u.options(options)
|
206
|
-
url = u.option(options
|
207
|
-
|
228
|
+
url = u.option(u.pluckKey(options, 'url'), $link.attr('up-href'), $link.attr('href'))
|
229
|
+
html = u.option(u.pluckKey(options, 'html'))
|
230
|
+
target = u.option(u.pluckKey(options, 'target'), $link.attr('up-popup'), 'body')
|
208
231
|
options.position = u.option(options.position, $link.attr('up-position'), config.position)
|
209
232
|
options.animation = u.option(options.animation, $link.attr('up-animation'), config.openAnimation)
|
210
|
-
options.sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'))
|
233
|
+
options.sticky = u.option(options.sticky, u.castedAttr($link, 'up-sticky'), config.sticky)
|
211
234
|
options.history = if up.browser.canPushState() then u.option(options.history, u.castedAttr($link, 'up-history'), config.history) else false
|
212
235
|
options.confirm = u.option(options.confirm, $link.attr('up-confirm'))
|
213
|
-
animateOptions = up.motion.animateOptions(options, $link)
|
236
|
+
animateOptions = up.motion.animateOptions(options, $link, duration: config.openDuration, easing: config.openEasing)
|
214
237
|
|
215
238
|
up.browser.confirm(options).then ->
|
216
239
|
if up.bus.nobodyPrevents('up:popup:open', url: url, message: 'Opening popup')
|
217
|
-
wasOpen = isOpen()
|
218
|
-
close(animation: false) if wasOpen
|
219
240
|
options.beforeSwap = -> createFrame(target, options)
|
220
|
-
|
241
|
+
extractOptions = u.merge(options, animation: false)
|
242
|
+
if html
|
243
|
+
promise = up.extract(target, html, extractOptions)
|
244
|
+
else
|
245
|
+
promise = up.replace(target, url, extractOptions)
|
221
246
|
promise = promise.then ->
|
222
247
|
setPosition($link, options.position)
|
223
|
-
|
224
|
-
|
225
|
-
up.animate($('.up-popup'), options.animation, animateOptions)
|
248
|
+
promise = promise.then ->
|
249
|
+
up.animate($('.up-popup'), options.animation, animateOptions)
|
226
250
|
promise = promise.then ->
|
227
251
|
up.emit('up:popup:opened', message: 'Popup opened')
|
228
252
|
promise
|
229
253
|
else
|
230
254
|
# Although someone prevented the destruction, keep a uniform API for
|
231
|
-
# callers by returning a
|
232
|
-
u.
|
255
|
+
# callers by returning a promise that will never be resolved.
|
256
|
+
u.unresolvablePromise()
|
233
257
|
|
234
258
|
###*
|
235
259
|
This event is [emitted](/up.emit) when a popup is starting to open.
|
@@ -257,7 +281,7 @@ up.popup = (($) ->
|
|
257
281
|
@function up.popup.close
|
258
282
|
@param {Object} options
|
259
283
|
See options for [`up.animate`](/up.animate).
|
260
|
-
@return {
|
284
|
+
@return {Promise}
|
261
285
|
A promise that will be resolved once the modal's close
|
262
286
|
animation has finished.
|
263
287
|
@stable
|
@@ -271,17 +295,18 @@ up.popup = (($) ->
|
|
271
295
|
url: $popup.attr('up-covered-url'),
|
272
296
|
title: $popup.attr('up-covered-title')
|
273
297
|
)
|
298
|
+
animateOptions = up.motion.animateOptions(options, duration: config.closeDuration, easing: config.closeEasing)
|
299
|
+
u.extend(options, animateOptions)
|
274
300
|
currentUrl = undefined
|
275
|
-
|
276
|
-
|
277
|
-
|
301
|
+
promise = up.destroy($popup, options)
|
302
|
+
promise = promise.then -> up.emit('up:popup:closed', message: 'Popup closed')
|
303
|
+
promise
|
278
304
|
else
|
279
|
-
# Although someone prevented the destruction,
|
280
|
-
#
|
281
|
-
|
282
|
-
u.unresolvableDeferred()
|
305
|
+
# Although someone prevented the destruction, keep a uniform API
|
306
|
+
# for callers by returning a promise that will never be resolved.
|
307
|
+
u.unresolvablePromise()
|
283
308
|
else
|
284
|
-
u.
|
309
|
+
u.resolvedPromise()
|
285
310
|
|
286
311
|
###*
|
287
312
|
This event is [emitted](/up.emit) when a popup dialog
|
@@ -329,7 +354,7 @@ up.popup = (($) ->
|
|
329
354
|
<a href="/decks" up-popup=".deck_list">Switch deck</a>
|
330
355
|
<a href="/settings" up-popup=".options" up-sticky>Settings</a>
|
331
356
|
|
332
|
-
@selector
|
357
|
+
@selector [up-popup]
|
333
358
|
@param [up-position]
|
334
359
|
Defines where the popup is attached to the opening element.
|
335
360
|
|
@@ -394,6 +394,7 @@ up.proxy = (($) ->
|
|
394
394
|
promise = load(entry.request)
|
395
395
|
promise.done (args...) -> entry.deferred.resolve(args...)
|
396
396
|
promise.fail (args...) -> entry.deferred.reject(args...)
|
397
|
+
return
|
397
398
|
|
398
399
|
###*
|
399
400
|
Makes the proxy assume that `newRequest` has the same response as the
|
@@ -255,6 +255,10 @@ up.syntax = (($) ->
|
|
255
255
|
buildCompiler = (selector, args...) ->
|
256
256
|
callback = args.pop()
|
257
257
|
options = u.options(args[0], priority: 0)
|
258
|
+
if options.priority == 'first'
|
259
|
+
options.priority = Number.POSITIVE_INFINITY
|
260
|
+
else if options.priority == 'last'
|
261
|
+
options.priority = Number.NEGATIVE_INFINITY
|
258
262
|
selector: selector
|
259
263
|
callback: callback
|
260
264
|
priority: options.priority
|
@@ -266,7 +270,7 @@ up.syntax = (($) ->
|
|
266
270
|
return unless up.browser.isSupported()
|
267
271
|
newCompiler = buildCompiler(args...)
|
268
272
|
index = 0
|
269
|
-
while (oldCompiler = queue[index]) && (oldCompiler.priority
|
273
|
+
while (oldCompiler = queue[index]) && (oldCompiler.priority >= newCompiler.priority)
|
270
274
|
index += 1
|
271
275
|
queue.splice(index, 0, newCompiler)
|
272
276
|
|
@@ -322,6 +326,7 @@ up.syntax = (($) ->
|
|
322
326
|
u.findWithSelf($fragment, ".#{DESTROYABLE_CLASS}").each ->
|
323
327
|
$element = $(this)
|
324
328
|
destroyer = $element.data(DESTROYER_KEY)
|
329
|
+
$element.removeClass(DESTROYABLE_CLASS)
|
325
330
|
destroyer()
|
326
331
|
|
327
332
|
###*
|
@@ -407,8 +412,9 @@ up.syntax = (($) ->
|
|
407
412
|
@internal
|
408
413
|
###
|
409
414
|
snapshot = ->
|
410
|
-
|
411
|
-
|
415
|
+
setDefault = (compiler) -> compiler.isDefault = true
|
416
|
+
u.each(compilers, setDefault)
|
417
|
+
u.each(macros, setDefault)
|
412
418
|
|
413
419
|
###*
|
414
420
|
Resets the list of registered compiler directives to the
|
@@ -417,7 +423,9 @@ up.syntax = (($) ->
|
|
417
423
|
@internal
|
418
424
|
###
|
419
425
|
reset = ->
|
420
|
-
|
426
|
+
isDefault = (compiler) -> compiler.isDefault
|
427
|
+
compilers = u.select(compilers, isDefault)
|
428
|
+
macros = u.select(macros, isDefault)
|
421
429
|
|
422
430
|
up.on 'up:framework:boot', snapshot
|
423
431
|
up.on 'up:framework:reset', reset
|