unpoly-rails 0.55.1 → 0.56.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 +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
|
-
|