unpoly-rails 0.23.0 → 0.24.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 (40) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +68 -0
  3. data/dist/unpoly-bootstrap3.js +1 -1
  4. data/dist/unpoly-bootstrap3.min.js +1 -1
  5. data/dist/unpoly.css +18 -8
  6. data/dist/unpoly.js +498 -265
  7. data/dist/unpoly.min.css +1 -1
  8. data/dist/unpoly.min.js +3 -3
  9. data/lib/assets/javascripts/unpoly-bootstrap3/modal-ext.js.coffee +5 -2
  10. data/lib/assets/javascripts/unpoly/flow.js.coffee +3 -1
  11. data/lib/assets/javascripts/unpoly/form.js.coffee +3 -6
  12. data/lib/assets/javascripts/unpoly/layout.js.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/link.js.coffee +4 -2
  14. data/lib/assets/javascripts/unpoly/log.js.coffee +2 -2
  15. data/lib/assets/javascripts/unpoly/modal.js.coffee +125 -36
  16. data/lib/assets/javascripts/unpoly/motion.js.coffee +75 -37
  17. data/lib/assets/javascripts/unpoly/navigation.js.coffee +38 -26
  18. data/lib/assets/javascripts/unpoly/proxy.js.coffee +77 -52
  19. data/lib/assets/javascripts/unpoly/syntax.js.coffee +1 -0
  20. data/lib/assets/javascripts/unpoly/tooltip.js.coffee +2 -0
  21. data/lib/assets/javascripts/unpoly/util.js.coffee +138 -46
  22. data/lib/assets/stylesheets/unpoly/link.css.sass +1 -1
  23. data/lib/assets/stylesheets/unpoly/modal.css.sass +28 -15
  24. data/lib/unpoly/rails/version.rb +1 -1
  25. data/spec_app/Gemfile.lock +7 -7
  26. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +2 -0
  27. data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +5 -0
  28. data/spec_app/spec/javascripts/up/flow_spec.js.coffee +2 -2
  29. data/spec_app/spec/javascripts/up/history_spec.js.coffee +6 -4
  30. data/spec_app/spec/javascripts/up/link_spec.js.coffee +114 -5
  31. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +28 -18
  32. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +112 -7
  33. data/spec_app/spec/javascripts/up/navigation_spec.js.coffee +157 -121
  34. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
  35. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +42 -3
  36. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -2
  37. data/spec_app/spec/javascripts/up/util_spec.js.coffee +26 -1
  38. data/spec_app/vendor/assets/bower_components/jquery/.bower.json +1 -1
  39. metadata +3 -3
  40. data/spec_app/spec/javascripts/helpers/set_timer.js.coffee +0 -3
@@ -1,2 +1,2 @@
1
- [up-href][up-follow], [up-href][up-target]
1
+ [up-href]
2
2
  cursor: pointer
@@ -1,35 +1,50 @@
1
- $stratum: 10000
2
- $substratum-dialog: 1000
3
- $substratum-content: 2000
4
- $substratum-close: 3000
1
+ $stratum-backdrop: 10000
2
+ $stratum-elements: 11000
3
+
4
+ // These could actually be 1000, 2000, 3000 and 4000 since the `fixed` position of some elements defines
5
+ // a stacking context for all contained z-indexes.
6
+ //
7
+ // However, let's keep the option open that these elements will one day not have its stacking context.
8
+ //
9
+ // Also let's not do 1, 2, 3 and 4 so other elements have a chance to move themselves between the layers.
10
+ $substratum-dialog: 12000
11
+ $substratum-content: 13000
12
+ $substratum-close: 14000
5
13
 
6
14
  $close-height: 36px
7
15
  $close-width: 36px
8
16
  $close-font-size: 34px
9
17
 
10
- =transform($transform)
11
- transform: $transform
12
- -ms-transform: $transform // IE9
13
- -webkit-transform: $transform // Safari, Some Android
14
-
15
18
  .up-modal
19
+
20
+ .up-modal-backdrop
21
+ z-index: $stratum-backdrop
22
+ background-color: rgba(90, 90, 90, 0.4)
23
+ position: fixed
24
+ top: 0
25
+ right: 0
26
+ bottom: 0
27
+ left: 0
28
+
29
+ .up-modal-viewport
30
+ z-index: $stratum-elements
16
31
  position: fixed
17
32
  top: 0
18
33
  left: 0
19
34
  bottom: 0
20
35
  right: 0
21
- z-index: $stratum
22
- background-color: rgba(90, 90, 90, 0.4)
23
36
  overflow-x: hidden
24
- overflow-y: scroll
37
+ overflow-y: hidden
25
38
  // We prefer centering the dialog as an `inline-block`
26
39
  // to giving it a horizontal margin of `auto`. This way
27
40
  // the width of `.up-modal-dialog` is controlled by the
28
41
  // contents of `.up-modal-content`.
29
42
  text-align: center
30
43
 
44
+ .up-modal.up-modal-ready &
45
+ overflow-y: scroll
46
+
31
47
  .up-modal-dialog
32
- // The z-index lives in the stacking context of .up-modal
33
48
  z-index: $substratum-dialog
34
49
  // Make sure .up-modal-close is relative to the dialog
35
50
  position: relative
@@ -47,14 +62,12 @@ $close-font-size: 34px
47
62
  text-align: left
48
63
 
49
64
  .up-modal-content
50
- // The z-index lives in the stacking context of .up-modal
51
65
  z-index: $substratum-content
52
66
  padding: 20px
53
67
  background-color: #fff
54
68
  box-shadow: 0 0 10px 1px rgba(0, 0, 0, 0.3)
55
69
 
56
70
  .up-modal-close
57
- // The z-index lives in the stacking context of .up-modal
58
71
  z-index: $substratum-close
59
72
  position: absolute
60
73
  right: 0
@@ -4,6 +4,6 @@ module Unpoly
4
4
  # The current version of the unpoly-rails gem.
5
5
  # This version number is also used for releases of the Unpoly
6
6
  # frontend code.
7
- VERSION = '0.23.0'
7
+ VERSION = '0.24.0'
8
8
  end
9
9
  end
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: ..
3
3
  specs:
4
- unpoly-rails (0.22.1)
4
+ unpoly-rails (0.23.1)
5
5
  rails (>= 3)
6
6
 
7
7
  GEM
@@ -88,8 +88,8 @@ GEM
88
88
  phantomjs (>= 1.9)
89
89
  railties (>= 3.2.0)
90
90
  sprockets-rails
91
- jquery-rails (4.0.2)
92
- rails-dom-testing (~> 1.0)
91
+ jquery-rails (4.1.1)
92
+ rails-dom-testing (>= 1, < 3)
93
93
  railties (>= 4.2.0)
94
94
  thor (>= 0.14, < 2.0)
95
95
  json (1.8.3)
@@ -100,9 +100,9 @@ GEM
100
100
  mime-types (>= 1.16, < 3)
101
101
  mime-types (2.99)
102
102
  mini_portile2 (2.0.0)
103
- minitest (5.8.3)
103
+ minitest (5.8.4)
104
104
  multi_json (1.11.2)
105
- nokogiri (1.6.7.1)
105
+ nokogiri (1.6.7.2)
106
106
  mini_portile2 (~> 2.0.0.rc2)
107
107
  phantomjs (1.9.8.0)
108
108
  rack (1.6.4)
@@ -125,14 +125,14 @@ GEM
125
125
  activesupport (>= 4.2.0.beta, < 5.0)
126
126
  nokogiri (~> 1.6.0)
127
127
  rails-deprecated_sanitizer (>= 1.0.1)
128
- rails-html-sanitizer (1.0.2)
128
+ rails-html-sanitizer (1.0.3)
129
129
  loofah (~> 2.0)
130
130
  railties (4.2.0)
131
131
  actionpack (= 4.2.0)
132
132
  activesupport (= 4.2.0)
133
133
  rake (>= 0.8.7)
134
134
  thor (>= 0.18.1, < 2.0)
135
- rake (10.4.2)
135
+ rake (11.1.2)
136
136
  ref (1.0.5)
137
137
  rspec-core (3.4.1)
138
138
  rspec-support (~> 3.4.0)
@@ -1,4 +1,6 @@
1
1
  afterEach ->
2
+ console.debug('--- Resetting Unpoly after example ---')
3
+ up.motion.finish()
2
4
  up.reset()
3
5
  $('.up-error').remove()
4
6
  console.debug('--- Unpoly was reset after example ---')
@@ -0,0 +1,5 @@
1
+ beforeEach ->
2
+ jasmine.addMatchers
3
+ toContain: (util, customEqualityTesters) ->
4
+ compare: (object, expectedElement) ->
5
+ pass: up.util.contains(object, expectedElement)
@@ -480,7 +480,7 @@ describe 'up.flow', ->
480
480
  expect($ghost2).toHaveLength(1)
481
481
  expect($ghost2.css('opacity')).toBeAround(0.0, 0.1)
482
482
 
483
- @setTimer 40, ->
483
+ u.setTimer 40, ->
484
484
  expect($ghost1.css('opacity')).toBeAround(0.0, 0.2)
485
485
  expect($ghost2.css('opacity')).toBeAround(1.0, 0.2)
486
486
  done()
@@ -534,7 +534,7 @@ describe 'up.flow', ->
534
534
  promise = up.extract('.element', '<div class="element">version 2</div>', transition: 'cross-fade', duration: 30)
535
535
  promise.then(resolution)
536
536
  expect(resolution).not.toHaveBeenCalled()
537
- @setTimer 50, ->
537
+ u.setTimer 50, ->
538
538
  expect(resolution).toHaveBeenCalled()
539
539
  done()
540
540
 
@@ -1,4 +1,6 @@
1
1
  describe 'up.history', ->
2
+
3
+ u = up.util
2
4
 
3
5
  describe 'Javascript functions', ->
4
6
 
@@ -74,23 +76,23 @@ describe 'up.history', ->
74
76
  $('.viewport').scrollTop(250)
75
77
 
76
78
  history.back()
77
- @setTimer 50, =>
79
+ u.setTimer 50, ->
78
80
  respond() # we need to respond since we've never requested /two with the popTarget
79
81
  expect($('.viewport').scrollTop()).toBe(150)
80
82
 
81
83
  history.back()
82
- @setTimer 50, =>
84
+ u.setTimer 50, ->
83
85
  respond() # we need to respond since we've never requested /one with the popTarget
84
86
  expect($('.viewport').scrollTop()).toBe(50)
85
87
 
86
88
  history.forward()
87
- @setTimer 50, =>
89
+ u.setTimer 50, ->
88
90
  # No need to respond since we requested /two with the popTarget
89
91
  # when we went backwards
90
92
  expect($('.viewport').scrollTop()).toBe(150)
91
93
 
92
94
  history.forward()
93
- @setTimer 50, =>
95
+ u.setTimer 50, ->
94
96
  respond() # we need to respond since we've never requested /three with the popTarget
95
97
  expect($('.viewport').scrollTop()).toBe(250)
96
98
  done()
@@ -83,21 +83,21 @@ describe 'up.link', ->
83
83
  expect(document.title).toEqual('title from three')
84
84
 
85
85
  history.back()
86
- @setTimer 50, =>
86
+ u.setTimer 50, ->
87
87
  respondWith('restored text from two', 'restored title from two')
88
88
  expect($('.target')).toHaveText('restored text from two')
89
89
  expect(location.pathname).toEqual('/two')
90
90
  expect(document.title).toEqual('restored title from two')
91
91
 
92
92
  history.back()
93
- @setTimer 50, =>
93
+ u.setTimer 50, ->
94
94
  respondWith('restored text from one', 'restored title from one')
95
95
  expect($('.target')).toHaveText('restored text from one')
96
96
  expect(location.pathname).toEqual('/one')
97
97
  expect(document.title).toEqual('restored title from one')
98
98
 
99
99
  history.forward()
100
- @setTimer 50, =>
100
+ u.setTimer 50, ->
101
101
  # Since the response is cached, we don't have to respond
102
102
  expect($('.target')).toHaveText('restored text from two', 'restored title from two')
103
103
  expect(location.pathname).toEqual('/two')
@@ -105,6 +105,115 @@ describe 'up.link', ->
105
105
 
106
106
  done()
107
107
 
108
+ it 'does not add additional history entries when linking to the current URL', (done) ->
109
+
110
+ # By default, up.history will replace the <body> tag when
111
+ # the user presses the back-button. We reconfigure this
112
+ # so we don't lose the Jasmine runner interface.
113
+ up.history.config.popTargets = ['.container']
114
+
115
+ up.proxy.config.cacheExpiry = 0
116
+
117
+ respondWith = (text) =>
118
+ @respondWith """
119
+ <div class="container">
120
+ <div class='target'>#{text}</div>
121
+ </div>
122
+ """
123
+
124
+ followAndRespond = ($link, text) =>
125
+ promise = up.follow($link)
126
+ respondWith(text)
127
+ promise
128
+
129
+ $link1 = affix('a[href="/one"][up-target=".target"]')
130
+ $link2 = affix('a[href="/two"][up-target=".target"]')
131
+ $container = affix('.container')
132
+ $target = affix('.target').appendTo($container).text('original text')
133
+
134
+ followAndRespond($link1, 'text from one').then =>
135
+ expect($('.target')).toHaveText('text from one')
136
+ expect(location.pathname).toEqual('/one')
137
+
138
+ followAndRespond($link2, 'text from two').then =>
139
+ expect($('.target')).toHaveText('text from two')
140
+ expect(location.pathname).toEqual('/two')
141
+
142
+ followAndRespond($link2, 'text from two').then =>
143
+ expect($('.target')).toHaveText('text from two')
144
+ expect(location.pathname).toEqual('/two')
145
+
146
+ history.back()
147
+ u.setTimer 50, ->
148
+ respondWith('restored text from one')
149
+ expect($('.target')).toHaveText('restored text from one')
150
+ expect(location.pathname).toEqual('/one')
151
+
152
+ history.forward()
153
+ u.setTimer 50, ->
154
+ respondWith('restored text from two')
155
+ expect($('.target')).toHaveText('restored text from two')
156
+ expect(location.pathname).toEqual('/two')
157
+
158
+ done()
159
+
160
+ it 'does adds additional history entries when linking to the current URL, but with a different hash', (done) ->
161
+
162
+ # By default, up.history will replace the <body> tag when
163
+ # the user presses the back-button. We reconfigure this
164
+ # so we don't lose the Jasmine runner interface.
165
+ up.history.config.popTargets = ['.container']
166
+
167
+ up.proxy.config.cacheExpiry = 0
168
+
169
+ respondWith = (text) =>
170
+ @respondWith """
171
+ <div class="container">
172
+ <div class='target'>#{text}</div>
173
+ </div>
174
+ """
175
+
176
+ followAndRespond = ($link, text) =>
177
+ promise = up.follow($link)
178
+ respondWith(text)
179
+ promise
180
+
181
+ $link1 = affix('a[href="/one"][up-target=".target"]')
182
+ $link2 = affix('a[href="/two"][up-target=".target"]')
183
+ $link2WithHash = affix('a[href="/two#hash"][up-target=".target"]')
184
+ $container = affix('.container')
185
+ $target = affix('.target').appendTo($container).text('original text')
186
+
187
+ followAndRespond($link1, 'text from one').then =>
188
+ expect($('.target')).toHaveText('text from one')
189
+ expect(location.pathname).toEqual('/one')
190
+ expect(location.hash).toEqual('')
191
+
192
+ followAndRespond($link2, 'text from two').then =>
193
+ expect($('.target')).toHaveText('text from two')
194
+ expect(location.pathname).toEqual('/two')
195
+ expect(location.hash).toEqual('')
196
+
197
+ followAndRespond($link2WithHash, 'text from two with hash').then =>
198
+ expect($('.target')).toHaveText('text from two with hash')
199
+ expect(location.pathname).toEqual('/two')
200
+ expect(location.hash).toEqual('#hash')
201
+
202
+ history.back()
203
+ u.setTimer 50, ->
204
+ respondWith('restored text from two')
205
+ expect($('.target')).toHaveText('restored text from two')
206
+ expect(location.pathname).toEqual('/two')
207
+ expect(location.hash).toEqual('')
208
+
209
+ history.forward()
210
+ u.setTimer 50, ->
211
+ respondWith('restored text from two with hash')
212
+ expect($('.target')).toHaveText('restored text from two with hash')
213
+ expect(location.pathname).toEqual('/two')
214
+ expect(location.hash).toEqual('#hash')
215
+ done()
216
+
108
217
  describe 'with { restoreScroll: true } option', ->
109
218
 
110
219
  it 'does not reveal, but instead restores the scroll positions of all viewports around the target', ->
@@ -230,7 +339,7 @@ describe 'up.link', ->
230
339
  it 'does not follow a form with up-target attribute (bugfix)', ->
231
340
  $form = affix('form[up-target]')
232
341
  up.hello($form)
233
- followSpy = up.link.knife.mock('follow')
342
+ followSpy = up.link.knife.mock('follow').and.returnValue(u.resolvedPromise())
234
343
  $form.click()
235
344
  expect(followSpy).not.toHaveBeenCalled()
236
345
 
@@ -269,7 +378,7 @@ describe 'up.link', ->
269
378
 
270
379
  beforeEach ->
271
380
  @$link = affix('a[href="/path"][up-follow]')
272
- @followSpy = up.link.knife.mock('follow')
381
+ @followSpy = up.link.knife.mock('follow').and.returnValue(u.resolvedPromise())
273
382
  @defaultSpy = up.link.knife.mock('allowDefault').and.callFake((event) -> event.preventDefault())
274
383
 
275
384
  it "calls up.follow with the clicked link", ->
@@ -26,6 +26,25 @@ describe 'up.modal', ->
26
26
  expect($('.up-modal-dialog .after')).not.toExist()
27
27
  done()
28
28
 
29
+ describe 'up.modal.extract', ->
30
+
31
+ it 'opens a modal by extracting the given selector from the given HTML string', ->
32
+ oldHref = location.href
33
+ up.modal.extract '.middle', """
34
+ <div class="before">new-before</div>
35
+ <div class="middle">new-middle</div>
36
+ <div class="after">new-after</div>
37
+ """
38
+ expect($('.up-modal')).toExist()
39
+ expect($('.up-modal-dialog')).toExist()
40
+ expect($('.up-modal-dialog .middle')).toExist()
41
+ expect($('.up-modal-dialog .middle')).toHaveText('new-middle')
42
+ expect($('.up-modal-dialog .before')).not.toExist()
43
+ expect($('.up-modal-dialog .after')).not.toExist()
44
+
45
+ # Can't change URLs
46
+ expect(location.href).toEqual(oldHref)
47
+
29
48
  describe 'up.modal.visit', ->
30
49
 
31
50
  it "brings its own scrollbar, padding the body on the right in order to prevent jumping", (done) ->
@@ -34,11 +53,11 @@ describe 'up.modal', ->
34
53
  @respondWith('<div class="container">text</div>')
35
54
 
36
55
  promise.then ->
37
-
38
56
  $modal = $('.up-modal')
57
+ $viewport = $modal.find('.up-modal-viewport')
39
58
  $body = $('body')
40
59
  expect($modal).toExist()
41
- expect($modal.css('overflow-y')).toEqual('scroll')
60
+ expect($viewport.css('overflow-y')).toEqual('scroll')
42
61
  expect($body.css('overflow-y')).toEqual('hidden')
43
62
  expect(parseInt($body.css('padding-right'))).toBeAround(assumedScrollbarWidth, 10)
44
63
 
@@ -80,12 +99,13 @@ describe 'up.modal', ->
80
99
  it 'returns the URL behind the modal overlay', (done) ->
81
100
  up.history.replace('/foo')
82
101
  expect(up.modal.coveredUrl()).toBeUndefined()
83
- up.modal.visit('/bar', target: '.container')
102
+ visitPromise = up.modal.visit('/bar', target: '.container')
84
103
  @respondWith('<div class="container">text</div>')
85
- expect(up.modal.coveredUrl()).toEndWith('/foo')
86
- up.modal.close().then ->
87
- expect(up.modal.coveredUrl()).toBeUndefined()
88
- done()
104
+ visitPromise.then ->
105
+ expect(up.modal.coveredUrl()).toEndWith('/foo')
106
+ up.modal.close().then ->
107
+ expect(up.modal.coveredUrl()).toBeUndefined()
108
+ done()
89
109
 
90
110
 
91
111
  describe 'up.modal.close', ->
@@ -104,7 +124,7 @@ describe 'up.modal', ->
104
124
 
105
125
  beforeEach ->
106
126
  @$link = affix('a[href="/path"][up-modal=".target"]')
107
- @followSpy = up.modal.knife.mock('follow')
127
+ @followSpy = up.modal.knife.mock('follow').and.returnValue(u.resolvedPromise())
108
128
  @defaultSpy = up.link.knife.mock('allowDefault').and.callFake((event) -> event.preventDefault())
109
129
 
110
130
  it 'opens the clicked link in a modal', ->
@@ -253,13 +273,3 @@ describe 'up.modal', ->
253
273
  @respondWith("<div class='popup-content'></div>")
254
274
  expect($('.up-modal')).toExist()
255
275
  expect($('.up-popup')).toExist()
256
-
257
- describe 'when following links inside a modal', ->
258
-
259
- it 'prefers to replace a selector within the modal', ->
260
-
261
- it 'auto-closes the modal if a selector behind the modal gets replaced'
262
-
263
- it "doesn't auto-close the modal if a selector behind the modal if the modal is sticky"
264
-
265
- it "doesn't auto-close the modal if the new fragment is a popup"