unpoly-rails 0.56.7 → 0.57.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 +74 -1
- data/dist/unpoly.js +1569 -793
- data/dist/unpoly.min.js +4 -4
- data/lib/assets/javascripts/unpoly.coffee +2 -0
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +25 -41
- data/lib/assets/javascripts/unpoly/bus.coffee.erb +20 -6
- data/lib/assets/javascripts/unpoly/classes/cache.coffee +23 -13
- data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +87 -0
- data/lib/assets/javascripts/unpoly/classes/focus_tracker.coffee +29 -0
- data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +7 -4
- data/lib/assets/javascripts/unpoly/classes/record.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/request.coffee +38 -45
- data/lib/assets/javascripts/unpoly/classes/response.coffee +16 -1
- data/lib/assets/javascripts/unpoly/classes/store/memory.coffee +26 -0
- data/lib/assets/javascripts/unpoly/classes/store/session.coffee +59 -0
- data/lib/assets/javascripts/unpoly/cookie.coffee +56 -0
- data/lib/assets/javascripts/unpoly/dom.coffee.erb +67 -39
- data/lib/assets/javascripts/unpoly/feedback.coffee +2 -2
- data/lib/assets/javascripts/unpoly/form.coffee.erb +23 -12
- data/lib/assets/javascripts/unpoly/history.coffee +2 -2
- data/lib/assets/javascripts/unpoly/layout.coffee.erb +118 -99
- data/lib/assets/javascripts/unpoly/link.coffee.erb +12 -5
- data/lib/assets/javascripts/unpoly/log.coffee +6 -5
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +9 -2
- data/lib/assets/javascripts/unpoly/motion.coffee.erb +2 -6
- data/lib/assets/javascripts/unpoly/namespace.coffee.erb +2 -2
- data/lib/assets/javascripts/unpoly/params.coffee.erb +522 -0
- data/lib/assets/javascripts/unpoly/popup.coffee.erb +3 -3
- data/lib/assets/javascripts/unpoly/proxy.coffee +42 -34
- data/lib/assets/javascripts/unpoly/{syntax.coffee → syntax.coffee.erb} +59 -117
- data/lib/assets/javascripts/unpoly/{util.coffee → util.coffee.erb} +206 -171
- 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 +0 -4
- data/spec_app/app/assets/stylesheets/integration_test.sass +7 -1
- data/spec_app/app/controllers/pages_controller.rb +4 -0
- data/spec_app/app/views/form_test/basics/new.erb +34 -5
- data/spec_app/app/views/form_test/submission_result.erb +2 -2
- data/spec_app/app/views/form_test/uploads/new.erb +15 -2
- data/spec_app/app/views/hash_test/unpoly.erb +30 -0
- data/spec_app/app/views/pages/start.erb +2 -1
- data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +17 -2
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_error.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +5 -0
- data/spec_app/spec/javascripts/up/browser_spec.js.coffee +8 -8
- data/spec_app/spec/javascripts/up/bus_spec.js.coffee +58 -20
- data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +78 -0
- data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +31 -0
- data/spec_app/spec/javascripts/up/classes/request_spec.coffee +50 -0
- data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +67 -0
- data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +113 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +133 -45
- data/spec_app/spec/javascripts/up/form_spec.js.coffee +13 -13
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +110 -26
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +1 -0
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +52 -51
- data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +2 -2
- data/spec_app/spec/javascripts/up/params_spec.coffee +768 -0
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +75 -36
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +48 -15
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +148 -131
- metadata +17 -5
- 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¶msKey=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:
|
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
|
64
|
+
next.after 100, =>
|
65
65
|
expect(resolution).not.toHaveBeenCalled()
|
66
66
|
|
67
|
-
next.after
|
67
|
+
next.after 200, =>
|
68
68
|
expect(resolution).toHaveBeenCalled()
|
69
69
|
|
70
|
-
describe 'with {
|
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',
|
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',
|
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 {
|
205
|
-
up.replace('.middle', '/path',
|
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
|
-
|
1349
|
-
|
1350
|
-
|
1351
|
-
|
1352
|
-
|
1353
|
-
|
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
|
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(@
|
1389
|
-
expect(@revealOptions).toEqual jasmine.objectContaining(top: true)
|
1455
|
+
expect(@revealedText).toEqual ['two', 'three']
|
1390
1456
|
|
1391
|
-
it "
|
1392
|
-
up.replace('.middle', '/path#
|
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(@
|
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.
|
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:
|
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.
|
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.
|
1778
|
+
expect(u.opacity($new)).toBeAround(0.0, 0.15)
|
1713
1779
|
|
1714
|
-
next.after
|
1715
|
-
expect(u.opacity($old)).toBeAround(0.5, 0.
|
1716
|
-
expect(u.opacity($new)).toBeAround(0.5, 0.
|
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 (
|
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.
|
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
|
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:
|
2391
|
+
up.destroy($element, animation: 'fade-out', duration: 200, easing: 'linear')
|
2327
2392
|
|
2328
2393
|
next ->
|
2329
|
-
expect($element).toHaveOpacity(1.0, 0.
|
2394
|
+
expect($element).toHaveOpacity(1.0, 0.15)
|
2330
2395
|
|
2331
|
-
next.after
|
2332
|
-
expect($element).toHaveOpacity(0.5, 0.
|
2396
|
+
next.after 100, ->
|
2397
|
+
expect($element).toHaveOpacity(0.5, 0.3)
|
2333
2398
|
|
2334
|
-
next.after (
|
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
|
-
|
2567
|
+
it 'returns undefined for an empty jQuery collection', ->
|
2568
|
+
expect(up.dom.layerOf($())).toBeUndefined()
|
2482
2569
|
|
2483
|
-
it '
|
2570
|
+
it 'returns undefined for undefined', ->
|
2571
|
+
expect(up.dom.layerOf(undefined)).toBeUndefined()
|
2484
2572
|
|