unpoly-rails 0.56.7 → 0.57.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.

Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +74 -1
  3. data/dist/unpoly.js +1569 -793
  4. data/dist/unpoly.min.js +4 -4
  5. data/lib/assets/javascripts/unpoly.coffee +2 -0
  6. data/lib/assets/javascripts/unpoly/browser.coffee.erb +25 -41
  7. data/lib/assets/javascripts/unpoly/bus.coffee.erb +20 -6
  8. data/lib/assets/javascripts/unpoly/classes/cache.coffee +23 -13
  9. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +87 -0
  10. data/lib/assets/javascripts/unpoly/classes/focus_tracker.coffee +29 -0
  11. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +7 -4
  12. data/lib/assets/javascripts/unpoly/classes/record.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/request.coffee +38 -45
  14. data/lib/assets/javascripts/unpoly/classes/response.coffee +16 -1
  15. data/lib/assets/javascripts/unpoly/classes/store/memory.coffee +26 -0
  16. data/lib/assets/javascripts/unpoly/classes/store/session.coffee +59 -0
  17. data/lib/assets/javascripts/unpoly/cookie.coffee +56 -0
  18. data/lib/assets/javascripts/unpoly/dom.coffee.erb +67 -39
  19. data/lib/assets/javascripts/unpoly/feedback.coffee +2 -2
  20. data/lib/assets/javascripts/unpoly/form.coffee.erb +23 -12
  21. data/lib/assets/javascripts/unpoly/history.coffee +2 -2
  22. data/lib/assets/javascripts/unpoly/layout.coffee.erb +118 -99
  23. data/lib/assets/javascripts/unpoly/link.coffee.erb +12 -5
  24. data/lib/assets/javascripts/unpoly/log.coffee +6 -5
  25. data/lib/assets/javascripts/unpoly/modal.coffee.erb +9 -2
  26. data/lib/assets/javascripts/unpoly/motion.coffee.erb +2 -6
  27. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +2 -2
  28. data/lib/assets/javascripts/unpoly/params.coffee.erb +522 -0
  29. data/lib/assets/javascripts/unpoly/popup.coffee.erb +3 -3
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +42 -34
  31. data/lib/assets/javascripts/unpoly/{syntax.coffee → syntax.coffee.erb} +59 -117
  32. data/lib/assets/javascripts/unpoly/{util.coffee → util.coffee.erb} +206 -171
  33. data/lib/unpoly/rails/version.rb +1 -1
  34. data/package.json +1 -1
  35. data/spec_app/Gemfile.lock +1 -1
  36. data/spec_app/app/assets/javascripts/integration_test.coffee +0 -4
  37. data/spec_app/app/assets/stylesheets/integration_test.sass +7 -1
  38. data/spec_app/app/controllers/pages_controller.rb +4 -0
  39. data/spec_app/app/views/form_test/basics/new.erb +34 -5
  40. data/spec_app/app/views/form_test/submission_result.erb +2 -2
  41. data/spec_app/app/views/form_test/uploads/new.erb +15 -2
  42. data/spec_app/app/views/hash_test/unpoly.erb +30 -0
  43. data/spec_app/app/views/pages/start.erb +2 -1
  44. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +17 -2
  45. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +5 -0
  46. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +1 -1
  47. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +5 -0
  48. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +8 -8
  49. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +58 -20
  50. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +78 -0
  51. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +31 -0
  52. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +50 -0
  53. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +67 -0
  54. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +113 -0
  55. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +133 -45
  56. data/spec_app/spec/javascripts/up/form_spec.js.coffee +13 -13
  57. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +110 -26
  58. data/spec_app/spec/javascripts/up/link_spec.js.coffee +1 -1
  59. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +1 -0
  60. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +52 -51
  61. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +2 -2
  62. data/spec_app/spec/javascripts/up/params_spec.coffee +768 -0
  63. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +75 -36
  64. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +48 -15
  65. data/spec_app/spec/javascripts/up/util_spec.js.coffee +148 -131
  66. metadata +17 -5
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
@@ -0,0 +1,31 @@
1
+ #describe 'up.FocusTracker', ->
2
+ #
3
+ # describe '#lastField', ->
4
+ #
5
+ # it 'returns undefined if no field is focused', ->
6
+ # tracker = new up.FocusTracker()
7
+ # expect(tracker.lastField()).toBeUndefined()
8
+ #
9
+ # it 'returns a <input type="text"> after it was focused', ->
10
+ # tracker = new up.FocusTracker()
11
+ # $form = affix('form')
12
+ # $input = $form.affix('input[type=text]')
13
+ # $input.focus()
14
+ # expect(tracker.lastField()).toEqual($input[0])
15
+ #
16
+ # it 'returns a <select> after it was focused', ->
17
+ # tracker = new up.FocusTracker()
18
+ # $form = affix('form')
19
+ # $select = $form.affix('select')
20
+ # $option = $select.affix('option')
21
+ # $select.focus()
22
+ # expect(tracker.lastField()).toEqual($select[0])
23
+ #
24
+ # it 'returns the field that was most recently focused after a series of focus/blur'
25
+ #
26
+ # it 'returns undefined after a field was focused, but then detached'
27
+ #
28
+ # it 'returns a previously focused field for some time after it was blurred, so we can retrieve the last field after the user submits'
29
+ #
30
+ # it 'returns undefined after a field was blurred and some time has passed'
31
+ #
@@ -0,0 +1,50 @@
1
+ describe 'up.Request', ->
2
+
3
+ describe '#url', ->
4
+
5
+ it 'returns the given URL', ->
6
+ request = new up.Request(url: 'http://host.com/foo')
7
+ expect(request.url).toEqual('http://host.com/foo')
8
+
9
+ it 'does not include a hash anchor of the constructed URL', ->
10
+ request = new up.Request(url: 'http://host.com/foo#hash')
11
+ expect(request.url).toEqual('http://host.com/foo')
12
+
13
+ it "merges { params } for HTTP methods that don't allow a payload", ->
14
+ request = new up.Request(url: 'http://host.com/foo?urlKey=urlValue', params: { paramsKey: 'paramsValue' }, method: 'get')
15
+ expect(request.url).toEqual('http://host.com/foo?urlKey=urlValue&paramsKey=paramsValue')
16
+
17
+ it 'excludes { params } for HTTP methods that allow a payload', ->
18
+ request = new up.Request(url: 'http://host.com/foo?key=value', method: 'post')
19
+ expect(request.url).toEqual('http://host.com/foo')
20
+
21
+ describe '#method', ->
22
+
23
+ it 'defaults to "GET"', ->
24
+ request = new up.Request(url: 'http://host.com/foo')
25
+ expect(request.method).toEqual('GET')
26
+
27
+ describe '#hash', ->
28
+
29
+ it 'returns the hash anchor from the constructed URL', ->
30
+ request = new up.Request(url: 'http://host.com/foo#hash')
31
+ expect(request.hash).toEqual('#hash')
32
+
33
+ it 'returns undefined if the constructed URL had no hash anchor', ->
34
+ request = new up.Request(url: 'http://host.com/foo')
35
+ expect(request.hash).toBeUndefined()
36
+
37
+ describe '#params', ->
38
+
39
+ it 'returns the constructed params for HTTP methods that allow a payload', ->
40
+ params = { key: 'value' }
41
+ request = new up.Request(url: 'http://host.com/foo', params: params, method: 'post')
42
+ expect(request.params).toEqual(params)
43
+
44
+ it "returns undefined for HTTP methods that don't allow a payload", ->
45
+ request = new up.Request(url: 'http://host.com/foo', params: { key: 'value' }, method: 'get')
46
+ expect(request.params).toBeUndefined()
47
+
48
+ it 'returns the merged { params } and params from the URL for HTTP methods that allow a payload', ->
49
+ request = new up.Request(url: 'http://host.com/foo?urlKey=urlValue', params: { paramsKey: 'paramsValue' }, method: 'post')
50
+ expect(request.params).toEqual(paramsKey: 'paramsValue', urlKey: 'urlValue')
@@ -0,0 +1,67 @@
1
+ describe 'up.store.Memory', ->
2
+
3
+ describe '#get', ->
4
+
5
+ it 'returns an item that was previously set', ->
6
+ store = new up.store.Memory()
7
+ store.set('foo', 'value of foo')
8
+ store.set('bar', 'value of bar')
9
+
10
+ expect(store.get('foo')).toEqual('value of foo')
11
+ expect(store.get('bar')).toEqual('value of bar')
12
+
13
+ it 'returns undefined if no item with that key was set', ->
14
+ store = new up.store.Memory()
15
+ store.set('foo', 'value of foo')
16
+
17
+ expect(store.get('bar')).toBeUndefined()
18
+
19
+ describe '#keys', ->
20
+
21
+ it 'returns an array of keys in the store', ->
22
+ store = new up.store.Memory()
23
+ store.set('foo', 'value of foo')
24
+ store.set('bar', 'value of bar')
25
+
26
+ expect(store.keys().sort()).toEqual ['bar', 'foo']
27
+
28
+ it 'does not return keys for entries that were removed (bugfix)', ->
29
+ store = new up.store.Memory()
30
+ store.set('foo', 'value of foo')
31
+ store.set('bar', 'value of bar')
32
+ store.remove('bar')
33
+
34
+ expect(store.keys().sort()).toEqual ['foo']
35
+
36
+ describe '#values', ->
37
+
38
+ it 'returns an array of values in the store', ->
39
+ store = new up.store.Memory()
40
+ store.set('foo', 'value of foo')
41
+ store.set('bar', 'value of bar')
42
+
43
+ expect(store.values().sort()).toEqual ['value of bar', 'value of foo']
44
+
45
+ describe '#clear', ->
46
+
47
+ it 'removes all keys from the store', ->
48
+ store = new up.store.Memory()
49
+ store.set('foo', 'value of foo')
50
+ store.set('bar', 'value of bar')
51
+
52
+ store.clear()
53
+
54
+ expect(store.get('foo')).toBeUndefined()
55
+ expect(store.get('bar')).toBeUndefined()
56
+
57
+ describe '#remove', ->
58
+
59
+ it 'removes the given key from the store', ->
60
+ store = new up.store.Memory()
61
+ store.set('foo', 'value of foo')
62
+ store.set('bar', 'value of bar')
63
+
64
+ store.remove('foo')
65
+
66
+ expect(store.get('foo')).toBeUndefined()
67
+ expect(store.get('bar')).toEqual('value of bar')
@@ -0,0 +1,113 @@
1
+ describe 'up.store.Session', ->
2
+
3
+ u = up.util
4
+
5
+ afterEach ->
6
+ sessionStorage.removeItem('spec')
7
+
8
+ describe '#get', ->
9
+
10
+ it 'returns an item that was previously set', ->
11
+ store = new up.store.Session('spec')
12
+ store.set('foo', 'value of foo')
13
+ store.set('bar', 'value of bar')
14
+
15
+ expect(store.get('foo')).toEqual('value of foo')
16
+ expect(store.get('bar')).toEqual('value of bar')
17
+
18
+ it 'returns undefined if no item with that key was set', ->
19
+ store = new up.store.Session('spec')
20
+ store.set('foo', 'value of foo')
21
+
22
+ expect(store.get('bar')).toBeUndefined()
23
+
24
+ it 'does not read keys from a store with anotther root key', ->
25
+ store = new up.store.Session('spec.v1')
26
+ store.set('foo', 'value of foo')
27
+ expect(store.get('foo')).toEqual('value of foo')
28
+
29
+ store = new up.store.Session('spec.v2')
30
+ expect(store.get('foo')).toBeUndefined()
31
+
32
+ describe '#set', ->
33
+
34
+ it 'stores the given item in window.sessionStorage where it survives a follow without Unpoly', ->
35
+ store = new up.store.Session('spec')
36
+ store.set('foo', 'value of foo')
37
+
38
+ expect(window.sessionStorage.getItem('spec')).toContain('foo')
39
+
40
+ it 'stores boolean values across sessions', ->
41
+ store1 = new up.store.Session('spec')
42
+ store1.set('foo', true)
43
+ store1.set('bar', false)
44
+
45
+ store2 = new up.store.Session('spec')
46
+ expect(store2.get('foo')).toEqual(true)
47
+ expect(store2.get('bar')).toEqual(false)
48
+
49
+ it 'stores number values across sessions', ->
50
+ store1 = new up.store.Session('spec')
51
+ store1.set('foo', 123)
52
+
53
+ store2 = new up.store.Session('spec')
54
+ expect(store2.get('foo')).toEqual(123)
55
+
56
+ it 'stores structured values across sessions', ->
57
+ store1 = new up.store.Session('spec')
58
+ store1.set('foo', { bar: ['baz', 'bam'] })
59
+
60
+ store2 = new up.store.Session('spec')
61
+ storedValue = store2.get('foo')
62
+ expect(u.isObject(storedValue)).toBe(true)
63
+ expect(storedValue).toEqual { bar: ['baz', 'bam'] }
64
+
65
+ describe '#keys', ->
66
+
67
+ it 'returns an array of keys in the store', ->
68
+ store = new up.store.Session('spec')
69
+ store.set('foo', 'value of foo')
70
+ store.set('bar', 'value of bar')
71
+
72
+ expect(store.keys().sort()).toEqual ['bar', 'foo']
73
+
74
+ it 'does not return keys for entries that were removed (bugfix)', ->
75
+ store = new up.store.Session('spec')
76
+ store.set('foo', 'value of foo')
77
+ store.set('bar', 'value of bar')
78
+ store.remove('bar')
79
+
80
+ expect(store.keys().sort()).toEqual ['foo']
81
+
82
+ describe '#values', ->
83
+
84
+ it 'returns an array of values in the store', ->
85
+ store = new up.store.Session('spec')
86
+ store.set('foo', 'value of foo')
87
+ store.set('bar', 'value of bar')
88
+
89
+ expect(store.values().sort()).toEqual ['value of bar', 'value of foo']
90
+
91
+ describe '#clear', ->
92
+
93
+ it 'removes all keys from the store', ->
94
+ store = new up.store.Session('spec')
95
+ store.set('foo', 'value of foo')
96
+ store.set('bar', 'value of bar')
97
+
98
+ store.clear()
99
+
100
+ expect(store.get('foo')).toBeUndefined()
101
+ expect(store.get('bar')).toBeUndefined()
102
+
103
+ describe '#remove', ->
104
+
105
+ it 'removes the given key from the store', ->
106
+ store = new up.store.Session('spec')
107
+ store.set('foo', 'value of foo')
108
+ store.set('bar', 'value of bar')
109
+
110
+ store.remove('foo')
111
+
112
+ expect(store.get('foo')).toBeUndefined()
113
+ expect(store.get('bar')).toEqual('value of bar')
@@ -52,7 +52,7 @@ describe 'up.dom', ->
52
52
 
53
53
  it 'returns a promise that will be fulfilled once the server response was received and the swap transition has completed', asyncSpec (next) ->
54
54
  resolution = jasmine.createSpy()
55
- promise = up.replace('.middle', '/path', transition: 'cross-fade', duration: 50)
55
+ promise = up.replace('.middle', '/path', transition: 'cross-fade', duration: 200)
56
56
  promise.then(resolution)
57
57
  expect(resolution).not.toHaveBeenCalled()
58
58
  expect($('.middle')).toHaveText('old-middle')
@@ -61,17 +61,17 @@ describe 'up.dom', ->
61
61
  @respond()
62
62
  expect(resolution).not.toHaveBeenCalled()
63
63
 
64
- next.after 20, =>
64
+ next.after 100, =>
65
65
  expect(resolution).not.toHaveBeenCalled()
66
66
 
67
- next.after 80, =>
67
+ next.after 200, =>
68
68
  expect(resolution).toHaveBeenCalled()
69
69
 
70
- describe 'with { data } option', ->
70
+ describe 'with { params } option', ->
71
71
 
72
72
  it "uses the given params as a non-GET request's payload", asyncSpec (next) ->
73
73
  givenParams = { 'foo-key': 'foo-value', 'bar-key': 'bar-value' }
74
- up.replace('.middle', '/path', method: 'put', data: givenParams)
74
+ up.replace('.middle', '/path', method: 'put', params: givenParams)
75
75
 
76
76
  next =>
77
77
  expect(@lastRequest().data()['foo-key']).toEqual(['foo-value'])
@@ -79,7 +79,7 @@ describe 'up.dom', ->
79
79
 
80
80
  it "encodes the given params into the URL of a GET request", asyncSpec (next) ->
81
81
  givenParams = { 'foo-key': 'foo-value', 'bar-key': 'bar-value' }
82
- up.replace('.middle', '/path', method: 'get', data: givenParams)
82
+ up.replace('.middle', '/path', method: 'get', params: givenParams)
83
83
  next => expect(@lastRequest().url).toMatchUrl('/path?foo-key=foo-value&bar-key=bar-value')
84
84
 
85
85
  it 'uses a HTTP method given as { method } option', asyncSpec (next) ->
@@ -201,8 +201,8 @@ describe 'up.dom', ->
201
201
  next => @respond(responseHeaders: { 'X-Up-Location': '/other-path' })
202
202
  next => expect(location.href).toMatchUrl('/other-path')
203
203
 
204
- it 'adds params from a { data } option to the URL of a GET request', asyncSpec (next) ->
205
- up.replace('.middle', '/path', data: { 'foo-key': 'foo value', 'bar-key': 'bar value' })
204
+ it 'adds params from a { params } option to the URL of a GET request', asyncSpec (next) ->
205
+ up.replace('.middle', '/path', params: { 'foo-key': 'foo value', 'bar-key': 'bar value' })
206
206
  next => @respond()
207
207
  next => expect(location.href).toMatchUrl('/path?foo-key=foo%20value&bar-key=bar%20value')
208
208
 
@@ -1269,6 +1269,8 @@ describe 'up.dom', ->
1269
1269
  describe 'with { reveal } option', ->
1270
1270
 
1271
1271
  beforeEach ->
1272
+ up.history.config.enabled = true
1273
+
1272
1274
  @revealedHTML = []
1273
1275
  @revealedText = []
1274
1276
  @revealOptions = {}
@@ -1345,13 +1347,13 @@ describe 'up.dom', ->
1345
1347
  up.replace('.middle', '/path', failTarget: '.fail-target', reveal: false, failReveal: '&', origin: $origin)
1346
1348
 
1347
1349
  next =>
1348
- @respondWith
1349
- status: 500
1350
- responseText: """
1351
- <div class="fail-target">
1352
- new fail target text
1353
- </div>
1354
- """
1350
+ @respondWith
1351
+ status: 500
1352
+ responseText: """
1353
+ <div class="fail-target">
1354
+ new fail target text
1355
+ </div>
1356
+ """
1355
1357
 
1356
1358
  next =>
1357
1359
  expect(@revealedText).toEqual ['origin text']
@@ -1370,8 +1372,71 @@ describe 'up.dom', ->
1370
1372
 
1371
1373
  describe 'when there is an anchor #hash in the URL', ->
1372
1374
 
1373
- it 'scrolls to the top of a child with the ID of that #hash', asyncSpec (next) ->
1375
+ it 'scrolls to the top of an element with the ID of that #hash', asyncSpec (next) ->
1376
+ up.replace('.middle', '/path#hash', reveal: true)
1377
+ @responseText =
1378
+ """
1379
+ <div class="middle">
1380
+ <div id="hash"></div>
1381
+ </div>
1382
+ """
1383
+
1384
+ next =>
1385
+ @respond()
1386
+
1387
+ next =>
1388
+ expect(@revealedHTML).toEqual ['<div id="hash"></div>']
1389
+ expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1390
+
1391
+ it "scrolls to the top of an <a> element with the name of that hash", asyncSpec (next) ->
1374
1392
  up.replace('.middle', '/path#three', reveal: true)
1393
+ @responseText =
1394
+ """
1395
+ <div class="middle">
1396
+ <a name="three"></a>
1397
+ </div>
1398
+ """
1399
+
1400
+ next =>
1401
+ @respond()
1402
+
1403
+ next =>
1404
+ expect(@revealedHTML).toEqual ['<a name="three"></a>']
1405
+ expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1406
+
1407
+ it "scrolls to a hash that includes a dot character ('.') (bugfix)", asyncSpec (next) ->
1408
+ up.replace('.middle', '/path#foo.bar', reveal: true)
1409
+ @responseText =
1410
+ """
1411
+ <div class="middle">
1412
+ <a name="foo.bar"></a>
1413
+ </div>
1414
+ """
1415
+
1416
+ next =>
1417
+ @respond()
1418
+
1419
+ next =>
1420
+ expect(@revealedHTML).toEqual ['<a name="foo.bar"></a>']
1421
+ expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1422
+
1423
+ it 'does not scroll if { reveal: false } is also set', asyncSpec (next) ->
1424
+ up.replace('.middle', '/path#hash', reveal: false)
1425
+ @responseText =
1426
+ """
1427
+ <div class="middle">
1428
+ <div id="hash"></div>
1429
+ </div>
1430
+ """
1431
+
1432
+ next =>
1433
+ @respond()
1434
+
1435
+ next =>
1436
+ expect(@revealMock).not.toHaveBeenCalled()
1437
+
1438
+ it 'reveals multiple consecutive #hash targets with the same URL (bugfix)', asyncSpec (next) ->
1439
+ up.replace('.middle', '/path#two', reveal: true)
1375
1440
  @responseText =
1376
1441
  """
1377
1442
  <div class="middle">
@@ -1383,25 +1448,26 @@ describe 'up.dom', ->
1383
1448
 
1384
1449
  next =>
1385
1450
  @respond()
1451
+ up.replace('.middle', '/path#three', reveal: true)
1452
+ # response is already cached
1386
1453
 
1387
1454
  next =>
1388
- expect(@revealedHTML).toEqual ['<div id="three">three</div>']
1389
- expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
1455
+ expect(@revealedText).toEqual ['two', 'three']
1390
1456
 
1391
- it "reveals the entire element if it has no child with the ID of that #hash", asyncSpec (next) ->
1392
- up.replace('.middle', '/path#four', reveal: true)
1457
+ it "does not scroll if there is no element with the ID of that #hash", asyncSpec (next) ->
1458
+ up.replace('.middle', '/path#hash', reveal: true)
1459
+ @responseText =
1460
+ """
1461
+ <div class="middle">
1462
+ </div>
1463
+ """
1393
1464
 
1394
1465
  next =>
1395
- @responseText =
1396
- """
1397
- <div class="middle">
1398
- new-middle
1399
- </div>
1400
- """
1401
1466
  @respond()
1402
1467
 
1403
1468
  next =>
1404
- expect(@revealedText).toEqual ['new-middle']
1469
+ expect(@revealMock).not.toHaveBeenCalled()
1470
+
1405
1471
 
1406
1472
  it 'reveals a new element that is being appended', (done) ->
1407
1473
  promise = up.replace('.middle:after', '/path', reveal: true)
@@ -1621,7 +1687,7 @@ describe 'up.dom', ->
1621
1687
 
1622
1688
  it 'calls destructors when the replaced element is a singleton element like <body> (bugfix)', asyncSpec (next) ->
1623
1689
  # shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
1624
- up.motion.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
1690
+ up.util.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
1625
1691
  destructor = jasmine.createSpy('destructor')
1626
1692
  up.compiler '.container', -> destructor
1627
1693
  $container = affix('.container')
@@ -1696,7 +1762,7 @@ describe 'up.dom', ->
1696
1762
 
1697
1763
  it 'morphs between the old and new element', asyncSpec (next) ->
1698
1764
  affix('.element.v1').text('version 1')
1699
- up.extract('.element', '<div class="element v2">version 2</div>', transition: 'cross-fade', duration: 100, easing: 'linear')
1765
+ up.extract('.element', '<div class="element v2">version 2</div>', transition: 'cross-fade', duration: 200, easing: 'linear')
1700
1766
 
1701
1767
  $old = undefined
1702
1768
  $new = undefined
@@ -1706,23 +1772,23 @@ describe 'up.dom', ->
1706
1772
  $new = $('.element.v2')
1707
1773
 
1708
1774
  expect($old).toHaveLength(1)
1709
- expect(u.opacity($old)).toBeAround(1.0, 0.2)
1775
+ expect(u.opacity($old)).toBeAround(1.0, 0.15)
1710
1776
 
1711
1777
  expect($new).toHaveLength(1)
1712
- expect(u.opacity($new)).toBeAround(0.0, 0.2)
1778
+ expect(u.opacity($new)).toBeAround(0.0, 0.15)
1713
1779
 
1714
- next.after 50, =>
1715
- expect(u.opacity($old)).toBeAround(0.5, 0.2)
1716
- expect(u.opacity($new)).toBeAround(0.5, 0.2)
1780
+ next.after 100, =>
1781
+ expect(u.opacity($old)).toBeAround(0.5, 0.3)
1782
+ expect(u.opacity($new)).toBeAround(0.5, 0.3)
1717
1783
 
1718
- next.after (50 + 30), =>
1784
+ next.after (100 + 70), =>
1719
1785
  expect(u.opacity($new)).toBeAround(1.0, 0.1)
1720
1786
  expect($old).toBeDetached()
1721
1787
 
1722
1788
 
1723
1789
  it 'ignores a { transition } option when replacing a singleton element like <body>', asyncSpec (next) ->
1724
1790
  # shouldSwapElementsDirectly() is true for body, but can't have the example replace the Jasmine test runner UI
1725
- up.motion.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
1791
+ up.util.knife.mock('isSingletonElement').and.callFake ($element) -> $element.is('.container')
1726
1792
 
1727
1793
  affix('.container').text('old text')
1728
1794
 
@@ -1814,7 +1880,7 @@ describe 'up.dom', ->
1814
1880
  promise.then(resolution)
1815
1881
  expect(resolution).not.toHaveBeenCalled()
1816
1882
 
1817
- u.setTimer 40, ->
1883
+ u.setTimer 20, ->
1818
1884
  expect(resolution).not.toHaveBeenCalled()
1819
1885
 
1820
1886
  u.setTimer 200, ->
@@ -1991,7 +2057,6 @@ describe 'up.dom', ->
1991
2057
 
1992
2058
  it "only emits an event up:fragment:kept, but not an event up:fragment:inserted", asyncSpec (next) ->
1993
2059
  insertedListener = jasmine.createSpy('subscriber to up:fragment:inserted')
1994
- up.on('up:fragment:inserted', insertedListener)
1995
2060
  keptListener = jasmine.createSpy('subscriber to up:fragment:kept')
1996
2061
  up.on('up:fragment:kept', keptListener)
1997
2062
  up.on 'up:fragment:inserted', insertedListener
@@ -2323,15 +2388,15 @@ describe 'up.dom', ->
2323
2388
 
2324
2389
  it 'runs an animation before removal with { animate } option', asyncSpec (next) ->
2325
2390
  $element = affix('.element')
2326
- up.destroy($element, animation: 'fade-out', duration: 150, easing: 'linear')
2391
+ up.destroy($element, animation: 'fade-out', duration: 200, easing: 'linear')
2327
2392
 
2328
2393
  next ->
2329
- expect($element).toHaveOpacity(1.0, 0.2)
2394
+ expect($element).toHaveOpacity(1.0, 0.15)
2330
2395
 
2331
- next.after 75, ->
2332
- expect($element).toHaveOpacity(0.5, 0.2)
2396
+ next.after 100, ->
2397
+ expect($element).toHaveOpacity(0.5, 0.3)
2333
2398
 
2334
- next.after (75 + 40), ->
2399
+ next.after (100 + 75), ->
2335
2400
  expect($element).toBeDetached()
2336
2401
 
2337
2402
  it 'calls destructors for custom elements', (done) ->
@@ -2477,8 +2542,31 @@ describe 'up.dom', ->
2477
2542
  next =>
2478
2543
  expect(up.browser.navigate).toHaveBeenCalledWith('/source', jasmine.anything())
2479
2544
 
2545
+ describe 'up.dom.layerOf', ->
2546
+
2547
+ it 'returns "popup" for an element in a popup over the page', ->
2548
+ $popup = affix('.up-popup')
2549
+ $element = $popup.affix('.element')
2550
+ expect(up.dom.layerOf($element)).toEqual('popup')
2551
+
2552
+ it 'returns "popup" for an element in a popup over a modal', ->
2553
+ $modal = affix('.up-modal')
2554
+ $popupInModal = $modal.affix('.up-popup')
2555
+ $element = $popupInModal.affix('.element')
2556
+ expect(up.dom.layerOf($element)).toEqual('popup')
2557
+
2558
+ it 'returns "modal" for an element in a modal', ->
2559
+ $modal = affix('.up-modal')
2560
+ $element = $modal.affix('.element')
2561
+ expect(up.dom.layerOf($element)).toEqual('modal')
2562
+
2563
+ it 'returns "page" for an element below a modal or popup', ->
2564
+ $element = affix('.element')
2565
+ expect(up.dom.layerOf($element)).toEqual('page')
2480
2566
 
2481
- describe 'up.reset', ->
2567
+ it 'returns undefined for an empty jQuery collection', ->
2568
+ expect(up.dom.layerOf($())).toBeUndefined()
2482
2569
 
2483
- it 'should have tests'
2570
+ it 'returns undefined for undefined', ->
2571
+ expect(up.dom.layerOf(undefined)).toBeUndefined()
2484
2572