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,31 +1,43 @@
1
1
  ###**
2
- Application layout
3
- ==================
2
+ Scrolling viewports
3
+ ===================
4
4
 
5
- You can [make Unpoly aware](/up.layout.config) of fixed elements in your
5
+ The `up.viewport` module controls the scroll position of scrollable containers ("viewports").
6
+
7
+ The default viewport for any web application is the main document. An application may
8
+ define additional viewports by giving the CSS property `{ overflow-y: scroll }` to any `<div>`.
9
+
10
+
11
+ \#\#\# Revealing new content
12
+
13
+ When following a [link to a fragment](/a-up-target) Unpoly will automatically
14
+ scroll the document's viewport to [reveal](/up.viewport) the updated content.
15
+
16
+ You should [make Unpoly aware](/up.viewport.config#config.fixedTop) of fixed elements in your
6
17
  layout, such as navigation bars or headers. Unpoly will respect these sticky
7
- elements when [revealing elements](/up.reveal) or [opening a modal dialog](/a-up-modal).
18
+ elements when [revealing updated fragments](/up.reveal).
8
19
 
9
- This modules also contains functions to programmatically [scroll a viewport](/up.scroll)
10
- or [reveal an element within its viewport](/up.reveal).
20
+ You should also [tell Unpoly](/up.viewport.config#config.viewports) when your application has more than one viewport,
21
+ you should so Unpoly can pick the right viewport to scroll for each fragment update.
11
22
 
12
- Bootstrap integration
13
- ---------------------
23
+
24
+ \#\#\# Bootstrap integration
14
25
 
15
26
  When using Bootstrap integration (`unpoly-bootstrap3.js` and `unpoly-bootstrap3.css`)
16
27
  Unpoly will automatically be aware of sticky Bootstrap components such as
17
28
  [fixed navbar](https://getbootstrap.com/examples/navbar-fixed-top/).
18
29
 
19
- @class up.layout
30
+ @module up.viewport
20
31
  ###
21
- up.layout = (($) ->
32
+ up.viewport = do ->
22
33
 
23
34
  u = up.util
35
+ e = up.element
24
36
 
25
37
  ###**
26
38
  Configures the application layout.
27
39
 
28
- @property up.layout.config
40
+ @property up.viewport.config
29
41
  @param {Array} [config.viewports]
30
42
  An array of CSS selectors that find viewports
31
43
  (containers that scroll their contents).
@@ -41,40 +53,43 @@ up.layout = (($) ->
41
53
  An array of CSS selectors that find elements anchored to the
42
54
  right edge of the screen (using `right:0` with `position: fixed` or `position: absolute`).
43
55
  See [`[up-anchored="right"]`](/up-anchored-right) for details.
44
- @param {number} [config.duration=0]
45
- The duration of the scrolling animation in milliseconds.
46
- Setting this to `0` will disable scrolling animations.
47
- @param {string} [config.easing='swing']
48
- The timing function that controls the animation's acceleration.
49
- See [W3C documentation](http://www.w3.org/TR/css3-transitions/#transition-timing-function)
50
- for a list of pre-defined timing functions.
51
- @param {number} [config.snap=50]
56
+ @param {number} [config.revealSnap=50]
52
57
  When [revealing](/up.reveal) elements, Unpoly will scroll an viewport
53
- to the top when the revealed element is closer to the top than `config.snap`.
54
- @param {number} [config.substance=150]
55
- A number indicating how many top pixel rows of an element to [reveal](/up.reveal).
58
+ to the top when the revealed element is closer to the top than `config.revealSnap`.
59
+ @param {number} [config.revealPadding=0]
60
+ The desired padding between a [revealed](/up.reveal) element and the
61
+ closest [viewport](/up.viewport) edge (in pixels).
62
+ @param {number} [config.scrollSpeed=1]
63
+ The speed of the scrolling motion when [scrolling](/up.scroll) with `{ behavior: 'smooth' }`.
64
+
65
+ The default value (`1`) roughly corresponds to the speed of Chrome's
66
+ [native smooth scrolling](https://developer.mozilla.org/en-US/docs/Web/API/ScrollToOptions/behavior).
56
67
  @stable
57
68
  ###
58
- config = u.config
69
+ config = new up.Config
59
70
  duration: 0
60
- viewports: ['.up-modal-viewport', '[up-viewport]']
71
+ viewports: ['.up-modal-viewport', '[up-viewport]', '[up-fixed]']
61
72
  fixedTop: ['[up-fixed~=top]']
62
73
  fixedBottom: ['[up-fixed~=bottom]']
63
74
  anchoredRight: ['[up-anchored~=right]', '[up-fixed~=top]', '[up-fixed~=bottom]', '[up-fixed~=right]']
64
- snap: 50
65
- substance: 150
66
- easing: 'swing'
75
+ revealSnap: 50
76
+ revealPadding: 0,
77
+ scrollSpeed: 1
78
+
79
+ # up.legacy.renamedProperty(config, 'snap', 'revealSnap')
80
+ # up.legacy.removedProperty(config, 'easing')
81
+ # up.legacy.removedProperty(config, 'duration')
67
82
 
68
83
  lastScrollTops = new up.Cache
69
84
  size: 30,
70
85
  key: up.history.normalizeUrl
71
86
 
72
- scrollingTracker = new up.MotionTracker('scrolling')
87
+ scrollingController = new up.MotionController('scrolling')
73
88
 
74
89
  reset = ->
75
90
  config.reset()
76
91
  lastScrollTops.clear()
77
- scrollingTracker.reset()
92
+ scrollingController.reset()
78
93
 
79
94
  ###**
80
95
  Scrolls the given viewport to the given Y-position.
@@ -86,16 +101,13 @@ up.layout = (($) ->
86
101
 
87
102
  This will scroll a `<div class="main">...</div>` to a Y-position of 100 pixels:
88
103
 
89
- up.scroll('.main', 100);
104
+ up.scroll('.main', 100)
90
105
 
91
106
  \#\#\# Animating the scrolling motion
92
107
 
93
108
  The scrolling can (optionally) be animated.
94
109
 
95
- up.scroll('.main', 100, {
96
- easing: 'swing',
97
- duration: 250
98
- });
110
+ up.scroll('.main', 100, { behavior: 'smooth' })
99
111
 
100
112
  If the given viewport is already in a scroll animation when `up.scroll()`
101
113
  is called a second time, the previous animation will instantly jump to the
@@ -106,49 +118,26 @@ up.layout = (($) ->
106
118
  The container element to scroll.
107
119
  @param {number} scrollPos
108
120
  The absolute number of pixels to set the scroll position to.
109
- @param {number}[options.duration]
110
- The number of miliseconds for the scrolling's animation.
111
- @param {string}[options.easing]
112
- The timing function that controls the acceleration for the scrolling's animation.
121
+ @param {string}[options.behavior='auto']
122
+ When set to `'auto'`, this will immediately scroll to the new position.
123
+
124
+ When set to `'smooth'`, this will scroll smoothly to the new position.
125
+ @param {number}[options.speed]
126
+ The speed of the scrolling motion when scrolling with `{ behavior: 'smooth' }`.
127
+
128
+ Defaults to `up.viewport.config.scrollSpeed`.
113
129
  @return {Promise}
114
130
  A promise that will be fulfilled when the scrolling ends.
115
131
  @experimental
116
132
  ###
117
133
  scroll = (viewport, scrollTop, options) ->
118
- $viewport = $(viewport)
119
- options = u.options(options)
120
- options.duration = u.option(options.duration, config.duration)
121
- options.easing = u.option(options.easing, config.easing)
122
-
123
- finishScrolling($viewport).then ->
124
- if up.motion.isEnabled() && options.duration > 0
125
- scrollWithAnimateNow($viewport, scrollTop, options)
126
- else
127
- scrollAbruptlyNow($viewport, scrollTop)
128
-
129
- scrollWithAnimateNow = ($scrollable, scrollTop, animateOptions) ->
130
- start = ->
131
- finish = ->
132
- # jQuery exposes a finish() method that completes all animations orchestrated through jQuery.
133
- # This will also resolve the promise returned by $element.animate(..).promise().
134
- $scrollable.finish()
135
-
136
- $scrollable.on(scrollingTracker.eventName, finish)
137
- scrollDone = $scrollable.animate({ scrollTop }, animateOptions).promise()
138
- scrollDone.then -> $scrollable.off(scrollingTracker.eventName)
139
- scrollDone
140
-
141
- # Tracker will either finish or wait for previous scrolling animations before starting the next
142
- scrollingTracker.claim($scrollable, start)
143
-
144
- scrollAbruptlyNow = ($scrollable, scrollTop) ->
145
- $scrollable.scrollTop(scrollTop)
146
- Promise.resolve()
134
+ motion = new up.ScrollMotion(viewport, scrollTop, options)
135
+ scrollingController.startMotion(viewport, motion, options)
147
136
 
148
137
  ###**
149
138
  Finishes scrolling animations in the given element, its ancestors or its descendants.
150
139
 
151
- @function up.layout.finishScrolling
140
+ @function up.viewport.finishScrolling
152
141
  @param {string|Element|jQuery}
153
142
  @return {Promise}
154
143
  @internal
@@ -157,16 +146,16 @@ up.layout = (($) ->
157
146
  # Don't emit expensive events if no animation can be running anyway
158
147
  return Promise.resolve() unless up.motion.isEnabled()
159
148
 
160
- $scrollable = viewportOf(element)
161
- scrollingTracker.finish($scrollable)
149
+ scrollable = closest(element)
150
+ scrollingController.finish(scrollable)
162
151
 
163
152
  ###**
164
- @function up.layout.anchoredRight
153
+ @function up.viewport.anchoredRight
165
154
  @internal
166
155
  ###
167
156
  anchoredRight = ->
168
157
  selector = config.anchoredRight.join(',')
169
- $(selector)
158
+ e.all(selector)
170
159
 
171
160
  ###**
172
161
  @function measureObstruction
@@ -175,7 +164,7 @@ up.layout = (($) ->
175
164
  ###
176
165
  measureObstruction = (viewportHeight) ->
177
166
  composeHeight = (obstructor, distanceFromEdgeProps) ->
178
- distanceFromEdge = u.sum(distanceFromEdgeProps, (prop) -> u.readComputedStyleNumber(obstructor, prop)) || 0
167
+ distanceFromEdge = u.sum(distanceFromEdgeProps, (prop) -> e.styleNumber(obstructor, prop)) || 0
179
168
  distanceFromEdge + obstructor.offsetHeight
180
169
 
181
170
  measureTopObstructor = (obstructor) ->
@@ -184,11 +173,11 @@ up.layout = (($) ->
184
173
  measureBottomObstructor = (obstructor) ->
185
174
  composeHeight(obstructor, ['bottom', 'margin-bottom'])
186
175
 
187
- $topObstructors = $(config.fixedTop.join(', '))
188
- $bottomObstructors = $(config.fixedBottom.join(', '))
176
+ topObstructors = e.all(config.fixedTop.join(', '))
177
+ bottomObstructors = e.all(config.fixedBottom.join(', '))
189
178
 
190
- topObstructions = u.map($topObstructors, measureTopObstructor)
191
- bottomObstructions = u.map($bottomObstructors, measureBottomObstructor)
179
+ topObstructions = u.map(topObstructors, measureTopObstructor)
180
+ bottomObstructions = u.map(bottomObstructors, measureBottomObstructor)
192
181
 
193
182
  top: Math.max(0, topObstructions...)
194
183
  bottom: Math.max(0, bottomObstructions...)
@@ -209,7 +198,7 @@ up.layout = (($) ->
209
198
  - the currently open [modal](/up.modal)
210
199
  - an element with the attribute `[up-viewport]`
211
200
  - the `<body>` element
212
- - an element matching the selector you have configured using `up.layout.config.viewports.push('my-custom-selector')`
201
+ - an element matching the selector you have configured using `up.viewport.config.viewports.push('my-custom-selector')`
213
202
 
214
203
  \#\#\# Fixed elements obstruction the viewport
215
204
 
@@ -221,79 +210,47 @@ up.layout = (($) ->
221
210
  To make `up.reveal()` aware fixed elements you can either:
222
211
 
223
212
  - give the element an attribute [`up-fixed="top"`](/up-fixed-top) or [`up-fixed="bottom"`](up-fixed-bottom)
224
- - [configure default options](/up.layout.config) for `fixedTop` or `fixedBottom`
213
+ - [configure default options](/up.viewport.config) for `fixedTop` or `fixedBottom`
225
214
 
226
215
  @function up.reveal
227
216
  @param {string|Element|jQuery} element
228
- @param {number} [options.duration]
229
- @param {string} [options.easing]
217
+ @param {number} [options.speed]
230
218
  @param {string} [options.snap]
231
219
  @param {string|Element|jQuery} [options.viewport]
232
220
  @param {boolean} [options.top=false]
233
221
  Whether to scroll the viewport so that the first element row aligns
234
222
  with the top edge of the viewport.
235
- @return {Promise}
236
- A promise that fulfills when the element is revealed.
237
- @stable
238
- ###
239
- reveal = (elementOrSelector, options) ->
240
- $element = $(elementOrSelector).first() # we can only reveal one element
241
- up.puts 'Revealing fragment %o', $element.get(0)
242
- options = u.options(options)
223
+ @param {string}[options.behavior='auto']
224
+ When set to `'auto'`, this will immediately scroll to the new position.
243
225
 
244
- u.rejectOnError ->
245
- $viewport = if options.viewport then $(options.viewport) else viewportOf($element)
226
+ When set to `'smooth'`, this will scroll smoothly to the new position.
227
+ @param {number}[options.speed]
228
+ The speed of the scrolling motion when scrolling with `{ behavior: 'smooth' }`.
246
229
 
247
- snap = u.option(options.snap, config.snap)
230
+ Defaults to `up.viewport.config.scrollSpeed`.
231
+ @param {number} [config.padding=0]
232
+ The desired padding between the revealed element and the
233
+ closest [viewport](/up.viewport) edge (in pixels).
234
+ @param {number|boolean} [config.snap]
235
+ Whether to snap to the top of the viewport if the new scroll position
236
+ after revealing the element is close to the top edge.
248
237
 
249
- viewportIsDocument = $viewport.is(up.browser.documentViewportSelector())
250
- viewportHeight = if viewportIsDocument then u.clientSize().height else $viewport.outerHeight()
251
- originalScrollPos = $viewport.scrollTop()
252
- newScrollPos = originalScrollPos
238
+ You may pass a maximum number of pixels under which to snap to the top.
253
239
 
254
- offsetShift = undefined
255
- obstruction = undefined
240
+ Passing `false` will disable snapping.
256
241
 
257
- if viewportIsDocument
258
- obstruction = measureObstruction(viewportHeight)
259
- # Within the body, $.position will always return the distance
260
- # from the document top and *not* the distance of the viewport
261
- # top. This is what the calculations below expect, so don't shift.
262
- offsetShift = 0
263
- else
264
- obstruction = { top: 0, bottom: 0 }
265
- # When the scrolled element is not <body> but instead a container
266
- # with overflow-y: scroll, $.position returns the distance to the
267
- # viewport's currently visible top edge (instead of the distance to
268
- # the first row of the viewport's entire canvas buffer).
269
- # http://codepen.io/anon/pen/jPojGE
270
- offsetShift = originalScrollPos
271
-
272
- predictFirstVisibleRow = -> newScrollPos + obstruction.top
273
- predictLastVisibleRow = -> newScrollPos + viewportHeight - obstruction.bottom
274
-
275
- elementDims = u.measure($element, relative: $viewport, includeMargin: true)
276
- firstElementRow = elementDims.top + offsetShift
277
- lastElementRow = firstElementRow + Math.min(elementDims.height, config.substance)
278
-
279
- if lastElementRow > predictLastVisibleRow()
280
- # Try to show the full height of the element
281
- newScrollPos += (lastElementRow - predictLastVisibleRow())
282
-
283
- if firstElementRow < predictFirstVisibleRow() || options.top
284
- # If the full element does not fit, scroll to the first row
285
- newScrollPos = firstElementRow - obstruction.top
286
-
287
- if newScrollPos < snap && elementDims.top < (0.5 * viewportHeight)
288
- newScrollPos = 0
289
-
290
- if newScrollPos != originalScrollPos
291
- scroll($viewport, newScrollPos, options)
292
- else
293
- Promise.resolve()
242
+ Passing `true` will use the snap pixel value from `up.viewport.config.revealSnap`.
243
+ @return {Promise}
244
+ A promise that fulfills when the element is revealed.
245
+ @stable
246
+ ###
247
+ reveal = (elementOrSelector, options) ->
248
+ element = e.get(elementOrSelector)
249
+ motion = new up.RevealMotion(element, options)
250
+ scrollingController.startMotion(element, motion, options)
294
251
 
295
252
  ###**
296
- @function up.layout.scrollAfterInsertFragment
253
+ @function up.viewport.scrollAfterInsertFragment
297
254
  @param {boolean|object} [options.restoreScroll]
298
255
  @param {boolean|string|jQuery|Element} [options.reveal]
299
256
  @param {boolean|string} [options.reveal]
@@ -301,45 +258,42 @@ up.layout = (($) ->
301
258
  A promise that is fulfilled when the scrolling has finished.
302
259
  @internal
303
260
  ###
304
- scrollAfterInsertFragment = (selectorOrElement, options) ->
305
- options = u.options(options)
306
- $element = $(selectorOrElement)
307
-
261
+ scrollAfterInsertFragment = (element, options = {}) ->
308
262
  hashOpt = options.hash
309
263
  revealOpt = options.reveal
310
264
  restoreScrollOpt = options.restoreScroll
311
265
 
312
- durationOptions = u.only(options, 'duration')
266
+ scrollOptions = u.only(options, 'scrollBehavior', 'scrollSpeed')
313
267
 
314
268
  if restoreScrollOpt
315
269
  # If options.restoreScroll is an object, its keys map viewport selectors
316
270
  # to scroll positions. If it is just true, we leave the scrollTops option
317
271
  # undefined and let restoreScroll() retrieve previous scrollTops from cache.
318
272
  givenTops = u.presence(restoreScrollOpt, u.isObject)
319
- return restoreScroll(around: $element, scrollTops: givenTops)
273
+ return restoreScroll(around: element, scrollTops: givenTops)
320
274
 
321
275
  else if hashOpt && revealOpt == true # hash revealing can be disabled with { reveal: false }
322
- return revealHash(hashOpt, durationOptions)
276
+ return revealHash(hashOpt, scrollOptions)
323
277
 
324
278
  else if revealOpt
325
279
  # We allow to pass another element as { reveal } option
326
280
  if u.isElement(revealOpt) || u.isJQuery(revealOpt)
327
- $element = $(revealOpt)
281
+ element = e.get(revealOpt)
328
282
 
329
283
  # We allow to pass a selector as { reveal } option
330
284
  else if u.isString(revealOpt)
331
- selector = up.dom.resolveSelector(revealOpt, options.origin)
332
- $element = up.first(selector)
285
+ selector = e.resolveSelector(revealOpt, options.origin)
286
+ element = up.fragment.first(selector)
333
287
 
334
288
  else
335
- # We reveal the given $element
289
+ # We reveal the given element
336
290
 
337
291
  # If selectorOrElement was a CSS selector, don't blow up by calling reveal()
338
292
  # with an empty jQuery collection. This might happen if a failed form submission
339
293
  # reveals the first validation error message, but the error is shown in an
340
294
  # unexpected element.
341
- if $element.length
342
- return reveal($element, durationOptions)
295
+ if element
296
+ return reveal(element, scrollOptions)
343
297
 
344
298
  else
345
299
  # If we didn't need to scroll above, just return a resolved promise
@@ -358,60 +312,168 @@ up.layout = (($) ->
358
312
 
359
313
  If no element matches the given `#hash` anchor, a resolved promise is returned.
360
314
 
361
- @function up.layout.revealHash
315
+ \#\#\# Example
316
+
317
+ up.revealHash('#chapter2')
318
+
319
+ @function up.viewport.revealHash
320
+ @param {string} hash
321
+
362
322
  @return {Promise}
363
323
  A promise that is fulfilled when scroll position has changed to match the location hash.
364
324
  @experimental
365
325
  ###
366
326
  revealHash = (hash) ->
367
- if (hash) && ($match = firstHashTarget(hash))
368
- reveal($match, top: true)
327
+ if (hash) && (match = firstHashTarget(hash))
328
+ reveal(match, top: true)
369
329
  else
370
330
  Promise.resolve()
371
331
 
372
- viewportSelector = ->
332
+ allSelector = ->
373
333
  # On Edge the document viewport can be changed from CSS
374
- [up.browser.documentViewportSelector(), config.viewports...].join(',')
334
+ [rootSelector(), config.viewports...].join(',')
375
335
 
376
336
  ###**
377
- Returns the viewport for the given element.
337
+ Returns the scrolling container for the given element.
378
338
 
379
- Returns the [document's scrolling element](https://developer.mozilla.org/en-US/docs/Web/API/Document/scrollingElement)
380
- if no closer viewpoint exists.
339
+ Returns the [document's scrolling element](/up.viewport.root)
340
+ if no closer viewport exists.
381
341
 
382
- @function up.layout.viewportOf
342
+ @function up.viewport.closest
383
343
  @param {string|Element|jQuery} selectorOrElement
384
- @return {jQuery}
385
- @internal
344
+ @return {Element}
345
+ @experimental
386
346
  ###
387
- viewportOf = (selectorOrElement, options = {}) ->
388
- $element = $(selectorOrElement)
389
- $element.closest(viewportSelector())
347
+ closest = (selectorOrElement) ->
348
+ element = e.get(selectorOrElement)
349
+ e.closest(element, allSelector())
390
350
 
391
351
  ###**
392
352
  Returns a jQuery collection of all the viewports contained within the
393
353
  given selector or element.
394
354
 
395
- @function up.layout.viewportsWithin
355
+ @function up.viewport.subtree
396
356
  @param {string|Element|jQuery} selectorOrElement
397
- @return jQuery
357
+ @return List<Element>
398
358
  @internal
399
359
  ###
400
- viewportsWithin = (selectorOrElement) ->
401
- $element = $(selectorOrElement)
402
- u.selectInSubtree($element, viewportSelector())
360
+ getSubtree = (selectorOrElement) ->
361
+ element = e.get(selectorOrElement)
362
+ e.subtree(element, allSelector())
363
+
364
+ getAround = (selectorOrElement) ->
365
+ element = e.get(selectorOrElement)
366
+ e.list(closest(element), getSubtree(element))
403
367
 
404
368
  ###**
405
- Returns a jQuery collection of all the viewports on the screen.
369
+ Returns a list of all the viewports on the screen.
406
370
 
407
- @function up.layout.viewports
371
+ @function up.viewport.all
408
372
  @internal
409
373
  ###
410
- viewports = ->
411
- $(viewportSelector())
374
+ getAll = ->
375
+ e.all(allSelector())
376
+
377
+ rootSelector = ->
378
+ # The spec says this should be <html> in standards mode
379
+ # and <body> in quirks mode. However, it is currently (2018-07)
380
+ # always <body> in Webkit browsers (not Blink). Luckily Webkit
381
+ # also supports document.scrollingElement.
382
+ if element = document.scrollingElement
383
+ element.tagName
384
+ else
385
+ # IE11
386
+ 'html'
387
+
388
+ ###**
389
+ Return the [scrolling element](https://developer.mozilla.org/en-US/docs/Web/API/document/scrollingElement)
390
+ for the browser's main content area.
391
+
392
+ @function up.viewport.root
393
+ @return {Element}
394
+ @experimental
395
+ ###
396
+ getRoot = ->
397
+ document.querySelector(rootSelector())
398
+
399
+ rootWidth = ->
400
+ # This should happen on the <html> element, regardless of document.scrollingElement
401
+ e.root().clientWidth
402
+
403
+ rootHeight = ->
404
+ # This should happen on the <html> element, regardless of document.scrollingElement
405
+ e.root().clientHeight
406
+
407
+ isRoot = (element) ->
408
+ e.matches(element, rootSelector())
409
+
410
+ ###**
411
+ Returns whether the given element is currently showing a vertical scrollbar.
412
+
413
+ @function up.viewport.rootHasVerticalScrollbar
414
+ @internal
415
+ ###
416
+ rootHasVerticalScrollbar = ->
417
+ # We could also check if scrollHeight > offsetHeight for the document viewport.
418
+ # However, we would also need to check overflow-y for that element.
419
+ # Also we have no control whether developers set the property on <body> or <html>.
420
+ # https://tylercipriani.com/blog/2014/07/12/crossbrowser-javascript-scrollbar-detection/
421
+ window.innerWidth > document.documentElement.offsetWidth
422
+
423
+ ###**
424
+ Returns the element that controls the `overflow-y` behavior for the
425
+ [document viewport](/up.viewport.root()).
426
+
427
+ @function up.viewport.rootOverflowElement
428
+ @internal
429
+ ###
430
+ rootOverflowElement = ->
431
+ body = document.body
432
+ html = document.documentElement
433
+
434
+ element = u.find([html, body], wasChosenAsOverflowingElement)
435
+ element || getRoot()
436
+
437
+ ###**
438
+ Returns whether the given element was chosen as the overflowing
439
+ element by the developer.
440
+
441
+ We have no control whether developers set the property on <body> or
442
+ <html>. The developer also won't know what is going to be the
443
+ [scrolling element](/up.viewport.root()) on the user's brower.
444
+
445
+ @function wasChosenAsOverflowingElement
446
+ @internal
447
+ ###
448
+ wasChosenAsOverflowingElement = (element) ->
449
+ overflowY = e.style(element, 'overflow-y')
450
+ overflowY == 'auto' || overflowY == 'scroll'
451
+
452
+ ###**
453
+ Returns the width of a scrollbar.
454
+
455
+ This only runs once per page load.
456
+
457
+ @function up.viewport.scrollbarWidth
458
+ @internal
459
+ ###
460
+ scrollbarWidth = u.memoize ->
461
+ # This is how Bootstrap does it also:
462
+ # https://github.com/twbs/bootstrap/blob/c591227602996c542b9fd0cb65cff3cc9519bdd5/dist/js/bootstrap.js#L1187
463
+ outerStyle =
464
+ position: 'absolute'
465
+ top: '0'
466
+ left: '0'
467
+ width: '100px'
468
+ height: '100px' # Firefox needs at least 100px to show a scrollbar
469
+ overflowY: 'scroll'
470
+ outer = up.element.affix(document.body, '[up-viewport]', { style: outerStyle })
471
+ width = outer.offsetWidth - outer.clientWidth
472
+ up.element.remove(outer)
473
+ width
412
474
 
413
475
  scrollTopKey = (viewport) ->
414
- u.selectorForElement(viewport)
476
+ e.toSelector(viewport)
415
477
 
416
478
  ###**
417
479
  Returns a hash with scroll positions.
@@ -419,64 +481,54 @@ up.layout = (($) ->
419
481
  Each key in the hash is a viewport selector. The corresponding
420
482
  value is the viewport's top scroll position:
421
483
 
422
- up.layout.scrollTops()
484
+ up.viewport.scrollTops()
423
485
  => { '.main': 0, '.sidebar': 73 }
424
486
 
425
- @function up.layout.scrollTops
487
+ @function up.viewport.scrollTops
426
488
  @return Object<string, number>
427
489
  @internal
428
490
  ###
429
491
  scrollTops = ->
430
- topsBySelector = {}
431
- for group in config.viewports
432
- $(group).each ->
433
- $viewport = $(this)
434
- key = scrollTopKey($viewport)
435
- top = $viewport.scrollTop()
436
- topsBySelector[key] = top
437
- topsBySelector
492
+ u.mapObject getAll(), (viewport) ->
493
+ [scrollTopKey(viewport), viewport.scrollTop]
438
494
 
439
495
  ###**
440
- @function up.layout.fixedChildren
496
+ @function up.viewport.fixedElements
441
497
  @internal
442
498
  ###
443
- fixedChildren = (root = undefined) ->
444
- root ||= document.body
445
- $root = $(root)
446
- $elements = $root.find('[up-fixed]')
447
- $elements = $elements.add($root.find(config.fixedTop.join(', '))) if u.isPresent(config.fixedTop)
448
- $elements = $elements.add($root.find(config.fixedBottom.join(', '))) if u.isPresent(config.fixedBottom)
449
- $elements
499
+ fixedElements = (root = document) ->
500
+ queryParts = ['[up-fixed]'].concat(config.fixedTop).concat(config.fixedBottom)
501
+ root.querySelectorAll(queryParts.join(','))
450
502
 
451
503
  ###**
452
504
  Saves the top scroll positions of all the
453
- viewports configured in [`up.layout.config.viewports`](/up.layout.config).
505
+ viewports configured in [`up.viewport.config.viewports`](/up.viewport.config).
454
506
 
455
507
  The scroll positions will be associated with the current URL.
456
- They can later be restored by calling [`up.layout.restoreScroll()`](/up.layout.restoreScroll)
508
+ They can later be restored by calling [`up.viewport.restoreScroll()`](/up.viewport.restoreScroll)
457
509
  at the same URL.
458
510
 
459
511
  Unpoly automatically saves scroll positions whenever a fragment was updated on the page.
460
512
 
461
- @function up.layout.saveScroll
513
+ @function up.viewport.saveScroll
462
514
  @param {string} [options.url]
463
515
  @param {Object<string, number>} [options.tops]
464
516
  @experimental
465
517
  ###
466
518
  saveScroll = (options = {}) ->
467
- url = u.option(options.url, up.history.url())
468
- tops = u.option(options.tops, scrollTops())
519
+ url = options.url ? up.history.url()
520
+ tops = options.tops ? scrollTops()
469
521
  lastScrollTops.set(url, tops)
470
522
 
471
523
  ###**
472
- Restores [previously saved](/up.layout.saveScroll) scroll positions of viewports
473
- viewports configured in [`up.layout.config.viewports`](/up.layout.config).
524
+ Restores [previously saved](/up.viewport.saveScroll) scroll positions of viewports
525
+ viewports configured in [`up.viewport.config.viewports`](/up.viewport.config).
474
526
 
475
527
  Unpoly automatically restores scroll positions when the user presses the back button.
476
528
  You can disable this behavior by setting [`up.history.config.restoreScroll = false`](/up.history.config).
477
529
 
478
- @function up.layout.restoreScroll
479
- @param {jQuery} [options.around]
530
+ @function up.viewport.restoreScroll
531
+ @param {Element} [options.around]
480
532
  If set, only restores viewports that are either an ancestor
481
533
  or descendant of the given element.
482
534
  @return {Promise}
@@ -485,20 +537,11 @@ up.layout = (($) ->
485
537
  ###
486
538
  restoreScroll = (options = {}) ->
487
539
  url = up.history.url()
488
-
489
- $viewports = undefined
490
-
491
- if options.around
492
- $descendantViewports = viewportsWithin(options.around)
493
- $ancestorViewports = viewportOf(options.around)
494
- $viewports = $ancestorViewports.add($descendantViewports)
495
- else
496
- $viewports = viewports()
497
-
540
+ viewports = if options.around then getAround(options.around) else getAll()
498
541
  scrollTopsForUrl = options.scrollTops || lastScrollTops.get(url) || {}
499
542
 
500
543
  up.log.group 'Restoring scroll positions for URL %s to %o', url, scrollTopsForUrl, ->
501
- allScrollPromises = u.map $viewports, (viewport) ->
544
+ allScrollPromises = u.map viewports, (viewport) ->
502
545
  key = scrollTopKey(viewport)
503
546
  scrollTop = scrollTopsForUrl[key] || 0
504
547
  scroll(viewport, scrollTop, duration: 0)
@@ -508,59 +551,68 @@ up.layout = (($) ->
508
551
  ###**
509
552
  @internal
510
553
  ###
511
- absolutize = ($element, options) ->
512
- options = u.options(options, afterMeasure: u.noop)
513
- $viewport = up.layout.viewportOf($element)
514
- originalDims = u.measure($element, relative: true, inner: true)
515
- originalOffset = $element.offset()
516
- options.afterMeasure()
517
-
518
- u.writeInlineStyle $element,
554
+ absolutize = (elementOrSelector, options = {}) ->
555
+ element = e.get(elementOrSelector)
556
+ viewport = up.viewport.closest(element)
557
+
558
+ viewportRect = viewport.getBoundingClientRect()
559
+ originalRect = element.getBoundingClientRect()
560
+
561
+ boundsRect = new up.Rect
562
+ left: originalRect.left - viewportRect.left
563
+ top: originalRect.top - viewportRect.top
564
+ width: originalRect.width
565
+ height: originalRect.height
566
+
567
+ # Allow the caller to run code before we start shifting elements around.
568
+ options.afterMeasure?()
569
+
570
+ e.setStyle element,
519
571
  # If the element had a layout context before, make sure the
520
572
  # ghost will have layout context as well (and vice versa).
521
- position: if u.readComputedStyle($element, 'position') == 'static' then 'static' else 'relative'
522
- top: 'auto'
523
- right: 'auto'
524
- bottom: 'auto'
525
- left: 'auto'
526
- width: '100%'
527
- height: '100%'
573
+ position: if element.style.position == 'static' then 'static' else 'relative'
574
+ top: 'auto' # CSS default
575
+ right: 'auto' # CSS default
576
+ bottom: 'auto' # CSS default
577
+ left: 'auto' # CSS default
578
+ width: '100%' # stretch to the .up-bounds width we set below
579
+ height: '100%' # stretch to the .up-bounds height we set below
528
580
 
529
581
  # Wrap the ghost in another container so its margin can expand
530
582
  # freely. If we would position the element directly (old implementation),
531
583
  # it would gain a layout context which cannot be crossed by margins.
532
- $bounds = $('<div class="up-bounds"></div>')
533
- boundsStyle = u.merge(originalDims, position: 'absolute')
534
- u.writeInlineStyle($bounds, boundsStyle)
535
- $bounds.insertBefore($element)
536
- $element.appendTo($bounds)
584
+ bounds = e.createFromSelector('.up-bounds')
585
+ # Insert the bounds object before our element, then move element into it.
586
+ e.insertBefore(element, bounds)
587
+ bounds.appendChild(element)
537
588
 
538
- top = originalDims.top
589
+ moveBounds = (diffX, diffY) ->
590
+ boundsRect.left += diffX
591
+ boundsRect.top += diffY
592
+ e.setStyle(bounds, boundsRect)
539
593
 
540
- moveTop = (diff) ->
541
- if diff != 0
542
- top += diff
543
- u.writeInlineStyle($bounds, { top })
594
+ # Position the bounds initially
595
+ moveBounds(0, 0)
544
596
 
545
- # In theory, $element should not have moved visually.
546
- # However, $element (or a child of $element) might collapse its margin
547
- # against a previous sibling element, and now that it is absolute it does
548
- # not have the same sibling. So we manually correct $element's top
549
- # position so it aligns with the previous top position.
550
- moveTop(originalOffset.top - $element.offset().top)
597
+ # In theory, element should not have moved visually. However, element
598
+ # (or a child of element) might collapse its margin against a previous
599
+ # sibling element, and now that it is absolute it does not have the
600
+ # same sibling. So we manually correct element's top position so it aligns
601
+ # with the previous top position.
602
+ newElementRect = element.getBoundingClientRect()
603
+ moveBounds(originalRect.left - newElementRect.left, originalRect.top - newElementRect.top)
551
604
 
552
- $fixedElements = up.layout.fixedChildren($element)
553
- for fixedElement in $fixedElements
554
- u.fixedToAbsolute(fixedElement, $viewport)
605
+ u.each(fixedElements(element), e.fixedToAbsolute)
555
606
 
556
- { $element, $bounds, moveTop }
607
+ bounds: bounds
608
+ moveBounds: moveBounds
557
609
 
558
610
  ###**
559
611
  Marks this element as a scrolling container ("viewport").
560
612
 
561
613
  Apply this attribute if your app uses a custom panel layout with fixed positioning
562
614
  instead of scrolling `<body>`. As an alternative you can also push a selector
563
- matching your custom viewport to the [`up.layout.config.viewports`](/up.layout.config) array.
615
+ matching your custom viewport to the [`up.viewport.config.viewports`](/up.viewport.config) array.
564
616
 
565
617
  [`up.reveal()`](/up.reveal) will always try to scroll the viewport closest
566
618
  to the element that is being revealed. By default this is the `<body>` element.
@@ -618,7 +670,7 @@ up.layout = (($) ->
618
670
  Unpoly will then scroll the viewport far enough that the revealed element is fully visible.
619
671
 
620
672
  Instead of using this attribute,
621
- you can also configure a selector in [`up.layout.config.fixedTop`](/up.layout.config#config.fixedTop).
673
+ you can also configure a selector in [`up.viewport.config.fixedTop`](/up.viewport.config#config.fixedTop).
622
674
 
623
675
  \#\#\# Example
624
676
 
@@ -638,7 +690,7 @@ up.layout = (($) ->
638
690
  Unpoly will then scroll the viewport far enough that the revealed element is fully visible.
639
691
 
640
692
  Instead of using this attribute,
641
- you can also configure a selector in [`up.layout.config.fixedBottom`](/up.layout.config#config.fixedBottom).
693
+ you can also configure a selector in [`up.viewport.config.fixedBottom`](/up.viewport.config#config.fixedBottom).
642
694
 
643
695
  \#\#\# Example
644
696
 
@@ -662,7 +714,7 @@ up.layout = (($) ->
662
714
  with a CSS of `right: 0` with `position: fixed` or `position:absolute`.
663
715
 
664
716
  Instead of giving this attribute to any affected element,
665
- you can also configure a selector in [`up.layout.config.anchoredRight`](/up.layout.config#config.anchoredRight).
717
+ you can also configure a selector in [`up.viewport.config.anchoredRight`](/up.viewport.config#config.anchoredRight).
666
718
 
667
719
  \#\#\# Example
668
720
 
@@ -685,22 +737,29 @@ up.layout = (($) ->
685
737
  ###
686
738
 
687
739
  ###**
688
- @function up.layout.firstHashTarget
740
+ @function up.viewport.firstHashTarget
689
741
  @internal
690
742
  ###
691
743
  firstHashTarget = (hash) ->
692
744
  if hash = pureHash(hash)
693
- byID = u.attributeSelector('id', hash)
694
- byName = 'a' + u.attributeSelector('name', hash)
695
- up.first("#{byID},#{byName}")
696
-
745
+ selector = [
746
+ # First match an <* up-id="hash">. This won't be picked up without JS,
747
+ # preventing the scroll position from jump if up.viewport.revealPadding
748
+ # is set.
749
+ e.attributeSelector('up-id', hash),
750
+ # Match an <* id="hash">
751
+ e.attributeSelector('id', hash),
752
+ # Match an <a name="hash">
753
+ 'a' + e.attributeSelector('name', hash)
754
+ ].join(',')
755
+ up.fragment.first(selector)
697
756
 
698
757
  ###**
699
758
  Returns `'foo'` if the hash is `'#foo'`.
700
759
 
701
760
  Returns undefined if the hash is `'#'`, `''` or `undefined`.
702
761
 
703
- @function up.browser.hash
762
+ @function pureHash
704
763
  @internal
705
764
  ###
706
765
  pureHash = (value) ->
@@ -708,6 +767,8 @@ up.layout = (($) ->
708
767
  value = value.substr(1)
709
768
  u.presence(value)
710
769
 
770
+
771
+
711
772
  up.on 'up:app:booted', -> revealHash(location.hash)
712
773
 
713
774
  up.on 'up:framework:reset', reset
@@ -718,19 +779,28 @@ up.layout = (($) ->
718
779
  firstHashTarget: firstHashTarget
719
780
  scroll: scroll
720
781
  config: config
721
- viewportOf: viewportOf
722
- viewportsWithin: viewportsWithin
723
- viewports: viewports
782
+ closest: closest
783
+ subtree: getSubtree
784
+ around: getAround
785
+ all: getAll
786
+ rootSelector: rootSelector
787
+ root: getRoot
788
+ rootWidth: rootWidth
789
+ rootHeight: rootHeight
790
+ rootHasVerticalScrollbar: rootHasVerticalScrollbar
791
+ rootOverflowElement: rootOverflowElement
792
+ isRoot: isRoot
793
+ scrollbarWidth: scrollbarWidth
724
794
  scrollTops: scrollTops
725
795
  saveScroll: saveScroll
726
796
  restoreScroll: restoreScroll
727
797
  scrollAfterInsertFragment: scrollAfterInsertFragment
728
798
  anchoredRight: anchoredRight
729
- fixedChildren: fixedChildren
799
+ fixedElements: fixedElements
730
800
  absolutize: absolutize
731
801
 
732
- )(jQuery)
802
+ up.scroll = up.viewport.scroll
803
+ up.reveal = up.viewport.reveal
804
+ up.revealHash = up.viewport.revealHash
733
805
 
734
- up.scroll = up.layout.scroll
735
- up.reveal = up.layout.reveal
736
- up.revealHash = up.layout.revealHash
806
+ up.legacy.renamedModule 'layout', 'viewport'