unpoly-rails 0.55.1 → 0.56.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +59 -2
- data/dist/unpoly-bootstrap3.js +6 -4
- data/dist/unpoly-bootstrap3.min.js +1 -1
- data/dist/unpoly.js +1323 -805
- data/dist/unpoly.min.js +4 -3
- data/lib/assets/javascripts/unpoly-bootstrap3/{navigation-ext.coffee → feedback-ext.coffee} +2 -0
- data/lib/assets/javascripts/unpoly/browser.coffee.erb +7 -7
- data/lib/assets/javascripts/unpoly/bus.coffee.erb +5 -6
- data/lib/assets/javascripts/unpoly/classes/css_transition.coffee +127 -0
- data/lib/assets/javascripts/unpoly/classes/extract_plan.coffee +1 -1
- data/lib/assets/javascripts/unpoly/classes/motion_tracker.coffee +62 -32
- data/lib/assets/javascripts/unpoly/classes/url_set.coffee +27 -0
- data/lib/assets/javascripts/unpoly/dom.coffee.erb +78 -99
- data/lib/assets/javascripts/unpoly/feedback.coffee +147 -96
- data/lib/assets/javascripts/unpoly/form.coffee.erb +26 -2
- data/lib/assets/javascripts/unpoly/history.coffee +2 -1
- data/lib/assets/javascripts/unpoly/layout.coffee.erb +68 -12
- data/lib/assets/javascripts/unpoly/link.coffee.erb +10 -4
- data/lib/assets/javascripts/unpoly/modal.coffee.erb +11 -9
- data/lib/assets/javascripts/unpoly/{motion.coffee → motion.coffee.erb} +184 -322
- data/lib/assets/javascripts/unpoly/popup.coffee.erb +13 -12
- data/lib/assets/javascripts/unpoly/radio.coffee +1 -1
- data/lib/assets/javascripts/unpoly/syntax.coffee +8 -17
- data/lib/assets/javascripts/unpoly/tooltip.coffee +11 -11
- data/lib/assets/javascripts/unpoly/util.coffee +332 -145
- data/lib/unpoly/rails/version.rb +1 -1
- data/package.json +1 -1
- data/spec_app/Gemfile.lock +1 -1
- data/spec_app/app/assets/javascripts/integration_test.coffee +1 -0
- data/spec_app/app/assets/stylesheets/integration_test.sass +1 -0
- data/spec_app/app/assets/stylesheets/jasmine_specs.sass +4 -0
- data/spec_app/app/views/motion_test/transitions.erb +13 -0
- data/spec_app/app/views/pages/start.erb +1 -0
- data/spec_app/spec/javascripts/helpers/to_be_attached.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_be_detached.coffee +5 -0
- data/spec_app/spec/javascripts/helpers/to_contain.js.coffee +1 -1
- data/spec_app/spec/javascripts/helpers/to_have_opacity.coffee +11 -0
- data/spec_app/spec/javascripts/helpers/to_have_own_property.js.coffee +5 -0
- data/spec_app/spec/javascripts/up/dom_spec.js.coffee +217 -102
- data/spec_app/spec/javascripts/up/feedback_spec.js.coffee +162 -44
- data/spec_app/spec/javascripts/up/layout_spec.js.coffee +97 -10
- data/spec_app/spec/javascripts/up/link_spec.js.coffee +3 -3
- data/spec_app/spec/javascripts/up/modal_spec.js.coffee +22 -20
- data/spec_app/spec/javascripts/up/motion_spec.js.coffee +344 -228
- data/spec_app/spec/javascripts/up/popup_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/syntax_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/tooltip_spec.js.coffee +1 -1
- data/spec_app/spec/javascripts/up/util_spec.js.coffee +194 -0
- metadata +11 -4
@@ -2,12 +2,13 @@
|
|
2
2
|
Navigation feedback
|
3
3
|
===================
|
4
4
|
|
5
|
-
Unpoly automatically adds
|
6
|
-
currently loading ([`.up-active`](/a.up-active)) or
|
7
|
-
pointing to the current location ([`.up-current`](/a.up-current)).
|
5
|
+
Unpoly automatically adds the class [`.up-active`](/a.up-active) to links or forms while they are loading.
|
8
6
|
|
9
|
-
By
|
10
|
-
|
7
|
+
By marking navigation elements as [`[up-nav]`](/up-nav), contained links that point to the current location
|
8
|
+
automatically get the [`.up-current`](/up-nav-a.up-current) class.
|
9
|
+
|
10
|
+
You should style [`.up-active`](/a.up-active) and [`.up-current`](/up-nav a.up-current) with CSS to
|
11
|
+
provide instant feedback to user interactions. This improves the perceived speed of your interface.
|
11
12
|
|
12
13
|
\#\#\# Example
|
13
14
|
|
@@ -45,92 +46,122 @@ up.feedback = (($) ->
|
|
45
46
|
@property up.feedback.config
|
46
47
|
@param {Array<string>} [config.currentClasses]
|
47
48
|
An array of classes to set on [links that point the current location](/a.up-current).
|
49
|
+
@param {Array<string>} [config.navs]
|
50
|
+
An array of CSS selectors that match [navigation components](/up-nav).
|
48
51
|
@stable
|
49
52
|
###
|
50
53
|
config = u.config
|
51
54
|
currentClasses: ['up-current']
|
55
|
+
navs: ['[up-nav]']
|
56
|
+
|
57
|
+
previousUrlSet = undefined
|
58
|
+
currentUrlSet = undefined
|
52
59
|
|
53
60
|
reset = ->
|
54
61
|
config.reset()
|
55
|
-
|
56
|
-
|
57
|
-
classes = config.currentClasses
|
58
|
-
classes = classes.concat(['up-current'])
|
59
|
-
classes = u.uniq(classes)
|
60
|
-
classes.join(' ')
|
62
|
+
previousUrlSet = undefined
|
63
|
+
currentUrlSet = undefined
|
61
64
|
|
62
65
|
CLASS_ACTIVE = 'up-active'
|
63
|
-
|
66
|
+
SELECTOR_LINK = 'a, [up-href]'
|
67
|
+
|
68
|
+
navSelector = ->
|
69
|
+
config.navs.join(',')
|
64
70
|
|
65
71
|
normalizeUrl = (url) ->
|
66
72
|
if u.isPresent(url)
|
67
73
|
u.normalizeUrl(url, stripTrailingSlash: true)
|
68
74
|
|
75
|
+
NORMALIZED_SECTION_URLS_KEY = 'up-normalized-urls'
|
76
|
+
|
69
77
|
sectionUrls = ($section) ->
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
url = normalizeUrl(url)
|
77
|
-
urls.push(url)
|
78
|
+
# Check if we have computed the URLs before.
|
79
|
+
# Computation is sort of expensive (multiplied by number of links),
|
80
|
+
# so we cache the results in a data attribute.
|
81
|
+
unless urls = $section.data(NORMALIZED_SECTION_URLS_KEY)
|
82
|
+
urls = buildSectionUrls($section)
|
83
|
+
$section.data(NORMALIZED_SECTION_URLS_KEY, urls)
|
78
84
|
urls
|
79
85
|
|
80
|
-
|
81
|
-
urls =
|
82
|
-
urls = u.compact(urls)
|
83
|
-
|
84
|
-
matches = (testUrl) ->
|
85
|
-
if testUrl.substr(-1) == '*'
|
86
|
-
doesMatchPrefix(testUrl.slice(0, -1))
|
87
|
-
else
|
88
|
-
doesMatchFully(testUrl)
|
89
|
-
|
90
|
-
doesMatchFully = (testUrl) ->
|
91
|
-
u.contains(urls, testUrl)
|
92
|
-
|
93
|
-
doesMatchPrefix = (prefix) ->
|
94
|
-
u.detect urls, (url) ->
|
95
|
-
url.indexOf(prefix) == 0
|
96
|
-
|
97
|
-
matchesAny = (testUrls) ->
|
98
|
-
u.detect(testUrls, matches)
|
99
|
-
|
100
|
-
matchesAny: matchesAny
|
101
|
-
|
102
|
-
locationChanged = ->
|
103
|
-
currentUrls = urlSet([
|
104
|
-
up.browser.url(),
|
105
|
-
up.modal.url(),
|
106
|
-
up.modal.coveredUrl(),
|
107
|
-
up.popup.url(),
|
108
|
-
up.popup.coveredUrl()
|
109
|
-
])
|
110
|
-
|
111
|
-
klass = currentClass()
|
86
|
+
buildSectionUrls = ($section) ->
|
87
|
+
urls = []
|
112
88
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
89
|
+
# A link with an unsafe method will never be higlighted with .up-current,
|
90
|
+
# so we cache an empty array.
|
91
|
+
if up.link.isSafe($section)
|
92
|
+
for attr in ['href', 'up-href', 'up-alias']
|
93
|
+
if value = u.presentAttr($section, attr)
|
94
|
+
# Allow to include multiple space-separated URLs in [up-alias]
|
95
|
+
for url in value.split(/\s+/)
|
96
|
+
unless url == '#'
|
97
|
+
url = normalizeUrl(url)
|
98
|
+
urls.push(url)
|
99
|
+
urls
|
118
100
|
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
101
|
+
buildCurrentUrlSet = ->
|
102
|
+
urls = [
|
103
|
+
up.browser.url(), # The URL displayed in the address bar
|
104
|
+
up.modal.url(), # Even when a modal does not change the address bar, we consider the URL of its content
|
105
|
+
up.modal.coveredUrl(), # The URL of the page behind the modal
|
106
|
+
up.popup.url(), # Even when a popup does not change the address bar, we consider the URL of its content
|
107
|
+
up.popup.coveredUrl() # The URL of the page behind the popup
|
108
|
+
]
|
109
|
+
new up.UrlSet(urls, { normalizeUrl })
|
110
|
+
|
111
|
+
updateAllNavigationSectionsIfLocationChanged = ->
|
112
|
+
previousUrlSet = currentUrlSet
|
113
|
+
currentUrlSet = buildCurrentUrlSet()
|
114
|
+
unless currentUrlSet.isEqual(previousUrlSet)
|
115
|
+
updateAllNavigationSections($('body'))
|
116
|
+
|
117
|
+
updateAllNavigationSections = ($root) ->
|
118
|
+
$navs = u.selectInSubtree($root, navSelector())
|
119
|
+
$sections = u.selectInSubtree($navs, SELECTOR_LINK)
|
120
|
+
updateCurrentClassForLinks($sections)
|
121
|
+
|
122
|
+
updateNavigationSectionsInNewFragment = ($fragment) ->
|
123
|
+
if $fragment.closest(navSelector()).length
|
124
|
+
# If the new fragment is an [up-nav], or if the new fragment is a child of an [up-nav],
|
125
|
+
# all links in the new fragment are considered sections that we need to update.
|
126
|
+
# Note that:
|
127
|
+
# - The [up-nav] element might not be part of this update.
|
128
|
+
# It might already be in the DOM, and only a child was updated.
|
129
|
+
# - The $fragment might be a link itself
|
130
|
+
# - We do not need to update sibling links of $fragment that have been processed before.
|
131
|
+
$sections = u.selectInSubtree($fragment, SELECTOR_LINK)
|
132
|
+
updateCurrentClassForLinks($sections)
|
133
|
+
else
|
134
|
+
updateAllNavigationSections($fragment)
|
135
|
+
|
136
|
+
updateCurrentClassForLinks = ($links) ->
|
137
|
+
currentUrlSet ||= buildCurrentUrlSet()
|
138
|
+
u.each $links, (link) ->
|
139
|
+
$link = $(link)
|
140
|
+
urls = sectionUrls($link)
|
141
|
+
|
142
|
+
# We use Element#classList to manipulate classes instead of jQuery's
|
143
|
+
# addClass and removeClass. Since we are in an inner loop, we want to
|
144
|
+
# be as fast as we can.
|
145
|
+
classList = link.classList
|
146
|
+
if currentUrlSet.matchesAny(urls)
|
147
|
+
for klass in config.currentClasses
|
148
|
+
# Once we drop IE11 support in 2020 we can call add() with multiple arguments
|
149
|
+
classList.add(klass)
|
150
|
+
else
|
151
|
+
for klass in config.currentClasses
|
152
|
+
# Once we drop IE11 support in 2020 we can call remove() with multiple arguments
|
153
|
+
classList.remove(klass)
|
123
154
|
|
124
155
|
###**
|
125
|
-
@function
|
156
|
+
@function findActivatableArea
|
126
157
|
@param {string|Element|jQuery} elementOrSelector
|
127
158
|
@internal
|
128
159
|
###
|
129
|
-
|
160
|
+
findActivatableArea = (elementOrSelector) ->
|
130
161
|
$area = $(elementOrSelector)
|
131
|
-
if $area.is(
|
162
|
+
if $area.is(SELECTOR_LINK)
|
132
163
|
# Try to enlarge links that are expanded with [up-expand] on a surrounding container.
|
133
|
-
$area = u.presence($area.parent(
|
164
|
+
$area = u.presence($area.parent(SELECTOR_LINK)) || $area
|
134
165
|
$area
|
135
166
|
|
136
167
|
###**
|
@@ -167,7 +198,7 @@ up.feedback = (($) ->
|
|
167
198
|
elementOrSelector = args.shift()
|
168
199
|
action = args.pop()
|
169
200
|
options = u.options(args[0])
|
170
|
-
$element =
|
201
|
+
$element = findActivatableArea(elementOrSelector)
|
171
202
|
unless options.preload
|
172
203
|
$element.addClass(CLASS_ACTIVE)
|
173
204
|
if action
|
@@ -249,39 +280,57 @@ up.feedback = (($) ->
|
|
249
280
|
@internal
|
250
281
|
###
|
251
282
|
stop = (elementOrSelector) ->
|
252
|
-
$element =
|
283
|
+
$element = findActivatableArea(elementOrSelector)
|
253
284
|
$element.removeClass(CLASS_ACTIVE)
|
254
285
|
|
255
286
|
###**
|
256
|
-
|
257
|
-
|
287
|
+
Marks this element as a navigation component, such as a menu or navigation bar.
|
288
|
+
|
289
|
+
When a link within an `[up-nav]` element points to the current location, it is assigned the `.up-current` class. When the browser navigates to another location, the class is removed automatically.
|
258
290
|
|
259
|
-
|
291
|
+
You may also assign `[up-nav]` to an individual link instead of an navigational container.
|
260
292
|
|
261
|
-
|
293
|
+
If you don't want to manually add this attribute to every navigational element, you can configure selectors to automatically match your navigation components in [`up.feedback.config.navs`](/up.feedback.config#config.navs).
|
294
|
+
|
295
|
+
|
296
|
+
\#\#\# Example
|
297
|
+
|
298
|
+
Let's take a simple menu with two links. The menu has been marked with the `[up-nav]` attribute:
|
299
|
+
|
300
|
+
<div up-nav>
|
262
301
|
<a href="/foo">Foo</a>
|
263
302
|
<a href="/bar">Bar</a>
|
264
|
-
</
|
303
|
+
</div>
|
265
304
|
|
266
|
-
If the browser location changes to `/foo`, the
|
305
|
+
If the browser location changes to `/foo`, the first link is marked as `.up-current`:
|
267
306
|
|
268
|
-
<nav>
|
307
|
+
<div up-nav>
|
269
308
|
<a href="/foo" class="up-current">Foo</a>
|
270
309
|
<a href="/bar">Bar</a>
|
271
|
-
</
|
310
|
+
</div>
|
311
|
+
|
312
|
+
If the browser location changes to `/bar`, the first link automatically loses its `.up-current` class. Now the second link is marked as `.up-current`:
|
313
|
+
|
314
|
+
<div up-nav>
|
315
|
+
<a href="/foo">Foo</a>
|
316
|
+
<a href="/bar" class="up-current">Bar</a>
|
317
|
+
</div>
|
318
|
+
|
272
319
|
|
273
|
-
\#\#\# What
|
320
|
+
\#\#\# What is considered to be "current"?
|
274
321
|
|
275
322
|
The current location is considered to be either:
|
276
323
|
|
277
324
|
- the URL displayed in the browser window's location bar
|
278
|
-
- the source URL of a
|
279
|
-
- the
|
325
|
+
- the source URL of a [modal dialog](/up.modal)
|
326
|
+
- the URL of the page behind a [modal dialog](/up.modal)
|
327
|
+
- the source URL of a [popup overlay](/up.popup)
|
328
|
+
- the URL of the content behind a [popup overlay](/up.popup)
|
280
329
|
|
281
330
|
A link matches the current location (and is marked as `.up-current`) if it matches either:
|
282
331
|
|
283
332
|
- the link's `href` attribute
|
284
|
-
- the link's
|
333
|
+
- the link's `up-href` attribute
|
285
334
|
- a space-separated list of URLs in the link's `up-alias` attribute
|
286
335
|
|
287
336
|
\#\#\# Matching URL by prefix
|
@@ -289,27 +338,29 @@ up.feedback = (($) ->
|
|
289
338
|
You can mark a link as `.up-current` whenever the current URL matches a prefix.
|
290
339
|
To do so, end the `up-alias` attribute in an asterisk (`*`).
|
291
340
|
|
292
|
-
For instance, the following link is highlighted for both `/reports` and `/reports/123`:
|
341
|
+
For instance, the following `[up-nav]` link is highlighted for both `/reports` and `/reports/123`:
|
293
342
|
|
294
|
-
<a href="/reports" up-alias="/reports/*">Reports</a>
|
343
|
+
<a up-nav href="/reports" up-alias="/reports/*">Reports</a>
|
295
344
|
|
296
|
-
@selector
|
345
|
+
@selector [up-nav]
|
297
346
|
@stable
|
298
347
|
###
|
299
|
-
|
300
|
-
|
301
|
-
|
302
|
-
|
303
|
-
|
304
|
-
|
305
|
-
|
306
|
-
|
307
|
-
|
308
|
-
|
309
|
-
|
310
|
-
|
311
|
-
|
312
|
-
|
348
|
+
|
349
|
+
###**
|
350
|
+
When a link within an `[up-nav]` element points to the current location, it is assigned the `.up-current` class.
|
351
|
+
|
352
|
+
See [`[up-nav]`](/up-nav) for more documentation and examples.
|
353
|
+
|
354
|
+
@selector [up-nav] a.up-current
|
355
|
+
@stable
|
356
|
+
###
|
357
|
+
|
358
|
+
# Even when the modal or popup does not change history, we consider the URLs of the content it displays.
|
359
|
+
up.on 'up:history:pushed up:history:replaced up:history:restored up:modal:opened up:modal:closed up:popup:opened up:popup:closed', (event) ->
|
360
|
+
updateAllNavigationSectionsIfLocationChanged()
|
361
|
+
|
362
|
+
up.on 'up:fragment:inserted', (event, $newFragment) ->
|
363
|
+
updateNavigationSectionsInNewFragment($newFragment)
|
313
364
|
|
314
365
|
# The framework is reset between tests
|
315
366
|
up.on 'up:framework:reset', reset
|
@@ -45,7 +45,7 @@ up.form = (($) ->
|
|
45
45
|
@internal
|
46
46
|
###
|
47
47
|
fieldSelector = ->
|
48
|
-
|
48
|
+
config.fields.join(',')
|
49
49
|
|
50
50
|
###**
|
51
51
|
Submits a form via AJAX and updates a page fragment with the response.
|
@@ -283,7 +283,7 @@ up.form = (($) ->
|
|
283
283
|
delay = u.option(u.presentAttr($element, 'up-delay'), options.delay, config.observeDelay)
|
284
284
|
delay = parseInt(delay)
|
285
285
|
|
286
|
-
$fields =
|
286
|
+
$fields = u.selectInSubtree($element, fieldSelector())
|
287
287
|
|
288
288
|
destructors = u.map $fields, (field) ->
|
289
289
|
observeField($(field), delay, callback)
|
@@ -816,6 +816,28 @@ up.form = (($) ->
|
|
816
816
|
A CSS selector for elements whose visibility depends on this field's value.
|
817
817
|
@stable
|
818
818
|
###
|
819
|
+
|
820
|
+
###**
|
821
|
+
Only shows this element if an input field with [`[up-switch]`](/input-up-switch) has one of the given values.
|
822
|
+
|
823
|
+
See [`input[up-switch]`](/input-up-switch) for more documentation and examples.
|
824
|
+
|
825
|
+
@selector [up-show-for]
|
826
|
+
@param {string} [up-show-for]
|
827
|
+
A space-separated list of input values for which this element should be shown.
|
828
|
+
@stable
|
829
|
+
###
|
830
|
+
|
831
|
+
###**
|
832
|
+
Hides this element if an input field with [`[up-switch]`](/input-up-switch) has one of the given values.
|
833
|
+
|
834
|
+
See [`input[up-switch]`](/input-up-switch) for more documentation and examples.
|
835
|
+
|
836
|
+
@selector [up-hide-for]
|
837
|
+
@param {string} [up-hide-for]
|
838
|
+
A space-separated list of input values for which this element should be hidden.
|
839
|
+
@stable
|
840
|
+
###
|
819
841
|
up.compiler '[up-switch]', ($field) ->
|
820
842
|
switchTargets($field)
|
821
843
|
|
@@ -944,6 +966,8 @@ up.form = (($) ->
|
|
944
966
|
###
|
945
967
|
up.compiler '[up-autosubmit]', ($formOrField) -> autosubmit($formOrField)
|
946
968
|
|
969
|
+
up.compiler '[autofocus]', { batch: true }, ($input) -> $input.last().focus()
|
970
|
+
|
947
971
|
up.on 'up:framework:reset', reset
|
948
972
|
|
949
973
|
<% if ENV['JS_KNIFE'] %>knife: eval(Knife.point)<% end %>
|
@@ -57,7 +57,7 @@ up.layout = (($) ->
|
|
57
57
|
###
|
58
58
|
config = u.config
|
59
59
|
duration: 0
|
60
|
-
viewports: [
|
60
|
+
viewports: ['.up-modal-viewport', '[up-viewport]']
|
61
61
|
fixedTop: ['[up-fixed~=top]']
|
62
62
|
fixedBottom: ['[up-fixed~=bottom]']
|
63
63
|
anchoredRight: ['[up-anchored~=right]', '[up-fixed~=top]', '[up-fixed~=bottom]', '[up-fixed~=right]']
|
@@ -74,7 +74,7 @@ up.layout = (($) ->
|
|
74
74
|
reset = ->
|
75
75
|
config.reset()
|
76
76
|
lastScrollTops.clear()
|
77
|
-
scrollingTracker.
|
77
|
+
scrollingTracker.reset()
|
78
78
|
|
79
79
|
###**
|
80
80
|
Scrolls the given viewport to the given Y-position.
|
@@ -161,6 +161,9 @@ up.layout = (($) ->
|
|
161
161
|
@internal
|
162
162
|
###
|
163
163
|
finishScrolling = (element) ->
|
164
|
+
# Don't emit expensive events if no animation can be running anyway
|
165
|
+
return Promise.resolve() unless up.motion.isEnabled()
|
166
|
+
|
164
167
|
$scrollable = scrollableElementForViewport(element)
|
165
168
|
scrollingTracker.finish($scrollable)
|
166
169
|
|
@@ -169,7 +172,8 @@ up.layout = (($) ->
|
|
169
172
|
@internal
|
170
173
|
###
|
171
174
|
anchoredRight = ->
|
172
|
-
|
175
|
+
selector = config.anchoredRight.join(',')
|
176
|
+
$(selector)
|
173
177
|
|
174
178
|
###**
|
175
179
|
@function measureObstruction
|
@@ -179,10 +183,10 @@ up.layout = (($) ->
|
|
179
183
|
measureObstruction = ->
|
180
184
|
measurePosition = (obstructor, cssAttr) ->
|
181
185
|
$obstructor = $(obstructor)
|
182
|
-
anchorPosition = $obstructor
|
186
|
+
anchorPosition = u.readComputedStyleNumber($obstructor, cssAttr)
|
183
187
|
unless u.isPresent(anchorPosition)
|
184
188
|
up.fail("Fixed element %o must have a CSS attribute %s", $obstructor.get(0), cssAttr)
|
185
|
-
|
189
|
+
anchorPosition + $obstructor.height()
|
186
190
|
|
187
191
|
fixedTopBottoms = for obstructor in $(config.fixedTop.join(', '))
|
188
192
|
measurePosition(obstructor, 'top')
|
@@ -313,22 +317,23 @@ up.layout = (($) ->
|
|
313
317
|
Promise.resolve()
|
314
318
|
|
315
319
|
viewportSelector = ->
|
316
|
-
|
320
|
+
config.viewports.join(',')
|
317
321
|
|
318
322
|
###**
|
319
323
|
Returns the viewport for the given element.
|
320
324
|
|
321
|
-
|
325
|
+
Returns `$(document)` if no better viewpoint could be found.
|
322
326
|
|
323
327
|
@function up.layout.viewportOf
|
324
328
|
@param {string|Element|jQuery} selectorOrElement
|
329
|
+
@return {jQuery}
|
325
330
|
@internal
|
326
331
|
###
|
327
332
|
viewportOf = (selectorOrElement, options = {}) ->
|
328
333
|
$element = $(selectorOrElement)
|
329
|
-
$viewport = viewportSelector()
|
330
|
-
if $viewport.length == 0
|
331
|
-
|
334
|
+
$viewport = $element.closest(viewportSelector())
|
335
|
+
if $viewport.length == 0
|
336
|
+
$viewport = $(document)
|
332
337
|
$viewport
|
333
338
|
|
334
339
|
###**
|
@@ -342,7 +347,7 @@ up.layout = (($) ->
|
|
342
347
|
###
|
343
348
|
viewportsWithin = (selectorOrElement) ->
|
344
349
|
$element = $(selectorOrElement)
|
345
|
-
|
350
|
+
u.selectInSubtree($element, viewportSelector())
|
346
351
|
|
347
352
|
###**
|
348
353
|
Returns a jQuery collection of all the viewports on the screen.
|
@@ -351,7 +356,7 @@ up.layout = (($) ->
|
|
351
356
|
@internal
|
352
357
|
###
|
353
358
|
viewports = ->
|
354
|
-
|
359
|
+
$(document).add(viewportSelector())
|
355
360
|
|
356
361
|
scrollTopKey = (viewport) ->
|
357
362
|
$viewport = $(viewport)
|
@@ -497,6 +502,56 @@ up.layout = (($) ->
|
|
497
502
|
selector += ", a[name='#{selector}']"
|
498
503
|
selector
|
499
504
|
|
505
|
+
###**
|
506
|
+
@internal
|
507
|
+
###
|
508
|
+
absolutize = ($element, options) ->
|
509
|
+
options = u.options(options, afterMeasure: u.noop)
|
510
|
+
$viewport = up.layout.viewportOf($element)
|
511
|
+
originalDims = u.measure($element, relative: true, inner: true)
|
512
|
+
originalOffset = $element.offset()
|
513
|
+
options.afterMeasure()
|
514
|
+
|
515
|
+
u.writeInlineStyle $element,
|
516
|
+
# If the element had a layout context before, make sure the
|
517
|
+
# ghost will have layout context as well (and vice versa).
|
518
|
+
position: if u.readComputedStyle($element, 'position') == 'static' then 'static' else 'relative'
|
519
|
+
top: 'auto'
|
520
|
+
right: 'auto'
|
521
|
+
bottom: 'auto'
|
522
|
+
left: 'auto'
|
523
|
+
width: '100%'
|
524
|
+
height: '100%'
|
525
|
+
|
526
|
+
# Wrap the ghost in another container so its margin can expand
|
527
|
+
# freely. If we would position the element directly (old implementation),
|
528
|
+
# it would gain a layout context which cannot be crossed by margins.
|
529
|
+
$bounds = $('<div class="up-bounds"></div>')
|
530
|
+
boundsStyle = u.merge(originalDims, position: 'absolute')
|
531
|
+
u.writeInlineStyle($bounds, boundsStyle)
|
532
|
+
$bounds.insertBefore($element)
|
533
|
+
$element.appendTo($bounds)
|
534
|
+
|
535
|
+
top = originalDims.top
|
536
|
+
|
537
|
+
moveTop = (diff) ->
|
538
|
+
if diff != 0
|
539
|
+
top += diff
|
540
|
+
u.writeInlineStyle($bounds, { top })
|
541
|
+
|
542
|
+
# In theory, $element should not have moved visually.
|
543
|
+
# However, $element (or a child of $element) might collapse its margin
|
544
|
+
# against a previous sibling element, and now that it is absolute it does
|
545
|
+
# not have the same sibling. So we manually correct $element's top
|
546
|
+
# position so it aligns with the previous top position.
|
547
|
+
moveTop(originalOffset.top - $element.offset().top)
|
548
|
+
|
549
|
+
$fixedElements = up.layout.fixedChildren($element)
|
550
|
+
for fixedElement in $fixedElements
|
551
|
+
u.fixedToAbsolute(fixedElement, $viewport)
|
552
|
+
|
553
|
+
{ $element, $bounds, moveTop }
|
554
|
+
|
500
555
|
###**
|
501
556
|
Marks this element as a scrolling container ("viewport").
|
502
557
|
|
@@ -653,6 +708,7 @@ up.layout = (($) ->
|
|
653
708
|
revealOrRestoreScroll: revealOrRestoreScroll
|
654
709
|
anchoredRight: anchoredRight
|
655
710
|
fixedChildren: fixedChildren
|
711
|
+
absolutize: absolutize
|
656
712
|
|
657
713
|
)(jQuery)
|
658
714
|
|