unpoly-rails 0.26.2 → 0.27.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.

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