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.

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