unpoly-rails 0.37.0 → 0.50.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 (88) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +127 -25
  3. data/LICENSE +1 -1
  4. data/README_RAILS.md +4 -2
  5. data/Rakefile +6 -1
  6. data/dist/unpoly.js +3192 -2198
  7. data/dist/unpoly.min.js +4 -3
  8. data/lib/assets/javascripts/unpoly/browser.coffee +51 -63
  9. data/lib/assets/javascripts/unpoly/bus.coffee +58 -33
  10. data/lib/assets/javascripts/unpoly/classes/cache.coffee +117 -0
  11. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_cascade.coffee +3 -3
  12. data/lib/assets/javascripts/unpoly/{dom → classes}/extract_plan.coffee +1 -1
  13. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +57 -0
  14. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +52 -0
  15. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +95 -0
  16. data/lib/assets/javascripts/unpoly/classes/record.coffee +16 -0
  17. data/lib/assets/javascripts/unpoly/classes/request.coffee +228 -0
  18. data/lib/assets/javascripts/unpoly/classes/response.coffee +138 -0
  19. data/lib/assets/javascripts/unpoly/dom.coffee +151 -142
  20. data/lib/assets/javascripts/unpoly/feedback.coffee +67 -38
  21. data/lib/assets/javascripts/unpoly/form.coffee +156 -139
  22. data/lib/assets/javascripts/unpoly/history.coffee +22 -19
  23. data/lib/assets/javascripts/unpoly/layout.coffee +108 -90
  24. data/lib/assets/javascripts/unpoly/link.coffee +159 -158
  25. data/lib/assets/javascripts/unpoly/log.coffee +5 -5
  26. data/lib/assets/javascripts/unpoly/modal.coffee +93 -81
  27. data/lib/assets/javascripts/unpoly/motion.coffee +291 -250
  28. data/lib/assets/javascripts/unpoly/popup.coffee +67 -53
  29. data/lib/assets/javascripts/unpoly/protocol.coffee +67 -16
  30. data/lib/assets/javascripts/unpoly/proxy.coffee +282 -211
  31. data/lib/assets/javascripts/unpoly/rails.coffee +3 -14
  32. data/lib/assets/javascripts/unpoly/syntax.coffee +54 -49
  33. data/lib/assets/javascripts/unpoly/tooltip.coffee +18 -25
  34. data/lib/assets/javascripts/unpoly/util.coffee +236 -477
  35. data/lib/assets/javascripts/unpoly.coffee +1 -1
  36. data/lib/unpoly/rails/inspector.rb +67 -22
  37. data/lib/unpoly/rails/version.rb +1 -1
  38. data/package.json +1 -1
  39. data/spec_app/Gemfile.lock +13 -13
  40. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  41. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -1
  42. data/spec_app/app/assets/stylesheets/jasmine_specs.sass +10 -0
  43. data/spec_app/app/controllers/binding_test_controller.rb +19 -2
  44. data/spec_app/app/controllers/method_test_controller.rb +16 -0
  45. data/spec_app/app/views/layouts/jasmine_rails/spec_runner.html.erb +20 -0
  46. data/spec_app/app/views/method_test/form_target.erb +17 -0
  47. data/spec_app/app/views/method_test/page1.erb +11 -0
  48. data/spec_app/app/views/method_test/page2.erb +6 -0
  49. data/spec_app/app/views/pages/start.erb +33 -19
  50. data/spec_app/config/initializers/assets.rb +5 -0
  51. data/spec_app/config/routes.rb +3 -0
  52. data/spec_app/spec/controllers/binding_test_controller_spec.rb +82 -27
  53. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +17 -0
  54. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +102 -0
  55. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -1
  56. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +5 -2
  57. data/spec_app/spec/javascripts/helpers/promise_state.js +18 -0
  58. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +9 -0
  59. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +22 -0
  60. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +11 -3
  61. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +10 -0
  62. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +5 -0
  63. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +13 -0
  64. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +13 -6
  65. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +92 -33
  66. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +64 -15
  67. data/spec_app/spec/javascripts/up/classes/.keep +0 -0
  68. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +1 -0
  69. data/spec_app/spec/javascripts/up/dom_spec.js.coffee +759 -551
  70. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +155 -82
  71. data/spec_app/spec/javascripts/up/form_spec.js.coffee +490 -349
  72. data/spec_app/spec/javascripts/up/history_spec.js.coffee +226 -179
  73. data/spec_app/spec/javascripts/up/layout_spec.js.coffee +253 -185
  74. data/spec_app/spec/javascripts/up/link_spec.js.coffee +416 -270
  75. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +459 -330
  76. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +198 -153
  77. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +9 -0
  78. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +240 -175
  79. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +38 -0
  80. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +777 -303
  81. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +24 -8
  82. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +40 -23
  83. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +80 -66
  84. data/spec_app/spec/javascripts/up/util_spec.js.coffee +227 -201
  85. data/spec_app/vendor/asset-libs/es6-promise-4.1.6/es6-promise.auto.js +1159 -0
  86. metadata +30 -7
  87. data/spec_app/spec/javascripts/helpers/reset_path.js.coffee +0 -7
  88. data/spec_app/spec/javascripts/helpers/to_equal_url.coffee +0 -11
@@ -21,7 +21,7 @@ describe 'up.popup', ->
21
21
  afterEach ->
22
22
  @restoreBodyHeight()
23
23
 
24
- it "loads this link's destination in a popup positioned under the given link", ->
24
+ it "loads this link's destination in a popup positioned under the given link", asyncSpec (next) ->
25
25
  $container = affix('.container')
26
26
  $container.css
27
27
  position: 'absolute'
@@ -32,25 +32,33 @@ describe 'up.popup', ->
32
32
 
33
33
  up.popup.attach($link)
34
34
 
35
- expect(@lastRequest().url).toMatch /\/path\/to$/
36
- @respondWith """
37
- <div class="before">new-before</div>
38
- <div class="middle">new-middle</div>
39
- <div class="after">new-after</div>
40
- """
41
-
42
- $popup = $('.up-popup')
43
-
44
- expect($popup).toExist()
45
- expect($popup.find('.middle')).toHaveText('new-middle')
46
- expect($popup.find('.before')).not.toExist()
47
- expect($popup.find('.after')).not.toExist()
48
-
49
- expect($popup.css('position')).toEqual('absolute')
50
-
51
- expect($popup).toSitBelow($link)
35
+ next =>
36
+ expect(@lastRequest().url).toMatch /\/path\/to$/
37
+ @respondWith """
38
+ <div class="before">new-before</div>
39
+ <div class="middle">new-middle</div>
40
+ <div class="after">new-after</div>
41
+ """
42
+
43
+ next =>
44
+ $popup = $('.up-popup')
45
+ expect($popup).toExist()
46
+ expect($popup.find('.middle')).toHaveText('new-middle')
47
+ expect($popup.find('.before')).not.toExist()
48
+ expect($popup.find('.after')).not.toExist()
49
+ expect($popup.css('position')).toEqual('absolute')
50
+ expect($popup).toSitBelow($link)
51
+
52
+ it 'always makes a request for the given selector, and does not "improve" the selector with a fallback', asyncSpec (next) ->
53
+ $container = affix('.container')
54
+ $link = $container.affix('a[href="/path/to"][up-popup=".content"]').text('link')
55
+ up.popup.attach($link)
56
+ next =>
57
+ expect(jasmine.Ajax.requests.count()).toEqual(1)
58
+ headers = @lastRequest().requestHeaders
59
+ expect(headers['X-Up-Target']).toEqual('.content')
52
60
 
53
- it 'gives the popup { position: "fixed" } if the given link is fixed', ->
61
+ it 'gives the popup { position: "fixed" } if the given link is fixed', asyncSpec (next) ->
54
62
  # Let's test the harder case where the document is scrolled
55
63
  up.layout.scroll(document, 50)
56
64
  $container = affix('.container')
@@ -61,31 +69,41 @@ describe 'up.popup', ->
61
69
  $link = $container.affix('a[href="/path/to"][up-popup=".content"]').text('link')
62
70
 
63
71
  up.popup.attach($link)
64
- @respondWith('<div class="content">popup-content</div>')
65
- $popup = $('.up-popup')
66
- expect($popup.css('position')).toEqual('fixed')
67
72
 
68
- expect($popup).toSitBelow($link)
73
+ next =>
74
+ @respondWith('<div class="content">popup-content</div>')
75
+
76
+ next =>
77
+ $popup = $('.up-popup')
78
+ expect($popup.css('position')).toEqual('fixed')
79
+ expect($popup).toSitBelow($link)
69
80
 
70
- it 'does not explode if the popup was closed before the response was received', ->
81
+ it 'never resolves the open() promise and shows no error if close() was called before the response was received', asyncSpec (next) ->
71
82
  $span = affix('span')
72
- up.popup.attach($span, url: '/foo', target: '.container')
73
- up.popup.close()
74
- respond = => @respondWith('<div class="container">text</div>')
75
- expect(respond).not.toThrowError()
76
- expect($('.up-error')).not.toExist()
83
+ openPromise = up.popup.attach($span, url: '/foo', target: '.container')
84
+
85
+ next =>
86
+ up.popup.close()
87
+
88
+ next =>
89
+ respond = => @respondWith('<div class="container">text</div>')
90
+ expect(respond).not.toThrowError()
91
+
92
+ next.await =>
93
+ expect($('.up-toast')).not.toExist()
94
+ promise = promiseState(openPromise)
95
+ promise.then (result) => expect(result.state).toEqual('pending')
77
96
 
78
97
  describe 'with { html } option', ->
79
98
 
80
- it 'extracts the selector from the given HTML string', (done) ->
99
+ it 'extracts the selector from the given HTML string', asyncSpec (next) ->
81
100
  $span = affix('span')
82
- up.popup.attach($span, target: '.container', html: "<div class='container'>container contents</div>").then ->
83
- expect($('.up-popup')).toHaveText('container contents')
84
- done()
101
+ next.await up.popup.attach($span, target: '.container', html: "<div class='container'>container contents</div>")
102
+ next => expect($('.up-popup')).toHaveText('container contents')
85
103
 
86
104
  describe 'opening a popup while another modal is open', ->
87
105
 
88
- it 'closes the current popup and wait for its close animation to finish before starting the open animation of a second popup', (done) ->
106
+ it 'closes the current popup and wait for its close animation to finish before starting the open animation of a second popup', asyncSpec (next) ->
89
107
  $span = affix('span')
90
108
  up.popup.config.openAnimation = 'fade-in'
91
109
  up.popup.config.openDuration = 5
@@ -98,11 +116,12 @@ describe 'up.popup', ->
98
116
 
99
117
  up.popup.attach($span, { target: '.target', html: '<div class="target">response1</div>' })
100
118
 
101
- # First popup is starting opening animation
102
- expect(events).toEqual ['up:popup:open']
103
- expect($('.target')).toHaveText('response1')
119
+ next =>
120
+ # First popup is starting opening animation
121
+ expect(events).toEqual ['up:popup:open']
122
+ expect($('.target')).toHaveText('response1')
104
123
 
105
- u.setTimer 80, ->
124
+ next.after 80, ->
106
125
  # First popup has completed opening animation
107
126
  expect(events).toEqual ['up:popup:open', 'up:popup:opened']
108
127
  expect($('.target')).toHaveText('response1')
@@ -110,35 +129,36 @@ describe 'up.popup', ->
110
129
  # We open another popup, which will cause the first modal to start closing
111
130
  up.popup.attach($span, { target: '.target', html: '<div class="target">response2</div>' })
112
131
 
113
- u.setTimer 20, ->
114
-
115
- # Second popup is still waiting for first popup's closing animation to finish.
116
- expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close']
117
- expect($('.target')).toHaveText('response1')
118
-
119
- u.setTimer 200, ->
120
-
121
- # First popup has finished closing, second popup has finished opening.
122
- expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close', 'up:popup:closed', 'up:popup:open', 'up:popup:opened']
123
- expect($('.target')).toHaveText('response2')
132
+ next.after 20, ->
133
+ # Second popup is still waiting for first popup's closing animation to finish.
134
+ expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close']
135
+ expect($('.target')).toHaveText('response1')
124
136
 
125
- done()
137
+ next.after 200, ->
138
+ # First popup has finished closing, second popup has finished opening.
139
+ expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close', 'up:popup:closed', 'up:popup:open', 'up:popup:opened']
140
+ expect($('.target')).toHaveText('response2')
126
141
 
127
142
  describe 'up.popup.coveredUrl', ->
128
143
 
129
144
  describeCapability 'canPushState', ->
130
145
 
131
- it 'returns the URL behind the popup', (done) ->
146
+ it 'returns the URL behind the popup', asyncSpec (next) ->
147
+ up.history.config.enabled = true
132
148
  up.history.replace('/foo')
133
149
  expect(up.popup.coveredUrl()).toBeMissing()
134
150
 
135
151
  $popupLink = affix('a[href="/bar"][up-popup=".container"][up-history="true"]')
136
152
  Trigger.clickSequence($popupLink)
137
- @respondWith('<div class="container">text</div>')
138
- expect(up.popup.coveredUrl()).toEqualUrl('/foo')
139
- up.popup.close().then ->
153
+
154
+ next =>
155
+ @respondWith('<div class="container">text</div>')
156
+ expect(up.popup.coveredUrl()).toMatchUrl('/foo')
157
+
158
+ next.await up.popup.close()
159
+
160
+ next =>
140
161
  expect(up.popup.coveredUrl()).toBeMissing()
141
- done()
142
162
 
143
163
  describe 'up.popup.close', ->
144
164
 
@@ -155,38 +175,37 @@ describe 'up.popup', ->
155
175
  beforeEach ->
156
176
  @stubAttach = =>
157
177
  @$link = affix('a[href="/path"][up-popup=".target"]')
158
- @attachSpy = up.popup.knife.mock('attachAsap').and.returnValue(u.resolvedPromise())
159
- @defaultSpy = up.link.knife.mock('allowDefault').and.callFake((event) -> event.preventDefault())
178
+ @attachSpy = up.popup.knife.mock('attachAsap').and.returnValue(Promise.resolve())
179
+ @defaultSpy = spyOn(up.link, 'allowDefault').and.callFake((event) -> event.preventDefault())
160
180
 
161
- it 'opens the clicked link in a popup', ->
181
+ it 'opens the clicked link in a popup', asyncSpec (next) ->
162
182
  @stubAttach()
163
183
  Trigger.click(@$link)
164
- expect(@attachSpy).toHaveBeenCalledWith(@$link)
184
+ next => expect(@attachSpy).toHaveBeenCalledWith(@$link, {})
165
185
 
166
186
  # IE does not call JavaScript and always performs the default action on right clicks
167
- unless navigator.userAgent.match(/Trident/)
168
- it 'does nothing if the right mouse button is used', ->
187
+ unless AgentDetector.isIE() || AgentDetector.isEdge()
188
+ it 'does nothing if the right mouse button is used', asyncSpec (next) ->
169
189
  @stubAttach()
170
190
  Trigger.click(@$link, button: 2)
171
- expect(@attachSpy).not.toHaveBeenCalled()
191
+ next => expect(@attachSpy).not.toHaveBeenCalled()
172
192
 
173
- it 'does nothing if shift is pressed during the click', ->
193
+ it 'does nothing if shift is pressed during the click', asyncSpec (next) ->
174
194
  @stubAttach()
175
195
  Trigger.click(@$link, shiftKey: true)
176
- expect(@attachSpy).not.toHaveBeenCalled()
196
+ next => expect(@attachSpy).not.toHaveBeenCalled()
177
197
 
178
- it 'does nothing if ctrl is pressed during the click', ->
198
+ it 'does nothing if ctrl is pressed during the click', asyncSpec (next) ->
179
199
  @stubAttach()
180
200
  Trigger.click(@$link, ctrlKey: true)
181
- expect(@attachSpy).not.toHaveBeenCalled()
201
+ next => expect(@attachSpy).not.toHaveBeenCalled()
182
202
 
183
- it 'does nothing if meta is pressed during the click', ->
203
+ it 'does nothing if meta is pressed during the click', asyncSpec (next) ->
184
204
  @stubAttach()
185
205
  Trigger.click(@$link, metaKey: true)
186
- expect(@attachSpy).not.toHaveBeenCalled()
187
-
188
- it 'closes an existing popup before opening the new popup', ->
206
+ next => expect(@attachSpy).not.toHaveBeenCalled()
189
207
 
208
+ it 'closes an existing popup before opening the new popup', asyncSpec (next) ->
190
209
  up.popup.config.openDuration = 0
191
210
  up.popup.config.closeDuration = 0
192
211
 
@@ -198,15 +217,21 @@ describe 'up.popup', ->
198
217
  up.on event, -> events.push(event)
199
218
 
200
219
  Trigger.click($link1)
201
- expect(events).toEqual ['up:popup:open']
202
- @respondWith('<div class="target">text1</div>')
203
- expect(events).toEqual ['up:popup:open', 'up:popup:opened']
204
220
 
205
- Trigger.click($link2)
206
- expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close', 'up:popup:closed', 'up:popup:open']
207
- @respondWith('<div class="target">text1</div>')
221
+ next =>
222
+ expect(events).toEqual ['up:popup:open']
223
+ @respondWith('<div class="target">text1</div>')
224
+
225
+ next =>
226
+ expect(events).toEqual ['up:popup:open', 'up:popup:opened']
227
+ Trigger.click($link2)
228
+
229
+ next =>
230
+ expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close', 'up:popup:closed', 'up:popup:open']
231
+ @respondWith('<div class="target">text1</div>')
208
232
 
209
- expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close', 'up:popup:closed', 'up:popup:open', 'up:popup:opened']
233
+ next =>
234
+ expect(events).toEqual ['up:popup:open', 'up:popup:opened', 'up:popup:close', 'up:popup:closed', 'up:popup:open', 'up:popup:opened']
210
235
 
211
236
 
212
237
  describe 'with [up-instant] modifier', ->
@@ -215,42 +240,44 @@ describe 'up.popup', ->
215
240
  @stubAttach()
216
241
  @$link.attr('up-instant', '')
217
242
 
218
- it 'opens the modal on mousedown (instead of on click)', ->
243
+ it 'opens the modal on mousedown (instead of on click)', asyncSpec (next) ->
219
244
  Trigger.mousedown(@$link)
220
- expect(@attachSpy.calls.mostRecent().args[0]).toEqual(@$link)
245
+ next => expect(@attachSpy.calls.mostRecent().args[0]).toEqual(@$link)
221
246
 
222
- it 'does nothing on mouseup', ->
247
+ it 'does nothing on mouseup', asyncSpec (next) ->
223
248
  Trigger.mouseup(@$link)
224
- expect(@attachSpy).not.toHaveBeenCalled()
249
+ next => expect(@attachSpy).not.toHaveBeenCalled()
225
250
 
226
- it 'does nothing on click', ->
251
+ it 'does nothing on click', asyncSpec (next) ->
227
252
  Trigger.click(@$link)
228
- expect(@attachSpy).not.toHaveBeenCalled()
253
+ next => expect(@attachSpy).not.toHaveBeenCalled()
229
254
 
230
255
  # IE does not call JavaScript and always performs the default action on right clicks
231
- unless navigator.userAgent.match(/Trident/)
232
- it 'does nothing if the right mouse button is pressed down', ->
256
+ unless AgentDetector.isIE() || AgentDetector.isEdge()
257
+ it 'does nothing if the right mouse button is pressed down', asyncSpec (next) ->
233
258
  Trigger.mousedown(@$link, button: 2)
234
- expect(@attachSpy).not.toHaveBeenCalled()
259
+ next => expect(@attachSpy).not.toHaveBeenCalled()
235
260
 
236
- it 'does nothing if shift is pressed during mousedown', ->
261
+ it 'does nothing if shift is pressed during mousedown', asyncSpec (next) ->
237
262
  Trigger.mousedown(@$link, shiftKey: true)
238
- expect(@attachSpy).not.toHaveBeenCalled()
263
+ next => expect(@attachSpy).not.toHaveBeenCalled()
239
264
 
240
- it 'does nothing if ctrl is pressed during mousedown', ->
265
+ it 'does nothing if ctrl is pressed during mousedown', asyncSpec (next) ->
241
266
  Trigger.mousedown(@$link, ctrlKey: true)
242
- expect(@attachSpy).not.toHaveBeenCalled()
267
+ next => expect(@attachSpy).not.toHaveBeenCalled()
243
268
 
244
- it 'does nothing if meta is pressed during mousedown', ->
269
+ it 'does nothing if meta is pressed during mousedown', asyncSpec (next) ->
245
270
  Trigger.mousedown(@$link, metaKey: true)
246
- expect(@attachSpy).not.toHaveBeenCalled()
271
+ next => expect(@attachSpy).not.toHaveBeenCalled()
247
272
 
248
273
  describe 'with [up-method] modifier', ->
249
274
 
250
- it 'honours the given method', ->
275
+ it 'honours the given method', asyncSpec (next) ->
251
276
  $link = affix('a[href="/path"][up-popup=".target"][up-method="post"]')
252
277
  Trigger.click($link)
253
- expect(@lastRequest().method).toEqual 'POST'
278
+
279
+ next =>
280
+ expect(@lastRequest().method).toEqual 'POST'
254
281
 
255
282
  describe '[up-close]', ->
256
283
 
@@ -263,70 +290,84 @@ describe 'up.popup', ->
263
290
 
264
291
  describe 'when clicked inside a popup', ->
265
292
 
266
- it 'closes the open popup and halts the event chain', (done) ->
293
+ it 'closes the open popup and halts the event chain', asyncSpec (next) ->
267
294
  $opener = affix('a')
268
295
  up.popup.attach($opener, html: '<div class="target">text</div>', target: '.target')
269
- $popup = affix('.up-popup')
270
- $closer = $popup.affix('a[up-close]') # link is within the popup
271
- up.hello($closer)
272
- Trigger.clickSequence($closer)
273
- u.nextFrame ->
296
+
297
+ next =>
298
+ $popup = affix('.up-popup')
299
+ $closer = $popup.affix('a[up-close]') # link is within the popup
300
+ up.hello($closer)
301
+ Trigger.clickSequence($closer)
302
+
303
+ next =>
274
304
  expect(up.popup.isOpen()).toBe(false)
275
305
  expect(backgroundClicked).not.toHaveBeenCalled()
276
- done()
277
306
 
278
307
  describe 'when clicked inside a popup when a modal is open', ->
279
308
 
280
- it 'closes the popup, but not the modal', (done) ->
281
-
309
+ it 'closes the popup, but not the modal', asyncSpec (next) ->
282
310
  up.modal.extract '.modalee', '<div class="modalee"></div>'
283
- $modalee = $('.up-modal .modalee')
284
- $opener = $modalee.affix('a')
285
- up.popup.attach($opener, html: '<div class="popupee">text</div>', target: '.popupee')
286
- $popupee = $('.up-popup .popupee')
287
- $closer = $popupee.affix('a[up-close]') # link is within the popup
288
- up.hello($closer)
289
- Trigger.clickSequence($closer)
290
- u.nextFrame ->
311
+
312
+ next =>
313
+ $modalee = $('.up-modal .modalee')
314
+ $opener = $modalee.affix('a')
315
+ up.popup.attach($opener, html: '<div class="popupee">text</div>', target: '.popupee')
316
+
317
+ next =>
318
+ $popupee = $('.up-popup .popupee')
319
+ $closer = $popupee.affix('a[up-close]') # link is within the popup
320
+ up.hello($closer)
321
+ Trigger.clickSequence($closer)
322
+
323
+ next =>
291
324
  expect(up.popup.isOpen()).toBe(false)
292
325
  expect(up.modal.isOpen()).toBe(true)
293
326
  expect(backgroundClicked).not.toHaveBeenCalled()
294
- done()
295
327
 
296
328
  describe 'when no popup is open', ->
297
329
 
298
- it 'does nothing and allows the event chain to continue', (done) ->
330
+ it 'does nothing and allows the event chain to continue', asyncSpec (next) ->
299
331
  $link = affix('a[up-close]') # link is outside the popup
300
332
  up.hello($link)
301
333
  Trigger.clickSequence($link)
302
- u.nextFrame ->
334
+
335
+ next =>
303
336
  expect(up.popup.isOpen()).toBe(false)
304
337
  expect(backgroundClicked).toHaveBeenCalled()
305
- done()
306
338
 
307
339
  describe 'when replacing content', ->
308
340
 
309
341
  beforeEach ->
310
342
  up.motion.config.enabled = false
311
343
 
312
- it 'prefers to replace a selector within the popup', ->
344
+ it 'prefers to replace a selector within the popup', asyncSpec (next) ->
313
345
  $outside = affix('.foo').text('old outside')
314
346
  $link = affix('.link')
315
347
  up.popup.attach($link, target: '.foo', html: "<div class='foo'>old inside</div>")
316
- up.extract('.foo', "<div class='foo'>new text</div>")
317
- expect($outside).toBeInDOM()
318
- expect($outside).toHaveText('old outside')
319
- expect($('.up-popup')).toHaveText('new text')
320
348
 
321
- it 'auto-closes the popup when a replacement from inside the popup affects a selector behind the popup', ->
349
+ next =>
350
+ up.extract('.foo', "<div class='foo'>new text</div>")
351
+
352
+ next =>
353
+ expect($outside).toBeInDOM()
354
+ expect($outside).toHaveText('old outside')
355
+ expect($('.up-popup')).toHaveText('new text')
356
+
357
+ it 'auto-closes the popup when a replacement from inside the popup affects a selector behind the popup', asyncSpec (next) ->
322
358
  affix('.outside').text('old outside')
323
359
  $link = affix('.link')
324
360
  up.popup.attach($link, target: '.inside', html: "<div class='inside'>old inside</div>")
325
- up.extract('.outside', "<div class='outside'>new outside</div>", origin: $('.inside'))
326
- expect($('.outside')).toHaveText('new outside')
327
- expect($('.up-popup')).not.toExist()
328
361
 
329
- it 'does not restore the covered URL when auto-closing', (done) ->
362
+ next =>
363
+ up.extract('.outside', "<div class='outside'>new outside</div>", origin: $('.inside'))
364
+
365
+ next =>
366
+ expect($('.outside')).toHaveText('new outside')
367
+ expect($('.up-popup')).not.toExist()
368
+
369
+ it 'does not restore the covered URL when auto-closing (since it would override the URL from the triggering update)', asyncSpec (next) ->
370
+ up.history.config.enabled = true
330
371
  up.motion.config.enabled = true
331
372
  up.popup.config.openDuration = 0
332
373
  up.popup.config.closeDuration = 20
@@ -334,108 +375,132 @@ describe 'up.popup', ->
334
375
 
335
376
  affix('.outside').text('old outside')
336
377
  $link = affix('.link')
337
- whenPopupOpen = up.popup.attach($link, url: '/path', target: '.inside')
338
- @respondWith("<div class='inside'>old inside</div>")
378
+ up.popup.attach($link, url: '/path', target: '.inside')
379
+
380
+ next =>
381
+ @respondWith("<div class='inside'>old inside</div>") # Populate pop-up
339
382
 
340
- whenPopupOpen.then ->
383
+ next =>
341
384
  up.extract('.outside', "<div class='outside'>new outside</div>",
342
385
  origin: $('.inside'), history: '/new-location') # Provoke auto-close
343
386
 
344
- u.setTimer 50, ->
345
- expect(location.href).toEqualUrl '/new-location'
346
- done()
387
+ next =>
388
+ expect(location.href).toMatchUrl '/new-location'
347
389
 
348
- it 'does not auto-close the popup when a replacement from inside the popup affects a selector inside the popup', ->
390
+ it 'does not auto-close the popup when a replacement from inside the popup affects a selector inside the popup', asyncSpec (next) ->
349
391
  affix('.outside').text('old outside')
350
392
  $link = affix('.link')
351
- up.popup.attach($link, target: '.inside')
352
- @respondWith("<div class='inside'>old inside</div>")
353
- up.extract('.inside', "<div class='inside'>new inside</div>", origin: $('.inside'))
354
- expect($('.inside')).toHaveText('new inside')
355
- expect($('.up-popup')).toExist()
393
+ up.popup.attach($link, html: "<div class='inside'>old inside</div>", target: '.inside')
394
+
395
+ next =>
396
+ up.extract('.inside', "<div class='inside'>new inside</div>", origin: $('.inside'))
397
+
398
+ next =>
399
+ expect($('.inside')).toHaveText('new inside')
400
+ expect($('.up-popup')).toExist()
356
401
 
357
- it 'does not auto-close the popup when a replacement from outside the popup affects a selector outside the popup', ->
402
+ it 'does not auto-close the popup when a replacement from outside the popup affects a selector outside the popup', asyncSpec (next) ->
358
403
  affix('.outside').text('old outside')
359
404
  $link = affix('.link')
360
405
  up.popup.attach($link, target: '.inside', html: "<div class='inside'>old inside</div>")
361
- up.extract('.outside', "<div class='outside'>new outside</div>", origin: $('.outside'))
362
- expect($('.outside')).toHaveText('new outside')
363
- expect($('.up-popup')).toExist()
364
406
 
365
- it 'does not auto-close the popup when a replacement from outside the popup affects a selector inside the popup', ->
407
+ next =>
408
+ up.extract('.outside', "<div class='outside'>new outside</div>", origin: $('.outside'))
409
+
410
+ next =>
411
+ expect($('.outside')).toHaveText('new outside')
412
+ expect($('.up-popup')).toExist()
413
+
414
+ it 'does not auto-close the popup when a replacement from outside the popup affects a selector inside the popup', asyncSpec (next) ->
366
415
  affix('.outside').text('old outside')
367
416
  $link = affix('.link')
368
417
  up.popup.attach($link, target: '.inside', html: "<div class='inside'>old inside</div>")
369
- up.extract('.inside', "<div class='inside'>new inside</div>", origin: $('.outside'))
370
- expect($('.inside')).toHaveText('new inside')
371
- expect($('.up-popup')).toExist()
418
+
419
+ next =>
420
+ up.extract('.inside', "<div class='inside'>new inside</div>", origin: $('.outside'))
421
+
422
+ next =>
423
+ expect($('.inside')).toHaveText('new inside')
424
+ expect($('.up-popup')).toExist()
372
425
 
373
426
  describe 'when clicking on the body', ->
374
427
 
375
428
  beforeEach ->
376
429
  up.motion.config.enabled = false
377
430
 
378
- it 'closes the popup', (done) ->
431
+ it 'closes the popup', asyncSpec (next) ->
379
432
  affix('.outside').text('old outside')
380
433
  $link = affix('.link')
381
434
  up.popup.attach($link, target: '.inside', html: "<div class='inside'>inside</div>")
382
- expect(up.popup.isOpen()).toBe(true)
383
- Trigger.clickSequence($('body'))
384
- u.nextFrame ->
435
+
436
+ next =>
437
+ expect(up.popup.isOpen()).toBe(true)
438
+ Trigger.clickSequence($('body'))
439
+
440
+ next =>
385
441
  expect(up.popup.isOpen()).toBe(false)
386
- done()
387
442
 
388
- it 'closes the popup when a an [up-instant] link removes its parent (and thus a click event never bubbles up to the document)', (done) ->
443
+ it 'closes the popup when a an [up-instant] link removes its parent (and thus a click event never bubbles up to the document)', asyncSpec (next) ->
389
444
  $parent = affix('.parent')
390
445
  $parentReplacingLink = $parent.affix('a[href="/foo"][up-target=".parent"][up-instant]')
391
446
  $popupOpener = affix('.link')
392
447
  up.popup.attach($popupOpener, target: '.inside', html: "<div class='inside'>inside</div>")
393
- expect(up.popup.isOpen()).toBe(true)
394
- Trigger.clickSequence($parentReplacingLink)
395
- u.nextFrame ->
448
+
449
+ next =>
450
+ expect(up.popup.isOpen()).toBe(true)
451
+ Trigger.clickSequence($parentReplacingLink)
452
+
453
+ next =>
396
454
  expect(up.popup.isOpen()).toBe(false)
397
- done()
398
455
 
399
- it 'closes the popup when the user clicks on an [up-target] link outside the popup', (done) ->
456
+ it 'closes the popup when the user clicks on an [up-target] link outside the popup', asyncSpec (next) ->
400
457
  $target = affix('.target')
401
458
  $outsideLink = affix('a[href="/foo"][up-target=".target"]')
402
459
  $popupOpener = affix('.link')
403
460
  up.popup.attach($popupOpener, target: '.inside', html: "<div class='inside'>inside</div>")
404
- expect(up.popup.isOpen()).toBe(true)
405
- Trigger.clickSequence($outsideLink)
406
- u.nextFrame ->
461
+
462
+ next =>
463
+ expect(up.popup.isOpen()).toBe(true)
464
+ Trigger.clickSequence($outsideLink)
465
+
466
+ next =>
407
467
  expect(up.popup.isOpen()).toBe(false)
408
- done()
409
468
 
410
- it 'closes the popup when the user clicks on an [up-instant] link outside the popup', (done) ->
469
+ it 'closes the popup when the user clicks on an [up-instant] link outside the popup', asyncSpec (next) ->
411
470
  $target = affix('.target')
412
471
  $outsideLink = affix('a[href="/foo"][up-target=".target"][up-instant]')
413
472
  $popupOpener = affix('.link')
414
473
  up.popup.attach($popupOpener, target: '.inside', html: "<div class='inside'>inside</div>")
415
- expect(up.popup.isOpen()).toBe(true)
416
- Trigger.clickSequence($outsideLink)
417
- u.nextFrame ->
474
+
475
+ next =>
476
+ expect(up.popup.isOpen()).toBe(true)
477
+ Trigger.clickSequence($outsideLink)
478
+
479
+ next =>
418
480
  expect(up.popup.isOpen()).toBe(false)
419
- done()
420
481
 
421
- it 'does not close the popup if a link outside the popup is followed with the up.follow function (bugfix)', (done) ->
482
+ it 'does not close the popup if a link outside the popup is followed with the up.follow function (bugfix)', asyncSpec (next) ->
422
483
  $target = affix('.target')
423
484
  $outsideLink = affix('a[href="/foo"][up-target=".target"]')
424
485
  $popupOpener = affix('.link')
425
486
  up.popup.attach($popupOpener, target: '.inside', html: "<div class='inside'>inside</div>")
426
- expect(up.popup.isOpen()).toBe(true)
427
- up.follow($outsideLink)
428
- u.nextFrame ->
487
+
488
+ next =>
489
+ expect(up.popup.isOpen()).toBe(true)
490
+ up.follow($outsideLink)
491
+
492
+ next =>
429
493
  expect(up.popup.isOpen()).toBe(true)
430
- done()
431
494
 
432
- it 'does not close the popup if a form outside the popup is followed with the up.submit function (bugfix)', (done) ->
495
+ it 'does not close the popup if a form outside the popup is followed with the up.submit function (bugfix)', asyncSpec (next) ->
433
496
  $target = affix('.target')
434
497
  $outsideForm = affix('form[action="/foo"][up-target=".target"]')
435
498
  $popupOpener = affix('.link')
436
499
  up.popup.attach($popupOpener, target: '.inside', html: "<div class='inside'>inside</div>")
437
- expect(up.popup.isOpen()).toBe(true)
438
- up.submit($outsideForm)
439
- u.nextFrame ->
500
+
501
+ next =>
502
+ expect(up.popup.isOpen()).toBe(true)
503
+ up.submit($outsideForm)
504
+
505
+ next =>
440
506
  expect(up.popup.isOpen()).toBe(true)
441
- done()