unpoly-rails 0.36.2 → 0.37.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 +12 -0
- data/README.md +1 -1
- data/dist/unpoly.js +137 -73
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly/bus.coffee +2 -2
- data/lib/assets/javascripts/unpoly/dom/extract_plan.coffee +4 -2
- data/lib/assets/javascripts/unpoly/dom.coffee +26 -12
- data/lib/assets/javascripts/unpoly/form.coffee +19 -1
- data/lib/assets/javascripts/unpoly/layout.coffee +1 -1
- data/lib/assets/javascripts/unpoly/link.coffee +9 -2
- data/lib/assets/javascripts/unpoly/modal.coffee +1 -0
- data/lib/assets/javascripts/unpoly/popup.coffee +1 -0
- data/lib/assets/javascripts/unpoly/syntax.coffee +48 -29
- data/lib/assets/javascripts/unpoly/util.coffee +12 -5
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/assets/javascripts/integration_test.coffee +4 -0
- data/spec_app/app/controllers/replace_test_controller.rb +5 -0
- data/spec_app/app/views/pages/start.erb +4 -0
- data/spec_app/app/views/replace_test/_nav.erb +6 -0
- data/spec_app/app/views/replace_test/page1.erb +14 -0
- data/spec_app/app/views/replace_test/page2.erb +14 -0
- data/spec_app/config/routes.rb +1 -0
- data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +1 -2
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +69 -1
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +5 -3
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +174 -153
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +77 -19
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +34 -0
- metadata +6 -2
@@ -222,6 +222,8 @@ up.dom = (($) ->
|
|
222
222
|
same layer as the element that triggered the replacement (see `options.origin`).
|
223
223
|
If that element is not known, or no match was found in that layer,
|
224
224
|
Unpoly will search in other layers, starting from the topmost layer.
|
225
|
+
@param {String} [options.failLayer='auto']
|
226
|
+
The name of the layer that ought to be updated if the server sends a non-200 status code.
|
225
227
|
|
226
228
|
@return {Promise}
|
227
229
|
A promise that will be resolved when the page has been updated.
|
@@ -243,6 +245,8 @@ up.dom = (($) ->
|
|
243
245
|
failureOptions = u.merge options,
|
244
246
|
humanizedTarget: 'failure target'
|
245
247
|
provideTarget: undefined # don't provide a target if we're targeting the failTarget
|
248
|
+
u.renameKey(failureOptions, 'failTransition', 'transition')
|
249
|
+
u.renameKey(failureOptions, 'failLayer', 'layer')
|
246
250
|
|
247
251
|
target = bestPreflightSelector(selectorOrElement, successOptions)
|
248
252
|
failTarget = bestPreflightSelector(options.failTarget, failureOptions)
|
@@ -304,8 +308,6 @@ up.dom = (($) ->
|
|
304
308
|
options.history = false unless u.isString(options.history)
|
305
309
|
options.source = 'keep' unless u.isString(options.source)
|
306
310
|
else
|
307
|
-
options.transition = options.failTransition
|
308
|
-
options.failTransition = undefined
|
309
311
|
if isReloadable # e.g. GET returns 500 Internal Server Error
|
310
312
|
options.history = url unless options.history is false
|
311
313
|
options.source = url unless options.source is false
|
@@ -472,14 +474,19 @@ up.dom = (($) ->
|
|
472
474
|
promise = u.resolvedPromise()
|
473
475
|
|
474
476
|
else
|
475
|
-
|
477
|
+
# This needs to happen before prepareClean() below.
|
478
|
+
options.keepPlans = transferKeepableElements($old, $new, options)
|
476
479
|
|
477
|
-
|
480
|
+
# Collect destructor functions before swapping the elements.
|
481
|
+
# Detaching an element from the DOM will cause jQuery to remove the data properties
|
482
|
+
# where we store constructor functions.
|
483
|
+
clean = up.syntax.prepareClean($old)
|
478
484
|
|
479
|
-
|
485
|
+
replacement = ->
|
486
|
+
if isSingletonElement($old)
|
480
487
|
# jQuery will actually let us .insertBefore the new <body> tag,
|
481
488
|
# but that's probably bad Karma.
|
482
|
-
|
489
|
+
swapSingletonElement($old, $new)
|
483
490
|
# We cannot morph the <body> tag
|
484
491
|
transition = false
|
485
492
|
else
|
@@ -502,13 +509,18 @@ up.dom = (($) ->
|
|
502
509
|
|
503
510
|
# Wrap the replacement as a destroy animation, so $old will
|
504
511
|
# get marked as .up-destroying right away.
|
505
|
-
promise = destroy($old, animation: replacement)
|
512
|
+
promise = destroy($old, { clean, animation: replacement })
|
506
513
|
|
507
514
|
promise
|
508
515
|
|
516
|
+
isSingletonElement = ($element) ->
|
517
|
+
$element.is('body')
|
518
|
+
|
509
519
|
# This is a separate method so we can mock it in specs
|
510
|
-
|
511
|
-
|
520
|
+
swapSingletonElement = ($old, $new) ->
|
521
|
+
# jQuery will actually let us .insertBefore the new <body> tag,
|
522
|
+
# but that's probably bad Karma.
|
523
|
+
$old.replaceWith($new)
|
512
524
|
|
513
525
|
transferKeepableElements = ($old, $new, options) ->
|
514
526
|
keepPlans = []
|
@@ -750,6 +762,7 @@ up.dom = (($) ->
|
|
750
762
|
$match = undefined
|
751
763
|
if u.isPresent(origin)
|
752
764
|
originLayer = layerOf(origin)
|
765
|
+
# Make the origin's layer the top priority
|
753
766
|
u.remove(layers, originLayer)
|
754
767
|
layers.unshift(originLayer)
|
755
768
|
for layer in layers
|
@@ -809,6 +822,7 @@ up.dom = (($) ->
|
|
809
822
|
@stable
|
810
823
|
###
|
811
824
|
destroy = (selectorOrElement, options) ->
|
825
|
+
options = u.options(options, animation: false)
|
812
826
|
|
813
827
|
$element = $(selectorOrElement)
|
814
828
|
unless $element.is('.up-placeholder, .up-tooltip, .up-modal, .up-popup')
|
@@ -817,7 +831,6 @@ up.dom = (($) ->
|
|
817
831
|
if $element.length == 0
|
818
832
|
u.resolvedDeferred()
|
819
833
|
else if up.bus.nobodyPrevents('up:fragment:destroy', $element: $element, message: destroyMessage)
|
820
|
-
options = u.options(options, animation: false)
|
821
834
|
animateOptions = up.motion.animateOptions(options)
|
822
835
|
$element.addClass('up-destroying')
|
823
836
|
# If e.g. a modal or popup asks us to restore a URL, do this
|
@@ -829,7 +842,8 @@ up.dom = (($) ->
|
|
829
842
|
up.motion.animate($element, options.animation, animateOptions)
|
830
843
|
|
831
844
|
animationDeferred.then ->
|
832
|
-
up.syntax.clean($element)
|
845
|
+
options.clean ||= -> up.syntax.clean($element)
|
846
|
+
options.clean()
|
833
847
|
# Emit this while $element is still part of the DOM, so event
|
834
848
|
# listeners bound to the document will receive the event.
|
835
849
|
up.emit 'up:fragment:destroyed', $element: $element, message: destroyedMessage
|
@@ -838,7 +852,7 @@ up.dom = (($) ->
|
|
838
852
|
else
|
839
853
|
# Although someone prevented the destruction, keep a uniform API for
|
840
854
|
# callers by returning a Deferred that will never be resolved.
|
841
|
-
|
855
|
+
u.unresolvableDeferred()
|
842
856
|
|
843
857
|
###*
|
844
858
|
Before a page fragment is being [destroyed](/up.destroy), this
|
@@ -108,6 +108,13 @@ up.form = (($) ->
|
|
108
108
|
@param {Object} [options.headers={}]
|
109
109
|
An object of additional header key/value pairs to send along
|
110
110
|
with the request.
|
111
|
+
@param {String} [options.layer='auto']
|
112
|
+
The name of the layer that ought to be updated. Valid values are
|
113
|
+
`auto`, `page`, `modal` and `popup`.
|
114
|
+
|
115
|
+
If set to `auto` (default), Unpoly will try to find a match in the form's layer.
|
116
|
+
@param {String} [options.failLayer='auto']
|
117
|
+
The name of the layer that ought to be updated if the server sends a non-200 status code.
|
111
118
|
@return {Promise}
|
112
119
|
A promise for the successful form submission.
|
113
120
|
@stable
|
@@ -131,6 +138,7 @@ up.form = (($) ->
|
|
131
138
|
options.restoreScroll = u.option(options.restoreScroll, u.castedAttr($form, 'up-restore-scroll'))
|
132
139
|
options.origin = u.option(options.origin, $form)
|
133
140
|
options.layer = u.option(options.layer, $form.attr('up-layer'), 'auto')
|
141
|
+
options.failLayer = u.option(options.failLayer, $form.attr('up-fail-layer'), 'auto')
|
134
142
|
options.data = u.requestDataFromForm($form)
|
135
143
|
options = u.merge(options, up.motion.animateOptions(options, $form))
|
136
144
|
|
@@ -445,7 +453,7 @@ up.form = (($) ->
|
|
445
453
|
if switcher
|
446
454
|
$(switcher)
|
447
455
|
else
|
448
|
-
u.fail('Could not find [up-switch] field for %o', $
|
456
|
+
u.fail('Could not find [up-switch] field for %o', $target.get(0))
|
449
457
|
|
450
458
|
###*
|
451
459
|
Forms with an `up-target` attribute are [submitted via AJAX](/up.submit)
|
@@ -539,6 +547,16 @@ up.form = (($) ->
|
|
539
547
|
Alternately you can use an attribute `data-method`
|
540
548
|
([Rails UJS](https://github.com/rails/jquery-ujs/wiki/Unobtrusive-scripting-support-for-jQuery))
|
541
549
|
or `method` (vanilla HTML) for the same purpose.
|
550
|
+
@param {String} [up-layer='auto']
|
551
|
+
The name of the layer that ought to be updated. Valid values are
|
552
|
+
`auto`, `page`, `modal` and `popup`.
|
553
|
+
|
554
|
+
If set to `auto` (default), Unpoly will try to find a match in the form's layer.
|
555
|
+
If no match was found in that layer,
|
556
|
+
Unpoly will search in other layers, starting from the topmost layer.
|
557
|
+
@param {String} [up-fail-layer='auto']
|
558
|
+
The name of the layer that ought to be updated if the server sends a
|
559
|
+
non-200 status code.
|
542
560
|
@param {String} [up-reveal='true']
|
543
561
|
Whether to reveal the target element within its viewport before updating.
|
544
562
|
@param {String} [up-restore-scroll='false']
|
@@ -432,7 +432,7 @@ up.layout = (($) ->
|
|
432
432
|
else
|
433
433
|
$viewports = viewports()
|
434
434
|
|
435
|
-
scrollTopsForUrl = lastScrollTops.get(url)
|
435
|
+
scrollTopsForUrl = lastScrollTops.get(url) || {}
|
436
436
|
|
437
437
|
up.log.group 'Restoring scroll positions for URL %s to %o', url, scrollTopsForUrl, ->
|
438
438
|
$viewports.each ->
|
@@ -157,6 +157,9 @@ up.link = (($) ->
|
|
157
157
|
If set to `auto` (default), Unpoly will try to find a match in the
|
158
158
|
same layer as the given link. If no match was found in that layer,
|
159
159
|
Unpoly will search in other layers, starting from the topmost layer.
|
160
|
+
@param {String} [options.failLayer='auto']
|
161
|
+
The name of the layer that ought to be updated if the server sends a non-200 status code.
|
162
|
+
|
160
163
|
@return {Promise}
|
161
164
|
A promise that will be resolved when the link destination
|
162
165
|
has been loaded and rendered.
|
@@ -180,6 +183,7 @@ up.link = (($) ->
|
|
180
183
|
options.method = followMethod($link, options)
|
181
184
|
options.origin = u.option(options.origin, $link)
|
182
185
|
options.layer = u.option(options.layer, $link.attr('up-layer'), 'auto')
|
186
|
+
options.failLayer = u.option(options.failLayer, $link.attr('up-fail-layer'), 'auto')
|
183
187
|
options.confirm = u.option(options.confirm, $link.attr('up-confirm'))
|
184
188
|
options = u.merge(options, up.motion.animateOptions(options, $link))
|
185
189
|
|
@@ -357,9 +361,12 @@ up.link = (($) ->
|
|
357
361
|
The name of the layer that ought to be updated. Valid values are
|
358
362
|
`auto`, `page`, `modal` and `popup`.
|
359
363
|
|
360
|
-
If set to `auto` (default), Unpoly will try to find a match in the
|
361
|
-
|
364
|
+
If set to `auto` (default), Unpoly will try to find a match in the link's layer.
|
365
|
+
If no match was found in that layer,
|
362
366
|
Unpoly will search in other layers, starting from the topmost layer.
|
367
|
+
@param {String} [up-fail-layer='auto']
|
368
|
+
The name of the layer that ought to be updated if the server sends a
|
369
|
+
non-200 status code.
|
363
370
|
@param [up-history]
|
364
371
|
Whether to push an entry to the browser history when following the link.
|
365
372
|
|
@@ -451,6 +451,7 @@ up.modal = (($) ->
|
|
451
451
|
options.confirm = u.option(options.confirm, $link.attr('up-confirm'))
|
452
452
|
options.method = up.link.followMethod($link, options)
|
453
453
|
options.layer = 'modal'
|
454
|
+
options.failLayer = u.option(options.failLayer, $link.attr('up-fail-layer'), 'auto')
|
454
455
|
animateOptions = up.motion.animateOptions(options, $link, duration: flavorDefault('openDuration', options.flavor), easing: flavorDefault('openEasing', options.flavor))
|
455
456
|
|
456
457
|
# Although we usually fall back to full page loads if a browser doesn't support pushState,
|
@@ -246,6 +246,7 @@ up.popup = (($) ->
|
|
246
246
|
options.confirm = u.option(options.confirm, $anchor.attr('up-confirm'))
|
247
247
|
options.method = up.link.followMethod($anchor, options)
|
248
248
|
options.layer = 'popup'
|
249
|
+
options.failLayer = u.option(options.failLayer, $anchor.attr('up-fail-layer'), 'auto')
|
249
250
|
animateOptions = up.motion.animateOptions(options, $anchor, duration: config.openDuration, easing: config.openEasing)
|
250
251
|
|
251
252
|
up.browser.whenConfirmed(options).then ->
|
@@ -129,10 +129,10 @@ up.syntax = (($) ->
|
|
129
129
|
For instance, a container for a [Google Map](https://developers.google.com/maps/documentation/javascript/tutorial)
|
130
130
|
might attach the location and names of its marker pins:
|
131
131
|
|
132
|
-
<div class=
|
133
|
-
{ lat: 48.36, lng: 10.99, title:
|
134
|
-
{ lat: 48.75, lng: 11.45, title:
|
135
|
-
]
|
132
|
+
<div class='google-map' up-data='[
|
133
|
+
{ "lat": 48.36, "lng": 10.99, "title": "Friedberg" },
|
134
|
+
{ "lat": 48.75, "lng": 11.45, "title": "Ingolstadt" }
|
135
|
+
]'></div>
|
136
136
|
|
137
137
|
The JSON will parsed and handed to your compiler as a second argument:
|
138
138
|
|
@@ -282,22 +282,32 @@ up.syntax = (($) ->
|
|
282
282
|
value = if u.isString(compiler.keep) then compiler.keep else ''
|
283
283
|
$jqueryElement.attr('up-keep', value)
|
284
284
|
returnValue = compiler.callback.apply(nativeElement, [$jqueryElement, data($jqueryElement)])
|
285
|
-
|
286
|
-
addDestructor($jqueryElement, destructor)
|
285
|
+
addDestructor($jqueryElement, returnValue)
|
287
286
|
|
288
|
-
|
287
|
+
###*
|
288
|
+
Tries to find a list of destructors in a compiler's return value.
|
289
|
+
|
290
|
+
@param {Object} returnValue
|
291
|
+
@return {Function|undefined}
|
292
|
+
@internal
|
293
|
+
###
|
294
|
+
normalizeDestructor = (returnValue) ->
|
289
295
|
if u.isFunction(returnValue)
|
290
|
-
[returnValue]
|
291
|
-
else if u.isArray(returnValue) && u.all(returnValue, u.isFunction)
|
292
296
|
returnValue
|
293
|
-
else
|
294
|
-
|
297
|
+
else if u.isArray(returnValue) && u.all(returnValue, u.isFunction)
|
298
|
+
u.sequence(returnValue...)
|
299
|
+
|
300
|
+
addDestructor = ($element, newDestructor) ->
|
301
|
+
if newDestructor = normalizeDestructor(newDestructor)
|
302
|
+
$element.addClass(DESTRUCTIBLE_CLASS)
|
303
|
+
# The initial destructor function is a function that removes the destructor class and data.
|
304
|
+
elementDestructor = $element.data(DESTRUCTORS_KEY) || -> removeDestructors($element)
|
305
|
+
elementDestructor = u.sequence(elementDestructor, newDestructor)
|
306
|
+
$element.data(DESTRUCTORS_KEY, elementDestructor)
|
295
307
|
|
296
|
-
|
297
|
-
$
|
298
|
-
|
299
|
-
destructors.push(destructor)
|
300
|
-
$jqueryElement.data(DESTRUCTORS_KEY, destructors)
|
308
|
+
removeDestructors = ($element) ->
|
309
|
+
$element.removeData(DESTRUCTORS_KEY)
|
310
|
+
$element.removeClass(DESTRUCTIBLE_CLASS)
|
301
311
|
|
302
312
|
###*
|
303
313
|
Applies all compilers on the given element and its descendants.
|
@@ -317,10 +327,11 @@ up.syntax = (($) ->
|
|
317
327
|
for compiler in queue
|
318
328
|
$matches = u.findWithSelf($fragment, compiler.selector)
|
319
329
|
|
330
|
+
# Exclude all elements that are descendants of the subtrees we want to keep.
|
320
331
|
$matches = $matches.filter ->
|
321
332
|
$match = $(this)
|
322
|
-
u.all $skipSubtrees, (
|
323
|
-
$match.closest(
|
333
|
+
u.all $skipSubtrees, (skipSubtree) ->
|
334
|
+
$match.closest(skipSubtree).length == 0
|
324
335
|
|
325
336
|
if $matches.length
|
326
337
|
up.log.group ("Compiling '%s' on %d element(s)" unless compiler.isDefault), compiler.selector, $matches.length, ->
|
@@ -338,16 +349,23 @@ up.syntax = (($) ->
|
|
338
349
|
@internal
|
339
350
|
###
|
340
351
|
clean = ($fragment) ->
|
352
|
+
prepareClean($fragment)()
|
353
|
+
|
354
|
+
###*
|
355
|
+
@function up.syntax.prepareClean
|
356
|
+
@param {jQuery} $fragment
|
357
|
+
@return {Function}
|
358
|
+
@internal
|
359
|
+
###
|
360
|
+
prepareClean = ($fragment) ->
|
361
|
+
destructors = []
|
341
362
|
u.findWithSelf($fragment, ".#{DESTRUCTIBLE_CLASS}").each ->
|
342
|
-
$element = $(this)
|
343
|
-
destructors = $element.data(DESTRUCTORS_KEY)
|
344
363
|
# Although destructible elements should always have an array of destructors, we might be
|
345
364
|
# destroying a clone of such an element. E.g. Unpoly creates a clone when keeping an
|
346
365
|
# [up-keep] element, and that clone still has the .up-destructible class.
|
347
|
-
if
|
348
|
-
destructor
|
349
|
-
|
350
|
-
$element.removeClass(DESTRUCTIBLE_CLASS)
|
366
|
+
if destructor = $(this).data(DESTRUCTORS_KEY)
|
367
|
+
destructors.push(destructor)
|
368
|
+
u.sequence(destructors...)
|
351
369
|
|
352
370
|
###*
|
353
371
|
Checks if the given element has an [`up-data`](/up-data) attribute.
|
@@ -359,7 +377,7 @@ up.syntax = (($) ->
|
|
359
377
|
|
360
378
|
You have an element with JSON data serialized into an `up-data` attribute:
|
361
379
|
|
362
|
-
<span class=
|
380
|
+
<span class='person' up-data='{ "age": 18, "name": "Bob" }'>Bob</span>
|
363
381
|
|
364
382
|
Calling `up.syntax.data()` will deserialize the JSON string into a JavaScript object:
|
365
383
|
|
@@ -382,10 +400,10 @@ up.syntax = (($) ->
|
|
382
400
|
For instance, a container for a [Google Map](https://developers.google.com/maps/documentation/javascript/tutorial)
|
383
401
|
might attach the location and names of its marker pins:
|
384
402
|
|
385
|
-
<div class=
|
386
|
-
{ lat: 48.36, lng: 10.99, title:
|
387
|
-
{ lat: 48.75, lng: 11.45, title:
|
388
|
-
]
|
403
|
+
<div class='google-map' up-data='[
|
404
|
+
{ "lat": 48.36, "lng": 10.99, "title": "Friedberg" },
|
405
|
+
{ "lat": 48.75, "lng": 11.45, "title": "Ingolstadt" }
|
406
|
+
]'></div>
|
389
407
|
|
390
408
|
The JSON will parsed and handed to your compiler as a second argument:
|
391
409
|
|
@@ -454,6 +472,7 @@ up.syntax = (($) ->
|
|
454
472
|
macro: macro
|
455
473
|
compile: compile
|
456
474
|
clean: clean
|
475
|
+
prepareClean: prepareClean
|
457
476
|
data: data
|
458
477
|
|
459
478
|
)(jQuery)
|
@@ -28,14 +28,14 @@ up.util = (($) ->
|
|
28
28
|
@internal
|
29
29
|
###
|
30
30
|
memoize = (func) ->
|
31
|
-
|
31
|
+
cachedValue = undefined
|
32
32
|
cached = false
|
33
33
|
(args...) ->
|
34
34
|
if cached
|
35
|
-
|
35
|
+
cachedValue
|
36
36
|
else
|
37
37
|
cached = true
|
38
|
-
|
38
|
+
cachedValue = func(args...)
|
39
39
|
|
40
40
|
###*
|
41
41
|
Returns if the given port is the default port for the given protocol.
|
@@ -1775,6 +1775,9 @@ up.util = (($) ->
|
|
1775
1775
|
delete object[key]
|
1776
1776
|
value
|
1777
1777
|
|
1778
|
+
renameKey = (object, oldKey, newKey) ->
|
1779
|
+
object[newKey] = pluckKey(object, oldKey)
|
1780
|
+
|
1778
1781
|
pluckData = (elementOrSelector, key) ->
|
1779
1782
|
$element = $(elementOrSelector)
|
1780
1783
|
value = $element.data(key)
|
@@ -1893,6 +1896,10 @@ up.util = (($) ->
|
|
1893
1896
|
|
1894
1897
|
###*
|
1895
1898
|
@function up.util.sequence
|
1899
|
+
@param {Array<Function>} functions...
|
1900
|
+
@return {Function}
|
1901
|
+
A function that will call all `functions` if called.
|
1902
|
+
|
1896
1903
|
@internal
|
1897
1904
|
###
|
1898
1905
|
sequence = (functions...) ->
|
@@ -1940,7 +1947,7 @@ up.util = (($) ->
|
|
1940
1947
|
###*
|
1941
1948
|
Like `$old.replaceWith($new)`, but keeps event handlers bound to `$old`.
|
1942
1949
|
|
1943
|
-
Note that this is a memory leak unless you re-attach `$
|
1950
|
+
Note that this is a memory leak unless you re-attach `$old` to the DOM aferwards.
|
1944
1951
|
|
1945
1952
|
@function up.util.detachWith
|
1946
1953
|
@internal
|
@@ -1974,7 +1981,6 @@ up.util = (($) ->
|
|
1974
1981
|
isTruthy = (object) ->
|
1975
1982
|
!!object
|
1976
1983
|
|
1977
|
-
isDetached: isDetached
|
1978
1984
|
requestDataAsArray: requestDataAsArray
|
1979
1985
|
requestDataAsQuery: requestDataAsQuery
|
1980
1986
|
appendRequestData: appendRequestData
|
@@ -2071,6 +2077,7 @@ up.util = (($) ->
|
|
2071
2077
|
error: fail
|
2072
2078
|
pluckData: pluckData
|
2073
2079
|
pluckKey: pluckKey
|
2080
|
+
renameKey: renameKey
|
2074
2081
|
extractOptions: extractOptions
|
2075
2082
|
isDetached: isDetached
|
2076
2083
|
noop: noop
|
data/lib/unpoly/rails/version.rb
CHANGED
data/package.json
CHANGED
data/spec_app/Gemfile.lock
CHANGED
@@ -0,0 +1,6 @@
|
|
1
|
+
<ul>
|
2
|
+
<li><a href="page1" up-target="body">Link to page1, replacing <code>body</code></a></li>
|
3
|
+
<li><a href="page1" up-target=".content">Link to page1, replacing <code>.content</code></a></li>
|
4
|
+
<li><a href="page2" up-target="body">Link to page2, replacing <code>body</code></a></li>
|
5
|
+
<li><a href="page2" up-target=".content">Link to page2, replacing <code>.content</code></a></li>
|
6
|
+
</ul>
|
data/spec_app/config/routes.rb
CHANGED
@@ -7,6 +7,7 @@ Rails.application.routes.draw do
|
|
7
7
|
get 'css_test/:action', controller: 'css_test'
|
8
8
|
get 'error_test/:action', controller: 'error_test'
|
9
9
|
post 'error_test/:action', controller: 'error_test'
|
10
|
+
get 'replace_test/:action', controller: 'replace_test'
|
10
11
|
|
11
12
|
namespace :form_test do
|
12
13
|
resource :basic, only: [:new, :create]
|
@@ -3,6 +3,5 @@ beforeEach ->
|
|
3
3
|
@titleBeforeExample = document.title
|
4
4
|
|
5
5
|
afterEach ->
|
6
|
-
|
7
|
-
history.replaceState?({ fromResetPathHelper: true }, @titleBeforeExample, @hrefBeforeExample)
|
6
|
+
history.replaceState?({ fromResetPathHelper: true }, @titleBeforeExample, @hrefBeforeExample)
|
8
7
|
document.title = @titleBeforeExample
|
@@ -47,6 +47,30 @@ describe 'up.dom', ->
|
|
47
47
|
expect(resolution).toHaveBeenCalled()
|
48
48
|
expect($('.middle')).toHaveText('new-middle')
|
49
49
|
|
50
|
+
describe 'cleaning up', ->
|
51
|
+
|
52
|
+
it 'calls destructors on the replaced element', ->
|
53
|
+
destructor = jasmine.createSpy('destructor')
|
54
|
+
up.compiler '.container', -> destructor
|
55
|
+
$container = affix('.container')
|
56
|
+
up.hello($container)
|
57
|
+
up.replace('.container', '/path')
|
58
|
+
@respondWith '<div class="container">new text</div>'
|
59
|
+
expect('.container').toHaveText('new text')
|
60
|
+
expect(destructor).toHaveBeenCalled()
|
61
|
+
|
62
|
+
it 'calls destructors when the replaced element is a singleton element like <body> (bugfix)', ->
|
63
|
+
# isSingletonElement() is true for body, but can't have the example replace the Jasmine test runner UI
|
64
|
+
up.dom.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
|
65
|
+
destructor = jasmine.createSpy('destructor')
|
66
|
+
up.compiler '.container', -> destructor
|
67
|
+
$container = affix('.container')
|
68
|
+
up.hello($container)
|
69
|
+
up.replace('.container', '/path')
|
70
|
+
@respondWith '<div class="container">new text</div>'
|
71
|
+
expect('.container').toHaveText('new text')
|
72
|
+
expect(destructor).toHaveBeenCalled()
|
73
|
+
|
50
74
|
describe 'transitions', ->
|
51
75
|
|
52
76
|
it 'returns a promise that will be resolved once the server response was received and the swap transition has completed', (done) ->
|
@@ -64,7 +88,7 @@ describe 'up.dom', ->
|
|
64
88
|
done()
|
65
89
|
|
66
90
|
it 'ignores a { transition } option when replacing the body element', (done) ->
|
67
|
-
up.dom.knife.mock('
|
91
|
+
up.dom.knife.mock('swapSingletonElement') # can't have the example replace the Jasmine test runner UI
|
68
92
|
up.dom.knife.mock('destroy') # if we don't swap the body, up.dom will destroy it
|
69
93
|
replaceCallback = jasmine.createSpy()
|
70
94
|
promise = up.replace('body', '/path', transition: 'cross-fade', duration: 50)
|
@@ -1201,6 +1225,50 @@ describe 'up.dom', ->
|
|
1201
1225
|
Trigger.click($keeper)
|
1202
1226
|
expect(handler).toHaveBeenCalled()
|
1203
1227
|
|
1228
|
+
it 'does not call destructors on a kept alement', ->
|
1229
|
+
handler = jasmine.createSpy('event handler')
|
1230
|
+
destructor = jasmine.createSpy('destructor')
|
1231
|
+
up.compiler '.keeper', ($keeper) ->
|
1232
|
+
return destructor
|
1233
|
+
|
1234
|
+
$container = affix('.container')
|
1235
|
+
$container.html """
|
1236
|
+
<div class="keeper" up-keep>old-text</div>
|
1237
|
+
"""
|
1238
|
+
up.hello($container)
|
1239
|
+
|
1240
|
+
up.extract '.container', """
|
1241
|
+
<div class='container'>
|
1242
|
+
<div class="keeper" up-keep>new-text</div>
|
1243
|
+
</div>
|
1244
|
+
"""
|
1245
|
+
|
1246
|
+
$keeper = $('.keeper')
|
1247
|
+
expect($keeper).toHaveText('old-text')
|
1248
|
+
expect(destructor).not.toHaveBeenCalled()
|
1249
|
+
|
1250
|
+
it 'calls destructors when a kept element is eventually removed from the DOM', ->
|
1251
|
+
handler = jasmine.createSpy('event handler')
|
1252
|
+
destructor = jasmine.createSpy('destructor')
|
1253
|
+
up.compiler '.keeper', ($keeper) ->
|
1254
|
+
return destructor
|
1255
|
+
|
1256
|
+
$container = affix('.container')
|
1257
|
+
$container.html """
|
1258
|
+
<div class="keeper" up-keep>old-text</div>
|
1259
|
+
"""
|
1260
|
+
up.hello($container)
|
1261
|
+
|
1262
|
+
up.extract '.container', """
|
1263
|
+
<div class='container'>
|
1264
|
+
<div class="keeper">new-text</div>
|
1265
|
+
</div>
|
1266
|
+
"""
|
1267
|
+
|
1268
|
+
$keeper = $('.keeper')
|
1269
|
+
expect($keeper).toHaveText('new-text')
|
1270
|
+
expect(destructor).toHaveBeenCalled()
|
1271
|
+
|
1204
1272
|
it 'lets listeners cancel the keeping by preventing default on an up:fragment:keep event', ->
|
1205
1273
|
$keeper = affix('.keeper[up-keep]').text('old-inside')
|
1206
1274
|
$keeper.on 'up:fragment:keep', (event) -> event.preventDefault()
|
@@ -151,13 +151,15 @@ describe 'up.feedback', ->
|
|
151
151
|
@respondWith('<div class="main">new-text</div>')
|
152
152
|
expect($area).not.toHaveClass('up-active')
|
153
153
|
|
154
|
-
it 'marks clicked modal openers as .up-active while the modal is loading', ->
|
154
|
+
it 'marks clicked modal openers as .up-active while the modal is loading', (done) ->
|
155
155
|
$link = affix('a[href="/foo"][up-modal=".main"]')
|
156
156
|
affix('.main')
|
157
157
|
Trigger.clickSequence($link)
|
158
158
|
expect($link).toHaveClass('up-active')
|
159
|
-
|
160
|
-
|
159
|
+
u.nextFrame =>
|
160
|
+
@respondWith('<div class="main">new-text</div>')
|
161
|
+
expect($link).not.toHaveClass('up-active')
|
162
|
+
done()
|
161
163
|
|
162
164
|
it 'removes .up-active from a clicked modal opener if the target is already preloaded (bugfix)', ->
|
163
165
|
$link = affix('a[href="/foo"][up-modal=".main"]')
|