unpoly-rails 0.24.1 → 0.25.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
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
|