unpoly-rails 0.60.2 → 0.62.1

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 (39) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -2
  3. data/CHANGELOG.md +105 -0
  4. data/Rakefile +5 -1
  5. data/dist/unpoly.js +199 -80
  6. data/dist/unpoly.min.js +4 -4
  7. data/lib/assets/javascripts/unpoly/browser.coffee.erb +10 -5
  8. data/lib/assets/javascripts/unpoly/classes/body_shifter.coffee +21 -12
  9. data/lib/assets/javascripts/unpoly/classes/compile_pass.coffee +2 -2
  10. data/lib/assets/javascripts/unpoly/classes/event_listener.coffee +1 -1
  11. data/lib/assets/javascripts/unpoly/classes/follow_variant.coffee +11 -3
  12. data/lib/assets/javascripts/unpoly/classes/params.coffee.erb +2 -1
  13. data/lib/assets/javascripts/unpoly/element.coffee.erb +4 -4
  14. data/lib/assets/javascripts/unpoly/event.coffee.erb +12 -4
  15. data/lib/assets/javascripts/unpoly/form.coffee.erb +77 -15
  16. data/lib/assets/javascripts/unpoly/fragment.coffee.erb +7 -3
  17. data/lib/assets/javascripts/unpoly/link.coffee.erb +1 -1
  18. data/lib/assets/javascripts/unpoly/modal.coffee.erb +4 -2
  19. data/lib/assets/javascripts/unpoly/motion.coffee.erb +1 -1
  20. data/lib/assets/javascripts/unpoly/protocol.coffee +1 -1
  21. data/lib/assets/javascripts/unpoly/syntax.coffee.erb +3 -4
  22. data/lib/assets/javascripts/unpoly/util.coffee.erb +2 -1
  23. data/lib/assets/javascripts/unpoly/viewport.coffee.erb +26 -2
  24. data/lib/unpoly/rails/version.rb +1 -1
  25. data/package.json +1 -1
  26. data/spec_app/Gemfile +2 -4
  27. data/spec_app/Gemfile.lock +23 -27
  28. data/spec_app/app/views/css_test/modal.erb +1 -1
  29. data/spec_app/app/views/css_test/popup.erb +1 -1
  30. data/spec_app/app/views/pages/start.erb +2 -1
  31. data/spec_app/app/views/replace_test/table.erb +16 -0
  32. data/spec_app/spec/javascripts/up/event_spec.js.coffee +34 -0
  33. data/spec_app/spec/javascripts/up/form_spec.js.coffee +137 -9
  34. data/spec_app/spec/javascripts/up/fragment_spec.js.coffee +36 -1
  35. data/spec_app/spec/javascripts/up/link_spec.js.coffee +7 -2
  36. data/spec_app/spec/javascripts/up/modal_spec.js.coffee +23 -1
  37. data/spec_app/spec/javascripts/up/popup_spec.js.coffee +2 -1
  38. data/spec_app/spec/javascripts/up/util_spec.js.coffee +14 -0
  39. metadata +4 -4
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: e81d8b9e7b4c364737eecb1091048a67453e9de955c3a7b64ea2bc2aea73fc69
4
- data.tar.gz: fd5642ddc460de37bf3e1f3f30bafea666ed1a5b4da7905aed6f7ff2d0653029
3
+ metadata.gz: '079979a47b22899b849e8b53275d23c2f6bf8a4ac14bf1b6001ac00330518cc9'
4
+ data.tar.gz: 033c8335552505a36214999db839d7c54a7e22c93fd168d8d8b25b7ccb5e40aa
5
5
  SHA512:
6
- metadata.gz: 0d045ec8b2dcbc5ded5a4829e0ad9f6ce68bdaa141fa7248580caa7f320b002a821c0b1796083f28dec463f8a4a4f021f84d49f5d28b9cf2af995474682a266d
7
- data.tar.gz: 65579fb7e1e0b1d2b94e6102efac109d3679d499c5fd1a7596988da0111f3d50498627eceb4e813ecaf7afa0ef8bd3d15f64d0c2109a9c5941d115597e2a3564
6
+ metadata.gz: d7be2bf8bd27d709d479000a591320731ac8fdbe2c0f613883a41af4258d4125bf4e793ec1712f83f56083f27335c39d1a5136cc8a501440fe35403ebd536cff
7
+ data.tar.gz: d4493b591597cda5245c5d0cf5000d034922cde09316fc030776ceacaaf44330975199eb3b9bd1c5c3a61134c56e38a8f175f6b7e126c50c33c54377d3e31927
@@ -1,2 +1 @@
1
- 2.1.2
2
-
1
+ 2.3.8
@@ -6,6 +6,111 @@ Changes to this project will be documented in this file.
6
6
  You may browse a formatted and hyperlinked version of this file at <https://unpoly.com/changes>.
7
7
 
8
8
 
9
+ 0.62.1
10
+ ------
11
+
12
+ This is another maintenance release while we're finishing [the next major version of Unpoly](https://groups.google.com/forum/#!topic/unpoly/FDdVjxbjNLg).
13
+
14
+ Community members were involved in every change of this release:
15
+
16
+ - [`up.submit()`](/up.submit) has a new options `{ params }`. It may be used to pass extra form [parameters](/up.Params) that will be submitted in addition to the parameters from the form. (fix by @robinvdvleuten)
17
+ - [`a[up-modal]`](/a-up-modal) will now honor an [`[up-cache]`](/a-up-target#up-cache) attribute on the same link. (fix by @adam12)
18
+ - Prevent destructor function from being called twice if [`up.destroy()`](/up.destroy) is called twice with the same element (reported by @kratob)
19
+ - On devices that don't show a vertical scrollbar, users can no longer scroll the underlying page while a [modal overlay](/up.modal) is open. (reported by @msurdi)
20
+
21
+
22
+ 0.62.0
23
+ ------
24
+
25
+ This release backports a number of accessibility improvements from [the next major version of Unpoly](https://groups.google.com/forum/#!topic/unpoly/FDdVjxbjNLg).
26
+ We encourage everyone to upgrade to this release in order to better support users with visual impairments.
27
+
28
+ The following changes are included:
29
+
30
+ - Links with an [`[up-instant]`](/a-up-instant) attribute can now be followed with the keyboard.
31
+ - Fragments that are being [destroyed](/up.destroy) now get an [`[aria-hidden=true]`](https://developer.mozilla.org/en-US/docs/Web/Accessibility/ARIA/ARIA_Techniques/Using_the_aria-hidden_attribute)
32
+ attribute while its disappearance is being animated. When a fragment is being swapped with a new version, the old fragment version is also
33
+ given `[aria-hidden=true]` while it's disappearing.
34
+ - [Modal dialogs](/up.modal) now get an [`[aria-modal=true]`](https://a11ysupport.io/tech/aria/aria-modal_attribute) attribute.
35
+
36
+ The next major version of Unpoly will include additional accessibility improvements. In particular the
37
+ new modal ("layer") implementation will implement all best practices for accessible dialogs.
38
+
39
+
40
+ 0.61.1
41
+ ------
42
+
43
+ This is a maintenance release while we're getting ready for [the next major version of Unpoly](https://groups.google.com/forum/#!topic/unpoly/FDdVjxbjNLg).
44
+
45
+ - Fix a bug where [`up.destroy()`](/up.destroy) wouldn't clean up the global jQuery cache. This is only relevant when using Unpoly together with jQuery.
46
+ - Fields outside a <form> are now recognized when they have a matching [form] attribute (fixes #85)
47
+ - [`up.form.fields()`](/up.form.fields) now accepts a jQuery collection as a first argument, as was already documented.
48
+
49
+
50
+ 0.61.0
51
+ ------
52
+
53
+ This release makes it easier to migrate to a recent version of Unpoly when your app still depends on jQuery.
54
+ Unpoly dropped its jQuery dependency with version 0.60.0, but retains optional jQuery support through functions like
55
+ [`up.$compiler()`](/up.$compiler) and [`up.$on()`](/up.$on). All Unpoly functions that take a native element as an
56
+ argument may also be called with a jQuery collection as an argument.
57
+
58
+ The following changes to the optional jQuery support were implemented:
59
+
60
+ - In an ES6 build pipeline, Unpoly's jQuery support no longer requires `window.jQuery` to be defined before
61
+ Unpoly is imported into the build. You still need to define `window.jQuery`, but you may do so at any time in your
62
+ scripts, regardless of load order.
63
+ - jQuery support functions like [`up.$compiler()`](/up.$compiler) now fail with a helpful message if the developer
64
+ forgets to define `window.jQuery`.
65
+
66
+ This release also exposes some convenience functions and selectors:
67
+
68
+ - New experimental function [`up.event.halt()`](/up.event.halt). It prevents the event from bubbling up the DOM.
69
+ It also prevents other event handlers bound on the same element. It also prevents the event's default action.
70
+ - New experimental function [`up.form.fields()`](/up.form.fields). It returns a list of form fields within the given element.
71
+ - The selector [`form[up-validate]`](/form-up-validate) is now supported. It performs
72
+ [server-side validation](/input-up-validate) when any fieldset within this form changes. Previously only the variant
73
+ [`input[up-validate]`](/input-up-validate) was supported.
74
+
75
+
76
+
77
+ 0.60.3
78
+ ------
79
+
80
+ [`[up-validate]`](/up-validate) again recognizes the `[up-fieldset]` attribute to find the form fragment
81
+ that should be replaced with validation results.
82
+
83
+ In the example below, changing the `email` input would only validate the first fieldset:
84
+
85
+ ```html
86
+ <form action="/users" id="registration">
87
+
88
+ <div up-fieldset>
89
+ Validation message
90
+ <input type="text" name="email" up-validate />
91
+ </div>
92
+
93
+ <div up-fieldset>
94
+ Validation message
95
+ <input type="password" name="password" up-validate />
96
+ </div>
97
+
98
+ </form>
99
+ ```
100
+
101
+
102
+ 0.60.2
103
+ ------
104
+
105
+ - When [submitting](/up-form) a form with a GET method, any query parameters in the form's `[action]` URL are now discarded.
106
+ This matches the standard browser behavior when submitting a form without Unpoly.
107
+ - When [submitting](/up-form) a form with a POST method, any query parameters in the form's `[action]` URL are now kept in the
108
+ URL, instead of being merged into the form's data payload.
109
+ This matches the standard browser behavior when submitting a form without Unpoly.
110
+ - New experimental function [`up.Params.stripURL(url)`](/up.Params.stripURL).
111
+ It returns the given URL without its [query string](https://en.wikipedia.org/wiki/Query_string).
112
+
113
+
9
114
  0.60.1
10
115
  ------
11
116
 
data/Rakefile CHANGED
@@ -42,7 +42,10 @@ end
42
42
 
43
43
  namespace :publish do
44
44
  task :confirm do
45
- puts "Are you ready to publish a new Unpoly version to all release channels? [y/N] "
45
+ puts "Before publishing new Unpoly version:"
46
+ puts "- Bump the version in version.rb"
47
+ puts "- Update the CHANGELOG"
48
+ puts "Ready to publish to all release channels? [y/N] "
46
49
  reply = STDIN.gets.strip.downcase
47
50
  unless reply == 'y'
48
51
  puts "Aborted"
@@ -105,6 +108,7 @@ namespace :publish do
105
108
  puts "Now remember to:"
106
109
  puts "- update unpoly.com so user see the updated CHANGELOG and CDN link"
107
110
  puts "- send a message to the e-mail group announcing the new release"
111
+ puts "- tweet a link to the CHANGELOG as @unpolyjs"
108
112
  end
109
113
 
110
114
  desc 'Build artifacts, push to git and release to package managers'
@@ -5,7 +5,7 @@
5
5
 
6
6
  (function() {
7
7
  window.up = {
8
- version: "0.60.2"
8
+ version: "0.62.1"
9
9
  };
10
10
 
11
11
  }).call(this);
@@ -595,7 +595,7 @@ to not include another library in your asset bundle.
595
595
  @stable
596
596
  */
597
597
  isJQuery = function(object) {
598
- return up.browser.canJQuery() && (object instanceof jQuery);
598
+ return !!(object != null ? object.jquery : void 0);
599
599
  };
600
600
 
601
601
  /***
@@ -1898,8 +1898,10 @@ Internet Explorer 10 or lower
1898
1898
  */
1899
1899
 
1900
1900
  (function() {
1901
+ var slice = [].slice;
1902
+
1901
1903
  up.browser = (function() {
1902
- var canAnimationFrame, canConsole, canControlScrollRestoration, canCssTransition, canCustomElements, canDOMParser, canFormData, canInputEvent, canInspectFormData, canJQuery, canPromise, canPushState, isIE10OrWorse, isIE11, isSupported, navigate, popCookie, submitForm, u, url, whenConfirmed;
1904
+ var callJQuery, canAnimationFrame, canConsole, canControlScrollRestoration, canCssTransition, canCustomElements, canDOMParser, canFormData, canInputEvent, canInspectFormData, canJQuery, canPromise, canPushState, isIE10OrWorse, isIE11, isSupported, navigate, popCookie, submitForm, u, url, whenConfirmed;
1903
1905
  u = up.util;
1904
1906
 
1905
1907
  /***
@@ -2038,15 +2040,15 @@ Internet Explorer 10 or lower
2038
2040
  canCustomElements = u.memoize(function() {
2039
2041
  return !!window.customElements;
2040
2042
  });
2041
- canJQuery = u.memoize(function() {
2042
- return !!window.jQuery;
2043
- });
2044
2043
  canAnimationFrame = u.memoize(function() {
2045
2044
  return 'requestAnimationFrame' in window;
2046
2045
  });
2047
2046
  canControlScrollRestoration = u.memoize(function() {
2048
2047
  return 'scrollRestoration' in history;
2049
2048
  });
2049
+ canJQuery = function() {
2050
+ return !!window.jQuery;
2051
+ };
2050
2052
  popCookie = function(name) {
2051
2053
  var ref, value;
2052
2054
  value = (ref = document.cookie.match(new RegExp(name + "=(\\w+)"))) != null ? ref[1] : void 0;
@@ -2086,6 +2088,12 @@ Internet Explorer 10 or lower
2086
2088
  isSupported = function() {
2087
2089
  return !isIE10OrWorse() && canConsole() && canDOMParser() && canFormData() && canCssTransition() && canInputEvent() && canPromise() && canAnimationFrame();
2088
2090
  };
2091
+ callJQuery = function() {
2092
+ var args;
2093
+ args = 1 <= arguments.length ? slice.call(arguments, 0) : [];
2094
+ canJQuery() || up.fail("jQuery must be published as window.jQuery");
2095
+ return jQuery.apply(null, args);
2096
+ };
2089
2097
  return {
2090
2098
  url: url,
2091
2099
  navigate: navigate,
@@ -2094,11 +2102,12 @@ Internet Explorer 10 or lower
2094
2102
  canFormData: canFormData,
2095
2103
  canInspectFormData: canInspectFormData,
2096
2104
  canCustomElements: canCustomElements,
2097
- canJQuery: canJQuery,
2098
2105
  canControlScrollRestoration: canControlScrollRestoration,
2106
+ canJQuery: canJQuery,
2099
2107
  whenConfirmed: whenConfirmed,
2100
2108
  isSupported: isSupported,
2101
2109
  popCookie: popCookie,
2110
+ jQuery: callJQuery,
2102
2111
  isIE11: isIE11
2103
2112
  };
2104
2113
  })();
@@ -2346,7 +2355,7 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
2346
2355
  @function up.element.closest
2347
2356
  @param {Element} element
2348
2357
  The element on which to start the search.
2349
- @param {string}
2358
+ @param {string} selector
2350
2359
  The CSS selector to match.
2351
2360
  @return {Element|null|undefined} element
2352
2361
  The matching element.
@@ -2762,11 +2771,11 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
2762
2771
  element.className // returns 'klass'
2763
2772
 
2764
2773
  @function up.element.affix
2765
- @params {Element} parent
2774
+ @param {Element} parent
2766
2775
  The parent to which to attach the created element.
2767
- @params {string} selector
2776
+ @param {string} selector
2768
2777
  The CSS selector from which to create an element.
2769
- @params {Object} attrs
2778
+ @param {Object} attrs
2770
2779
  An object of attributes to set on the created element.
2771
2780
  @param {Object} attrs.text
2772
2781
  The [text content](https://developer.mozilla.org/en-US/docs/Web/API/Node/textContent) of the created element.
@@ -3419,32 +3428,32 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
3419
3428
  }
3420
3429
 
3421
3430
  BodyShifter.prototype.shift = function() {
3422
- var anchor, body, bodyRightPadding, bodyRightShift, elementRight, elementRightShift, i, len, overflowElement, ref, results, scrollbarWidth;
3423
- if (!up.viewport.rootHasVerticalScrollbar()) {
3424
- return;
3425
- }
3426
- body = document.body;
3431
+ var anchor, body, bodyRightPadding, bodyRightShift, elementRight, elementRightShift, i, len, overflowElement, ref, results, rootHadVerticalScrollbar, scrollbarWidth;
3432
+ rootHadVerticalScrollbar = up.viewport.rootHasVerticalScrollbar();
3427
3433
  overflowElement = up.viewport.rootOverflowElement();
3428
- scrollbarWidth = up.viewport.scrollbarWidth();
3429
- bodyRightPadding = e.styleNumber(body, 'paddingRight');
3430
- bodyRightShift = scrollbarWidth + bodyRightPadding;
3431
- this.unshiftFns.push(e.setTemporaryStyle(body, {
3432
- paddingRight: bodyRightShift
3433
- }));
3434
3434
  this.unshiftFns.push(e.setTemporaryStyle(overflowElement, {
3435
3435
  overflowY: 'hidden'
3436
3436
  }));
3437
- ref = up.viewport.anchoredRight();
3438
- results = [];
3439
- for (i = 0, len = ref.length; i < len; i++) {
3440
- anchor = ref[i];
3441
- elementRight = e.styleNumber(anchor, 'right');
3442
- elementRightShift = scrollbarWidth + elementRight;
3443
- results.push(this.unshiftFns.push(e.setTemporaryStyle(anchor, {
3444
- right: elementRightShift
3445
- })));
3437
+ if (rootHadVerticalScrollbar) {
3438
+ body = document.body;
3439
+ scrollbarWidth = up.viewport.scrollbarWidth();
3440
+ bodyRightPadding = e.styleNumber(body, 'paddingRight');
3441
+ bodyRightShift = scrollbarWidth + bodyRightPadding;
3442
+ this.unshiftFns.push(e.setTemporaryStyle(body, {
3443
+ paddingRight: bodyRightShift
3444
+ }));
3445
+ ref = up.viewport.anchoredRight();
3446
+ results = [];
3447
+ for (i = 0, len = ref.length; i < len; i++) {
3448
+ anchor = ref[i];
3449
+ elementRight = e.styleNumber(anchor, 'right');
3450
+ elementRightShift = scrollbarWidth + elementRight;
3451
+ results.push(this.unshiftFns.push(e.setTemporaryStyle(anchor, {
3452
+ right: elementRightShift
3453
+ })));
3454
+ }
3455
+ return results;
3446
3456
  }
3447
- return results;
3448
3457
  };
3449
3458
 
3450
3459
  BodyShifter.prototype.unshift = function() {
@@ -3774,7 +3783,7 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
3774
3783
 
3775
3784
  CompilePass.prototype.compileOneElement = function(compiler, element) {
3776
3785
  var compileArgs, data, destructorOrDestructors, elementArg, result;
3777
- elementArg = compiler.jQuery ? jQuery(element) : element;
3786
+ elementArg = compiler.jQuery ? up.browser.jQuery(element) : element;
3778
3787
  compileArgs = [elementArg];
3779
3788
  if (compiler.length !== 1) {
3780
3789
  data = up.syntax.data(element);
@@ -3788,7 +3797,7 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
3788
3797
 
3789
3798
  CompilePass.prototype.compileBatch = function(compiler, elements) {
3790
3799
  var compileArgs, dataList, elementsArgs, result;
3791
- elementsArgs = compiler.jQuery ? jQuery(elements) : elements;
3800
+ elementsArgs = compiler.jQuery ? up.browser.jQuery(elements) : elements;
3792
3801
  compileArgs = [elementsArgs];
3793
3802
  if (compiler.length !== 1) {
3794
3803
  dataList = u.map(elements, up.syntax.data);
@@ -4126,7 +4135,7 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
4126
4135
  element = e.closest(element, this.selector);
4127
4136
  }
4128
4137
  if (element) {
4129
- elementArg = this.jQuery ? jQuery(element) : element;
4138
+ elementArg = this.jQuery ? up.browser.jQuery(element) : element;
4130
4139
  args = [event, elementArg];
4131
4140
  expectedArgCount = this.callback.length;
4132
4141
  if (!(expectedArgCount === 1 || expectedArgCount === 2)) {
@@ -4683,8 +4692,9 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
4683
4692
 
4684
4693
  FollowVariant.prototype.onClick = function(event, link) {
4685
4694
  if (up.link.shouldProcessEvent(event, link)) {
4686
- if (e.matches(link, '[up-instant]')) {
4687
- return up.event.halt(event);
4695
+ if (e.matches(link, '[up-instant]') && link.upInstantSupported) {
4696
+ up.event.halt(event);
4697
+ link.upInstantSupported = false;
4688
4698
  } else {
4689
4699
  up.event.consumeAction(event);
4690
4700
  return this.followLink(link);
@@ -4696,6 +4706,7 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
4696
4706
 
4697
4707
  FollowVariant.prototype.onMousedown = function(event, link) {
4698
4708
  if (up.link.shouldProcessEvent(event, link)) {
4709
+ link.upInstantSupported = true;
4699
4710
  up.event.consumeAction(event);
4700
4711
  return this.followLink(link);
4701
4712
  }
@@ -5347,18 +5358,19 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
5347
5358
  if (u.isMissing(raw)) {
5348
5359
 
5349
5360
  } else if (raw instanceof this.constructor) {
5350
- return (ref = this.entries).push.apply(ref, raw.entries);
5361
+ (ref = this.entries).push.apply(ref, raw.entries);
5351
5362
  } else if (u.isArray(raw)) {
5352
- return (ref1 = this.entries).push.apply(ref1, raw);
5363
+ (ref1 = this.entries).push.apply(ref1, raw);
5353
5364
  } else if (u.isString(raw)) {
5354
- return this.addAllFromQuery(raw);
5365
+ this.addAllFromQuery(raw);
5355
5366
  } else if (u.isFormData(raw)) {
5356
- return this.addAllFromFormData(raw);
5367
+ this.addAllFromFormData(raw);
5357
5368
  } else if (u.isObject(raw)) {
5358
- return this.addAllFromObject(raw);
5369
+ this.addAllFromObject(raw);
5359
5370
  } else {
5360
- return up.fail("Unsupport params type: %o", raw);
5371
+ up.fail("Unsupport params type: %o", raw);
5361
5372
  }
5373
+ return this;
5362
5374
  };
5363
5375
 
5364
5376
  Params.prototype.addAllFromObject = function(object) {
@@ -5708,7 +5720,7 @@ It complements [native `Element` methods](https://www.w3schools.com/jsref/dom_ob
5708
5720
  };
5709
5721
 
5710
5722
 
5711
- /**
5723
+ /***
5712
5724
  Returns the given URL without its [query string](https://en.wikipedia.org/wiki/Query_string).
5713
5725
 
5714
5726
  \#\#\# Example
@@ -6951,7 +6963,7 @@ There are some advantages to using `up.on()`:
6951
6963
  for [event delegation](https://davidwalsh.name/event-delegate):
6952
6964
 
6953
6965
  var form = document.querySelector('form')
6954
- document.addEventListener(form, 'click', 'a', function(event, link) {
6966
+ up.on(form, 'click', 'a', function(event, link) {
6955
6967
  console.log("Click on a link %o within %o", link, form)
6956
6968
  })
6957
6969
 
@@ -7263,14 +7275,22 @@ There are some advantages to using `up.on()`:
7263
7275
  };
7264
7276
 
7265
7277
  /***
7266
- Stops the given event from propagating and prevents the default action.
7278
+ Prevents the event from bubbling up the DOM.
7279
+ Also prevents other event handlers bound on the same element.
7280
+ Also prevents the event's default action.
7281
+
7282
+ \#\#\# Example
7283
+
7284
+ up.on('click', 'link.disabled', function(event) {
7285
+ up.event.halt(event)
7286
+ })
7267
7287
 
7268
7288
  @function up.event.halt
7269
- @internal
7289
+ @param {Event} event
7290
+ @experimental
7270
7291
  */
7271
7292
  halt = function(event) {
7272
7293
  event.stopImmediatePropagation();
7273
- event.stopPropagation();
7274
7294
  return event.preventDefault();
7275
7295
  };
7276
7296
 
@@ -7579,7 +7599,7 @@ an existing cookie should be deleted.
7579
7599
  The parameter name can be configured as a string or as function that returns the parameter name.
7580
7600
  If no name is set, no token will be sent.
7581
7601
 
7582
- Defaults to the `content` attribute of a `<meta>` tag named `csrf-token`:
7602
+ Defaults to the `content` attribute of a `<meta>` tag named `csrf-param`:
7583
7603
 
7584
7604
  <meta name="csrf-param" content="authenticity_token" />
7585
7605
 
@@ -8234,7 +8254,7 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
8234
8254
  /***
8235
8255
  Registers a [compiler](/up.compiler) that is run before all other compilers.
8236
8256
 
8237
- Use `up.macro()` to register a compiler that sets multiply Unpoly attributes.
8257
+ Use `up.macro()` to register a compiler that sets multiple Unpoly attributes.
8238
8258
 
8239
8259
  \#\#\# Example
8240
8260
 
@@ -8413,15 +8433,14 @@ or when a matching fragment is [inserted via AJAX](/up.link) later.
8413
8433
  var cleanables;
8414
8434
  cleanables = e.subtree(fragment, '.up-can-clean');
8415
8435
  return u.each(cleanables, function(cleanable) {
8416
- var destructor, destructors, i, len, results;
8417
- if (destructors = cleanable.upDestructors) {
8418
- results = [];
8436
+ var destructor, destructors, i, len;
8437
+ if (destructors = u.pluckKey(cleanable, 'upDestructors')) {
8419
8438
  for (i = 0, len = destructors.length; i < len; i++) {
8420
8439
  destructor = destructors[i];
8421
- results.push(destructor());
8440
+ destructor();
8422
8441
  }
8423
- return results;
8424
8442
  }
8443
+ return cleanable.classList.remove('up-can-clean');
8425
8444
  });
8426
8445
  };
8427
8446
 
@@ -9380,13 +9399,37 @@ Unpoly will automatically be aware of sticky Bootstrap components such as
9380
9399
 
9381
9400
  The scroll positions will be associated with the current URL.
9382
9401
  They can later be restored by calling [`up.viewport.restoreScroll()`](/up.viewport.restoreScroll)
9383
- at the same URL.
9402
+ at the same URL, or by following a link with an [`[up-restore-scroll]`](/a-up-follow#up-restore-scroll)
9403
+ attribute.
9404
+
9405
+ Unpoly automatically saves scroll positions before a [fragment update](/up.replace)
9406
+ you will rarely need to call this function yourself.
9407
+
9408
+ \#\#\# Examples
9409
+
9410
+ Should you need to save the current scroll positions outside of a [fragment update](/up.replace),
9411
+ you may call:
9384
9412
 
9385
- Unpoly automatically saves scroll positions whenever a fragment was updated on the page.
9413
+ up.viewport.saveScroll()
9414
+
9415
+ Instead of saving the current scroll positions for the current URL, you may also pass another
9416
+ url or vertical scroll positionsfor each viewport:
9417
+
9418
+ up.viewport.saveScroll({
9419
+ url: '/inbox',
9420
+ tops: {
9421
+ 'body': 0,
9422
+ '.sidebar', 100,
9423
+ '.main', 320
9424
+ }
9425
+ })
9386
9426
 
9387
9427
  @function up.viewport.saveScroll
9388
9428
  @param {string} [options.url]
9429
+ The URL for which to save scroll positions.
9430
+ If omitted, the current browser location is used.
9389
9431
  @param {Object<string, number>} [options.tops]
9432
+ An object mapping viewport selectors to vertical scroll positions in pixels.
9390
9433
  @experimental
9391
9434
  */
9392
9435
  saveScroll = function(options) {
@@ -9767,7 +9810,7 @@ is built from `up.fragment` functions. You may use them to extend Unpoly from yo
9767
9810
 
9768
9811
  \#\#\# Example
9769
9812
 
9770
- Let's say your curent HTML looks like this:
9813
+ Let's say your current HTML looks like this:
9771
9814
 
9772
9815
  <div class="one">old one</div>
9773
9816
  <div class="two">old two</div>
@@ -10018,7 +10061,7 @@ is built from `up.fragment` functions. You may use them to extend Unpoly from yo
10018
10061
 
10019
10062
  \#\#\# Example
10020
10063
 
10021
- Let's say your curent HTML looks like this:
10064
+ Let's say your current HTML looks like this:
10022
10065
 
10023
10066
  <div class="one">old one</div>
10024
10067
  <div class="two">old two</div>
@@ -10568,7 +10611,11 @@ is built from `up.fragment` functions. You may use them to extend Unpoly from yo
10568
10611
  var parent;
10569
10612
  parent = element.parentNode;
10570
10613
  up.syntax.clean(element);
10571
- e.remove(element);
10614
+ if (up.browser.canJQuery()) {
10615
+ jQuery(element).remove();
10616
+ } else {
10617
+ e.remove(element);
10618
+ }
10572
10619
  return emitFragmentDestroyed(element, {
10573
10620
  parent: parent,
10574
10621
  log: options.log
@@ -10593,7 +10640,8 @@ is built from `up.fragment` functions. You may use them to extend Unpoly from yo
10593
10640
  @stable
10594
10641
  */
10595
10642
  markElementAsDestroying = function(element) {
10596
- return element.classList.add('up-destroying');
10643
+ element.classList.add('up-destroying');
10644
+ return element.setAttribute('aria-hidden', 'true');
10597
10645
  };
10598
10646
 
10599
10647
  /***
@@ -10789,7 +10837,7 @@ You can define custom animations using [`up.transition()`](/up.transition) and
10789
10837
 
10790
10838
  You can pass additional options:
10791
10839
 
10792
- up.animate('warning', '.fade-in', {
10840
+ up.animate('.warning', 'fade-in', {
10793
10841
  delay: 1000,
10794
10842
  duration: 250,
10795
10843
  easing: 'linear'
@@ -12107,7 +12155,7 @@ This makes for an unfriendly experience:
12107
12155
  - State changes caused by AJAX updates get lost during the page transition.
12108
12156
  - Unsaved form changes get lost during the page transition.
12109
12157
  - The JavaScript VM is reset during the page transition.
12110
- - If the page layout is composed from multiple srollable containers
12158
+ - If the page layout is composed from multiple scrollable containers
12111
12159
  (e.g. a pane view), the scroll positions get lost during the page transition.
12112
12160
  - The user sees a "flash" as the browser loads and renders the new page,
12113
12161
  even if large portions of the old and new page are the same (navigation, layout, etc.).
@@ -12831,7 +12879,7 @@ open dialogs with sub-forms, etc. all without losing form state.
12831
12879
  @stable
12832
12880
  */
12833
12881
  config = new up.Config({
12834
- validateTargets: ['fieldset:has(&)', 'label:has(&)', 'form:has(&)'],
12882
+ validateTargets: ['[up-fieldset]:has(&)', 'fieldset:has(&)', 'label:has(&)', 'form:has(&)'],
12835
12883
  fields: ['select', 'input:not([type=submit]):not([type=image])', 'button[type]:not([type=submit])', 'textarea'],
12836
12884
  submitButtons: ['input[type=submit]', 'input[type=image]', 'button[type=submit]', 'button:not([type])'],
12837
12885
  observeDelay: 0
@@ -12844,16 +12892,42 @@ open dialogs with sub-forms, etc. all without losing form state.
12844
12892
  @function up.form.fieldSelector
12845
12893
  @internal
12846
12894
  */
12847
- fieldSelector = function() {
12848
- return config.fields.join(',');
12895
+ fieldSelector = function(suffix) {
12896
+ if (suffix == null) {
12897
+ suffix = '';
12898
+ }
12899
+ return config.fields.map(function(field) {
12900
+ return field + suffix;
12901
+ }).join(',');
12849
12902
  };
12850
12903
 
12851
12904
  /***
12905
+ Returns a list of form fields within the given element.
12906
+
12907
+ You can configure what Unpoly considers a form field by adding CSS selectors to the
12908
+ [`up.form.config.fields`](/up.form.config#config.fields) array.
12909
+
12910
+ If the given element is itself a form field, a list of that given element is returned.
12911
+
12852
12912
  @function up.form.fields
12853
- @internal
12913
+ @param {Element|jQuery} root
12914
+ The element to scan for contained form fields.
12915
+
12916
+ If the element is itself a form field, a list of that element is returned.
12917
+ @return {NodeList<Element>|Array<Element>}
12918
+ @experimental
12854
12919
  */
12855
12920
  findFields = function(root) {
12856
- return e.subtree(root, fieldSelector());
12921
+ var fields, outsideFieldSelector, outsideFields;
12922
+ root = e.get(root);
12923
+ fields = e.subtree(root, fieldSelector());
12924
+ if (e.matches(root, 'form[id]')) {
12925
+ outsideFieldSelector = fieldSelector(e.attributeSelector('form', root.id));
12926
+ outsideFields = e.all(outsideFieldSelector);
12927
+ fields.push.apply(fields, outsideFields);
12928
+ fields = u.uniq(fields);
12929
+ }
12930
+ return fields;
12857
12931
  };
12858
12932
 
12859
12933
  /****
@@ -12980,6 +13054,9 @@ open dialogs with sub-forms, etc. all without losing form state.
12980
13054
  If set to `'auto'` (default), Unpoly will try to find a match in the form's layer.
12981
13055
  @param {string} [options.failLayer='auto']
12982
13056
  The name of the layer that ought to be updated if the server sends a non-200 status code.
13057
+ @param {Object|FormData|string|Array|up.Params} [options.params]
13058
+ Extra form [parameters](/up.Params) that will be submitted in addition to
13059
+ the parameters from the form.
12983
13060
  @return {Promise}
12984
13061
  A promise for the successful form submission.
12985
13062
  @stable
@@ -13029,7 +13106,7 @@ open dialogs with sub-forms, etc. all without losing form state.
13029
13106
  if (options.failLayer == null) {
13030
13107
  options.failLayer = form.getAttribute('up-fail-layer');
13031
13108
  }
13032
- options.params = up.Params.fromForm(form);
13109
+ options.params = up.Params.fromForm(form).addAll(options.params);
13033
13110
  options = u.merge(options, up.motion.animateOptions(options, form));
13034
13111
  if (options.validate) {
13035
13112
  options.headers || (options.headers = {});
@@ -13379,7 +13456,7 @@ open dialogs with sub-forms, etc. all without losing form state.
13379
13456
 
13380
13457
  \#\#\# Redirects
13381
13458
 
13382
- Unpoly requires an additional response headers to detect redirects,
13459
+ Unpoly requires an additional response header to detect redirects,
13383
13460
  which are otherwise undetectable for an AJAX client.
13384
13461
 
13385
13462
  After the form's action performs a redirect, the next response should echo
@@ -13614,7 +13691,19 @@ open dialogs with sub-forms, etc. all without losing form state.
13614
13691
  This defaults to a fieldset or form group around the validating field.
13615
13692
  @stable
13616
13693
  */
13617
- up.on('change', '[up-validate]', function(event, field) {
13694
+
13695
+ /***
13696
+ Performs [server-side validation](/input-up-validate) when any fieldset within this form changes.
13697
+
13698
+ You can configure what Unpoly considers a fieldset by adding CSS selectors to the
13699
+ [`up.form.config.validateTargets`](/up.form.config#config.validateTargets) array.
13700
+
13701
+ @selector form[up-validate]
13702
+ @stable
13703
+ */
13704
+ up.on('change', '[up-validate]', function(event) {
13705
+ var field;
13706
+ field = findFields(event.target)[0];
13618
13707
  return u.muteRejection(validate(field));
13619
13708
  });
13620
13709
 
@@ -13732,18 +13821,16 @@ open dialogs with sub-forms, etc. all without losing form state.
13732
13821
 
13733
13822
  The programmatic variant of this is the [`up.observe()`](/up.observe) function.
13734
13823
 
13735
- \#\#\# Examples
13824
+ \#\#\# Example
13736
13825
 
13737
13826
  The following would run a global `showSuggestions(value)` function
13738
13827
  whenever the `<input>` changes:
13739
13828
 
13740
- <form>
13741
- <input name="query" up-observe="showSuggestions(value)">
13742
- </form>
13829
+ <input name="query" up-observe="showSuggestions(value)">
13743
13830
 
13744
13831
  \#\#\# Callback context
13745
13832
 
13746
- The script given to `up-observe` runs with the following context:
13833
+ The script given to `[up-observe]` runs with the following context:
13747
13834
 
13748
13835
  | Name | Type | Description |
13749
13836
  | -------- | --------- | ------------------------------------- |
@@ -13751,6 +13838,20 @@ open dialogs with sub-forms, etc. all without losing form state.
13751
13838
  | `this` | `Element` | The form field |
13752
13839
  | `$field` | `jQuery` | The form field as a jQuery collection |
13753
13840
 
13841
+ \#\#\# Observing radio buttons
13842
+
13843
+ Multiple radio buttons with the same `[name]` (a radio button group)
13844
+ produce a single value for the form.
13845
+
13846
+ To observe radio buttons group, use the `[up-observe]` attribute on an
13847
+ element that contains all radio button elements with a given name:
13848
+
13849
+ <div up-observe="formatSelected(value)">
13850
+ <input type="radio" name="format" value="html"> HTML format
13851
+ <input type="radio" name="format" value="pdf"> PDF format
13852
+ <input type="radio" name="format" value="txt"> Text format
13853
+ </div>
13854
+
13754
13855
  @selector input[up-observe]
13755
13856
  @param {string} up-observe
13756
13857
  The code to run when the field's value changes.
@@ -13799,7 +13900,7 @@ open dialogs with sub-forms, etc. all without losing form state.
13799
13900
  });
13800
13901
 
13801
13902
  /***
13802
- [Observes](/up.observe) this form field and submits the form when its value changes.
13903
+ Submits this field's form when this field changes its values.
13803
13904
 
13804
13905
  Both the form and the changed field will be assigned a CSS class [`up-active`](/form-up-active)
13805
13906
  while the autosubmitted form is loading.
@@ -13815,6 +13916,20 @@ open dialogs with sub-forms, etc. all without losing form state.
13815
13916
  <input type="checkbox" name="archive"> Include archive
13816
13917
  </form>
13817
13918
 
13919
+ \#\#\# Auto-submitting radio buttons
13920
+
13921
+ Multiple radio buttons with the same `[name]` (a radio button group)
13922
+ produce a single value for the form.
13923
+
13924
+ To auto-submit radio buttons group, use the `[up-submit]` attribute on an
13925
+ element that contains all radio button elements with a given name:
13926
+
13927
+ <div up-autosubmit>
13928
+ <input type="radio" name="format" value="html"> HTML format
13929
+ <input type="radio" name="format" value="pdf"> PDF format
13930
+ <input type="radio" name="format" value="txt"> Text format
13931
+ </div>
13932
+
13818
13933
  @selector input[up-autosubmit]
13819
13934
  @param {string} up-delay
13820
13935
  The number of miliseconds to wait after a change before the form is submitted.
@@ -13822,7 +13937,7 @@ open dialogs with sub-forms, etc. all without losing form state.
13822
13937
  */
13823
13938
 
13824
13939
  /***
13825
- [Observes](/up.observe) this form and submits the form when *any* field changes.
13940
+ Submits the form when *any* field changes.
13826
13941
 
13827
13942
  Both the form and the field will be assigned a CSS class [`up-active`](/form-up-active)
13828
13943
  while the autosubmitted form is loading.
@@ -14698,6 +14813,7 @@ or function.
14698
14813
  var closeElement, contentElement, dialogStyles, html, modalElement;
14699
14814
  html = templateHtml();
14700
14815
  state.modalElement = modalElement = e.createFromHtml(html);
14816
+ modalElement.setAttribute('aria-modal', 'true');
14701
14817
  modalElement.setAttribute('up-flavor', state.flavor);
14702
14818
  if (u.isPresent(state.position)) {
14703
14819
  modalElement.setAttribute('up-position', state.position);
@@ -14736,7 +14852,7 @@ or function.
14736
14852
  var link = document.querySelector('a')
14737
14853
  up.modal.follow(link)
14738
14854
 
14739
- Any option attributes for [`a[up-modal]`](/a.up-modal) will be honored.
14855
+ Any option attributes for [`a[up-modal]`](/a-up-modal) will be honored.
14740
14856
 
14741
14857
  Emits events [`up:modal:open`](/up:modal:open) and [`up:modal:opened`](/up:modal:opened).
14742
14858
 
@@ -14919,6 +15035,9 @@ or function.
14919
15035
  if (options.failLayer == null) {
14920
15036
  options.failLayer = (ref12 = link.getAttribute('up-fail-layer')) != null ? ref12 : 'auto';
14921
15037
  }
15038
+ if (options.cache == null) {
15039
+ options.cache = e.booleanAttr(link, 'up-cache');
15040
+ }
14922
15041
  animateOptions = up.motion.animateOptions(options, link, {
14923
15042
  duration: flavorDefault('openDuration', options.flavor),
14924
15043
  easing: flavorDefault('openEasing', options.flavor)
@@ -15175,7 +15294,7 @@ or function.
15175
15294
 
15176
15295
  Clicking would request the path `/blog` and select `.blog-list` from
15177
15296
  the HTML response. Unpoly will dim the page
15178
- and place the matching `.blog-list` tag will be placed in
15297
+ and place the matching `.blog-list` tag in
15179
15298
  a modal dialog.
15180
15299
 
15181
15300
  @selector a[up-modal]