unpoly-rails 0.26.2 → 0.27.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 (37) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +33 -1
  3. data/dist/unpoly.js +704 -446
  4. data/dist/unpoly.min.js +3 -3
  5. data/lib/assets/javascripts/unpoly/browser.js.coffee +18 -9
  6. data/lib/assets/javascripts/unpoly/bus.js.coffee +28 -1
  7. data/lib/assets/javascripts/unpoly/flow.js.coffee +1 -1
  8. data/lib/assets/javascripts/unpoly/history.js.coffee +54 -22
  9. data/lib/assets/javascripts/unpoly/link.js.coffee +1 -1
  10. data/lib/assets/javascripts/unpoly/log.js.coffee +19 -12
  11. data/lib/assets/javascripts/unpoly/modal.js.coffee +119 -124
  12. data/lib/assets/javascripts/unpoly/motion.js.coffee +1 -0
  13. data/lib/assets/javascripts/unpoly/navigation.js.coffee +2 -6
  14. data/lib/assets/javascripts/unpoly/popup.js.coffee +136 -126
  15. data/lib/assets/javascripts/unpoly/proxy.js.coffee +0 -2
  16. data/lib/assets/javascripts/unpoly/syntax.js.coffee +1 -1
  17. data/lib/assets/javascripts/unpoly/tooltip.js.coffee +101 -46
  18. data/lib/assets/javascripts/unpoly/util.js.coffee +76 -7
  19. data/lib/unpoly/rails/version.rb +1 -1
  20. data/spec_app/Gemfile.lock +1 -1
  21. data/spec_app/app/assets/stylesheets/integration_test.sass +4 -0
  22. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +5 -0
  23. data/spec_app/app/views/css_test/modal.erb +3 -0
  24. data/spec_app/app/views/css_test/modal_contents.erb +5 -0
  25. data/spec_app/app/views/css_test/popup.erb +11 -11
  26. data/spec_app/app/views/css_test/tooltip.erb +12 -5
  27. data/spec_app/app/views/pages/start.erb +4 -0
  28. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +2 -3
  29. data/spec_app/spec/javascripts/up/flow_spec.js.coffee +97 -88
  30. data/spec_app/spec/javascripts/up/history_spec.js.coffee +100 -1
  31. data/spec_app/spec/javascripts/up/link_spec.js.coffee +18 -16
  32. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +102 -97
  33. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +89 -75
  34. data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +17 -5
  35. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +89 -70
  36. data/spec_app/spec/javascripts/up/util_spec.js.coffee +23 -0
  37. metadata +4 -2
@@ -37,7 +37,7 @@ describe 'up.history', ->
37
37
  $element = up.hello(affix('a[href="/three"][up-back]').text('text'))
38
38
  expect($element.attr('up-href')).toBeUndefined()
39
39
 
40
- describe 'scroll restauration', ->
40
+ describe 'scroll restoration', ->
41
41
 
42
42
  describeCapability 'canPushState', ->
43
43
 
@@ -96,3 +96,102 @@ describe 'up.history', ->
96
96
  respond() # we need to respond since we've never requested /three with the popTarget
97
97
  expect($('.viewport').scrollTop()).toBe(250)
98
98
  done()
99
+
100
+ describe 'events', ->
101
+
102
+ describeCapability 'canPushState', ->
103
+
104
+ it 'emits up:history:* events as the user goes forwards and backwards through history', (done) ->
105
+ up.proxy.config.cacheSize = 0
106
+ up.history.config.popTargets = ['.viewport']
107
+
108
+ affix('.viewport .content')
109
+ respond = =>
110
+ @respondWith """
111
+ <div class="viewport">
112
+ <div class="content">content</div>
113
+ </div>
114
+ """
115
+
116
+ events = []
117
+ u.each ['up:history:pushed', 'up:history:restored'], (eventName) ->
118
+ up.on eventName, (event) ->
119
+ events.push [eventName, event.url]
120
+
121
+ normalize = up.history.normalizeUrl
122
+
123
+ up.replace('.content', '/one')
124
+ respond()
125
+
126
+ expect(events).toEqual [
127
+ ['up:history:pushed', normalize('/one')]
128
+ ]
129
+
130
+ up.replace('.content', '/two')
131
+ respond()
132
+
133
+ expect(events).toEqual [
134
+ ['up:history:pushed', normalize('/one')]
135
+ ['up:history:pushed', normalize('/two')]
136
+ ]
137
+
138
+ up.replace('.content', '/three')
139
+ respond()
140
+
141
+ expect(events).toEqual [
142
+ ['up:history:pushed', normalize('/one')]
143
+ ['up:history:pushed', normalize('/two')]
144
+ ['up:history:pushed', normalize('/three')]
145
+ ]
146
+
147
+ history.back()
148
+ u.setTimer 50, ->
149
+ respond()
150
+
151
+ expect(events).toEqual [
152
+ ['up:history:pushed', normalize('/one')]
153
+ ['up:history:pushed', normalize('/two')]
154
+ ['up:history:pushed', normalize('/three')]
155
+ ['up:history:restored', normalize('/two')]
156
+ ]
157
+
158
+ history.back()
159
+ u.setTimer 50, ->
160
+ respond()
161
+
162
+ expect(events).toEqual [
163
+ ['up:history:pushed', normalize('/one')]
164
+ ['up:history:pushed', normalize('/two')]
165
+ ['up:history:pushed', normalize('/three')]
166
+ ['up:history:restored', normalize('/two')]
167
+ ['up:history:restored', normalize('/one')]
168
+ ]
169
+
170
+ history.forward()
171
+ u.setTimer 50, ->
172
+ respond()
173
+
174
+ expect(events).toEqual [
175
+ ['up:history:pushed', normalize('/one')]
176
+ ['up:history:pushed', normalize('/two')]
177
+ ['up:history:pushed', normalize('/three')]
178
+ ['up:history:restored', normalize('/two')]
179
+ ['up:history:restored', normalize('/one')]
180
+ ['up:history:restored', normalize('/two')]
181
+ ]
182
+
183
+ history.forward()
184
+ u.setTimer 50, ->
185
+ respond() # we need to respond since we've never requested /three with the popTarget
186
+
187
+ expect(events).toEqual [
188
+ ['up:history:pushed', normalize('/one')]
189
+ ['up:history:pushed', normalize('/two')]
190
+ ['up:history:pushed', normalize('/three')]
191
+ ['up:history:restored', normalize('/two')]
192
+ ['up:history:restored', normalize('/one')]
193
+ ['up:history:restored', normalize('/two')]
194
+ ['up:history:restored', normalize('/three')]
195
+ ]
196
+
197
+ done()
@@ -367,22 +367,24 @@ describe 'up.link', ->
367
367
 
368
368
  describe 'with [up-transition] modifier', ->
369
369
 
370
- it 'morphs between the old and new target element', (done) ->
371
- affix('.target.old')
372
- $link = affix('a[href="/path"][up-target=".target"][up-transition="cross-fade"][up-duration="300"][up-easing="linear"]')
373
- $link.click()
374
- @respondWith '<div class="target new">new text</div>'
375
-
376
- $oldGhost = $('.target.old.up-ghost')
377
- $newGhost = $('.target.new.up-ghost')
378
- expect($oldGhost).toExist()
379
- expect($newGhost).toExist()
380
- expect(u.opacity($oldGhost)).toBeAround(1, 0.15)
381
- expect(u.opacity($newGhost)).toBeAround(0, 0.15)
382
- u.setTimer 150, ->
383
- expect(u.opacity($oldGhost)).toBeAround(0.5, 0.15)
384
- expect(u.opacity($newGhost)).toBeAround(0.5, 0.15)
385
- done()
370
+ describeCapability 'canCssTransition', ->
371
+
372
+ it 'morphs between the old and new target element', (done) ->
373
+ affix('.target.old')
374
+ $link = affix('a[href="/path"][up-target=".target"][up-transition="cross-fade"][up-duration="300"][up-easing="linear"]')
375
+ $link.click()
376
+ @respondWith '<div class="target new">new text</div>'
377
+
378
+ $oldGhost = $('.target.old.up-ghost')
379
+ $newGhost = $('.target.new.up-ghost')
380
+ expect($oldGhost).toExist()
381
+ expect($newGhost).toExist()
382
+ expect(u.opacity($oldGhost)).toBeAround(1, 0.15)
383
+ expect(u.opacity($newGhost)).toBeAround(0, 0.15)
384
+ u.setTimer 150, ->
385
+ expect(u.opacity($oldGhost)).toBeAround(0.5, 0.15)
386
+ expect(u.opacity($newGhost)).toBeAround(0.5, 0.15)
387
+ done()
386
388
 
387
389
  it 'does not add a history entry when an up-history attribute is set to "false"', ->
388
390
  oldPathname = location.pathname
@@ -68,20 +68,21 @@ describe 'up.modal', ->
68
68
  expect(parseInt($body.css('padding-right'))).toBe(0)
69
69
  done()
70
70
 
71
- it "gives the scrollbar to .up-modal instead of .up-modal-viewport while animating, so we don't see scaled scrollbars in a zoom-in animation", (done) ->
72
- openPromise = up.modal.extract('.container', '<div class="container">text</div>', animation: 'fade-in', duration: 100)
73
- $modal = $('.up-modal')
74
- $viewport = $modal.find('.up-modal-viewport')
75
- expect($modal.css('overflow-y')).toEqual('scroll')
76
- expect($viewport.css('overflow-y')).toEqual('hidden')
77
- openPromise.then ->
78
- expect($modal.css('overflow-y')).toEqual('auto')
79
- expect($viewport.css('overflow-y')).toEqual('scroll')
80
- closePromise = up.modal.close(animation: 'fade-out', duration: 100)
71
+ describeCapability 'canCssTransition', ->
72
+
73
+ it "gives the scrollbar to .up-modal instead of .up-modal-viewport while animating, so we don't see scaled scrollbars in a zoom-in animation", (done) ->
74
+ openPromise = up.modal.extract('.container', '<div class="container">text</div>', animation: 'fade-in', duration: 100)
75
+ $modal = $('.up-modal')
76
+ $viewport = $modal.find('.up-modal-viewport')
81
77
  expect($modal.css('overflow-y')).toEqual('scroll')
82
78
  expect($viewport.css('overflow-y')).toEqual('hidden')
83
- done()
84
-
79
+ openPromise.then ->
80
+ expect($modal.css('overflow-y')).not.toEqual('scroll')
81
+ expect($viewport.css('overflow-y')).toEqual('scroll')
82
+ closePromise = up.modal.close(animation: 'fade-out', duration: 100)
83
+ expect($modal.css('overflow-y')).toEqual('scroll')
84
+ expect($viewport.css('overflow-y')).toEqual('hidden')
85
+ done()
85
86
 
86
87
  it 'does not add right padding to the body if the body has overflow-y: hidden', (done) ->
87
88
  restoreBody = u.temporaryCss($('body'), 'overflow-y': 'hidden')
@@ -133,99 +134,95 @@ describe 'up.modal', ->
133
134
 
134
135
  it 'does not open multiple modals or pad the body twice if the user starts loading a second modal before the first was done loading', (done) ->
135
136
  up.modal.config.closeDuration = 10
136
- promise1 = up.modal.visit('/path1', target: '.container', animation: 'fade-in', duration: 100)
137
- promise2 = up.modal.visit('/path2', target: '.container', animation: 'fade-in', duration: 100)
138
- expect(jasmine.Ajax.requests.count()).toBe(2)
139
- request1 = jasmine.Ajax.requests.at(0)
140
- request2 = jasmine.Ajax.requests.at(1)
141
-
142
- u.setTimer 10, =>
143
- @respondWith('<div class="container">response1</div>', request: request1)
144
- u.setTimer 10, =>
145
- @respondWith('<div class="container">response2</div>', request: request2)
146
- u.setTimer 110, =>
147
- expect($('.up-modal').length).toBe(1)
148
- expect($('.up-modal-dialog').length).toBe(1)
149
- expect($('.container')).toHaveText('response2')
150
- bodyPadding = parseInt($('body').css('padding-right'))
151
- expect(bodyPadding).toBeAround(assumedScrollbarWidth, 10)
152
- expect(bodyPadding).not.toBeAround(2 * assumedScrollbarWidth, 2 * 5)
153
- done()
154
-
155
- it 'closes the current modal and wait for its close animation to finish before starting the open animation of a second modal', (done) ->
156
- up.modal.config.openAnimation = 'fade-in'
157
- up.modal.config.openDuration = 5
158
- up.modal.config.closeAnimation = 'fade-out'
159
- up.modal.config.closeDuration = 50
137
+ promise1 = up.modal.visit('/path1', target: '.container', animation: 'fade-in', duration: 50)
138
+ promise2 = up.modal.visit('/path2', target: '.container', animation: 'fade-in', duration: 50)
139
+ @respondWith('<div class="container">response1</div>')
140
+
141
+ u.setTimer 80, =>
142
+ @respondWith('<div class="container">response2</div>')
143
+ $.when(promise1, promise2).then ->
144
+ expect($('.up-modal').length).toBe(1)
145
+ expect($('.up-modal-dialog').length).toBe(1)
146
+ expect($('.container')).toHaveText('response2')
147
+ bodyPadding = parseInt($('body').css('padding-right'))
148
+ expect(bodyPadding).toBeAround(assumedScrollbarWidth, 10)
149
+ expect(bodyPadding).not.toBeAround(2 * assumedScrollbarWidth, 2 * 5)
150
+ done()
160
151
 
161
- events = []
162
- u.each ['up:modal:open', 'up:modal:opened', 'up:modal:close', 'up:modal:closed'], (event) ->
163
- up.on event, ->
164
- events.push(event)
152
+ describeCapability 'canCssTransition', ->
165
153
 
166
- up.modal.extract('.target', '<div class="target">response1</div>')
154
+ it 'closes the current modal and wait for its close animation to finish before starting the open animation of a second modal', (done) ->
155
+ up.modal.config.openAnimation = 'fade-in'
156
+ up.modal.config.openDuration = 5
157
+ up.modal.config.closeAnimation = 'fade-out'
158
+ up.modal.config.closeDuration = 50
167
159
 
168
- # First modal is starting opening animation
169
- expect(events).toEqual ['up:modal:open']
170
- expect($('.target')).toHaveText('response1')
160
+ events = []
161
+ u.each ['up:modal:open', 'up:modal:opened', 'up:modal:close', 'up:modal:closed'], (event) ->
162
+ up.on event, ->
163
+ events.push(event)
171
164
 
172
- u.setTimer 40, ->
173
- # First modal has completed opening animation
174
- expect(events).toEqual ['up:modal:open', 'up:modal:opened']
175
- expect($('.target')).toHaveText('response1')
165
+ up.modal.extract('.target', '<div class="target">response1</div>')
176
166
 
177
- up.modal.extract('.target', '<div class="target">response2</div>')
178
-
179
- # First modal is starting close animation. Second modal waits for that.
180
- expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:open', 'up:modal:close']
167
+ # First modal is starting opening animation
168
+ expect(events).toEqual ['up:modal:open']
181
169
  expect($('.target')).toHaveText('response1')
182
170
 
183
171
  u.setTimer 40, ->
184
-
185
- # Second modal is still waiting for first modal's closing animaton to finish.
186
- expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:open', 'up:modal:close']
172
+ # First modal has completed opening animation
173
+ expect(events).toEqual ['up:modal:open', 'up:modal:opened']
187
174
  expect($('.target')).toHaveText('response1')
188
175
 
189
- u.setTimer 100, ->
176
+ up.modal.extract('.target', '<div class="target">response2</div>')
190
177
 
191
- # First modal has finished closing, second modal has finished opening.
192
- expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:open', 'up:modal:close', 'up:modal:closed', 'up:modal:opened']
193
- expect($('.target')).toHaveText('response2')
178
+ expect($('.target')).toHaveText('response1')
194
179
 
195
- done()
180
+ u.setTimer 40, ->
196
181
 
197
- it 'closes an opening modal if a second modal starts opening before the first modal has finished its open animation', (done) ->
198
- up.modal.config.openAnimation = 'fade-in'
199
- up.modal.config.openDuration = 50
200
- up.modal.config.closeAnimation = 'fade-out'
201
- up.modal.config.closeDuration = 50
182
+ # Second modal is still waiting for first modal's closing animaton to finish.
183
+ expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:close']
184
+ expect($('.target')).toHaveText('response1')
202
185
 
203
- up.modal.extract('.target', '<div class="target">response1</div>')
186
+ u.setTimer 100, ->
204
187
 
205
- u.setTimer 10, ->
206
- # First modal is still in its opening animation
207
- expect($('.target')).toHaveText('response1')
188
+ # First modal has finished closing, second modal has finished opening.
189
+ expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:close', 'up:modal:closed', 'up:modal:open', 'up:modal:opened']
190
+ expect($('.target')).toHaveText('response2')
208
191
 
209
- up.modal.extract('.target', '<div class="target">response2</div>')
192
+ done()
210
193
 
211
- # First modal is starting close animation. Second modal waits for that.
212
- expect($('.target')).toHaveText('response1')
194
+ it 'closes an opening modal if a second modal starts opening before the first modal has finished its open animation', (done) ->
195
+ up.modal.config.openAnimation = 'fade-in'
196
+ up.modal.config.openDuration = 50
197
+ up.modal.config.closeAnimation = 'fade-out'
198
+ up.modal.config.closeDuration = 50
199
+
200
+ up.modal.extract('.target', '<div class="target">response1</div>')
213
201
 
214
202
  u.setTimer 10, ->
215
- # Second modal is still waiting for first modal's closing animaton to finish.
203
+ # First modal is still in its opening animation
216
204
  expect($('.target')).toHaveText('response1')
217
205
 
218
- u.setTimer 90, ->
219
- # First modal has finished closing, second modal has finished opening.
220
- expect($('.target')).toHaveText('response2')
206
+ up.modal.extract('.target', '<div class="target">response2</div>')
207
+
208
+ # First modal is starting close animation. Second modal waits for that.
209
+ expect($('.target')).toHaveText('response1')
221
210
 
222
- done()
211
+ u.setTimer 10, ->
212
+ # Second modal is still waiting for first modal's closing animaton to finish.
213
+ expect($('.target')).toHaveText('response1')
214
+
215
+ u.setTimer 90, ->
216
+ # First modal has finished closing, second modal has finished opening.
217
+ expect($('.target')).toHaveText('response2')
218
+
219
+ done()
223
220
 
224
221
  it 'uses the correct flavor config for the first and second modal', (done) ->
225
222
  up.modal.config.openAnimation = 'fade-in'
226
- up.modal.config.openDuration = 10
223
+ up.modal.config.openDuration = 20
227
224
  up.modal.config.closeAnimation = 'fade-out'
228
- up.modal.config.closeDuration = 10
225
+ up.modal.config.closeDuration = 20
229
226
  up.modal.flavor 'drawer',
230
227
  openAnimation: 'move-from-right'
231
228
  closeAnimation: 'move-to-right'
@@ -245,24 +242,31 @@ describe 'up.modal', ->
245
242
  { animation: 'fade-in', text: 'response1' }
246
243
  ]
247
244
 
248
- up.modal.extract('.target', '<div class="target">response2</div>', flavor: 'drawer')
245
+ u.setTimer 30, ->
249
246
 
250
- expect(animations).toEqual [
251
- { animation: 'fade-in', text: 'response1' },
252
- { animation: 'fade-out', text: 'response1' }
253
- ]
247
+ # first modal is now done animating
248
+ expect(animations).toEqual [
249
+ { animation: 'fade-in', text: 'response1' }
250
+ ]
254
251
 
255
- u.setTimer 20, ->
256
252
 
253
+ up.modal.extract('.target', '<div class="target">response2</div>', flavor: 'drawer')
257
254
  expect(animations).toEqual [
258
255
  { animation: 'fade-in', text: 'response1' },
259
256
  { animation: 'fade-out', text: 'response1' },
260
- { animation: 'move-from-right', text: 'response2' }
261
257
  ]
262
258
 
263
- expect($('.up-modal').attr('up-flavor')).toEqual('drawer')
259
+ u.setTimer 30, ->
264
260
 
265
- done()
261
+ expect(animations).toEqual [
262
+ { animation: 'fade-in', text: 'response1' },
263
+ { animation: 'fade-out', text: 'response1' },
264
+ { animation: 'move-from-right', text: 'response2' }
265
+ ]
266
+
267
+ expect($('.up-modal').attr('up-flavor')).toEqual('drawer')
268
+
269
+ done()
266
270
 
267
271
 
268
272
  it 'does not explode if up.modal.close() was called before the response was received', ->
@@ -278,13 +282,13 @@ describe 'up.modal', ->
278
282
 
279
283
  it 'returns the URL behind the modal overlay', (done) ->
280
284
  up.history.replace('/foo')
281
- expect(up.modal.coveredUrl()).toBeUndefined()
285
+ expect(up.modal.coveredUrl()).toBeMissing()
282
286
  visitPromise = up.modal.visit('/bar', target: '.container')
283
287
  @respondWith('<div class="container">text</div>')
284
288
  visitPromise.then ->
285
289
  expect(up.modal.coveredUrl()).toEndWith('/foo')
286
290
  up.modal.close().then ->
287
- expect(up.modal.coveredUrl()).toBeUndefined()
291
+ expect(up.modal.coveredUrl()).toBeMissing()
288
292
  done()
289
293
 
290
294
  describe 'up.modal.flavor', ->
@@ -327,7 +331,7 @@ describe 'up.modal', ->
327
331
  beforeEach ->
328
332
  @stubFollow = =>
329
333
  @$link = affix('a[href="/path"][up-modal=".target"]')
330
- @followSpy = up.modal.knife.mock('follow').and.returnValue(u.resolvedPromise())
334
+ @followSpy = up.modal.knife.mock('followAsap').and.returnValue(u.resolvedPromise())
331
335
  @defaultSpy = up.link.knife.mock('allowDefault').and.callFake((event) -> event.preventDefault())
332
336
 
333
337
  it 'opens the clicked link in a modal', ->
@@ -405,10 +409,9 @@ describe 'up.modal', ->
405
409
 
406
410
  describe 'when clicked inside a modal', ->
407
411
 
408
- it 'closes the open modal and prevents the default action', ->
409
- $modal = affix('.up-modal')
410
- $link = $modal.affix('a[up-close]') # link is within the modal
411
- up.hello($link)
412
+ it 'closes the open modal and prevents the default action', (done) ->
413
+ up.modal.extract('.target', '<div class="target"><a up-close>text</a></div>', animation: false)
414
+ $link = $('.up-modal a[up-close]') # link is within the modal
412
415
  wasDefaultPrevented = false
413
416
  wasClosed = false
414
417
  up.on 'click', 'a[up-close]', (event) ->
@@ -417,8 +420,10 @@ describe 'up.modal', ->
417
420
  up.on 'up:modal:close', ->
418
421
  wasClosed = true
419
422
  $link.click()
420
- expect(wasClosed).toBe(true)
421
- expect(wasDefaultPrevented).toBe(true)
423
+ u.nextFrame ->
424
+ expect(wasClosed).toBe(true)
425
+ expect(wasDefaultPrevented).toBe(true)
426
+ done()
422
427
 
423
428
  describe 'when no modal is open', ->
424
429
 
@@ -60,101 +60,115 @@ describe 'up.motion', ->
60
60
 
61
61
  describe 'when called with an element or selector', ->
62
62
 
63
- it 'cancels an existing animation on the given element by instantly jumping to the last frame', ->
64
- $element = affix('.element').text('content')
65
- up.animate($element, { 'font-size': '40px', 'opacity': '0.33' }, duration: 10000)
66
- up.motion.finish($element)
67
- expect($element.css('font-size')).toEqual('40px')
68
- expect($element.css('opacity')).toEqual('0.33')
69
-
70
- it 'cancels animations on children of the given element', ->
71
- $parent = affix('.element')
72
- $child = $parent.affix('.child')
73
- up.animate($child, { 'font-size': '40px' }, duration: 10000)
74
- up.motion.finish($parent)
75
- expect($child.css('font-size')).toEqual('40px')
76
-
77
- it 'does not cancel animations on other elements', ->
78
- $element1 = affix('.element1').text('content1')
79
- $element2 = affix('.element2').text('content2')
80
- up.animate($element1, 'fade-in', duration: 10000)
81
- up.animate($element2, 'fade-in', duration: 10000)
82
- up.motion.finish($element1)
83
- expect(Number($element1.css('opacity'))).toEqual(1)
84
- expect(Number($element2.css('opacity'))).toEqual(0, 0.1)
85
-
86
- it 'restores existing transitions on the element', ->
87
- $element = affix('.element').text('content')
88
- $element.css('transition': 'font-size 3s ease')
89
- oldTransitionProperty = $element.css('transition-property')
90
- expect(oldTransitionProperty).toContain('font-size') # be paranoid
91
- up.animate($element, 'fade-in', duration: 10000)
92
- up.motion.finish($element)
93
- expect(u.opacity($element)).toEqual(1)
94
- currentTransitionProperty = $element.css('transition-property')
95
- expect(currentTransitionProperty).toEqual(oldTransitionProperty)
96
- expect(currentTransitionProperty).toContain('font-size')
97
- expect(currentTransitionProperty).not.toContain('opacity')
63
+ describeCapability 'canCssTransition', ->
98
64
 
99
- it 'cancels an existing transition on the element by instantly jumping to the last frame', ->
100
- $old = affix('.old').text('old content')
101
- $new = affix('.new').text('new content')
65
+ it 'cancels an existing animation on the given element by instantly jumping to the last frame', ->
66
+ $element = affix('.element').text('content')
67
+ up.animate($element, { 'font-size': '40px', 'opacity': '0.33' }, duration: 10000)
68
+ up.motion.finish($element)
69
+ expect($element.css('font-size')).toEqual('40px')
70
+ expect($element.css('opacity')).toEqual('0.33')
71
+
72
+ it 'cancels animations on children of the given element', ->
73
+ $parent = affix('.element')
74
+ $child = $parent.affix('.child')
75
+ up.animate($child, { 'font-size': '40px' }, duration: 10000)
76
+ up.motion.finish($parent)
77
+ expect($child.css('font-size')).toEqual('40px')
78
+
79
+ it 'does not cancel animations on other elements', ->
80
+ $element1 = affix('.element1').text('content1')
81
+ $element2 = affix('.element2').text('content2')
82
+ up.animate($element1, 'fade-in', duration: 10000)
83
+ up.animate($element2, 'fade-in', duration: 10000)
84
+ up.motion.finish($element1)
85
+ expect(Number($element1.css('opacity'))).toEqual(1)
86
+ expect(Number($element2.css('opacity'))).toEqual(0, 0.1)
87
+
88
+ it 'restores existing transitions on the element', ->
89
+ $element = affix('.element').text('content')
90
+ $element.css('transition': 'font-size 3s ease')
91
+ oldTransitionProperty = $element.css('transition-property')
92
+ expect(oldTransitionProperty).toBeDefined()
93
+ expect(oldTransitionProperty).toContain('font-size') # be paranoid
94
+ up.animate($element, 'fade-in', duration: 10000)
95
+ up.motion.finish($element)
96
+ expect(u.opacity($element)).toEqual(1)
97
+ currentTransitionProperty = $element.css('transition-property')
98
+ expect(currentTransitionProperty).toEqual(oldTransitionProperty)
99
+ expect(currentTransitionProperty).toContain('font-size')
100
+ expect(currentTransitionProperty).not.toContain('opacity')
101
+
102
+ it 'cancels an existing transition on the element by instantly jumping to the last frame', ->
103
+ $old = affix('.old').text('old content')
104
+ $new = affix('.new').text('new content')
102
105
 
103
- up.morph($old, $new, 'cross-fade', duration: 2000)
104
- expect($('.up-ghost').length).toBe(2)
106
+ up.morph($old, $new, 'cross-fade', duration: 2000)
107
+ expect($('.up-ghost').length).toBe(2)
105
108
 
106
- up.motion.finish($old)
109
+ up.motion.finish($old)
107
110
 
108
- expect($('.up-ghost').length).toBe(0)
109
- expect($old.css('display')).toEqual('none')
110
- expect($new.css('display')).toEqual('block')
111
+ expect($('.up-ghost').length).toBe(0)
112
+ expect($old.css('display')).toEqual('none')
113
+ expect($new.css('display')).toEqual('block')
111
114
 
112
- it 'can be called on either element involved in a transition', ->
113
- $old = affix('.old').text('old content')
114
- $new = affix('.new').text('new content')
115
+ it 'can be called on either element involved in a transition', ->
116
+ $old = affix('.old').text('old content')
117
+ $new = affix('.new').text('new content')
115
118
 
116
- up.morph($old, $new, 'cross-fade', duration: 2000)
117
- expect($('.up-ghost').length).toBe(2)
119
+ up.morph($old, $new, 'cross-fade', duration: 2000)
120
+ expect($('.up-ghost').length).toBe(2)
118
121
 
119
- up.motion.finish($new)
122
+ up.motion.finish($new)
120
123
 
121
- expect($('.up-ghost').length).toBe(0)
122
- expect($old.css('display')).toEqual('none')
123
- expect($new.css('display')).toEqual('block')
124
+ expect($('.up-ghost').length).toBe(0)
125
+ expect($old.css('display')).toEqual('none')
126
+ expect($new.css('display')).toEqual('block')
124
127
 
125
128
 
126
- it 'cancels transitions on children of the given element', ->
127
- $parent = affix('.parent')
128
- $old = $parent.affix('.old').text('old content')
129
- $new = $parent.affix('.new').text('new content')
129
+ it 'cancels transitions on children of the given element', ->
130
+ $parent = affix('.parent')
131
+ $old = $parent.affix('.old').text('old content')
132
+ $new = $parent.affix('.new').text('new content')
130
133
 
131
- up.morph($old, $new, 'cross-fade', duration: 2000)
132
- expect($('.up-ghost').length).toBe(2)
134
+ up.morph($old, $new, 'cross-fade', duration: 2000)
135
+ expect($('.up-ghost').length).toBe(2)
133
136
 
134
- up.motion.finish($parent)
137
+ up.motion.finish($parent)
135
138
 
136
- expect($('.up-ghost').length).toBe(0)
137
- expect($old.css('display')).toEqual('none')
138
- expect($new.css('display')).toEqual('block')
139
+ expect($('.up-ghost').length).toBe(0)
140
+ expect($old.css('display')).toEqual('none')
141
+ expect($new.css('display')).toEqual('block')
142
+
143
+ describeFallback 'canCssTransition', ->
144
+
145
+ it 'does nothing'
139
146
 
140
147
  describe 'when called without arguments', ->
141
148
 
142
- it 'cancels all animations on the screen', ->
143
- $element1 = affix('.element1').text('content1')
144
- $element2 = affix('.element2').text('content2')
149
+ describeCapability 'canCssTransition', ->
150
+
151
+ it 'cancels all animations on the screen', ->
152
+ $element1 = affix('.element1').text('content1')
153
+ $element2 = affix('.element2').text('content2')
154
+
155
+ up.animate($element1, 'fade-in', duration: 3000)
156
+ up.animate($element2, 'fade-in', duration: 3000)
157
+
158
+ expect(u.opacity($element1)).toBeAround(0.0, 0.1)
159
+ expect(u.opacity($element2)).toBeAround(0.0, 0.1)
160
+
161
+ up.motion.finish()
145
162
 
146
- up.animate($element1, 'fade-in', duration: 3000)
147
- up.animate($element2, 'fade-in', duration: 3000)
163
+ $element1 = $('.element1')
164
+ $element2 = $('.element2')
165
+ expect(u.opacity($element1)).toBe(1.0)
166
+ expect(u.opacity($element2)).toBe(1.0)
148
167
 
149
- expect(u.opacity($element1)).toBeAround(0.0, 0.1)
150
- expect(u.opacity($element2)).toBeAround(0.0, 0.1)
168
+ describeFallback 'canCssTransition', ->
151
169
 
152
- up.motion.finish()
170
+ it 'does nothing'
153
171
 
154
- $element1 = $('.element1')
155
- $element2 = $('.element2')
156
- expect(u.opacity($element1)).toBe(1.0)
157
- expect(u.opacity($element2)).toBe(1.0)
158
172
 
159
173
  describe 'up.morph', ->
160
174