unpoly-rails 0.57.0 → 0.60.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 (186) hide show
  1. checksums.yaml +4 -4
  2. data/CHANGELOG.md +393 -1
  3. data/Gemfile.lock +5 -2
  4. data/README.md +1 -1
  5. data/README_RAILS.md +1 -1
  6. data/Rakefile +10 -1
  7. data/design/es6.js +32 -0
  8. data/design/ie11.txt +9 -0
  9. data/design/measure_jquery/element_list.js +41 -0
  10. data/design/measure_jquery/up.on_vs_addEventListener.js +56 -0
  11. data/design/todo_jquery.txt +13 -0
  12. data/dist/unpoly-bootstrap3.js +8 -8
  13. data/dist/unpoly-bootstrap3.min.js +1 -1
  14. data/dist/unpoly.css +22 -20
  15. data/dist/unpoly.js +6990 -5336
  16. data/dist/unpoly.min.css +1 -1
  17. data/dist/unpoly.min.js +4 -4
  18. data/lib/assets/javascripts/unpoly-bootstrap3/viewport-ext.coffee +5 -0
  19. data/lib/assets/javascripts/unpoly.coffee +8 -6
  20. data/lib/assets/javascripts/unpoly/browser.coffee.erb +23 -118
  21. data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +36 -0
  22. data/lib/assets/javascripts/unpoly/classes/cache.coffee +4 -4
  23. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +45 -39
  24. data/lib/assets/javascripts/unpoly/classes/config.coffee +9 -0
  25. data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +18 -27
  26. data/lib/assets/javascripts/unpoly/classes/divertible_chain.coffee +39 -0
  27. data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +116 -0
  28. data/lib/assets/javascripts/unpoly/classes/extract_cascade.coffee +8 -8
  29. data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +19 -19
  30. data/lib/assets/javascripts/unpoly/classes/field_observer.coffee +54 -31
  31. data/lib/assets/javascripts/unpoly/classes/{focus_tracker.coffee → focus_follower.coffee} +2 -2
  32. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +25 -25
  33. data/lib/assets/javascripts/unpoly/classes/html_parser.coffee +4 -11
  34. data/lib/assets/javascripts/unpoly/classes/motion_controller.coffee +157 -0
  35. data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +525 -0
  36. data/lib/assets/javascripts/unpoly/classes/record.coffee +8 -2
  37. data/lib/assets/javascripts/unpoly/classes/rect.js +21 -0
  38. data/lib/assets/javascripts/unpoly/classes/request.coffee +41 -35
  39. data/lib/assets/javascripts/unpoly/classes/response.coffee +7 -3
  40. data/lib/assets/javascripts/unpoly/classes/reveal_motion.coffee +102 -0
  41. data/lib/assets/javascripts/unpoly/classes/scroll_motion.coffee +67 -0
  42. data/lib/assets/javascripts/unpoly/classes/selector.coffee +60 -0
  43. data/lib/assets/javascripts/unpoly/classes/tether.coffee +105 -0
  44. data/lib/assets/javascripts/unpoly/classes/url_set.coffee +12 -7
  45. data/lib/assets/javascripts/unpoly/element.coffee.erb +1126 -0
  46. data/lib/assets/javascripts/unpoly/event.coffee.erb +437 -0
  47. data/lib/assets/javascripts/unpoly/feedback.coffee +73 -94
  48. data/lib/assets/javascripts/unpoly/form.coffee.erb +188 -181
  49. data/lib/assets/javascripts/unpoly/{dom.coffee.erb → fragment.coffee.erb} +250 -283
  50. data/lib/assets/javascripts/unpoly/framework.coffee +67 -0
  51. data/lib/assets/javascripts/unpoly/history.coffee +29 -28
  52. data/lib/assets/javascripts/unpoly/legacy.coffee +60 -0
  53. data/lib/assets/javascripts/unpoly/link.coffee.erb +127 -119
  54. data/lib/assets/javascripts/unpoly/log.coffee +99 -19
  55. data/lib/assets/javascripts/unpoly/modal.coffee.erb +95 -118
  56. data/lib/assets/javascripts/unpoly/motion.coffee.erb +158 -138
  57. data/lib/assets/javascripts/unpoly/namespace.coffee.erb +0 -5
  58. data/lib/assets/javascripts/unpoly/popup.coffee.erb +119 -102
  59. data/lib/assets/javascripts/unpoly/protocol.coffee +11 -15
  60. data/lib/assets/javascripts/unpoly/proxy.coffee +62 -65
  61. data/lib/assets/javascripts/unpoly/radio.coffee +3 -5
  62. data/lib/assets/javascripts/unpoly/rails.coffee +8 -9
  63. data/lib/assets/javascripts/unpoly/syntax.coffee.erb +173 -125
  64. data/lib/assets/javascripts/unpoly/toast.coffee +25 -24
  65. data/lib/assets/javascripts/unpoly/tooltip.coffee +89 -79
  66. data/lib/assets/javascripts/unpoly/util.coffee.erb +579 -1074
  67. data/lib/assets/javascripts/unpoly/{layout.coffee.erb → viewport.coffee.erb} +334 -264
  68. data/lib/assets/stylesheets/unpoly/dom.sass +1 -1
  69. data/lib/assets/stylesheets/unpoly/layout.sass +2 -0
  70. data/lib/assets/stylesheets/unpoly/popup.sass +0 -1
  71. data/lib/assets/stylesheets/unpoly/tooltip.sass +17 -12
  72. data/lib/unpoly/rails/version.rb +1 -1
  73. data/package.json +1 -2
  74. data/spec_app/Gemfile +2 -1
  75. data/spec_app/Gemfile.lock +38 -27
  76. data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
  77. data/spec_app/app/assets/javascripts/jasmine_specs.coffee +1 -2
  78. data/spec_app/app/assets/stylesheets/integration_test.sass +14 -1
  79. data/spec_app/app/controllers/scroll_test_controller.rb +5 -0
  80. data/spec_app/app/views/css_test/modal.erb +6 -6
  81. data/spec_app/app/views/css_test/popup.erb +44 -18
  82. data/spec_app/app/views/css_test/tooltip.erb +23 -4
  83. data/spec_app/app/views/error_test/trigger.erb +1 -1
  84. data/spec_app/app/views/form_test/basics/new.erb +1 -3
  85. data/spec_app/app/views/pages/start.erb +9 -2
  86. data/spec_app/app/views/reveal_test/long1.erb +1 -1
  87. data/spec_app/app/views/reveal_test/long2.erb +1 -1
  88. data/spec_app/app/views/reveal_test/within_document_viewport.erb +24 -0
  89. data/spec_app/app/views/reveal_test/within_overflowing_div_viewport.erb +28 -0
  90. data/spec_app/app/views/scroll_test/long1.erb +30 -0
  91. data/spec_app/config/routes.rb +1 -0
  92. data/spec_app/spec/javascripts/helpers/agent_detector.coffee +3 -0
  93. data/spec_app/spec/javascripts/helpers/async_sequence.js.coffee +1 -0
  94. data/spec_app/spec/javascripts/helpers/browser_switches.js.coffee +17 -5
  95. data/spec_app/spec/javascripts/helpers/enable_logging.js.coffee +1 -1
  96. data/spec_app/spec/javascripts/helpers/fixture.js.coffee +25 -0
  97. data/spec_app/spec/javascripts/helpers/jquery_no_conflict.js +1 -0
  98. data/spec_app/spec/javascripts/helpers/last_request.js.coffee +1 -0
  99. data/spec_app/spec/javascripts/helpers/mock_ajax.js.coffee +1 -1
  100. data/spec_app/spec/javascripts/helpers/parse_form_data.js.coffee +2 -2
  101. data/spec_app/spec/javascripts/helpers/protect_jasmine_runner.coffee +4 -1
  102. data/spec_app/spec/javascripts/helpers/remove_body_margin.js.coffee +3 -0
  103. data/spec_app/spec/javascripts/helpers/reset_history.js.coffee +2 -1
  104. data/spec_app/spec/javascripts/helpers/reset_knife.js.coffee +2 -2
  105. data/spec_app/spec/javascripts/helpers/reset_up.js.coffee +18 -11
  106. data/spec_app/spec/javascripts/helpers/restore_body_scroll.js.coffee +3 -0
  107. data/spec_app/spec/javascripts/helpers/show_lib_versions.coffee +3 -0
  108. data/spec_app/spec/javascripts/helpers/spec_util.coffee +47 -0
  109. data/spec_app/spec/javascripts/helpers/to_be_around.js.coffee +3 -0
  110. data/spec_app/spec/javascripts/helpers/to_be_array.coffee +5 -0
  111. data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +6 -2
  112. data/spec_app/spec/javascripts/helpers/to_be_blank.js.coffee +3 -0
  113. data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +6 -2
  114. data/spec_app/spec/javascripts/helpers/to_be_element.js.coffee +8 -0
  115. data/spec_app/spec/javascripts/helpers/to_be_error.coffee +3 -0
  116. data/spec_app/spec/javascripts/helpers/to_be_given.js.coffee +3 -0
  117. data/spec_app/spec/javascripts/helpers/to_be_hidden.js.coffee +8 -0
  118. data/spec_app/spec/javascripts/helpers/to_be_missing.js.coffee +3 -0
  119. data/spec_app/spec/javascripts/helpers/to_be_present.js.coffee +3 -0
  120. data/spec_app/spec/javascripts/helpers/to_be_scrolled_to.coffee +3 -0
  121. data/spec_app/spec/javascripts/helpers/to_be_visible.js.coffee +9 -0
  122. data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +3 -0
  123. data/spec_app/spec/javascripts/helpers/to_end_with.js.coffee +3 -0
  124. data/spec_app/spec/javascripts/helpers/to_equal_jquery.js.coffee +1 -2
  125. data/spec_app/spec/javascripts/helpers/to_equal_node_list.coffee +7 -0
  126. data/spec_app/spec/javascripts/helpers/to_equal_via_is_equal.js.coffee +7 -0
  127. data/spec_app/spec/javascripts/helpers/to_have_class.js.coffee +10 -0
  128. data/spec_app/spec/javascripts/helpers/to_have_descendant.js.coffee +10 -0
  129. data/spec_app/spec/javascripts/helpers/to_have_length.js.coffee +8 -0
  130. data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +7 -3
  131. data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +3 -0
  132. data/spec_app/spec/javascripts/helpers/to_have_request_method.js.coffee +1 -0
  133. data/spec_app/spec/javascripts/helpers/to_have_text.js.coffee +9 -0
  134. data/spec_app/spec/javascripts/helpers/to_have_unhandled_rejections.coffee +0 -21
  135. data/spec_app/spec/javascripts/helpers/to_match_list.coffee +14 -0
  136. data/spec_app/spec/javascripts/helpers/to_match_selector.coffee +3 -0
  137. data/spec_app/spec/javascripts/helpers/to_match_text.js.coffee +4 -1
  138. data/spec_app/spec/javascripts/helpers/to_match_url.coffee +1 -0
  139. data/spec_app/spec/javascripts/helpers/trigger.js.coffee +91 -7
  140. data/spec_app/spec/javascripts/helpers/wait_until_dom_ready.js.coffee +3 -0
  141. data/spec_app/spec/javascripts/up/browser_spec.js.coffee +23 -90
  142. data/spec_app/spec/javascripts/up/classes/cache_spec.js.coffee +3 -0
  143. data/spec_app/spec/javascripts/up/classes/config_spec.coffee +24 -0
  144. data/spec_app/spec/javascripts/up/classes/divertible_chain_spec.coffee +45 -0
  145. data/spec_app/spec/javascripts/up/classes/focus_tracker_spec.coffee +5 -2
  146. data/spec_app/spec/javascripts/up/classes/params_spec.coffee +557 -0
  147. data/spec_app/spec/javascripts/up/classes/request_spec.coffee +7 -4
  148. data/spec_app/spec/javascripts/up/classes/scroll_motion_spec.js.coffee +51 -0
  149. data/spec_app/spec/javascripts/up/classes/store/memory_spec.js.coffee +3 -0
  150. data/spec_app/spec/javascripts/up/classes/store/session_spec.js.coffee +3 -2
  151. data/spec_app/spec/javascripts/up/element_spec.coffee +897 -0
  152. data/spec_app/spec/javascripts/up/event_spec.js.coffee +496 -0
  153. data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +69 -48
  154. data/spec_app/spec/javascripts/up/form_spec.js.coffee +252 -194
  155. data/spec_app/spec/javascripts/up/{dom_spec.js.coffee → fragment_spec.js.coffee} +381 -388
  156. data/spec_app/spec/javascripts/up/history_spec.js.coffee +21 -19
  157. data/spec_app/spec/javascripts/up/jquery_spec.js.coffee +4 -0
  158. data/spec_app/spec/javascripts/up/legacy_spec.js.coffee +27 -0
  159. data/spec_app/spec/javascripts/up/link_spec.js.coffee +163 -160
  160. data/spec_app/spec/javascripts/up/log_spec.js.coffee +85 -12
  161. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +141 -123
  162. data/spec_app/spec/javascripts/up/motion_spec.js.coffee +117 -113
  163. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +60 -77
  164. data/spec_app/spec/javascripts/up/protocol_spec.js.coffee +1 -0
  165. data/spec_app/spec/javascripts/up/proxy_spec.js.coffee +85 -78
  166. data/spec_app/spec/javascripts/up/radio_spec.js.coffee +29 -22
  167. data/spec_app/spec/javascripts/up/rails_spec.js.coffee +14 -13
  168. data/spec_app/spec/javascripts/up/spec_spec.js.coffee +9 -0
  169. data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +96 -66
  170. data/spec_app/spec/javascripts/up/toast_spec.js.coffee +37 -0
  171. data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +31 -47
  172. data/spec_app/spec/javascripts/up/util_spec.js.coffee +725 -562
  173. data/spec_app/spec/javascripts/up/{layout_spec.js.coffee → viewport_spec.js.coffee} +175 -149
  174. metadata +57 -19
  175. data/lib/assets/javascripts/unpoly-bootstrap3/layout-ext.coffee +0 -5
  176. data/lib/assets/javascripts/unpoly/bus.coffee.erb +0 -518
  177. data/lib/assets/javascripts/unpoly/classes/extract_step.coffee +0 -4
  178. data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +0 -125
  179. data/lib/assets/javascripts/unpoly/params.coffee.erb +0 -522
  180. data/spec_app/spec/javascripts/helpers/append_fixture.js.coffee +0 -8
  181. data/spec_app/spec/javascripts/up/bus_spec.js.coffee +0 -210
  182. data/spec_app/spec/javascripts/up/namespace_spec.js.coffee +0 -9
  183. data/spec_app/spec/javascripts/up/params_spec.coffee +0 -768
  184. data/spec_app/vendor/asset-libs/jasmine-fixture-1.3.4/jasmine-fixture.js +0 -433
  185. data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/.bower.json +0 -26
  186. data/spec_app/vendor/asset-libs/jasmine-jquery-2.1.1/jasmine-jquery.js +0 -838
@@ -1,14 +1,15 @@
1
- describe 'up.layout', ->
1
+ u = up.util
2
+ e = up.element
3
+ $ = jQuery
2
4
 
3
- u = up.util
5
+ describe 'up.viewport', ->
4
6
 
5
7
  describe 'JavaScript functions', ->
6
8
 
7
9
  describe 'up.reveal', ->
8
10
 
9
11
  beforeEach ->
10
- up.layout.config.snap = 0
11
- up.layout.config.substance = 99999
12
+ up.viewport.config.revealSnap = 0
12
13
 
13
14
  describe 'when the viewport is the document', ->
14
15
 
@@ -19,7 +20,7 @@ describe 'up.layout', ->
19
20
  @$container = $('<div class="container">').prependTo($body)
20
21
  @$container.css(opacity: 0.2) # reduce flashing during test runs
21
22
 
22
- @clientHeight = u.clientSize().height
23
+ @clientHeight = up.viewport.rootHeight()
23
24
 
24
25
  elementPlans = [
25
26
  { height: @clientHeight, backgroundColor: 'yellow' }, # [0]
@@ -36,7 +37,7 @@ describe 'up.layout', ->
36
37
  @$container.remove()
37
38
 
38
39
  $documentViewport = ->
39
- $(up.browser.documentViewportSelector())
40
+ $(up.viewport.root())
40
41
 
41
42
  it 'reveals the given element', asyncSpec (next) ->
42
43
  up.reveal(@$elements[0])
@@ -79,8 +80,8 @@ describe 'up.layout', ->
79
80
  up.reveal(@$elements[2])
80
81
  next => expect($(document).scrollTop()).toBe(@clientHeight + 50 + 20)
81
82
 
82
- it 'snaps to the top if the space above the future-visible area is smaller than the value of config.snap', asyncSpec (next) ->
83
- up.layout.config.snap = 30
83
+ it 'snaps to the top if the space above the future-visible area is smaller than the value of config.revealSnap', asyncSpec (next) ->
84
+ up.viewport.config.revealSnap = 30
84
85
 
85
86
  @$elements[0].css(height: '20px')
86
87
 
@@ -107,7 +108,7 @@ describe 'up.layout', ->
107
108
  expect($(document).scrollTop()).toBe(0)
108
109
 
109
110
  it 'does not snap to the top if it would un-reveal an element at the bottom edge of the screen (bugfix)', asyncSpec (next) ->
110
- up.layout.config.snap = 100
111
+ up.viewport.config.revealSnap = 100
111
112
 
112
113
  up.reveal(@$elements[1])
113
114
 
@@ -121,7 +122,7 @@ describe 'up.layout', ->
121
122
 
122
123
 
123
124
  it 'scrolls far enough so the element is not obstructed by an element fixed to the top', asyncSpec (next) ->
124
- $topNav = affix('[up-fixed=top]').css(
125
+ $topNav = $fixture('[up-fixed=top]').css(
125
126
  position: 'fixed',
126
127
  top: '0',
127
128
  left: '0',
@@ -129,7 +130,7 @@ describe 'up.layout', ->
129
130
  height: '100px'
130
131
  )
131
132
 
132
- up.reveal(@$elements[0], viewport: @viewport)
133
+ up.reveal(@$elements[0])
133
134
 
134
135
  next =>
135
136
  # ---------------------
@@ -174,7 +175,7 @@ describe 'up.layout', ->
174
175
  expect($(document).scrollTop()).toBe(@clientHeight + 50 - 100 - 50)
175
176
 
176
177
  it 'scrolls far enough so the element is not obstructed by an element fixed to the top with margin, padding, border and non-zero top properties', asyncSpec (next) ->
177
- $topNav = affix('[up-fixed=top]').css(
178
+ $topNav = $fixture('[up-fixed=top]').css(
178
179
  position: 'fixed',
179
180
  top: '29px',
180
181
  margin: '16px',
@@ -205,7 +206,7 @@ describe 'up.layout', ->
205
206
  )
206
207
 
207
208
  it 'scrolls far enough so the element is not obstructed by an element fixed to the bottom', asyncSpec (next) ->
208
- $bottomNav = affix('[up-fixed=bottom]').css(
209
+ $bottomNav = $fixture('[up-fixed=bottom]').css(
209
210
  position: 'fixed',
210
211
  bottom: '0',
211
212
  left: '0',
@@ -247,7 +248,7 @@ describe 'up.layout', ->
247
248
  expect($(document).scrollTop()).toBe(@clientHeight + 50)
248
249
 
249
250
  it 'scrolls far enough so the element is not obstructed by an element fixed to the bottom with margin, padding, border and non-zero bottom properties', asyncSpec (next) ->
250
- $bottomNav = affix('[up-fixed=bottom]').css(
251
+ $bottomNav = $fixture('[up-fixed=bottom]').css(
251
252
  position: 'fixed',
252
253
  bottom: '29px',
253
254
  margin: '16px',
@@ -277,17 +278,29 @@ describe 'up.layout', ->
277
278
  )
278
279
 
279
280
  it 'does not crash when called with a CSS selector (bugfix)', (done) ->
280
- promise = up.reveal('.container')
281
- promiseState(promise).then (result) ->
282
- expect(result.state).toEqual('fulfilled')
281
+ promise = up.reveal('.container', { behavior: 'instant' })
282
+ promise.then ->
283
+ expect(true).toBe(true)
283
284
  done()
284
285
 
286
+ it 'scrolls the viewport to the first row if the element if the element is higher than the viewport', asyncSpec (next) ->
287
+ @$elements[0].css(height: '1000px')
288
+ @$elements[1].css(height: '3000px')
289
+
290
+ up.reveal(@$elements[1])
291
+
292
+ next =>
293
+ # [0] 0 ............ 999
294
+ # [1] 1000 ........ 4999
295
+ expect($(document).scrollTop()).toBe(1000)
296
+
297
+
285
298
  describe 'with { top: true } option', ->
286
299
 
287
300
  it 'scrolls the viewport to the first row of the element, even if that element is already fully revealed', asyncSpec (next) ->
288
301
  @$elements[0].css(height: '20px')
289
302
 
290
- up.reveal(@$elements[1], { top: true })
303
+ up.reveal(@$elements[1], { top: true, snap: false })
291
304
 
292
305
  next =>
293
306
  # [0] 0 ............ 19
@@ -301,7 +314,7 @@ describe 'up.layout', ->
301
314
  describe 'when the viewport is a container with overflow-y: scroll', ->
302
315
 
303
316
  it 'reveals the given element', asyncSpec (next) ->
304
- $viewport = affix('div').css
317
+ $viewport = $fixture('div').css
305
318
  'position': 'absolute'
306
319
  'top': '50px'
307
320
  'left': '50px'
@@ -326,7 +339,7 @@ describe 'up.layout', ->
326
339
 
327
340
  # See that the view only scrolls down as little as possible
328
341
  # in order to reveal the element
329
- up.reveal($elements[3], viewport: $viewport)
342
+ up.reveal($elements[3], viewport: $viewport[0])
330
343
 
331
344
  next =>
332
345
  # [0] 000..049
@@ -341,14 +354,14 @@ describe 'up.layout', ->
341
354
 
342
355
  # See that the view doesn't move if the element
343
356
  # is already revealed
344
- up.reveal($elements[2], viewport: $viewport)
357
+ up.reveal($elements[2], viewport: $viewport[0])
345
358
 
346
359
  next =>
347
360
  expect($viewport.scrollTop()).toBe(100)
348
361
 
349
362
  # See that the view scrolls as far down as it cans
350
363
  # to show the bottom element
351
- up.reveal($elements[5], viewport: $viewport)
364
+ up.reveal($elements[5], viewport: $viewport[0])
352
365
 
353
366
  next =>
354
367
  # [0] 000..049
@@ -361,7 +374,7 @@ describe 'up.layout', ->
361
374
  # ------------
362
375
  expect($viewport.scrollTop()).toBe(200)
363
376
 
364
- up.reveal($elements[1], viewport: $viewport)
377
+ up.reveal($elements[1], viewport: $viewport[0])
365
378
 
366
379
  next =>
367
380
  # See that the view only scrolls up as little as possible
@@ -376,111 +389,91 @@ describe 'up.layout', ->
376
389
  # [5] 250..299
377
390
  expect($viewport.scrollTop()).toBe(50)
378
391
 
379
- it 'only reveals the top number of pixels defined in config.substance', asyncSpec (next) ->
380
-
381
- up.layout.config.substance = 20
382
-
383
- $viewport = affix('div').css
384
- 'position': 'absolute'
385
- 'top': '50px'
386
- 'left': '50px'
387
- 'width': '100px'
388
- 'height': '100px'
389
- 'overflow-y': 'scroll'
390
- $elements = []
391
- u.each [0..5], ->
392
- $element = $('<div>').css(height: '50px')
393
- $element.appendTo($viewport)
394
- $elements.push($element)
395
-
396
- # [0] 000..049
397
- # [1] 050..099
398
- # [2] 100..149
399
- # [3] 150..199
400
- # [4] 200..249
401
- # [5] 250..299
402
-
403
- # Viewing 0 .. 99
404
- expect($viewport.scrollTop()).toBe(0)
405
-
406
- # See that the view only scrolls down as little as possible
407
- # in order to reveal the first 20 rows of the element
408
- up.reveal($elements[3], viewport: $viewport)
409
-
410
- next =>
411
- # Viewing 70 to 169
412
- expect($viewport.scrollTop()).toBe(50 + 20)
413
-
414
- # See that the view doesn't move if the element
415
- # is already revealed
416
- up.reveal($elements[2], viewport: $viewport)
417
-
418
- next =>
419
- expect($viewport.scrollTop()).toBe(50 + 20)
420
-
421
- # See that the view scrolls as far down as it cans
422
- # to show the first 20 rows of the bottom element
423
- up.reveal($elements[5], viewport: $viewport)
424
-
425
- next =>
426
- # Viewing 170 to 269
427
- expect($viewport.scrollTop()).toBe(150 + 20)
428
-
429
- # See that the view only scrolls up as little as possible
430
- # in order to reveal the first 20 rows element
431
- up.reveal($elements[2], viewport: $viewport)
432
-
433
- next =>
434
- # Viewing 100 to 199
435
- expect($viewport.scrollTop()).toBe(100)
436
-
437
- describe 'up.layout.revealHash', ->
392
+ describe 'up.viewport.revealHash', ->
438
393
 
439
394
  it 'reveals an element with an ID matching the given #hash', asyncSpec (next) ->
440
- revealSpy = up.layout.knife.mock('reveal')
441
- $match = affix('div#hash')
442
- up.layout.revealHash('#hash')
443
- next => expect(revealSpy).toHaveBeenCalledWith($match, top: true)
395
+ revealSpy = up.viewport.knife.mock('reveal')
396
+ $match = $fixture('div#hash')
397
+ up.viewport.revealHash('#hash')
398
+ next => expect(revealSpy).toHaveBeenCalledWith($match[0], top: true)
444
399
 
445
400
  it 'reveals a named anchor matching the given #hash', asyncSpec (next) ->
446
- revealSpy = up.layout.knife.mock('reveal')
447
- $match = affix('a[name="hash"]')
448
- up.layout.revealHash('#hash')
449
- next => expect(revealSpy).toHaveBeenCalledWith($match, top: true)
401
+ revealSpy = up.viewport.knife.mock('reveal')
402
+ $match = $fixture('a[name="hash"]')
403
+ up.viewport.revealHash('#hash')
404
+ next => expect(revealSpy).toHaveBeenCalledWith($match[0], top: true)
405
+
406
+ it 'reveals an element with an [up-id] attribute matching the given #hash', asyncSpec (next) ->
407
+ revealSpy = up.viewport.knife.mock('reveal')
408
+ $match = $fixture('div[up-id="hash"]')
409
+ up.viewport.revealHash('#hash')
410
+ next => expect(revealSpy).toHaveBeenCalledWith($match[0], top: true)
450
411
 
451
412
  it 'does nothing and returns a fulfilled promise if no element or anchor matches the given #hash', (done) ->
452
- revealSpy = up.layout.knife.mock('reveal')
453
- promise = up.layout.revealHash('#hash')
413
+ revealSpy = up.viewport.knife.mock('reveal')
414
+ promise = up.viewport.revealHash('#hash')
454
415
  expect(revealSpy).not.toHaveBeenCalled()
455
416
  promiseState(promise).then (result) ->
456
417
  expect(result.state).toEqual('fulfilled')
457
418
  done()
458
419
 
459
420
  it 'does nothing and returns a fulfilled promise if no #hash is given', (done) ->
460
- revealSpy = up.layout.knife.mock('reveal')
461
- promise = up.layout.revealHash('')
421
+ revealSpy = up.viewport.knife.mock('reveal')
422
+ promise = up.viewport.revealHash('')
462
423
  expect(revealSpy).not.toHaveBeenCalled()
463
424
  promiseState(promise).then (result) ->
464
425
  expect(result.state).toEqual('fulfilled')
465
426
  done()
466
427
 
467
- describe 'up.layout.viewportsWithin', ->
428
+ describe 'up.viewport.all', ->
468
429
 
469
- it 'should have tests'
430
+ it 'returns a list of all viewports on the screen', ->
431
+ viewportElement = $fixture('[up-viewport]')[0]
432
+ results = up.viewport.all()
433
+ expect(results).toMatchList([viewportElement, up.viewport.root()])
434
+
435
+ describe 'up.viewport.subtree', ->
436
+
437
+ it 'returns descendant viewports of the given element', ->
438
+ $motherViewport = $fixture('.mother[up-viewport]')
439
+ $element = $motherViewport.affix('.element')
440
+ $childViewport = $element.affix('.child[up-viewport]')
441
+ $grandChildViewport = $childViewport.affix('.grand-child[up-viewport]')
442
+ actual = up.viewport.subtree($element[0])
443
+ expected = $childViewport.add($grandChildViewport)
444
+
445
+ expect(actual).toMatchList(expected)
470
446
 
471
- describe 'up.layout.viewportOf', ->
447
+ it 'returns the given element if it is a viewport', ->
448
+ viewportElement = $fixture('[up-viewport]')[0]
449
+ results = up.viewport.subtree(viewportElement)
450
+ expect(results).toMatchList([viewportElement])
451
+
452
+ describe 'up.viewport.around', ->
453
+
454
+ it 'returns viewports that are either ancestors, descendants, or the given element itself', ->
455
+ $motherViewport = $fixture('.mother[up-viewport]')
456
+ $element = $motherViewport.affix('.element')
457
+ $childViewport = $element.affix('.child[up-viewport]')
458
+ $grandChildViewport = $childViewport.affix('.grand-child[up-viewport]')
459
+ actual = up.viewport.around($element[0])
460
+ expected = $motherViewport.add($childViewport).add($grandChildViewport)
461
+
462
+ expect(actual).toMatchList(expected)
463
+
464
+ describe 'up.viewport.closest', ->
472
465
 
473
466
  it 'seeks upwards from the given element', ->
474
- up.layout.config.viewports = ['.viewport1', '.viewport2']
475
- $viewport1 = affix('.viewport1')
476
- $viewport2 = affix('.viewport2')
477
- $element = affix('div').appendTo($viewport2)
478
- expect(up.layout.viewportOf($element)).toEqual($viewport2)
467
+ up.viewport.config.viewports = ['.viewport1', '.viewport2']
468
+ $viewport1 = $fixture('.viewport1')
469
+ $viewport2 = $fixture('.viewport2')
470
+ $element = $fixture('div').appendTo($viewport2)
471
+ expect(up.viewport.closest($element)).toEqual($viewport2[0])
479
472
 
480
473
  it 'returns the given element if it is a configured viewport itself', ->
481
- up.layout.config.viewports = ['.viewport']
482
- $viewport = affix('.viewport')
483
- expect(up.layout.viewportOf($viewport)).toEqual($viewport)
474
+ up.viewport.config.viewports = ['.viewport']
475
+ $viewport = $fixture('.viewport')
476
+ expect(up.viewport.closest($viewport)).toEqual($viewport[0])
484
477
 
485
478
  describe 'when no configured viewport matches', ->
486
479
 
@@ -489,44 +482,44 @@ describe 'up.layout', ->
489
482
  @resetHtmlCss?()
490
483
 
491
484
  it 'falls back to the scrolling element', ->
492
- $element = affix('.element').css(height: '3000px')
493
- $result = up.layout.viewportOf($element)
494
- expect($result).toMatchSelector(up.browser.documentViewportSelector())
485
+ $element = $fixture('.element').css(height: '3000px')
486
+ $result = up.viewport.closest($element)
487
+ expect($result).toMatchSelector(up.viewport.rootSelector())
495
488
 
496
489
  it 'falls back to the scrolling element if <body> is configured to scroll (fix for Edge)', ->
497
- $element = affix('.element').css(height: '3000px')
498
- @resetHtmlCss = u.writeTemporaryStyle('html', 'overflow-y': 'hidden')
499
- @resetBodyCss = u.writeTemporaryStyle('body', 'overflow-y': 'scroll')
500
- $result = up.layout.viewportOf($element)
501
- expect($result).toMatchSelector(up.browser.documentViewportSelector())
490
+ $element = $fixture('.element').css(height: '3000px')
491
+ @resetHtmlCss = e.setTemporaryStyle(document.documentElement, 'overflow-y': 'hidden')
492
+ @resetBodyCss = e.setTemporaryStyle(document.body, 'overflow-y': 'scroll')
493
+ $result = up.viewport.closest($element)
494
+ expect($result).toMatchSelector(up.viewport.rootSelector())
502
495
 
503
496
  it 'falls back to the scrolling element if <html> is configured to scroll (fix for Edge)', ->
504
- $element = affix('.element').css(height: '3000px')
505
- @resetHtmlCss = u.writeTemporaryStyle('html', 'overflow-y': 'scroll')
506
- @resetBodyCss = u.writeTemporaryStyle('body', 'overflow-y': 'hidden')
507
- $result = up.layout.viewportOf($element)
508
- expect($result).toMatchSelector(up.browser.documentViewportSelector())
497
+ $element = $fixture('.element').css(height: '3000px')
498
+ @resetHtmlCss = e.setTemporaryStyle(document.documentElement, 'overflow-y': 'scroll')
499
+ @resetBodyCss = e.setTemporaryStyle(document.body, 'overflow-y': 'hidden')
500
+ $result = up.viewport.closest($element)
501
+ expect($result).toMatchSelector(up.viewport.rootSelector())
509
502
 
510
- describe 'up.layout.restoreScroll', ->
503
+ describe 'up.viewport.restoreScroll', ->
511
504
 
512
505
  it "restores a viewport's previously saved scroll position", (done) ->
513
- $viewport = affix('#viewport[up-viewport]').css(height: '100px', overflowY: 'scroll')
506
+ $viewport = $fixture('#viewport[up-viewport]').css(height: '100px', overflowY: 'scroll')
514
507
  $content = $viewport.affix('.content').css(height: '1000px')
515
508
  up.hello($viewport)
516
509
  $viewport.scrollTop(50)
517
- up.layout.saveScroll()
510
+ up.viewport.saveScroll()
518
511
  $viewport.scrollTop(70)
519
512
 
520
- up.layout.restoreScroll().then ->
513
+ up.viewport.restoreScroll().then ->
521
514
  expect($viewport.scrollTop()).toEqual(50)
522
515
  done()
523
516
 
524
517
  it "scrolls a viewport to the top (and does not crash) if no previous scroll position is known", (done) ->
525
- $viewport = affix('#viewport[up-viewport]').css(height: '100px', overflowY: 'scroll')
518
+ $viewport = $fixture('#viewport[up-viewport]').css(height: '100px', overflowY: 'scroll')
526
519
  $content = $viewport.affix('.content').css(height: '1000px')
527
520
  $viewport.scrollTop(70)
528
521
 
529
- up.layout.restoreScroll().then ->
522
+ up.viewport.restoreScroll().then ->
530
523
  expect($viewport.scrollTop()).toEqual(0)
531
524
  done()
532
525
 
@@ -534,41 +527,74 @@ describe 'up.layout', ->
534
527
 
535
528
  it 'should have tests'
536
529
 
537
- describe 'up.layout.absolutize', ->
530
+ describe 'up.viewport.rootOverflowElement', ->
531
+
532
+ beforeEach ->
533
+ @body = document.body
534
+ @html = document.documentElement
535
+ @restoreBodyOverflowY = e.setTemporaryStyle(@body, 'overflow-y': 'visible')
536
+ @restoreHtmlOverflowY = e.setTemporaryStyle(@html, 'overflow-y': 'visible')
537
+
538
+ afterEach ->
539
+ @restoreBodyOverflowY()
540
+ @restoreHtmlOverflowY()
541
+
542
+ it 'returns the <html> element if the developer set { overflow-y: scroll } on it', ->
543
+ @html.style.overflowY = 'scroll'
544
+ expect(up.viewport.rootOverflowElement()).toBe(@html)
545
+
546
+ it 'returns the <html> element if the developer set { overflow-y: auto } on it', ->
547
+ @html.style.overflowY = 'auto'
548
+ expect(up.viewport.rootOverflowElement()).toBe(@html)
549
+
550
+ it 'returns the <body> element if the developer set { overflow-y: scroll } on it', ->
551
+ @body.style.overflowY = 'scroll'
552
+ expect(up.viewport.rootOverflowElement()).toBe(@body)
553
+
554
+ it 'returns the <body> element if the developer set { overflow-y: auto } on it', ->
555
+ @body.style.overflowY = 'auto'
556
+ expect(up.viewport.rootOverflowElement()).toBe(@body)
557
+
558
+ it 'returns the scrolling element if the developer set { overflow-y: visible } on both <html> and <body>', ->
559
+ @html.style.overflowY = 'visible'
560
+ @body.style.overflowY = 'visible'
561
+ expect(up.viewport.rootOverflowElement()).toBe(up.viewport.root())
562
+
563
+ describe 'up.viewport.absolutize', ->
538
564
 
539
565
  afterEach ->
540
566
  $('.up-bounds, .fixture').remove()
541
567
 
542
568
  it 'absolutely positions the element, preserving visual position and size', ->
543
- $element = affix('.element').text('element text').css(paddingTop: '20px', paddingLeft: '20px')
569
+ $element = $fixture('.element').text('element text').css(paddingTop: '20px', paddingLeft: '20px')
544
570
 
545
571
  expect($element.css('position')).toEqual('static')
546
- previousDims = u.measure($element)
572
+ previousDims = $element[0].getBoundingClientRect()
547
573
 
548
- up.layout.absolutize($element)
574
+ up.viewport.absolutize($element)
549
575
 
550
576
  expect($element.closest('.up-bounds').css('position')).toEqual('absolute')
551
577
 
552
- newDims = u.measure($element)
578
+ newDims = $element[0].getBoundingClientRect()
553
579
  expect(newDims).toEqual(previousDims)
554
580
 
555
581
  it 'accurately positions the ghost over an element with margins', ->
556
- $element = affix('.element').css(margin: '40px')
557
- previousDims = u.measure($element)
582
+ $element = $fixture('.element').css(margin: '40px')
583
+ previousDims = $element[0].getBoundingClientRect()
558
584
 
559
- up.layout.absolutize($element)
585
+ up.viewport.absolutize($element)
560
586
 
561
- newDims = u.measure($element)
587
+ newDims = $element[0].getBoundingClientRect()
562
588
  expect(newDims).toEqual(previousDims)
563
589
 
564
590
  it "doesn't change the position of a child whose margins no longer collapse", ->
565
- $element = affix('.element')
591
+ $element = $fixture('.element')
566
592
  $child = $('<div class="child">child text</div>').css(margin: '40px').appendTo($element)
567
- previousChildDims = u.measure($child)
593
+ previousChildDims = $child[0].getBoundingClientRect()
568
594
 
569
- up.layout.absolutize($element)
595
+ up.viewport.absolutize($element)
570
596
 
571
- newChildDims = u.measure($child)
597
+ newChildDims = $child[0].getBoundingClientRect()
572
598
  expect(newChildDims).toEqual(previousChildDims)
573
599
 
574
600
  it 'correctly positions an element within a scrolled body', ->
@@ -577,15 +603,15 @@ describe 'up.layout', ->
577
603
  $element2 = $('<div class="fixture"></div>').css(height: '100px').insertAfter($element1)
578
604
  $body.scrollTop(33)
579
605
 
580
- previousDims = u.measure($element2)
606
+ previousDims = $element2[0].getBoundingClientRect()
581
607
 
582
- up.layout.absolutize($element2)
608
+ up.viewport.absolutize($element2)
583
609
 
584
- newDims = u.measure($element2)
610
+ newDims = $element2[0].getBoundingClientRect()
585
611
  expect(newDims).toEqual(previousDims)
586
612
 
587
613
  it 'correctly positions an element within a scrolled parent element (that has overflow-y: scroll)', ->
588
- $viewport = affix('div').css
614
+ $viewport = $fixture('div').css
589
615
  overflowY: 'scroll'
590
616
  height: '50px'
591
617
 
@@ -593,15 +619,15 @@ describe 'up.layout', ->
593
619
  $element2 = $('<div class="fixture"></div>').css(height: '100px').insertAfter($element1)
594
620
  $viewport.scrollTop(33)
595
621
 
596
- previousDims = u.measure($element2)
622
+ previousDims = $element2[0].getBoundingClientRect()
597
623
 
598
- up.layout.absolutize($element2)
624
+ up.viewport.absolutize($element2)
599
625
 
600
- newDims = u.measure($element2)
626
+ newDims = $element2[0].getBoundingClientRect()
601
627
  expect(newDims).toEqual(previousDims)
602
628
 
603
629
  it 'converts fixed elements within the copies to absolutely positioning (relative to the closest offset parent)', ->
604
- $element = affix('.element').css
630
+ $element = $fixture('.element').css
605
631
  position: 'absolute'
606
632
  top: '50px'
607
633
  left: '50px'
@@ -610,7 +636,7 @@ describe 'up.layout', ->
610
636
  left: '77px'
611
637
  top: '77px'
612
638
  $fixedChild.appendTo($element)
613
- up.layout.absolutize($element)
639
+ up.viewport.absolutize($element)
614
640
 
615
641
  expect($fixedChild.css(['position', 'left', 'top'])).toEqual
616
642
  position: 'absolute',
@@ -618,12 +644,12 @@ describe 'up.layout', ->
618
644
  top: '27px'
619
645
 
620
646
  it "does not convert fixed elements outside the element's subtree (bugfix)", ->
621
- $element = affix('.element').css(position: 'absolute')
647
+ $element = $fixture('.element').css(position: 'absolute')
622
648
  $fixedChild = $('<div class="fixed-child" up-fixed></div>').css(position: 'fixed')
623
649
  $fixedChild.appendTo($element)
624
- $fixedSibling = affix('[up-fixed]').css(position: 'fixed')
650
+ $fixedSibling = $fixture('[up-fixed]').css(position: 'fixed')
625
651
 
626
- up.layout.absolutize($element)
652
+ up.viewport.absolutize($element)
627
653
 
628
654
  expect($fixedChild.css('position')).toEqual('absolute')
629
655
  expect($fixedSibling.css('position')).toEqual('fixed')