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.

Files changed (50) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +59 -2
  3. data/dist/unpoly-bootstrap3.js +6 -4
  4. data/dist/unpoly-bootstrap3.min.js +1 -1
  5. data/dist/unpoly.js +1323 -805
  6. data/dist/unpoly.min.js +4 -3
  7. data/lib/assets/javascripts/unpoly-bootstrap3/{navigation-ext.coffee → feedback-ext.coffee} +2 -0
  8. data/lib/assets/javascripts/unpoly/browser.coffee.erb +7 -7
  9. data/lib/assets/javascripts/unpoly/bus.coffee.erb +5 -6
  10. data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +127 -0
  11. data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +1 -1
  12. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +62 -32
  13. data/lib/assets/javascripts/unpoly/classes/url_set.coffee +27 -0
  14. data/lib/assets/javascripts/unpoly/dom.coffee.erb +78 -99
  15. data/lib/assets/javascripts/unpoly/feedback.coffee +147 -96
  16. data/lib/assets/javascripts/unpoly/form.coffee.erb +26 -2
  17. data/lib/assets/javascripts/unpoly/history.coffee +2 -1
  18. data/lib/assets/javascripts/unpoly/layout.coffee.erb +68 -12
  19. data/lib/assets/javascripts/unpoly/link.coffee.erb +10 -4
  20. data/lib/assets/javascripts/unpoly/modal.coffee.erb +11 -9
  21. data/lib/assets/javascripts/unpoly/{motion.coffee → motion.coffee.erb} +184 -322
  22. data/lib/assets/javascripts/unpoly/popup.coffee.erb +13 -12
  23. data/lib/assets/javascripts/unpoly/radio.coffee +1 -1
  24. data/lib/assets/javascripts/unpoly/syntax.coffee +8 -17
  25. data/lib/assets/javascripts/unpoly/tooltip.coffee +11 -11
  26. data/lib/assets/javascripts/unpoly/util.coffee +332 -145
  27. data/lib/unpoly/rails/version.rb +1 -1
  28. data/package.json +1 -1
  29. data/spec_app/Gemfile.lock +1 -1
  30. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  31. data/spec_app/app/assets/stylesheets/integration_test.sass +1 -0
  32. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +4 -0
  33. data/spec_app/app/views/motion_test/transitions.erb +13 -0
  34. data/spec_app/app/views/pages/start.erb +1 -0
  35. data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +5 -0
  36. data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +5 -0
  37. data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +1 -1
  38. data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +11 -0
  39. data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +5 -0
  40. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +217 -102
  41. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +162 -44
  42. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +97 -10
  43. data/spec_app/spec/javascripts/up/link_spec.js.coffee +3 -3
  44. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +22 -20
  45. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +344 -228
  46. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
  47. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -1
  48. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +1 -1
  49. data/spec_app/spec/javascripts/up/util_spec.js.coffee +194 -0
  50. metadata +11 -4
@@ -3,60 +3,118 @@ describe 'up.feedback', ->
3
3
  u = up.util
4
4
 
5
5
  beforeEach ->
6
+ up.history.config.enabled = true
6
7
  up.modal.config.openAnimation = 'none'
7
8
  up.modal.config.closeAnimation = 'none'
9
+ up.popup.config.openAnimation = 'none'
10
+ up.popup.config.closeAnimation = 'none'
8
11
 
9
12
  describe 'unobtrusive behavior', ->
10
13
 
11
- describe '.up-current', ->
14
+ describe '[up-nav]', ->
12
15
 
13
- it 'marks a link as .up-current if it links to the current URL', ->
14
- spyOn(up.browser, 'url').and.returnValue('/foo')
15
- $currentLink = up.hello(affix('a[href="/foo"]'))
16
- $otherLink = up.hello(affix('a[href="/bar"]'))
16
+ it 'marks a child link as .up-current if it links to the current URL', ->
17
+ up.history.replace('/foo')
18
+ $nav = affix('div[up-nav]')
19
+ $currentLink = $nav.affix('a[href="/foo"]')
20
+ $otherLink = $nav.affix('a[href="/bar"]')
21
+ up.hello($nav)
17
22
  expect($currentLink).toHaveClass('up-current')
18
23
  expect($otherLink).not.toHaveClass('up-current')
19
24
 
25
+ it 'marks the element as .up-current if it is also a link to the current URL', ->
26
+ up.history.replace('/foo')
27
+ $currentLink = affix('a[href="/foo"][up-nav]')
28
+ $otherLink = affix('a[href="/bar"][up-nav]')
29
+ up.hello($currentLink)
30
+ up.hello($otherLink)
31
+ expect($currentLink).toHaveClass('up-current')
32
+ expect($otherLink).not.toHaveClass('up-current')
33
+
34
+ it 'does not mark a link as .up-current if the link is outside an [up-nav]', ->
35
+ up.history.replace('/foo')
36
+ $nav = affix('div[up-nav]')
37
+ $currentLinkInNav = $nav.affix('a[href="/foo"]')
38
+ $currentLinkOutsideNav = affix('a[href="/foo"]')
39
+ up.hello($nav)
40
+ expect($currentLinkInNav).toHaveClass('up-current')
41
+ expect($currentLinkOutsideNav).not.toHaveClass('up-current')
42
+
43
+ it 'marks a replaced child link as .up-current if it links to the current URL', asyncSpec (next) ->
44
+ up.history.replace('/foo')
45
+ $nav = affix('div[up-nav]')
46
+ $nav.affix('a.link[href="/bar"]').text('old link')
47
+ up.hello($nav)
48
+
49
+ expect('.link').toHaveText('old link')
50
+ expect('.link').not.toHaveClass('up-current')
51
+
52
+ up.replace('.link', '/src', history: false)
53
+
54
+ next =>
55
+ @respondWith """
56
+ <a class="link" href="/foo">
57
+ new link
58
+ </a>
59
+ """
60
+
61
+ next =>
62
+ expect('.link').toHaveText('new link')
63
+ expect('.link').toHaveClass('up-current')
64
+
20
65
  it 'marks any link as .up-current if its up-href attribute matches the current URL', ->
21
- spyOn(up.browser, 'url').and.returnValue('/foo')
22
- $currentLink = up.hello(affix('span[up-href="/foo"]'))
23
- $otherLink = up.hello(affix('span[up-href="/bar"]'))
66
+ up.history.replace('/foo')
67
+ $nav = affix('div[up-nav]')
68
+ $currentLink = $nav.affix('span[up-href="/foo"]')
69
+ $otherLink = $nav.affix('span[up-href="/bar"]')
70
+ up.hello($nav)
24
71
  expect($currentLink).toHaveClass('up-current')
25
72
  expect($otherLink).not.toHaveClass('up-current')
26
73
 
27
74
  it 'matches the current and destination URLs if they only differ by a trailing slash', ->
28
- spyOn(up.browser, 'url').and.returnValue('/foo')
29
- $currentLink = up.hello(affix('span[up-href="/foo/"]'))
75
+ up.history.replace('/foo')
76
+ $nav = affix('div[up-nav]')
77
+ $currentLink = $nav.affix('span[up-href="/foo/"]')
78
+ up.hello($nav)
30
79
  expect($currentLink).toHaveClass('up-current')
31
80
 
32
81
  it 'does not match the current and destination URLs if they differ in the search', ->
33
- spyOn(up.browser, 'url').and.returnValue('/foo?q=1')
34
- $currentLink = up.hello(affix('span[up-href="/foo?q=2"]'))
82
+ up.history.replace('/foo?q=1')
83
+ $nav = affix('div[up-nav]')
84
+ $currentLink = $nav.affix('span[up-href="/foo?q=2"]')
85
+ up.hello($nav)
35
86
  expect($currentLink).not.toHaveClass('up-current')
36
87
 
37
88
  it 'marks any link as .up-current if any of its space-separated up-alias values matches the current URL', ->
38
- spyOn(up.browser, 'url').and.returnValue('/foo')
39
- $currentLink = up.hello(affix('a[href="/x"][up-alias="/aaa /foo /bbb"]'))
40
- $otherLink = up.hello(affix('a[href="/y"][up-alias="/bar"]'))
89
+ up.history.replace('/foo')
90
+ $nav = affix('div[up-nav]')
91
+ $currentLink = $nav.affix('a[href="/x"][up-alias="/aaa /foo /bbb"]')
92
+ $otherLink = $nav.affix('a[href="/y"][up-alias="/bar"]')
93
+ up.hello($nav)
41
94
  expect($currentLink).toHaveClass('up-current')
42
95
  expect($otherLink).not.toHaveClass('up-current')
43
96
 
44
97
  it 'does not throw if the current location does not match an up-alias wildcard (bugfix)', ->
45
- inserter = -> up.hello(affix('a[up-alias="/qqqq*"]'))
98
+ inserter = -> up.hello(affix('a[up-nav][up-alias="/qqqq*"]'))
46
99
  expect(inserter).not.toThrow()
47
100
 
48
101
  it 'does not highlight a link to "#" (commonly used for JS-only buttons)', ->
49
- $link = up.hello(affix('a[href="#"]'))
102
+ $nav = affix('div[up-nav]')
103
+ $link = $nav.affix('a[href="#"]')
104
+ up.hello($nav)
50
105
  expect($link).not.toHaveClass('up-current')
51
106
 
52
107
  it 'does not highlight links with unsafe methods', ->
53
- spyOn(up.browser, 'url').and.returnValue('/foo')
54
- $defaultLink = up.hello(affix('a[href="/foo"]'))
55
- $getLink = up.hello(affix('a[href="/foo"][up-method="get"]'))
56
- $putLink = up.hello(affix('a[href="/foo"][up-method="put"]'))
57
- $patchLink = up.hello(affix('a[href="/foo"][up-method="patch"]'))
58
- $postLink = up.hello(affix('a[href="/foo"][up-method="post"]'))
59
- $deleteLink = up.hello(affix('a[href="/foo"][up-method="delete"]'))
108
+ up.history.replace('/foo')
109
+ $nav = affix('div[up-nav]')
110
+ $defaultLink = $nav.affix('a[href="/foo"]')
111
+ $getLink = $nav.affix('a[href="/foo"][up-method="get"]')
112
+ $putLink = $nav.affix('a[href="/foo"][up-method="put"]')
113
+ $patchLink = $nav.affix('a[href="/foo"][up-method="patch"]')
114
+ $postLink = $nav.affix('a[href="/foo"][up-method="post"]')
115
+ $deleteLink = $nav.affix('a[href="/foo"][up-method="delete"]')
116
+ up.hello($nav)
117
+
60
118
  expect($defaultLink).toHaveClass('up-current')
61
119
  expect($getLink).toHaveClass('up-current')
62
120
  expect($putLink).not.toHaveClass('up-current')
@@ -65,27 +123,57 @@ describe 'up.feedback', ->
65
123
  expect($deleteLink).not.toHaveClass('up-current')
66
124
 
67
125
  it 'marks URL prefixes as .up-current if an up-alias value ends in *', ->
68
- spyOn(up.browser, 'url').and.returnValue('/foo/123')
69
- $currentLink = up.hello(affix('a[href="/x"][up-alias="/aaa /foo/* /bbb"]'))
70
- $otherLink = up.hello(affix('a[href="/y"][up-alias="/bar"]'))
126
+ up.history.replace('/foo/123')
127
+
128
+ $nav = affix('div[up-nav]')
129
+ $currentLink = $nav.affix('a[href="/x"][up-alias="/aaa /foo/* /bbb"]')
130
+ $otherLink = $nav.affix('a[href="/y"][up-alias="/bar"]')
131
+ up.hello($nav)
132
+
71
133
  expect($currentLink).toHaveClass('up-current')
72
134
  expect($otherLink).not.toHaveClass('up-current')
73
135
 
74
- it 'allows to configure a custom "current" class, but always also sets .up-current', ->
75
- up.feedback.config.currentClasses = ['highlight']
76
- spyOn(up.browser, 'url').and.returnValue('/foo')
77
- $currentLink = up.hello(affix('a[href="/foo"]'))
78
- expect($currentLink).toHaveClass('highlight up-current')
136
+ it 'allows to configure a custom "current" class in addition to .up-current', ->
137
+ up.feedback.config.currentClasses.push('highlight')
138
+ up.history.replace('/foo')
139
+ $nav = affix('div[up-nav]')
140
+ $currentLink = $nav.affix('a[href="/foo"]')
141
+ up.hello($nav)
142
+
143
+ expect($currentLink).toHaveClass('highlight')
144
+ expect($currentLink).toHaveClass('up-current')
145
+
146
+ it 'allows to configure multiple additional "current" classes', ->
147
+ up.feedback.config.currentClasses.push('highlight1')
148
+ up.feedback.config.currentClasses.push('highlight2')
149
+ up.history.replace('/foo')
150
+ $nav = affix('div[up-nav]')
151
+ $currentLink = $nav.affix('a[href="/foo"]')
152
+ up.hello($nav)
153
+
154
+ expect($currentLink).toHaveClass('highlight1')
155
+ expect($currentLink).toHaveClass('highlight2')
156
+ expect($currentLink).toHaveClass('up-current')
157
+
158
+ it 'allows to configure additional nav selectors', ->
159
+ up.history.replace('/foo')
160
+ up.feedback.config.navs.push('.navi')
161
+ $nav = affix('div.navi')
162
+ $currentLink = $nav.affix('a[href="/foo"]')
163
+ $otherLink = $nav.affix('a[href="/bar"]')
164
+ up.hello($nav)
165
+ expect($currentLink).toHaveClass('up-current')
166
+ expect($otherLink).not.toHaveClass('up-current')
79
167
 
80
168
  describeCapability 'canPushState', ->
81
169
 
82
170
  describe 'updating .up-current marks wen the URL changes', ->
83
171
 
84
- beforeEach ->
85
- up.history.config.enabled = true
86
-
87
172
  it 'marks a link as .up-current if it links to the current URL, but is missing a trailing slash', asyncSpec (next) ->
88
- $link = affix('a[href="/foo"][up-target=".main"]')
173
+ $nav = affix('div[up-nav]')
174
+ $link = $nav.affix('a[href="/foo"][up-target=".main"]')
175
+ up.hello($nav)
176
+
89
177
  affix('.main')
90
178
  Trigger.clickSequence($link)
91
179
 
@@ -98,7 +186,10 @@ describe 'up.feedback', ->
98
186
  expect($link).toHaveClass('up-current')
99
187
 
100
188
  it 'marks a link as .up-current if it links to the current URL, but has an extra trailing slash', asyncSpec (next) ->
101
- $link = affix('a[href="/foo/"][up-target=".main"]')
189
+ $nav = affix('div[up-nav]')
190
+ $link = $nav.affix('a[href="/foo/"][up-target=".main"]')
191
+ up.hello($nav)
192
+
102
193
  affix('.main')
103
194
  Trigger.clickSequence($link)
104
195
 
@@ -113,9 +204,11 @@ describe 'up.feedback', ->
113
204
  it 'marks a link as .up-current if it links to an URL currently shown either within or below the modal', asyncSpec (next) ->
114
205
  up.history.replace('/foo')
115
206
 
116
- $backgroundLink = affix('a[href="/foo"]')
117
- $modalLink = affix('a[href="/bar"][up-modal=".main"]')
118
- $unrelatedLink = affix('a[href="/baz]')
207
+ $nav = affix('div[up-nav]')
208
+ $backgroundLink = $nav.affix('a[href="/foo"]')
209
+ $modalLink = $nav.affix('a[href="/bar"][up-modal=".main"]')
210
+ $unrelatedLink = $nav.affix('a[href="/baz"]')
211
+ up.hello($nav)
119
212
 
120
213
  Trigger.clickSequence($modalLink)
121
214
 
@@ -133,12 +226,21 @@ describe 'up.feedback', ->
133
226
  expect($modalLink).not.toHaveClass('up-current')
134
227
  expect($unrelatedLink).not.toHaveClass('up-current')
135
228
 
136
- it 'marks a link as .up-current if it links to the URL currently either within or below the popup', asyncSpec (next) ->
229
+ it "marks a link as .up-current if it links to the URL currently either within or below the popup, even if the popup doesn't change history", asyncSpec (next) ->
137
230
  up.history.replace('/foo')
138
231
 
139
- $backgroundLink = affix('a[href="/foo"]')
140
- $popupLink = affix('a[href="/bar"][up-popup=".main"]')
141
- $unrelatedLink = affix('a[href="/baz]')
232
+ # This is actually the default. Popups don't change the address bar by default,
233
+ # but we still want to cause their URL to mark links as current.
234
+ up.popup.config.history = false
235
+
236
+ $nav = affix('div[up-nav]')
237
+ $backgroundLink = $nav.affix('a[href="/foo"]')
238
+ $popupLink = $nav.affix('a[href="/bar"][up-popup=".main"]')
239
+ $unrelatedLink = $nav.affix('a[href="/baz"]')
240
+ up.hello($nav)
241
+
242
+ expect(up.browser.url()).toMatchUrl('/foo')
243
+ expect(up.popup.coveredUrl()).toBeMissing()
142
244
 
143
245
  next =>
144
246
  Trigger.clickSequence($popupLink)
@@ -147,6 +249,8 @@ describe 'up.feedback', ->
147
249
  @respondWith('<div class="main">new-text</div>')
148
250
 
149
251
  next =>
252
+ expect(up.browser.url()).toMatchUrl('/foo') # popup did not change history
253
+ expect(up.popup.url()).toMatchUrl('/bar') # popup still knows which URL it is displaying
150
254
  expect($backgroundLink).toHaveClass('up-current')
151
255
  expect($popupLink).toHaveClass('up-current')
152
256
  expect($unrelatedLink).not.toHaveClass('up-current')
@@ -158,6 +262,20 @@ describe 'up.feedback', ->
158
262
  expect($popupLink).not.toHaveClass('up-current')
159
263
  expect($unrelatedLink).not.toHaveClass('up-current')
160
264
 
265
+ it "respects links that are added to an existing [up-nav] by a fragment update", asyncSpec (next) ->
266
+ $nav = affix('.nav[up-nav]')
267
+ $link = $nav.affix('a[href="/foo"][up-target=".main"]')
268
+ $more = $nav.affix('.more')
269
+ up.hello($nav)
270
+
271
+ up.extract '.more', '<div class="more"><a href="/bar"></div>', history: '/bar'
272
+
273
+ next =>
274
+ $moreLink = $('.more').find('a')
275
+ expect($moreLink).toExist()
276
+ expect($moreLink).toHaveClass('up-current')
277
+
278
+
161
279
  describe '.up-active', ->
162
280
 
163
281
  describeCapability 'canPushState', ->
@@ -9,7 +9,6 @@ describe 'up.layout', ->
9
9
  beforeEach ->
10
10
  up.layout.config.snap = 0
11
11
  up.layout.config.substance = 99999
12
- up.layout.config.viewports = [document]
13
12
 
14
13
  describe 'when the viewport is the document', ->
15
14
 
@@ -403,7 +402,7 @@ describe 'up.layout', ->
403
402
 
404
403
  it 'should have tests'
405
404
 
406
- describe 'up.layout.viewportsOf', ->
405
+ describe 'up.layout.viewportOf', ->
407
406
 
408
407
  it 'seeks upwards from the given element', ->
409
408
  up.layout.config.viewports = ['.viewport1', '.viewport2']
@@ -417,19 +416,13 @@ describe 'up.layout', ->
417
416
  $viewport = affix('.viewport')
418
417
  expect(up.layout.viewportOf($viewport)).toEqual($viewport)
419
418
 
420
- it 'finds the document if the viewport is the document', ->
419
+ it 'finds the document if no better viewport matches', ->
421
420
  # This actually tests that the hierarchy returned by `$.parent`
422
421
  # is $element => ... => $('body') => $('html') => $(document)
423
- up.layout.config.viewports = [document]
422
+ up.layout.config.viewports = ['.other-viewport']
424
423
  $element = affix('div')
425
424
  expect(up.layout.viewportOf($element)).toEqual($(document))
426
425
 
427
- it 'throws an error if no viewport could be found', ->
428
- up.layout.config.viewports = ['.does-not-exist']
429
- $element = affix('div')
430
- lookup = -> up.layout.viewportOf($element)
431
- expect(lookup).toThrowError(/Could not find viewport/i)
432
-
433
426
  describe 'up.layout.restoreScroll', ->
434
427
 
435
428
  it "restores a viewport's previously saved scroll position", (done) ->
@@ -456,3 +449,97 @@ describe 'up.layout', ->
456
449
  describe 'up.scroll', ->
457
450
 
458
451
  it 'should have tests'
452
+
453
+ describe 'up.layout.absolutize', ->
454
+
455
+ afterEach ->
456
+ $('.up-bounds, .fixture').remove()
457
+
458
+ it 'absolutely positions the element, preserving visual position and size', ->
459
+ $element = affix('.element').text('element text').css(paddingTop: '20px', paddingLeft: '20px')
460
+
461
+ expect($element.css('position')).toEqual('static')
462
+ previousDims = u.measure($element)
463
+
464
+ up.layout.absolutize($element)
465
+
466
+ expect($element.closest('.up-bounds').css('position')).toEqual('absolute')
467
+
468
+ newDims = u.measure($element)
469
+ expect(newDims).toEqual(previousDims)
470
+
471
+ it 'accurately positions the ghost over an element with margins', ->
472
+ $element = affix('.element').css(margin: '40px')
473
+ previousDims = u.measure($element)
474
+
475
+ up.layout.absolutize($element)
476
+
477
+ newDims = u.measure($element)
478
+ expect(newDims).toEqual(previousDims)
479
+
480
+ it "doesn't change the position of a child whose margins no longer collapse", ->
481
+ $element = affix('.element')
482
+ $child = $('<div class="child">child text</div>').css(margin: '40px').appendTo($element)
483
+ previousChildDims = u.measure($child)
484
+
485
+ up.layout.absolutize($element)
486
+
487
+ newChildDims = u.measure($child)
488
+ expect(newChildDims).toEqual(previousChildDims)
489
+
490
+ it 'correctly positions an element within a scrolled body', ->
491
+ $body = $('body')
492
+ $element1 = $('<div class="fixture"></div>').css(height: '75px').prependTo($body)
493
+ $element2 = $('<div class="fixture"></div>').css(height: '100px').insertAfter($element1)
494
+ $body.scrollTop(33)
495
+
496
+ previousDims = u.measure($element2)
497
+
498
+ up.layout.absolutize($element2)
499
+
500
+ newDims = u.measure($element2)
501
+ expect(newDims).toEqual(previousDims)
502
+
503
+ it 'correctly positions an element within a scrolled parent element (that has overflow-y: scroll)', ->
504
+ $viewport = affix('div').css
505
+ overflowY: 'scroll'
506
+ height: '50px'
507
+
508
+ $element1 = $('<div class="fixture"></div>').css(height: '75px').prependTo($viewport)
509
+ $element2 = $('<div class="fixture"></div>').css(height: '100px').insertAfter($element1)
510
+ $viewport.scrollTop(33)
511
+
512
+ previousDims = u.measure($element2)
513
+
514
+ up.layout.absolutize($element2)
515
+
516
+ newDims = u.measure($element2)
517
+ expect(newDims).toEqual(previousDims)
518
+
519
+ it 'converts fixed elements within the copies to absolutely positioning (relative to the closest offset parent)', ->
520
+ $element = affix('.element').css
521
+ position: 'absolute'
522
+ top: '50px'
523
+ left: '50px'
524
+ $fixedChild = $('<div class="fixed-child" up-fixed></div>').css
525
+ position: 'fixed'
526
+ left: '77px'
527
+ top: '77px'
528
+ $fixedChild.appendTo($element)
529
+ up.layout.absolutize($element)
530
+
531
+ expect($fixedChild.css(['position', 'left', 'top'])).toEqual
532
+ position: 'absolute',
533
+ left: '27px',
534
+ top: '27px'
535
+
536
+ it "does not convert fixed elements outside the element's subtree (bugfix)", ->
537
+ $element = affix('.element').css(position: 'absolute')
538
+ $fixedChild = $('<div class="fixed-child" up-fixed></div>').css(position: 'fixed')
539
+ $fixedChild.appendTo($element)
540
+ $fixedSibling = affix('[up-fixed]').css(position: 'fixed')
541
+
542
+ up.layout.absolutize($element)
543
+
544
+ expect($fixedChild.css('position')).toEqual('absolute')
545
+ expect($fixedSibling.css('position')).toEqual('fixed')
@@ -525,7 +525,7 @@ describe 'up.link', ->
525
525
  buildEvent = ($element, attrs) ->
526
526
  event = Trigger.createMouseEvent('mousedown', attrs)
527
527
  event = $.event.fix(event) # convert native event to jQuery event
528
- event.target = u.unJQuery($element)
528
+ event.target = u.element($element)
529
529
  event
530
530
 
531
531
  it "returns true when the given event's target is the given link itself", ->
@@ -822,8 +822,8 @@ describe 'up.link', ->
822
822
  @respondWith '<div class="target new">new text</div>'
823
823
 
824
824
  next =>
825
- @$oldGhost = $('.target.old.up-ghost')
826
- @$newGhost = $('.target.new.up-ghost')
825
+ @$oldGhost = $('.target.old')
826
+ @$newGhost = $('.target.new')
827
827
  expect(@$oldGhost).toExist()
828
828
  expect(@$newGhost).toExist()
829
829
  expect(u.opacity(@$oldGhost)).toBeAround(1, 0.15)
@@ -132,23 +132,27 @@ describe 'up.modal', ->
132
132
  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) ->
133
133
  openPromise = up.modal.extract('.container', '<div class="container">text</div>', animation: 'fade-in', duration: 100)
134
134
 
135
- u.nextFrame =>
135
+ u.setTimer 50, ->
136
136
  $modal = $('.up-modal')
137
137
  $viewport = $modal.find('.up-modal-viewport')
138
+ expect($modal).toHaveClass('up-modal-animating')
138
139
  expect($modal.css('overflow-y')).toEqual('scroll')
139
140
  expect($viewport.css('overflow-y')).toEqual('hidden')
140
141
 
141
142
  openPromise.then ->
143
+ expect($modal).not.toHaveClass('up-modal-animating')
142
144
  expect($modal.css('overflow-y')).not.toEqual('scroll')
143
145
  expect($viewport.css('overflow-y')).toEqual('scroll')
144
- closePromise = up.modal.close(animation: 'fade-out', duration: 200)
145
- u.nextFrame ->
146
+ closePromise = up.modal.close(animation: 'fade-out', duration: 400)
147
+
148
+ u.setTimer 50, ->
149
+ expect($modal).toHaveClass('up-modal-animating')
146
150
  expect($modal.css('overflow-y')).toEqual('scroll')
147
151
  expect($viewport.css('overflow-y')).toEqual('hidden')
148
152
  done()
149
153
 
150
154
  it 'does not add right padding to the body if the body has overflow-y: hidden', (done) ->
151
- restoreBody = u.temporaryCss($('body'), 'overflow-y': 'hidden')
155
+ restoreBody = u.writeTemporaryStyle($('body'), overflowY: 'hidden')
152
156
 
153
157
  up.modal.extract('.container', '<div class="container">text</div>').then ->
154
158
  $body = $('body')
@@ -161,8 +165,8 @@ describe 'up.modal', ->
161
165
  done()
162
166
 
163
167
  it 'does not add right padding to the body if the body has overflow-y: auto, but does not currently have scrollbars', (done) ->
164
- restoreBody = u.temporaryCss($('body'), 'overflow-y': 'auto')
165
- restoreReporter = u.temporaryCss($('.jasmine_html-reporter'), 'height': '100px', 'overflow-y': 'hidden')
168
+ restoreBody = u.writeTemporaryStyle($('body'), overflowY: 'auto')
169
+ restoreReporter = u.writeTemporaryStyle($('.jasmine_html-reporter'), height: '100px', overflowY: 'hidden')
166
170
 
167
171
  up.modal.extract('.container', '<div class="container">text</div>').then ->
168
172
  $body = $('body')
@@ -243,9 +247,9 @@ describe 'up.modal', ->
243
247
 
244
248
  it 'closes the current modal and wait for its close animation to finish before starting the open animation of a second modal', asyncSpec (next) ->
245
249
  up.modal.config.openAnimation = 'fade-in'
246
- up.modal.config.openDuration = 50
250
+ up.modal.config.openDuration = 100
247
251
  up.modal.config.closeAnimation = 'fade-out'
248
- up.modal.config.closeDuration = 50
252
+ up.modal.config.closeDuration = 100
249
253
 
250
254
  events = []
251
255
  u.each ['up:modal:open', 'up:modal:opened', 'up:modal:close', 'up:modal:closed'], (event) ->
@@ -255,40 +259,38 @@ describe 'up.modal', ->
255
259
  up.modal.extract('.target', '<div class="target">response1</div>')
256
260
 
257
261
  next =>
258
- # First modal is starting opening animation (will take 50 ms)
262
+ # First modal is starting opening animation (will take 100 ms)
259
263
  expect(events).toEqual ['up:modal:open']
260
264
  expect($('.target')).toHaveText('response1')
261
265
 
262
- next.after (50 + 100), =>
263
- # First modal has completed opening animation after 50 ms
266
+ next.after (100 + (timingTolerance = 100)), =>
267
+ # First modal has completed opening animation after 100 ms
264
268
  expect(events).toEqual ['up:modal:open', 'up:modal:opened']
265
269
  expect($('.target')).toHaveText('response1')
266
270
 
267
- # We open another modal, which will cause the first modal to start closing (will take 50 ms)
271
+ # We open another modal, which will cause the first modal to start closing (will take 100 ms)
268
272
  up.modal.extract('.target', '<div class="target">response2</div>')
269
273
 
270
- next.after 25, =>
274
+ next.after 50, =>
271
275
  # Second modal is still waiting for first modal's closing animaton to finish.
272
276
  expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:close']
273
277
  expect($('.target')).toHaveText('response1')
274
278
 
275
- # I don't know why this spec is so off with timing.
276
- # We need to add 200ms to make it pass all of the time.
277
- next.after (25 + 50 + 200), =>
279
+ next.after (50 + 100 + (timingTolerance = 200)), =>
278
280
  # First modal has finished closing, second modal has finished opening.
279
281
  expect(events).toEqual ['up:modal:open', 'up:modal:opened', 'up:modal:close', 'up:modal:closed', 'up:modal:open', 'up:modal:opened']
280
282
  expect($('.target')).toHaveText('response2')
281
283
 
282
284
  it 'closes an opening modal if a second modal starts opening before the first modal has finished its open animation', asyncSpec (next) ->
283
285
  up.modal.config.openAnimation = 'fade-in'
284
- up.modal.config.openDuration = 50
286
+ up.modal.config.openDuration = 100
285
287
  up.modal.config.closeAnimation = 'fade-out'
286
- up.modal.config.closeDuration = 50
288
+ up.modal.config.closeDuration = 100
287
289
 
288
290
  # Open the first modal
289
291
  up.modal.extract('.target', '<div class="target">response1</div>')
290
292
 
291
- next.after 10, =>
293
+ next.after 50, =>
292
294
  # First modal is still in its opening animation
293
295
  expect($('.target')).toHaveText('response1')
294
296
 
@@ -303,7 +305,7 @@ describe 'up.modal', ->
303
305
  # Second modal is still waiting for first modal's closing animaton to finish.
304
306
  expect($('.target')).toHaveText('response1')
305
307
 
306
- next.after 150, =>
308
+ next.after (140 + (timingTolerance = 220)), =>
307
309
  # First modal has finished closing, second modal has finished opening.
308
310
  expect($('.target')).toHaveText('response2')
309
311