unpoly-rails 0.57.0 → 0.60.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 (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')