unpoly-rails 0.23.0 → 0.24.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 +68 -0
- data/dist/unpoly-bootstrap3.js +1 -1
- data/dist/unpoly-bootstrap3.min.js +1 -1
- data/dist/unpoly.css +18 -8
- data/dist/unpoly.js +498 -265
- data/dist/unpoly.min.css +1 -1
- data/dist/unpoly.min.js +3 -3
- data/lib/assets/javascripts/unpoly-bootstrap3/modal-ext.js.coffee +5 -2
- data/lib/assets/javascripts/unpoly/flow.js.coffee +3 -1
- data/lib/assets/javascripts/unpoly/form.js.coffee +3 -6
- data/lib/assets/javascripts/unpoly/layout.js.coffee +1 -1
- data/lib/assets/javascripts/unpoly/link.js.coffee +4 -2
- data/lib/assets/javascripts/unpoly/log.js.coffee +2 -2
- data/lib/assets/javascripts/unpoly/modal.js.coffee +125 -36
- data/lib/assets/javascripts/unpoly/motion.js.coffee +75 -37
- data/lib/assets/javascripts/unpoly/navigation.js.coffee +38 -26
- data/lib/assets/javascripts/unpoly/proxy.js.coffee +77 -52
- data/lib/assets/javascripts/unpoly/syntax.js.coffee +1 -0
- data/lib/assets/javascripts/unpoly/tooltip.js.coffee +2 -0
- data/lib/assets/javascripts/unpoly/util.js.coffee +138 -46
- data/lib/assets/stylesheets/unpoly/link.css.sass +1 -1
- data/lib/assets/stylesheets/unpoly/modal.css.sass +28 -15
- data/lib/unpoly/rails/version.rb +1 -1
- data/spec_app/Gemfile.lock +7 -7
- data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +2 -0
- data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/flow_spec.js.coffee +2 -2
- data/spec_app/spec/javascripts/up/history_spec.js.coffee +6 -4
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +114 -5
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +28 -18
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +112 -7
- data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +157 -121
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +42 -3
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -2
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +26 -1
- data/spec_app/vendor/assets/bower_components/jquery/.bower.json +1 -1
- metadata +3 -3
- data/spec_app/spec/javascripts/helpers/set_timer.js.coffee +0 -3
@@ -1,4 +1,6 @@
|
|
1
1
|
describe 'up.motion', ->
|
2
|
+
|
3
|
+
u = up.util
|
2
4
|
|
3
5
|
describe 'Javascript functions', ->
|
4
6
|
|
@@ -11,11 +13,11 @@ describe 'up.motion', ->
|
|
11
13
|
opacity = -> Number($element.css('opacity'))
|
12
14
|
up.animate($element, 'fade-in', duration: 200, easing: 'linear')
|
13
15
|
|
14
|
-
|
16
|
+
u.setTimer 0, ->
|
15
17
|
expect(opacity()).toBeAround(0.0, 0.25)
|
16
|
-
|
18
|
+
u.setTimer 100, ->
|
17
19
|
expect(opacity()).toBeAround(0.5, 0.25)
|
18
|
-
|
20
|
+
u.setTimer 200, ->
|
19
21
|
expect(opacity()).toBeAround(1.0, 0.25)
|
20
22
|
done()
|
21
23
|
|
@@ -42,6 +44,109 @@ describe 'up.motion', ->
|
|
42
44
|
up.animate($element, { 'font-size': '40px' }, duration: 10000, easing: 'linear')
|
43
45
|
expect($element.css('font-size')).toEqual('40px')
|
44
46
|
|
47
|
+
describe 'up.motion.finish', ->
|
48
|
+
|
49
|
+
describe 'when called with an element or selector', ->
|
50
|
+
|
51
|
+
it 'cancels an existing animation on the given element by instantly jumping to the last frame', ->
|
52
|
+
$element = affix('.element').text('content')
|
53
|
+
up.animate($element, { 'font-size': '40px', 'opacity': '0.33' }, duration: 10000)
|
54
|
+
up.motion.finish($element)
|
55
|
+
expect($element.css('font-size')).toEqual('40px')
|
56
|
+
expect($element.css('opacity')).toEqual('0.33')
|
57
|
+
|
58
|
+
it 'cancels animations on children of the given element', ->
|
59
|
+
$parent = affix('.element')
|
60
|
+
$child = $parent.affix('.child')
|
61
|
+
up.animate($child, { 'font-size': '40px' }, duration: 10000)
|
62
|
+
up.motion.finish($parent)
|
63
|
+
expect($child.css('font-size')).toEqual('40px')
|
64
|
+
|
65
|
+
it 'does not cancel animations on other elements', ->
|
66
|
+
$element1 = affix('.element1').text('content1')
|
67
|
+
$element2 = affix('.element2').text('content2')
|
68
|
+
up.animate($element1, 'fade-in', duration: 10000)
|
69
|
+
up.animate($element2, 'fade-in', duration: 10000)
|
70
|
+
up.motion.finish($element1)
|
71
|
+
expect(Number($element1.css('opacity'))).toEqual(1)
|
72
|
+
expect(Number($element2.css('opacity'))).toEqual(0, 0.1)
|
73
|
+
|
74
|
+
it 'restores existing transitions on the element', ->
|
75
|
+
$element = affix('.element').text('content')
|
76
|
+
$element.css('transition': 'font-size 3s ease')
|
77
|
+
oldTransition = $element.css('transition')
|
78
|
+
expect(oldTransition).toContain('font-size') # be paranoid
|
79
|
+
up.animate($element, 'fade-in', duration: 10000)
|
80
|
+
up.motion.finish($element)
|
81
|
+
expect(Number($element.css('opacity'))).toEqual(1)
|
82
|
+
currentTransition = $element.css('transition')
|
83
|
+
expect(currentTransition).toEqual(oldTransition)
|
84
|
+
expect(currentTransition).toContain('font-size')
|
85
|
+
expect(currentTransition).not.toContain('opacity')
|
86
|
+
expect(currentTransition).not.toContain('none')
|
87
|
+
expect(currentTransition).not.toContain('all')
|
88
|
+
|
89
|
+
it 'cancels an existing transition on the element by instantly jumping to the last frame', ->
|
90
|
+
$old = affix('.old').text('old content')
|
91
|
+
$new = affix('.new').text('new content')
|
92
|
+
|
93
|
+
up.morph($old, $new, 'cross-fade', duration: 2000)
|
94
|
+
expect($('.up-ghost').length).toBe(2)
|
95
|
+
|
96
|
+
up.motion.finish($old)
|
97
|
+
|
98
|
+
expect($('.up-ghost').length).toBe(0)
|
99
|
+
expect($old.css('display')).toEqual('none')
|
100
|
+
expect($new.css('display')).toEqual('block')
|
101
|
+
|
102
|
+
it 'can be called on either element involved in a transition', ->
|
103
|
+
$old = affix('.old').text('old content')
|
104
|
+
$new = affix('.new').text('new content')
|
105
|
+
|
106
|
+
up.morph($old, $new, 'cross-fade', duration: 2000)
|
107
|
+
expect($('.up-ghost').length).toBe(2)
|
108
|
+
|
109
|
+
up.motion.finish($new)
|
110
|
+
|
111
|
+
expect($('.up-ghost').length).toBe(0)
|
112
|
+
expect($old.css('display')).toEqual('none')
|
113
|
+
expect($new.css('display')).toEqual('block')
|
114
|
+
|
115
|
+
|
116
|
+
it 'cancels transitions on children of the given element', ->
|
117
|
+
$parent = affix('.parent')
|
118
|
+
$old = $parent.affix('.old').text('old content')
|
119
|
+
$new = $parent.affix('.new').text('new content')
|
120
|
+
|
121
|
+
up.morph($old, $new, 'cross-fade', duration: 2000)
|
122
|
+
expect($('.up-ghost').length).toBe(2)
|
123
|
+
|
124
|
+
up.motion.finish($parent)
|
125
|
+
|
126
|
+
expect($('.up-ghost').length).toBe(0)
|
127
|
+
expect($old.css('display')).toEqual('none')
|
128
|
+
expect($new.css('display')).toEqual('block')
|
129
|
+
|
130
|
+
describe 'when called without arguments', ->
|
131
|
+
|
132
|
+
it 'cancels all animations on the screen', ->
|
133
|
+
$element1 = affix('.element1').text('content1')
|
134
|
+
$element2 = affix('.element2').text('content2')
|
135
|
+
|
136
|
+
up.animate($element1, 'fade-in', duration: 3000)
|
137
|
+
up.animate($element2, 'fade-in', duration: 3000)
|
138
|
+
|
139
|
+
opacity = ($element) -> Number($element.css('opacity'))
|
140
|
+
|
141
|
+
expect(opacity($element1)).toBeAround(0.0, 0.1)
|
142
|
+
expect(opacity($element2)).toBeAround(0.0, 0.1)
|
143
|
+
|
144
|
+
up.motion.finish()
|
145
|
+
|
146
|
+
$element1 = $('.element1')
|
147
|
+
$element2 = $('.element2')
|
148
|
+
expect(opacity($element1)).toBe(1.0)
|
149
|
+
expect(opacity($element2)).toBe(1.0)
|
45
150
|
|
46
151
|
describe 'up.morph', ->
|
47
152
|
|
@@ -119,19 +224,19 @@ describe 'up.motion', ->
|
|
119
224
|
|
120
225
|
opacity = ($element) -> Number($element.css('opacity'))
|
121
226
|
|
122
|
-
|
227
|
+
u.setTimer 0, ->
|
123
228
|
expect(opacity($newGhost)).toBeAround(0.0, 0.25)
|
124
229
|
expect(opacity($oldGhost)).toBeAround(1.0, 0.25)
|
125
230
|
|
126
|
-
|
231
|
+
u.setTimer 80, ->
|
127
232
|
expect(opacity($newGhost)).toBeAround(0.4, 0.25)
|
128
233
|
expect(opacity($oldGhost)).toBeAround(0.6, 0.25)
|
129
234
|
|
130
|
-
|
235
|
+
u.setTimer 140, ->
|
131
236
|
expect(opacity($newGhost)).toBeAround(0.7, 0.25)
|
132
237
|
expect(opacity($oldGhost)).toBeAround(0.3, 0.25)
|
133
238
|
|
134
|
-
|
239
|
+
u.setTimer 250, ->
|
135
240
|
# Once our two ghosts have rendered their visual effect,
|
136
241
|
# we remove them from the DOM.
|
137
242
|
expect($newGhost).not.toBeInDOM()
|
@@ -1,132 +1,168 @@
|
|
1
1
|
describe 'up.navigation', ->
|
2
|
-
|
2
|
+
|
3
|
+
u = up.util
|
4
|
+
|
5
|
+
beforeEach ->
|
6
|
+
up.modal.config.openAnimation = 'none'
|
7
|
+
up.modal.config.closeAnimation = 'none'
|
8
|
+
|
3
9
|
describe 'unobtrusive behavior', ->
|
4
10
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
expect($unrelatedLink).not.toHaveClass('up-current')
|
78
|
-
|
79
|
-
up.modal.close().then ->
|
11
|
+
describe '.up-current', ->
|
12
|
+
|
13
|
+
it 'marks a link as .up-current if it links to the current URL', ->
|
14
|
+
spyOn(up.browser, 'url').and.returnValue('/foo')
|
15
|
+
$currentLink = up.hello(affix('a[href="/foo"]'))
|
16
|
+
$otherLink = up.hello(affix('a[href="/bar"]'))
|
17
|
+
expect($currentLink).toHaveClass('up-current')
|
18
|
+
expect($otherLink).not.toHaveClass('up-current')
|
19
|
+
|
20
|
+
it 'marks any link as .up-current if its up-href attribute matches the current URL', ->
|
21
|
+
spyOn(up.browser, 'url').and.returnValue('/foo')
|
22
|
+
$currentLink = up.hello(affix('span[up-href="/foo"]'))
|
23
|
+
$otherLink = up.hello(affix('span[up-href="/bar"]'))
|
24
|
+
expect($currentLink).toHaveClass('up-current')
|
25
|
+
expect($otherLink).not.toHaveClass('up-current')
|
26
|
+
|
27
|
+
it 'marks any link as .up-current if any of its space-separated up-alias values matches the current URL', ->
|
28
|
+
spyOn(up.browser, 'url').and.returnValue('/foo')
|
29
|
+
$currentLink = up.hello(affix('a[href="/x"][up-alias="/aaa /foo /bbb"]'))
|
30
|
+
$otherLink = up.hello(affix('a[href="/y"][up-alias="/bar"]'))
|
31
|
+
expect($currentLink).toHaveClass('up-current')
|
32
|
+
expect($otherLink).not.toHaveClass('up-current')
|
33
|
+
|
34
|
+
it 'does not throw if the current location does not match an up-alias wildcard (bugfix)', ->
|
35
|
+
inserter = -> up.hello(affix('a[up-alias="/qqqq*"]'))
|
36
|
+
expect(inserter).not.toThrow()
|
37
|
+
|
38
|
+
it 'does not highlight a link to "#" (commonly used for JS-only buttons)', ->
|
39
|
+
$link = up.hello(affix('a[href="#"]'))
|
40
|
+
expect($link).not.toHaveClass('up-current')
|
41
|
+
|
42
|
+
it 'marks URL prefixes as .up-current if an up-alias value ends in *', ->
|
43
|
+
spyOn(up.browser, 'url').and.returnValue('/foo/123')
|
44
|
+
$currentLink = up.hello(affix('a[href="/x"][up-alias="/aaa /foo/* /bbb"]'))
|
45
|
+
$otherLink = up.hello(affix('a[href="/y"][up-alias="/bar"]'))
|
46
|
+
expect($currentLink).toHaveClass('up-current')
|
47
|
+
expect($otherLink).not.toHaveClass('up-current')
|
48
|
+
|
49
|
+
it 'allows to configure a custom "current" class, but always also sets .up-current', ->
|
50
|
+
up.navigation.config.currentClasses = ['highlight']
|
51
|
+
spyOn(up.browser, 'url').and.returnValue('/foo')
|
52
|
+
$currentLink = up.hello(affix('a[href="/foo"]'))
|
53
|
+
expect($currentLink).toHaveClass('highlight up-current')
|
54
|
+
|
55
|
+
describeCapability 'canPushState', ->
|
56
|
+
|
57
|
+
it 'marks a link as .up-current if it links to the current URL, but is missing a trailing slash', ->
|
58
|
+
$link = affix('a[href="/foo"][up-target=".main"]')
|
59
|
+
affix('.main')
|
60
|
+
$link.click()
|
61
|
+
@respondWith
|
62
|
+
responseHeaders: { 'X-Up-Location': '/foo/' }
|
63
|
+
responseText: '<div class="main">new-text</div>'
|
64
|
+
expect($link).toHaveClass('up-current')
|
65
|
+
|
66
|
+
it 'marks a link as .up-current if it links to the current URL, but has an extra trailing slash', ->
|
67
|
+
$link = affix('a[href="/foo/"][up-target=".main"]')
|
68
|
+
affix('.main')
|
69
|
+
$link.click()
|
70
|
+
@respondWith
|
71
|
+
responseHeaders: { 'X-Up-Location': '/foo' }
|
72
|
+
responseText: '<div class="main">new-text</div>'
|
73
|
+
expect($link).toHaveClass('up-current')
|
74
|
+
|
75
|
+
it 'marks a link as .up-current if it links to an URL currently shown either within or below the modal', (done) ->
|
76
|
+
up.history.replace('/foo')
|
77
|
+
$backgroundLink = affix('a[href="/foo"]')
|
78
|
+
$modalLink = affix('a[href="/bar"][up-modal=".main"]')
|
79
|
+
$unrelatedLink = affix('a[href="/baz]')
|
80
|
+
|
81
|
+
Trigger.click($modalLink)
|
82
|
+
@respondWith('<div class="main">new-text</div>')
|
80
83
|
expect($backgroundLink).toHaveClass('up-current')
|
81
|
-
expect($modalLink).
|
84
|
+
expect($modalLink).toHaveClass('up-current')
|
82
85
|
expect($unrelatedLink).not.toHaveClass('up-current')
|
83
|
-
done()
|
84
86
|
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
87
|
+
up.modal.close().then ->
|
88
|
+
expect($backgroundLink).toHaveClass('up-current')
|
89
|
+
expect($modalLink).not.toHaveClass('up-current')
|
90
|
+
expect($unrelatedLink).not.toHaveClass('up-current')
|
91
|
+
done()
|
90
92
|
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
93
|
+
it 'marks a link as .up-current if it links to the URL currently either within or below the popup', (done) ->
|
94
|
+
up.history.replace('/foo')
|
95
|
+
$backgroundLink = affix('a[href="/foo"]')
|
96
|
+
$popupLink = affix('a[href="/bar"][up-popup=".main"]')
|
97
|
+
$unrelatedLink = affix('a[href="/baz]')
|
96
98
|
|
97
|
-
|
99
|
+
$popupLink.click()
|
100
|
+
@respondWith('<div class="main">new-text</div>')
|
98
101
|
expect($backgroundLink).toHaveClass('up-current')
|
99
|
-
expect($popupLink).
|
102
|
+
expect($popupLink).toHaveClass('up-current')
|
100
103
|
expect($unrelatedLink).not.toHaveClass('up-current')
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
104
|
+
|
105
|
+
up.popup.close().then ->
|
106
|
+
expect($backgroundLink).toHaveClass('up-current')
|
107
|
+
expect($popupLink).not.toHaveClass('up-current')
|
108
|
+
expect($unrelatedLink).not.toHaveClass('up-current')
|
109
|
+
done()
|
110
|
+
|
111
|
+
it 'changes .up-current marks as the URL changes'
|
112
|
+
|
113
|
+
describe '.up-active', ->
|
114
|
+
|
115
|
+
describeCapability 'canPushState', ->
|
116
|
+
|
117
|
+
it 'marks clicked links as .up-active until the request finishes', ->
|
118
|
+
$link = affix('a[href="/foo"][up-target=".main"]')
|
119
|
+
affix('.main')
|
120
|
+
$link.click()
|
121
|
+
expect($link).toHaveClass('up-active')
|
122
|
+
@respondWith('<div class="main">new-text</div>')
|
123
|
+
expect($link).not.toHaveClass('up-active')
|
124
|
+
|
125
|
+
it 'marks links with [up-instant] on mousedown as .up-active until the request finishes', ->
|
126
|
+
$link = affix('a[href="/foo"][up-instant][up-target=".main"]')
|
127
|
+
affix('.main')
|
128
|
+
Trigger.mousedown($link)
|
129
|
+
expect($link).toHaveClass('up-active')
|
130
|
+
@respondWith('<div class="main">new-text</div>')
|
131
|
+
expect($link).not.toHaveClass('up-active')
|
132
|
+
|
133
|
+
it 'prefers to mark an enclosing [up-expand] click area', ->
|
134
|
+
$area = affix('div[up-expand] a[href="/foo"][up-target=".main"]')
|
135
|
+
up.hello($area)
|
136
|
+
$link = $area.find('a')
|
137
|
+
affix('.main')
|
138
|
+
$link.click()
|
139
|
+
expect($link).not.toHaveClass('up-active')
|
140
|
+
expect($area).toHaveClass('up-active')
|
141
|
+
@respondWith('<div class="main">new-text</div>')
|
142
|
+
expect($area).not.toHaveClass('up-active')
|
143
|
+
|
144
|
+
it 'marks clicked modal openers as .up-active while the modal is loading', ->
|
145
|
+
$link = affix('a[href="/foo"][up-modal=".main"]')
|
146
|
+
affix('.main')
|
147
|
+
$link.click()
|
148
|
+
expect($link).toHaveClass('up-active')
|
149
|
+
@respondWith('<div class="main">new-text</div>')
|
150
|
+
expect($link).not.toHaveClass('up-active')
|
151
|
+
|
152
|
+
it 'removes .up-active from a clicked modal opener if the target is already preloaded (bugfix)', ->
|
153
|
+
$link = affix('a[href="/foo"][up-modal=".main"]')
|
154
|
+
up.proxy.preload($link)
|
155
|
+
@respondWith('<div class="main">new-text</div>')
|
156
|
+
$link.click()
|
157
|
+
expect('.up-modal .main').toHaveText('new-text')
|
158
|
+
expect($link).not.toHaveClass('up-active')
|
159
|
+
|
160
|
+
it 'removes .up-active from a clicked link if the target is already preloaded (bugfix)', ->
|
161
|
+
$link = affix('a[href="/foo"][up-target=".main"]')
|
162
|
+
affix('.main')
|
163
|
+
up.proxy.preload($link)
|
164
|
+
@respondWith('<div class="main">new-text</div>')
|
165
|
+
$link.click()
|
166
|
+
expect('.main').toHaveText('new-text')
|
167
|
+
expect($link).not.toHaveClass('up-active')
|
132
168
|
|
@@ -73,7 +73,7 @@ describe 'up.popup', ->
|
|
73
73
|
|
74
74
|
beforeEach ->
|
75
75
|
@$link = affix('a[href="/path"][up-popup=".target"]')
|
76
|
-
@attachSpy = up.popup.knife.mock('attach')
|
76
|
+
@attachSpy = up.popup.knife.mock('attach').and.returnValue(u.resolvedPromise())
|
77
77
|
@defaultSpy = up.link.knife.mock('allowDefault').and.callFake((event) -> event.preventDefault())
|
78
78
|
|
79
79
|
it 'opens the clicked link in a popup', ->
|