unpoly-rails 0.55.1 → 0.56.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 +59 -2
- data/dist/unpoly-bootstrap3.js +6 -4
- data/dist/unpoly-bootstrap3.min.js +1 -1
- data/dist/unpoly.js +1323 -805
- data/dist/unpoly.min.js +4 -3
- data/lib/assets/javascripts/unpoly-bootstrap3/{navigation-ext.coffee → feedback-ext.coffee} +2 -0
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +7 -7
- data/lib/assets/javascripts/unpoly/bus.coffee.erb +5 -6
- data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +127 -0
- data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +62 -32
- data/lib/assets/javascripts/unpoly/classes/url_set.coffee +27 -0
- data/lib/assets/javascripts/unpoly/dom.coffee.erb +78 -99
- data/lib/assets/javascripts/unpoly/feedback.coffee +147 -96
- data/lib/assets/javascripts/unpoly/form.coffee.erb +26 -2
- data/lib/assets/javascripts/unpoly/history.coffee +2 -1
- data/lib/assets/javascripts/unpoly/layout.coffee.erb +68 -12
- data/lib/assets/javascripts/unpoly/link.coffee.erb +10 -4
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +11 -9
- data/lib/assets/javascripts/unpoly/{motion.coffee → motion.coffee.erb} +184 -322
- data/lib/assets/javascripts/unpoly/popup.coffee.erb +13 -12
- data/lib/assets/javascripts/unpoly/radio.coffee +1 -1
- data/lib/assets/javascripts/unpoly/syntax.coffee +8 -17
- data/lib/assets/javascripts/unpoly/tooltip.coffee +11 -11
- data/lib/assets/javascripts/unpoly/util.coffee +332 -145
- 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 +1 -0
- data/spec_app/app/assets/stylesheets/integration_test.sass +1 -0
- data/spec_app/app/assets/stylesheets/jasmine_specs.sass +4 -0
- data/spec_app/app/views/motion_test/transitions.erb +13 -0
- data/spec_app/app/views/pages/start.erb +1 -0
- data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +11 -0
- data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +217 -102
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +162 -44
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +97 -10
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +3 -3
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +22 -20
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +344 -228
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +194 -0
- metadata +11 -4
@@ -11,11 +11,11 @@ describe 'up.motion', ->
|
|
11
11
|
up.animate($element, 'fade-in', duration: 200, easing: 'linear')
|
12
12
|
|
13
13
|
u.setTimer 5, ->
|
14
|
-
expect(
|
14
|
+
expect($element).toHaveOpacity(0.0, 0.25)
|
15
15
|
u.setTimer 100, ->
|
16
|
-
expect(
|
16
|
+
expect($element).toHaveOpacity(0.5, 0.25)
|
17
17
|
u.setTimer 200, ->
|
18
|
-
expect(
|
18
|
+
expect($element).toHaveOpacity(1.0, 0.25)
|
19
19
|
done()
|
20
20
|
|
21
21
|
it 'returns a promise that is fulfilled when the animation has completed', (done) ->
|
@@ -27,15 +27,52 @@ describe 'up.motion', ->
|
|
27
27
|
|
28
28
|
u.setTimer 50, ->
|
29
29
|
expect(resolveSpy).not.toHaveBeenCalled()
|
30
|
-
u.setTimer
|
30
|
+
u.setTimer 50 + (timingTolerance = 120), ->
|
31
31
|
expect(resolveSpy).toHaveBeenCalled()
|
32
32
|
done()
|
33
33
|
|
34
34
|
it 'cancels an existing animation on the element by instantly jumping to the last frame', asyncSpec (next) ->
|
35
35
|
$element = affix('.element').text('content')
|
36
36
|
up.animate($element, { 'font-size': '40px' }, duration: 10000, easing: 'linear')
|
37
|
-
|
38
|
-
next =>
|
37
|
+
|
38
|
+
next =>
|
39
|
+
up.animate($element, { 'fade-in' }, duration: 100, easing: 'linear')
|
40
|
+
|
41
|
+
next =>
|
42
|
+
expect($element.css('font-size')).toEqual('40px')
|
43
|
+
|
44
|
+
describe 'when up.animate() is called from inside an animation function', ->
|
45
|
+
|
46
|
+
it 'animates', (done) ->
|
47
|
+
$element = affix('.element').text('content')
|
48
|
+
|
49
|
+
animation = ($element, options) ->
|
50
|
+
u.writeInlineStyle($element, opacity: 0)
|
51
|
+
up.animate($element, { opacity: 1 }, options)
|
52
|
+
|
53
|
+
up.animate($element, animation, duration: 300, easing: 'linear')
|
54
|
+
|
55
|
+
u.setTimer 5, ->
|
56
|
+
expect($element).toHaveOpacity(0.0, 0.25)
|
57
|
+
u.setTimer 150, ->
|
58
|
+
expect($element).toHaveOpacity(0.5, 0.25)
|
59
|
+
u.setTimer 300, ->
|
60
|
+
expect($element).toHaveOpacity(1.0, 0.25)
|
61
|
+
done()
|
62
|
+
|
63
|
+
it "finishes animations only once", (done) ->
|
64
|
+
$element = affix('.element').text('content')
|
65
|
+
|
66
|
+
animation = ($element, options) ->
|
67
|
+
u.writeInlineStyle($element, opacity: 0)
|
68
|
+
up.animate($element, { opacity: 1 }, options)
|
69
|
+
|
70
|
+
up.animate($element, animation, duration: 200, easing: 'linear')
|
71
|
+
|
72
|
+
u.nextFrame =>
|
73
|
+
expect(up.motion.finishCount()).toEqual(1)
|
74
|
+
done()
|
75
|
+
|
39
76
|
|
40
77
|
describe 'with animations disabled globally', ->
|
41
78
|
|
@@ -77,7 +114,7 @@ describe 'up.motion', ->
|
|
77
114
|
|
78
115
|
next =>
|
79
116
|
expect($element.css('font-size')).toEqual('40px')
|
80
|
-
expect(
|
117
|
+
expect(u.opacity($element, 'opacity')).toBeAround(0.5, 0.01) # Safari sometimes has rounding errors
|
81
118
|
|
82
119
|
it 'cancels animations on children of the given element', asyncSpec (next) ->
|
83
120
|
$parent = affix('.element')
|
@@ -103,7 +140,7 @@ describe 'up.motion', ->
|
|
103
140
|
expect(Number($element1.css('opacity'))).toEqual(1)
|
104
141
|
expect(Number($element2.css('opacity'))).toBeAround(0, 0.1)
|
105
142
|
|
106
|
-
it 'restores
|
143
|
+
it 'restores CSS transitions from before the Unpoly animation', asyncSpec (next) ->
|
107
144
|
$element = affix('.element').text('content')
|
108
145
|
$element.css('transition': 'font-size 3s ease')
|
109
146
|
oldTransitionProperty = $element.css('transition-property')
|
@@ -121,38 +158,80 @@ describe 'up.motion', ->
|
|
121
158
|
expect(currentTransitionProperty).toContain('font-size')
|
122
159
|
expect(currentTransitionProperty).not.toContain('opacity')
|
123
160
|
|
124
|
-
it '
|
125
|
-
$
|
126
|
-
|
161
|
+
it 'pauses an existing CSS transitions and restores it once the Unpoly animation is done', asyncSpec (next) ->
|
162
|
+
$element = affix('.element').text('content').css
|
163
|
+
backgroundColor: 'yellow'
|
164
|
+
fontSize: '10px'
|
165
|
+
height: '20px'
|
127
166
|
|
128
|
-
|
167
|
+
expect(parseFloat($element.css('fontSize'))).toBeAround(10, 0.1)
|
168
|
+
expect(parseFloat($element.css('height'))).toBeAround(20, 0.1)
|
169
|
+
|
170
|
+
next.after 10, =>
|
171
|
+
$element.css
|
172
|
+
transition: 'font-size 500ms linear, height 500ms linear'
|
173
|
+
fontSize: '100px'
|
174
|
+
height: '200px'
|
175
|
+
|
176
|
+
next.after 250, =>
|
177
|
+
# Original CSS transition should now be ~50% done
|
178
|
+
@fontSizeBeforeAnimate = parseFloat($element.css('fontSize'))
|
179
|
+
@heightBeforeAnimate = parseFloat($element.css('height'))
|
180
|
+
|
181
|
+
expect(@fontSizeBeforeAnimate).toBeAround(0.5 * (100 - 10), 20)
|
182
|
+
expect(@heightBeforeAnimate).toBeAround(0.5 * (200 - 20), 40)
|
183
|
+
|
184
|
+
up.animate($element, 'fade-in', duration: 500, easing: 'linear')
|
185
|
+
|
186
|
+
next.after 250, =>
|
187
|
+
# Original CSS transition should remain paused at ~50%
|
188
|
+
# Unpoly animation should now be ~50% done
|
189
|
+
expect(parseFloat($element.css('fontSize'))).toBeAround(@fontSizeBeforeAnimate, 2)
|
190
|
+
expect(parseFloat($element.css('height'))).toBeAround(@heightBeforeAnimate, 2)
|
191
|
+
expect(parseFloat($element.css('opacity'))).toBeAround(0.5, 0.3)
|
192
|
+
|
193
|
+
next.after 250, =>
|
194
|
+
# Unpoly animation should now be done
|
195
|
+
# The original transition resumes. For technical reasons it will take
|
196
|
+
# its full duration for the remaining frames of the transition.
|
197
|
+
expect(parseFloat($element.css('opacity'))).toBeAround(1.0, 0.3)
|
198
|
+
|
199
|
+
next.after (500 + (tolerance = 125)), =>
|
200
|
+
expect(parseFloat($element.css('fontSize'))).toBeAround(100, 20)
|
201
|
+
expect(parseFloat($element.css('height'))).toBeAround(200, 40)
|
202
|
+
|
203
|
+
|
204
|
+
it 'cancels an existing transition on the old element by instantly jumping to the last frame', asyncSpec (next) ->
|
205
|
+
$v1 = affix('.element').text('v1')
|
206
|
+
$v2 = affix('.element').text('v2')
|
207
|
+
|
208
|
+
up.morph($v1, $v2, 'cross-fade', duration: 200)
|
129
209
|
|
130
210
|
next =>
|
131
|
-
expect($(
|
211
|
+
expect($v1).toHaveOpacity(1.0, 0.2)
|
212
|
+
expect($v2).toHaveOpacity(0.0, 0.2)
|
132
213
|
|
133
|
-
|
134
|
-
up.motion.finish($old)
|
214
|
+
up.motion.finish($v1)
|
135
215
|
|
136
|
-
next
|
137
|
-
expect($
|
138
|
-
expect($
|
139
|
-
expect($new.css('display')).toEqual('block')
|
216
|
+
next =>
|
217
|
+
expect($v1).toBeDetached()
|
218
|
+
expect($v2).toHaveOpacity(1.0, 0.2)
|
140
219
|
|
141
|
-
it '
|
142
|
-
$
|
143
|
-
$
|
220
|
+
it 'cancels an existing transition on the new element by instantly jumping to the last frame', asyncSpec (next) ->
|
221
|
+
$v1 = affix('.element').text('v1')
|
222
|
+
$v2 = affix('.element').text('v2')
|
144
223
|
|
145
|
-
up.morph($
|
224
|
+
up.morph($v1, $v2, 'cross-fade', duration: 200)
|
146
225
|
|
147
226
|
next =>
|
148
|
-
expect($(
|
227
|
+
expect($v1).toHaveOpacity(1.0, 0.2)
|
228
|
+
expect($v2).toHaveOpacity(0.0, 0.2)
|
149
229
|
|
150
|
-
up.motion.finish($
|
230
|
+
up.motion.finish($v2)
|
151
231
|
|
152
232
|
next =>
|
153
|
-
expect($
|
154
|
-
expect($
|
155
|
-
expect($new.css('display')).toEqual('block')
|
233
|
+
expect($v1).toBeDetached()
|
234
|
+
expect($v2).toHaveOpacity(1.0, 0.2)
|
156
235
|
|
157
236
|
|
158
237
|
it 'cancels transitions on children of the given element', asyncSpec (next) ->
|
@@ -163,14 +242,53 @@ describe 'up.motion', ->
|
|
163
242
|
up.morph($old, $new, 'cross-fade', duration: 2000)
|
164
243
|
|
165
244
|
next =>
|
166
|
-
expect($(
|
245
|
+
expect($old).toHaveOpacity(1.0, 0.1)
|
246
|
+
expect($new).toHaveOpacity(0.0, 0.1)
|
167
247
|
|
168
248
|
up.motion.finish($parent)
|
169
249
|
|
170
250
|
next =>
|
171
|
-
expect($
|
172
|
-
expect($
|
173
|
-
|
251
|
+
expect($old).toBeDetached()
|
252
|
+
expect($new).toHaveOpacity(1.0)
|
253
|
+
|
254
|
+
|
255
|
+
it 'does not leave .up-bounds elements in the DOM', asyncSpec (next) ->
|
256
|
+
$old = affix('.old').text('old content')
|
257
|
+
$new = affix('.new').text('new content')
|
258
|
+
|
259
|
+
up.morph($old, $new, 'cross-fade', duration: 2000)
|
260
|
+
|
261
|
+
next =>
|
262
|
+
up.motion.finish($old)
|
263
|
+
|
264
|
+
next =>
|
265
|
+
expect($old).toBeDetached()
|
266
|
+
expect($('.up-bounds').length).toBe(0)
|
267
|
+
|
268
|
+
|
269
|
+
it 'emits an up:motion:finish event on the given animating element, so custom animation functions can react to the finish request', asyncSpec (next) ->
|
270
|
+
$element = affix('.element').text('element text')
|
271
|
+
listener = jasmine.createSpy('finish event listener')
|
272
|
+
$element.on('up:motion:finish', listener)
|
273
|
+
|
274
|
+
up.animate($element, 'fade-in')
|
275
|
+
|
276
|
+
next =>
|
277
|
+
expect(listener).not.toHaveBeenCalled()
|
278
|
+
up.motion.finish()
|
279
|
+
|
280
|
+
next =>
|
281
|
+
expect(listener).toHaveBeenCalled()
|
282
|
+
|
283
|
+
|
284
|
+
it 'does not emit an up:motion:finish event if no element is animating', asyncSpec (next) ->
|
285
|
+
listener = jasmine.createSpy('finish event listener')
|
286
|
+
up.on('up:motion:finish', listener)
|
287
|
+
up.motion.finish()
|
288
|
+
|
289
|
+
next =>
|
290
|
+
expect(listener).not.toHaveBeenCalled()
|
291
|
+
|
174
292
|
|
175
293
|
describe 'when called without arguments', ->
|
176
294
|
|
@@ -195,120 +313,196 @@ describe 'up.motion', ->
|
|
195
313
|
|
196
314
|
describe 'up.morph', ->
|
197
315
|
|
198
|
-
it 'transitions between two element by
|
316
|
+
it 'transitions between two element by absolutely positioning one element above the other', asyncSpec (next) ->
|
317
|
+
$old = affix('.old').text('old content').css(width: '200px', width: '200px')
|
318
|
+
$new = affix('.new').text('new content').css(width: '200px', width: '200px').detach()
|
319
|
+
|
320
|
+
oldDims = u.measure($old)
|
199
321
|
|
200
|
-
$old = affix('.old').text('old content').css(
|
201
|
-
position: 'absolute'
|
202
|
-
top: '10px'
|
203
|
-
left: '11px',
|
204
|
-
width: '12px',
|
205
|
-
height: '13px'
|
206
|
-
)
|
207
|
-
$new = affix('.new').text('new content').css(
|
208
|
-
position: 'absolute'
|
209
|
-
top: '20px'
|
210
|
-
left: '21px',
|
211
|
-
width: '22px',
|
212
|
-
height: '23px'
|
213
|
-
)
|
214
322
|
up.morph($old, $new, 'cross-fade', duration: 200, easing: 'linear')
|
215
323
|
|
216
324
|
next =>
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
$
|
226
|
-
|
227
|
-
|
228
|
-
expect($
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
)
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
expect(
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
)
|
268
|
-
|
269
|
-
|
270
|
-
|
271
|
-
|
272
|
-
|
273
|
-
|
274
|
-
|
275
|
-
|
276
|
-
|
277
|
-
|
278
|
-
|
279
|
-
|
280
|
-
next.after 110, =>
|
281
|
-
# Once our two ghosts have rendered their visual effect,
|
282
|
-
# we remove them from the DOM.
|
283
|
-
expect(@$newGhost).not.toBeInDOM()
|
284
|
-
expect(@$oldGhost).not.toBeInDOM()
|
285
|
-
|
286
|
-
# The old element is still in the DOM, but hidden.
|
287
|
-
# Morphing does *not* remove the target element.
|
288
|
-
expect($old.css('display')).toEqual('none')
|
289
|
-
expect($new.css(['display', 'visibility'])).toEqual(
|
290
|
-
display: 'block',
|
291
|
-
visibility: 'visible'
|
292
|
-
)
|
293
|
-
|
294
|
-
it 'cancels an existing transition on the element by instantly jumping to the last frame', asyncSpec (next) ->
|
295
|
-
$old = affix('.old').text('old content')
|
296
|
-
$new = affix('.new').text('new content')
|
297
|
-
|
298
|
-
up.morph($old, $new, 'cross-fade', duration: 200)
|
325
|
+
expect(u.measure($old)).toEqual(oldDims)
|
326
|
+
expect(u.measure($new)).toEqual(oldDims)
|
327
|
+
|
328
|
+
expect(u.opacity($old)).toBeAround(1.0, 0.25)
|
329
|
+
expect(u.opacity($new)).toBeAround(0.0, 0.25)
|
330
|
+
|
331
|
+
next.after 100, =>
|
332
|
+
expect(u.opacity($old)).toBeAround(0.5, 0.25)
|
333
|
+
expect(u.opacity($new)).toBeAround(0.5, 0.25)
|
334
|
+
|
335
|
+
next.after (100 + (tolerance = 110)), =>
|
336
|
+
expect(u.opacity($new)).toBeAround(1.0, 0.25)
|
337
|
+
expect($old).toBeDetached()
|
338
|
+
|
339
|
+
it 'does not change the position of sibling elements (as long as the old and new elements are of equal size)', asyncSpec (next) ->
|
340
|
+
$container = affix('.container')
|
341
|
+
|
342
|
+
$before = $container.affix('.before').css(margin: '20px')
|
343
|
+
$old = $container.affix('.old').text('old content').css(width: '200px', width: '200px', margin: '20px')
|
344
|
+
$new = $container.affix('.new').text('new content').css(width: '200px', width: '200px', margin: '20px').detach()
|
345
|
+
$after = $container.affix('.before').css(margin: '20px')
|
346
|
+
|
347
|
+
beforeDims = u.measure($before)
|
348
|
+
afterDims = u.measure($after)
|
349
|
+
|
350
|
+
up.morph($old, $new, 'cross-fade', duration: 30, easing: 'linear')
|
351
|
+
|
352
|
+
next =>
|
353
|
+
expect(u.measure($before)).toEqual(beforeDims)
|
354
|
+
expect(u.measure($after)).toEqual(afterDims)
|
355
|
+
|
356
|
+
next.after 50, =>
|
357
|
+
expect(u.measure($before)).toEqual(beforeDims)
|
358
|
+
expect(u.measure($after)).toEqual(afterDims)
|
359
|
+
|
360
|
+
it 'transitions between two elements that are already positioned absolutely', asyncSpec (next) ->
|
361
|
+
elementStyles =
|
362
|
+
position: 'absolute'
|
363
|
+
left: '30px'
|
364
|
+
top: '30px'
|
365
|
+
width: '200px'
|
366
|
+
width: '200px'
|
367
|
+
$old = affix('.old').text('old content').css(elementStyles)
|
368
|
+
$new = affix('.new').text('new content').css(elementStyles).detach()
|
369
|
+
|
370
|
+
oldDims = u.measure($old)
|
371
|
+
|
372
|
+
up.morph($old, $new, 'cross-fade', duration: 100, easing: 'linear')
|
373
|
+
|
374
|
+
next =>
|
375
|
+
expect(u.measure($old)).toEqual(oldDims)
|
376
|
+
expect(u.measure($new)).toEqual(oldDims)
|
377
|
+
|
378
|
+
next.after (100 + (timingTolerance = 120)), =>
|
379
|
+
expect($old).toBeDetached()
|
380
|
+
expect(u.measure($new)).toEqual(oldDims)
|
381
|
+
|
382
|
+
it 'cancels an existing transition on the new element by instantly jumping to the last frame', asyncSpec (next) ->
|
383
|
+
$v1 = affix('.element').text('v1')
|
384
|
+
$v2 = affix('.element').text('v2')
|
385
|
+
$v3 = affix('.element').text('v3')
|
386
|
+
|
387
|
+
up.morph($v1, $v2, 'cross-fade', duration: 200)
|
299
388
|
|
300
389
|
next =>
|
301
|
-
|
302
|
-
expect(
|
390
|
+
expect($v1).toHaveOpacity(1.0, 0.2)
|
391
|
+
expect($v2).toHaveOpacity(0.0, 0.2)
|
303
392
|
|
304
|
-
up.morph($
|
393
|
+
up.morph($v2, $v3, 'cross-fade', duration: 200)
|
305
394
|
|
306
395
|
next =>
|
307
|
-
|
308
|
-
|
309
|
-
expect(
|
310
|
-
|
311
|
-
|
396
|
+
expect($v1).toBeDetached()
|
397
|
+
expect($v2).toHaveOpacity(1.0, 0.2)
|
398
|
+
expect($v3).toHaveOpacity(0.0, 0.2)
|
399
|
+
|
400
|
+
it 'detaches the old element in the DOM', (done) ->
|
401
|
+
$v1 = affix('.element').text('v1')
|
402
|
+
$v2 = affix('.element').text('v2')
|
403
|
+
|
404
|
+
morphDone = up.morph($v1, $v2, 'cross-fade', duration: 5)
|
405
|
+
|
406
|
+
morphDone.then ->
|
407
|
+
expect($v1).toBeDetached()
|
408
|
+
expect($v2).toBeAttached()
|
409
|
+
done()
|
410
|
+
|
411
|
+
it 'does not leave .up-bounds elements in the DOM', (done) ->
|
412
|
+
$v1 = affix('.element').text('v1')
|
413
|
+
$v2 = affix('.element').text('v2')
|
414
|
+
|
415
|
+
morphDone = up.morph($v1, $v2, 'cross-fade', duration: 5)
|
416
|
+
|
417
|
+
morphDone.then ->
|
418
|
+
expect('.up-bounds').not.toExist()
|
419
|
+
done()
|
420
|
+
|
421
|
+
|
422
|
+
describe 'when up.animate() is called from inside a transition function', ->
|
423
|
+
|
424
|
+
it 'animates', asyncSpec (next) ->
|
425
|
+
$old = affix('.old').text('old content')
|
426
|
+
$new = affix('.new').text('new content').detach()
|
427
|
+
|
428
|
+
oldDims = u.measure($old)
|
429
|
+
|
430
|
+
transition = ($old, $new, options) ->
|
431
|
+
up.animate($old, 'fade-out', options)
|
432
|
+
up.animate($new, 'fade-in', options)
|
433
|
+
|
434
|
+
up.morph($old, $new, transition, duration: 200, easing: 'linear')
|
435
|
+
|
436
|
+
next =>
|
437
|
+
expect(u.measure($old)).toEqual(oldDims)
|
438
|
+
expect(u.measure($new)).toEqual(oldDims)
|
439
|
+
|
440
|
+
expect(u.opacity($old)).toBeAround(1.0, 0.25)
|
441
|
+
expect(u.opacity($new)).toBeAround(0.0, 0.25)
|
442
|
+
|
443
|
+
next.after 100, =>
|
444
|
+
expect(u.opacity($old)).toBeAround(0.5, 0.25)
|
445
|
+
expect(u.opacity($new)).toBeAround(0.5, 0.25)
|
446
|
+
|
447
|
+
next.after (100 + (tolerance = 110)), =>
|
448
|
+
expect(u.opacity($new)).toBeAround(1.0, 0.1)
|
449
|
+
expect($old).toBeDetached()
|
450
|
+
expect($new).toBeAttached()
|
451
|
+
|
452
|
+
it 'finishes animations only once', asyncSpec (next) ->
|
453
|
+
$old = affix('.old').text('old content')
|
454
|
+
$new = affix('.new').text('new content').detach()
|
455
|
+
|
456
|
+
transition = ($old, $new, options) ->
|
457
|
+
up.animate($old, 'fade-out', options)
|
458
|
+
up.animate($new, 'fade-in', options)
|
459
|
+
|
460
|
+
up.morph($old, $new, transition, duration: 200, easing: 'linear')
|
461
|
+
|
462
|
+
next ->
|
463
|
+
expect(up.motion.finishCount()).toEqual(1)
|
464
|
+
|
465
|
+
describe 'when up.morph() is called from inside a transition function', ->
|
466
|
+
|
467
|
+
it 'morphs', asyncSpec (next) ->
|
468
|
+
$old = affix('.old').text('old content')
|
469
|
+
$new = affix('.new').text('new content').detach()
|
470
|
+
|
471
|
+
oldDims = u.measure($old)
|
472
|
+
|
473
|
+
transition = ($old, $new, options) ->
|
474
|
+
up.morph($old, $new, 'cross-fade', options)
|
475
|
+
|
476
|
+
up.morph($old, $new, transition, duration: 200, easing: 'linear')
|
477
|
+
|
478
|
+
next =>
|
479
|
+
expect(u.measure($old)).toEqual(oldDims)
|
480
|
+
expect(u.measure($new)).toEqual(oldDims)
|
481
|
+
|
482
|
+
expect(u.opacity($old)).toBeAround(1.0, 0.25)
|
483
|
+
expect(u.opacity($new)).toBeAround(0.0, 0.25)
|
484
|
+
|
485
|
+
next.after 100, =>
|
486
|
+
expect(u.opacity($old)).toBeAround(0.5, 0.25)
|
487
|
+
expect(u.opacity($new)).toBeAround(0.5, 0.25)
|
488
|
+
|
489
|
+
next.after (100 + (tolerance = 110)), =>
|
490
|
+
expect(u.opacity($new)).toBeAround(1.0, 0.25)
|
491
|
+
expect($old).toBeDetached()
|
492
|
+
expect($new).toBeAttached()
|
493
|
+
|
494
|
+
it "finishes animations only once", asyncSpec (next) ->
|
495
|
+
$old = affix('.old').text('old content')
|
496
|
+
$new = affix('.new').text('new content').detach()
|
497
|
+
|
498
|
+
transition = ($old, $new, options) ->
|
499
|
+
up.morph($old, $new, 'cross-fade', options)
|
500
|
+
|
501
|
+
up.morph($old, $new, transition, duration: 200, easing: 'linear')
|
502
|
+
|
503
|
+
next ->
|
504
|
+
expect(up.motion.finishCount()).toEqual(1)
|
505
|
+
|
312
506
|
|
313
507
|
describe 'with { reveal: true } option', ->
|
314
508
|
|
@@ -325,14 +519,11 @@ describe 'up.motion', ->
|
|
325
519
|
|
326
520
|
expect($container.scrollTop()).toEqual(300)
|
327
521
|
|
328
|
-
$new = affix('.new').
|
522
|
+
$new = affix('.new').css(height: '600px').detach()
|
329
523
|
|
330
524
|
up.morph($old, $new, 'cross-fade', duration: 50, reveal: true)
|
331
525
|
|
332
526
|
next =>
|
333
|
-
$oldGhost = $('.old.up-ghost')
|
334
|
-
$newGhost = $('.new.up-ghost')
|
335
|
-
|
336
527
|
# Container is scrolled up due to { reveal: true } option.
|
337
528
|
# Since $old and $new are sitting in the same viewport with a
|
338
529
|
# single shared scrollbar, this will make the ghost for $old jump.
|
@@ -340,41 +531,41 @@ describe 'up.motion', ->
|
|
340
531
|
|
341
532
|
# See that the ghost for $new is aligned with the top edge
|
342
533
|
# of the viewport.
|
343
|
-
expect($
|
534
|
+
expect($new.offset().top).toEqual(0)
|
344
535
|
|
345
|
-
# The
|
536
|
+
# The absolitized $old is shifted upwards to make it looks like it
|
346
537
|
# was at the scroll position before we revealed $new.
|
347
|
-
expect($
|
538
|
+
expect($old.offset().top).toEqual(-300)
|
348
539
|
|
349
540
|
describe 'with animations disabled globally', ->
|
350
541
|
|
351
542
|
beforeEach ->
|
352
543
|
up.motion.config.enabled = false
|
353
544
|
|
354
|
-
it "doesn't animate and
|
545
|
+
it "doesn't animate and detaches the old element instead", asyncSpec (next) ->
|
355
546
|
$old = affix('.old').text('old content')
|
356
547
|
$new = affix('.new').text('new content')
|
357
548
|
up.morph($old, $new, 'cross-fade', duration: 1000)
|
358
549
|
|
359
550
|
next =>
|
360
|
-
expect($old).
|
361
|
-
expect($new).
|
362
|
-
expect($new
|
551
|
+
expect($old).toBeDetached()
|
552
|
+
expect($new).toBeAttached()
|
553
|
+
expect($new).toHaveOpacity(1.0)
|
363
554
|
|
364
555
|
|
365
556
|
[false, null, undefined, 'none', 'none/none', '', [], [undefined, null], ['none', 'none'], ['none', {}]].forEach (noneTransition) ->
|
366
557
|
|
367
|
-
describe "when called with a `#{noneTransition}` transition", ->
|
558
|
+
describe "when called with a `#{JSON.stringify(noneTransition)}` transition", ->
|
368
559
|
|
369
|
-
it "doesn't animate and
|
560
|
+
it "doesn't animate and detaches the old element instead", asyncSpec (next) ->
|
370
561
|
$old = affix('.old').text('old content')
|
371
562
|
$new = affix('.new').text('new content')
|
372
563
|
up.morph($old, $new, noneTransition, duration: 1000)
|
373
564
|
|
374
565
|
next =>
|
375
|
-
expect($old).
|
376
|
-
expect($new).
|
377
|
-
expect($new
|
566
|
+
expect($old).toBeDetached()
|
567
|
+
expect($new).toBeAttached()
|
568
|
+
expect($new).toHaveOpacity(1.0)
|
378
569
|
|
379
570
|
|
380
571
|
describe 'up.transition', ->
|
@@ -384,78 +575,3 @@ describe 'up.motion', ->
|
|
384
575
|
describe 'up.animation', ->
|
385
576
|
|
386
577
|
it 'should have tests'
|
387
|
-
|
388
|
-
describe 'up.motion.prependCopy', ->
|
389
|
-
|
390
|
-
afterEach ->
|
391
|
-
$('.up-bounds, .up-ghost, .fixture').remove()
|
392
|
-
|
393
|
-
it 'clones the given element into a .up-ghost-bounds container and inserts it as a sibling before the element', ->
|
394
|
-
$element = affix('.element').text('element text')
|
395
|
-
up.motion.prependCopy($element)
|
396
|
-
$bounds = $element.prev()
|
397
|
-
expect($bounds).toExist()
|
398
|
-
expect($bounds).toHaveClass('up-bounds')
|
399
|
-
$ghost = $bounds.children(':first')# $ghost.find('.element')
|
400
|
-
expect($ghost).toExist()
|
401
|
-
expect($ghost).toHaveClass('element')
|
402
|
-
expect($ghost).toHaveText('element text')
|
403
|
-
|
404
|
-
it 'removes <script> tags from the cloned element', ->
|
405
|
-
$element = affix('.element')
|
406
|
-
$('<script></script>').appendTo($element)
|
407
|
-
up.motion.prependCopy($element)
|
408
|
-
$ghost = $('.up-ghost')
|
409
|
-
expect($ghost.find('script')).not.toExist()
|
410
|
-
|
411
|
-
it 'absolutely positions the ghost over the given element', ->
|
412
|
-
$element = affix('.element')
|
413
|
-
up.motion.prependCopy($element)
|
414
|
-
$ghost = $('.up-ghost')
|
415
|
-
expect($ghost.offset()).toEqual($element.offset())
|
416
|
-
expect($ghost.width()).toEqual($element.width())
|
417
|
-
expect($ghost.height()).toEqual($element.height())
|
418
|
-
|
419
|
-
it 'accurately positions the ghost over an element with margins', ->
|
420
|
-
$element = affix('.element').css(margin: '40px')
|
421
|
-
up.motion.prependCopy($element)
|
422
|
-
$ghost = $('.up-ghost')
|
423
|
-
expect($ghost.offset()).toEqual($element.offset())
|
424
|
-
|
425
|
-
it "doesn't change the position of a child whose margins no longer collapse", ->
|
426
|
-
$element = affix('.element')
|
427
|
-
$child = $('<div class="child"></div>').css(margin: '40px').appendTo($element)
|
428
|
-
up.motion.prependCopy($element)
|
429
|
-
$clonedChild = $('.up-ghost .child')
|
430
|
-
expect($clonedChild.offset().top).toBeAround($child.offset().top, 0.5)
|
431
|
-
expect($clonedChild.offset().left).toBeAround($child.offset().left, 0.5)
|
432
|
-
|
433
|
-
it 'correctly positions the ghost over an element within a scrolled body', ->
|
434
|
-
$body = $('body')
|
435
|
-
$element1 = $('<div class="fixture"></div>').css(height: '75px').prependTo($body)
|
436
|
-
$element2 = $('<div class="fixture"></div>').css(height: '100px').insertAfter($element1)
|
437
|
-
$body.scrollTop(17)
|
438
|
-
{ $bounds, $ghost } = up.motion.prependCopy($element2)
|
439
|
-
expect($bounds.css('position')).toBe('absolute')
|
440
|
-
expect($bounds.css('top')).toEqual('75px')
|
441
|
-
expect($ghost.css('position')).toBe('static')
|
442
|
-
|
443
|
-
it 'correctly positions the ghost over an element within a viewport with overflow-y: scroll'
|
444
|
-
|
445
|
-
it 'converts fixed elements within the copies to absolutely positioning', ->
|
446
|
-
$element = affix('.element').css
|
447
|
-
position: 'absolute'
|
448
|
-
top: '50px'
|
449
|
-
left: '50px'
|
450
|
-
$fixedChild = $('<div class="fixed-child" up-fixed></div>').css
|
451
|
-
position: 'fixed'
|
452
|
-
left: '77px'
|
453
|
-
top: '77px'
|
454
|
-
$fixedChild.appendTo($element)
|
455
|
-
up.motion.prependCopy($element, $('body'))
|
456
|
-
$fixedChildGhost = $('.up-ghost .fixed-child')
|
457
|
-
expect($fixedChildGhost.css(['position', 'left', 'top'])).toEqual
|
458
|
-
position: 'absolute',
|
459
|
-
left: '27px',
|
460
|
-
top: '27px'
|
461
|
-
|