unpoly-rails 3.11.0.rc1 → 3.11.0.rc12

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.
@@ -5,7 +5,7 @@
5
5
  /***/ (() => {
6
6
 
7
7
  window.up = {
8
- version: '3.11.0-rc1'
8
+ version: '3.11.0-rc12'
9
9
  };
10
10
 
11
11
 
@@ -146,6 +146,9 @@ up.util = (function () {
146
146
  function isNull(value) {
147
147
  return value === null;
148
148
  }
149
+ function isTruthy(value) {
150
+ return !!value;
151
+ }
149
152
  function isUndefined(value) {
150
153
  return value === undefined;
151
154
  }
@@ -423,7 +426,7 @@ up.util = (function () {
423
426
  const filtered = {};
424
427
  for (let key in object) {
425
428
  const value = object[key];
426
- if (tester(value, key, object)) {
429
+ if (tester(value, key)) {
427
430
  filtered[key] = object[key];
428
431
  }
429
432
  }
@@ -526,34 +529,36 @@ up.util = (function () {
526
529
  return Object.prototype.hasOwnProperty(k);
527
530
  }
528
531
  function isEqual(a, b) {
532
+ if (a === b) {
533
+ return true;
534
+ }
529
535
  if (a?.valueOf) {
530
536
  a = a.valueOf();
531
537
  }
532
538
  if (b?.valueOf) {
533
539
  b = b.valueOf();
534
540
  }
541
+ if (a === b) {
542
+ return true;
543
+ }
535
544
  if (typeof (a) !== typeof (b)) {
536
545
  return false;
537
546
  }
538
- else if (isList(a) && isList(b)) {
547
+ if (isList(a) && isList(b)) {
539
548
  return isEqualList(a, b);
540
549
  }
541
- else if (isObject(a) && a[isEqual.key]) {
550
+ if (isObject(a) && a[isEqual.key]) {
542
551
  return a[isEqual.key](b);
543
552
  }
544
- else if (isOptions(a) && isOptions(b)) {
545
- const aKeys = Object.keys(a);
546
- const bKeys = Object.keys(b);
547
- if (isEqualList(aKeys, bKeys)) {
548
- return every(aKeys, (aKey) => isEqual(a[aKey], b[aKey]));
549
- }
550
- else {
551
- return false;
552
- }
553
- }
554
- else {
555
- return a === b;
553
+ if (isOptions(a) && isOptions(b)) {
554
+ return isEqualOptions(a, b);
556
555
  }
556
+ return false;
557
+ }
558
+ function isEqualOptions(a, b) {
559
+ const aKeys = Object.keys(a);
560
+ const bKeys = Object.keys(b);
561
+ return isEqualList(aKeys, bKeys) && every(aKeys, (key) => isEqual(a[key], b[key]));
557
562
  }
558
563
  isEqual.key = 'up.util.isEqual';
559
564
  function isEqualList(a, b) {
@@ -792,36 +797,39 @@ up.util = (function () {
792
797
  }
793
798
  function maskPattern(str, patterns, { keepDelimiters = false } = {}) {
794
799
  let maskCount = 0;
795
- let maskPattern = /§(\d+)/g;
796
- let matches = [];
797
- let replaceLayers = 0;
798
- let replace = (replacePattern) => {
800
+ let maskPattern = /§\d+/g;
801
+ let maskings = {};
802
+ let replaceLayers = [];
803
+ let replace = (replacePattern, allowRestoreTransform) => {
799
804
  let didReplace = false;
800
805
  str = str.replaceAll(replacePattern, function (match) {
801
806
  didReplace = true;
802
- let glyph = '§' + (maskCount++);
803
- let mask;
807
+ let mask = '§' + (maskCount++);
808
+ let remain;
804
809
  let masked;
805
810
  if (keepDelimiters) {
806
811
  let startDelimiter = match[0];
807
812
  let endDelimiter = match.slice(-1);
808
813
  masked = match.slice(1, -1);
809
- mask = startDelimiter + glyph + endDelimiter;
814
+ remain = startDelimiter + mask + endDelimiter;
810
815
  }
811
816
  else {
812
817
  masked = match;
813
- mask = glyph;
818
+ remain = mask;
814
819
  }
815
- matches.push(masked);
816
- return mask;
820
+ maskings[mask] = masked;
821
+ return remain;
817
822
  });
818
823
  if (didReplace)
819
- replaceLayers++;
824
+ replaceLayers.unshift({ allowRestoreTransform });
820
825
  };
821
- [maskPattern, ...patterns].forEach(replace);
826
+ replace(maskPattern, false);
827
+ for (let pattern of patterns)
828
+ replace(pattern, true);
822
829
  let restore = (s, transform = identity) => {
823
- for (let i = 0; i < replaceLayers; i++) {
824
- s = s.replace(maskPattern, (match, placeholderIndex) => transform(matches[placeholderIndex]));
830
+ for (let { allowRestoreTransform } of replaceLayers) {
831
+ let iterationTransform = allowRestoreTransform ? transform : identity;
832
+ s = s.replace(maskPattern, (match) => iterationTransform(assert(maskings[match], isString)));
825
833
  }
826
834
  return s;
827
835
  };
@@ -835,6 +843,7 @@ up.util = (function () {
835
843
  function ensureDoubleQuotes(str) {
836
844
  if (str[0] === '"')
837
845
  return str;
846
+ assert(str[0] === "'");
838
847
  str = str.slice(1, -1);
839
848
  let transformed = str.replace(/(\\\\)|(\\')|(\\")|(")/g, function (_match, escapedBackslash, escapedSingleQuote, _doubleQuote) {
840
849
  return escapedBackslash
@@ -866,6 +875,14 @@ up.util = (function () {
866
875
  function spanObject(keys, value) {
867
876
  return mapObject(keys, (key) => [key, value]);
868
877
  }
878
+ function assert(value, testFn = isTruthy) {
879
+ if (testFn(value)) {
880
+ return value;
881
+ }
882
+ else {
883
+ throw "assert failed";
884
+ }
885
+ }
869
886
  return {
870
887
  parseURL,
871
888
  normalizeURL,
@@ -974,6 +991,7 @@ up.util = (function () {
974
991
  maskPattern,
975
992
  expressionOutline,
976
993
  parseString,
994
+ assert,
977
995
  };
978
996
  })();
979
997
 
@@ -1537,7 +1555,7 @@ up.element = (function () {
1537
1555
  }
1538
1556
  function extractFromStyleObject(style, keyOrKeys) {
1539
1557
  if (up.migrate.loaded)
1540
- keyOrKeys = up.migrate.fixStyleProps(keyOrKeys);
1558
+ keyOrKeys = up.migrate.fixGetStyleProps(keyOrKeys);
1541
1559
  if (u.isString(keyOrKeys)) {
1542
1560
  return style.getPropertyValue(keyOrKeys);
1543
1561
  }
@@ -1547,7 +1565,7 @@ up.element = (function () {
1547
1565
  }
1548
1566
  function setInlineStyle(element, props, unit = '') {
1549
1567
  if (up.migrate.loaded)
1550
- props = up.migrate.fixStyleProps(props, unit);
1568
+ props = up.migrate.fixSetStyleProps(props, unit);
1551
1569
  if (u.isString(props)) {
1552
1570
  element.setAttribute('style', props);
1553
1571
  }
@@ -1623,18 +1641,6 @@ up.element = (function () {
1623
1641
  return [element.parentElement, 'beforeend'];
1624
1642
  }
1625
1643
  }
1626
- function moveBefore(parent, movedElement, referenceElement) {
1627
- let fn = parent.moveBefore || parent.insertBefore;
1628
- fn.call(parent, movedElement, referenceElement);
1629
- }
1630
- function preservingAppend(parent, newNode) {
1631
- moveBefore(parent, newNode, null);
1632
- }
1633
- function preservingReplace(oldElement, newElement) {
1634
- let parent = oldElement.parentElement;
1635
- moveBefore(parent, newElement, oldElement);
1636
- oldElement.remove();
1637
- }
1638
1644
  return {
1639
1645
  subtree,
1640
1646
  subtreeFirst,
@@ -1703,8 +1709,6 @@ up.element = (function () {
1703
1709
  matchSelectorMap,
1704
1710
  elementLikeMatches,
1705
1711
  documentPosition,
1706
- preservingAppend,
1707
- preservingReplace,
1708
1712
  };
1709
1713
  })();
1710
1714
 
@@ -2187,8 +2191,8 @@ up.Change.Addition = class Addition extends up.Change {
2187
2191
  _responseOptions() {
2188
2192
  return { response: this._response };
2189
2193
  }
2190
- executeSteps(steps, responseDoc, noneOptions) {
2191
- return new up.Change.UpdateSteps({ steps, noneOptions }).execute(responseDoc);
2194
+ executeSteps({ steps, responseDoc, noneOptions, passRenderOptions = this.options }) {
2195
+ return new up.Change.UpdateSteps({ steps, noneOptions, passRenderOptions }).execute(responseDoc);
2192
2196
  }
2193
2197
  };
2194
2198
 
@@ -2200,8 +2204,8 @@ up.Change.Addition = class Addition extends up.Change {
2200
2204
  var _a;
2201
2205
  const u = up.util;
2202
2206
  up.RenderJob = (_a = class RenderJob {
2203
- constructor(options) {
2204
- this.options = options;
2207
+ constructor(renderOptions) {
2208
+ this.renderOptions = renderOptions;
2205
2209
  }
2206
2210
  execute() {
2207
2211
  this._rendered = this._executePromise();
@@ -2210,9 +2214,9 @@ up.RenderJob = (_a = class RenderJob {
2210
2214
  async _executePromise() {
2211
2215
  try {
2212
2216
  this._emitGuardEvent();
2213
- this.options = up.RenderOptions.preprocess(this.options);
2214
- up.browser.assertConfirmed(this.options);
2215
- up.RenderOptions.assertContentGiven(this.options);
2217
+ this.renderOptions = up.RenderOptions.preprocess(this.renderOptions);
2218
+ up.browser.assertConfirmed(this.renderOptions);
2219
+ up.RenderOptions.assertContentGiven(this.renderOptions);
2216
2220
  let result = await this._getChange().execute();
2217
2221
  this._handleResult(result);
2218
2222
  return result;
@@ -2224,7 +2228,7 @@ up.RenderJob = (_a = class RenderJob {
2224
2228
  }
2225
2229
  _handleResult(result) {
2226
2230
  if (result instanceof up.RenderResult) {
2227
- let { onRendered, onFinished } = result.options;
2231
+ let { onRendered, onFinished } = result.renderOptions;
2228
2232
  if (!result.none)
2229
2233
  up.error.guard(() => onRendered?.(result));
2230
2234
  let guardedOnFinished = function (result) {
@@ -2237,7 +2241,7 @@ up.RenderJob = (_a = class RenderJob {
2237
2241
  _handleError(error) {
2238
2242
  let prefix = error instanceof up.Aborted ? 'Rendering was aborted' : 'Error while rendering';
2239
2243
  up.puts('up.render()', `${prefix}: ${error.name}: ${error.message}`);
2240
- up.error.guard(() => this.options.onError?.(error));
2244
+ up.error.guard(() => this.renderOptions.onError?.(error));
2241
2245
  }
2242
2246
  get finished() {
2243
2247
  return this._awaitFinished();
@@ -2258,7 +2262,7 @@ up.RenderJob = (_a = class RenderJob {
2258
2262
  }
2259
2263
  _getChange() {
2260
2264
  let handleAbort = u.memoize((request) => this._handleAbortOption(request));
2261
- let renderOptions = { ...this.options, handleAbort };
2265
+ let renderOptions = { ...this.renderOptions, handleAbort };
2262
2266
  if (renderOptions.url) {
2263
2267
  return new up.Change.FromURL(renderOptions);
2264
2268
  }
@@ -2270,16 +2274,16 @@ up.RenderJob = (_a = class RenderJob {
2270
2274
  }
2271
2275
  }
2272
2276
  _emitGuardEvent() {
2273
- let guardEvent = u.pluckKey(this.options, 'guardEvent');
2277
+ let guardEvent = u.pluckKey(this.renderOptions, 'guardEvent');
2274
2278
  if (guardEvent) {
2275
- guardEvent.renderOptions = this.options;
2276
- if (up.emit(guardEvent, { target: this.options.origin }).defaultPrevented) {
2279
+ guardEvent.renderOptions = this.renderOptions;
2280
+ if (up.emit(guardEvent, { target: this.renderOptions.origin }).defaultPrevented) {
2277
2281
  throw new up.Aborted(`Rendering was prevented by ${guardEvent.type} listener`);
2278
2282
  }
2279
2283
  }
2280
2284
  }
2281
2285
  _handleAbortOption(request) {
2282
- let { abort } = this.options;
2286
+ let { abort } = this.renderOptions;
2283
2287
  if (!abort || !up.network.isBusy())
2284
2288
  return;
2285
2289
  let { bindFragments, bindLayer, origin, layer } = this._getChange().getPreflightProps();
@@ -2422,6 +2426,7 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2422
2426
  this._assertOpenEventEmitted();
2423
2427
  this.layer = this._buildLayer();
2424
2428
  this._baseLayer.peel({ history: !this.layer.history });
2429
+ this._baseLayer.saveHistory();
2425
2430
  up.layer.stack.push(this.layer);
2426
2431
  this.layer.createElements();
2427
2432
  this.layer.setupHandlers();
@@ -2431,14 +2436,15 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2431
2436
  this.handleLayerChangeRequests();
2432
2437
  this.responseDoc.commitElement(this._content);
2433
2438
  this.layer.setContent(this._content);
2434
- this.setReloadAttrs({ newElement: this._content, source: this.options.source });
2435
2439
  this.responseDoc.finalizeElement(this._content);
2440
+ this.setReloadAttrs({ newElement: this._content, source: this.options.source });
2441
+ up.hello(this.layer.element, { ...this.options, layer: this.layer, dataRoot: this._content });
2436
2442
  this._newOverlayResult = new up.RenderResult({
2437
2443
  layer: this.layer,
2438
2444
  fragments: [this._content],
2439
2445
  target: this.target,
2446
+ renderOptions: this.options,
2440
2447
  });
2441
- up.hello(this.layer.element, { ...this.options, layer: this.layer, dataRoot: this._content });
2442
2448
  this._handleScroll();
2443
2449
  this._newOverlayResult.finished = this._finish();
2444
2450
  this.layer.opening = false;
@@ -2449,7 +2455,10 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2449
2455
  if (this._otherLayersResult)
2450
2456
  return;
2451
2457
  let otherLayerSteps = this._getHungrySteps().other;
2452
- this._otherLayersResult = this.executeSteps(otherLayerSteps, this.responseDoc);
2458
+ this._otherLayersResult = this.executeSteps({
2459
+ steps: otherLayerSteps,
2460
+ responseDoc: this.responseDoc,
2461
+ });
2453
2462
  }
2454
2463
  async _finish() {
2455
2464
  await this.layer.startOpenAnimation();
@@ -2468,9 +2477,7 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2468
2477
  if (this.layer.history === 'auto') {
2469
2478
  this.layer.history = up.fragment.hasAutoHistory([this._content], this.layer);
2470
2479
  }
2471
- let { parent } = this.layer;
2472
- this.layer.history &&= parent.history;
2473
- parent.saveHistory();
2480
+ this.layer.history &&= this._baseLayer.history;
2474
2481
  this.layer.updateHistory(this.options);
2475
2482
  }
2476
2483
  _handleFocus() {
@@ -2595,13 +2602,20 @@ up.Change.UpdateLayer = (_a = class UpdateLayer extends up.Change.Addition {
2595
2602
  this.layer.updateHistory(this.options);
2596
2603
  }
2597
2604
  this.handleLayerChangeRequests();
2598
- this._currentLayerResult = this.executeSteps(this._steps, this.responseDoc, this.options);
2605
+ this._currentLayerResult = this.executeSteps({
2606
+ steps: this._steps,
2607
+ responseDoc: this.responseDoc,
2608
+ noneOptions: this.options,
2609
+ });
2599
2610
  }
2600
2611
  _renderOtherLayers() {
2601
2612
  if (this._otherLayersResult)
2602
2613
  return;
2603
2614
  let otherLayerSteps = this._getHungrySteps().other;
2604
- this._otherLayersResult = this.executeSteps(otherLayerSteps, this.responseDoc);
2615
+ this._otherLayersResult = this.executeSteps({
2616
+ steps: otherLayerSteps,
2617
+ responseDoc: this.responseDoc,
2618
+ });
2605
2619
  }
2606
2620
  _matchPreflight() {
2607
2621
  this._matchOldElements();
@@ -2682,30 +2696,30 @@ const e = up.element;
2682
2696
  up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2683
2697
  constructor(options) {
2684
2698
  super(options);
2699
+ this._steps = u.copy(u.assert(options.steps));
2700
+ this._passRenderOptions = u.assert(options.passRenderOptions);
2685
2701
  this._noneOptions = options.noneOptions || {};
2686
- this._steps = u.copy(options.steps);
2687
2702
  }
2688
2703
  execute(responseDoc) {
2689
2704
  this.responseDoc = responseDoc;
2690
2705
  this._steps = responseDoc.selectSteps(this._steps);
2691
2706
  this._steps = responseDoc.commitSteps(this._steps);
2692
- if (!this._steps.length) {
2693
- return this._executeNone();
2694
- }
2695
2707
  this.renderResult = new up.RenderResult({
2696
- layer: this._steps[0]?.layer,
2708
+ layer: this._passRenderOptions.layer,
2697
2709
  target: up.fragment.targetForSteps(this._steps),
2710
+ renderOptions: this._passRenderOptions,
2698
2711
  });
2699
- this._steps.reverse();
2700
- const motionEndPromises = this._steps.map((step) => this._executeStep(step));
2701
- this.renderResult.finished = this._finish(motionEndPromises);
2712
+ if (!this._steps.length) {
2713
+ this._handleFocus(null, this._noneOptions);
2714
+ this._handleScroll(null, this._noneOptions);
2715
+ }
2716
+ else {
2717
+ this._steps.reverse();
2718
+ const motionEndPromises = this._steps.map((step) => this._executeStep(step));
2719
+ this.renderResult.finished = this._finish(motionEndPromises);
2720
+ }
2702
2721
  return this.renderResult;
2703
2722
  }
2704
- _executeNone() {
2705
- this._handleFocus(null, this._noneOptions);
2706
- this._handleScroll(null, this._noneOptions);
2707
- return up.RenderResult.buildNone();
2708
- }
2709
2723
  async _finish(motionEndPromises) {
2710
2724
  await Promise.all(motionEndPromises);
2711
2725
  for (let step of this._steps) {
@@ -2718,10 +2732,9 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2718
2732
  this.renderResult.fragments.unshift(...newFragments);
2719
2733
  }
2720
2734
  _executeStep(step) {
2721
- this.setReloadAttrs(step);
2722
2735
  switch (step.placement) {
2723
2736
  case 'swap': {
2724
- let keepPlan = this._findKeepPlan(step);
2737
+ let keepPlan = up.fragment.keepPlan(step);
2725
2738
  if (keepPlan) {
2726
2739
  this._handleFocus(step.oldElement, step);
2727
2740
  this._handleScroll(step.oldElement, step);
@@ -2737,10 +2750,8 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2737
2750
  },
2738
2751
  afterInsert: () => {
2739
2752
  this._restoreDescendantKeepables(step);
2740
- this.responseDoc.finalizeElement(step.newElement);
2753
+ this._welcomeElement(step.newElement, step);
2741
2754
  this._finalizeDescendantKeepables(step);
2742
- up.hello(step.newElement, step);
2743
- this._addToResult(step.newElement);
2744
2755
  },
2745
2756
  beforeDetach: () => {
2746
2757
  up.script.clean(step.oldElement, { layer: step.layer });
@@ -2777,53 +2788,27 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2777
2788
  let wrapper = e.wrapChildren(step.newElement);
2778
2789
  let position = step.placement === 'before' ? 'afterbegin' : 'beforeend';
2779
2790
  step.oldElement.insertAdjacentElement(position, wrapper);
2780
- this.responseDoc.finalizeElement(wrapper);
2781
- up.hello(wrapper, step);
2782
- this._addToResult(wrapper);
2791
+ this._welcomeElement(wrapper, step);
2783
2792
  this._handleFocus(wrapper, step);
2784
2793
  this._handleScroll(wrapper, step);
2785
- return up.animate(wrapper, step.transition, step).then(() => e.unwrap(wrapper));
2794
+ return up.animate(wrapper, step.animation, step).then(() => e.unwrap(wrapper));
2786
2795
  }
2787
2796
  default: {
2788
2797
  up.fail('Unknown placement: %o', step.placement);
2789
2798
  }
2790
2799
  }
2791
2800
  }
2792
- _findKeepPlan(options) {
2793
- if (!options.keep) {
2794
- return;
2795
- }
2796
- const { oldElement, newElement } = options;
2797
- let doKeep = e.booleanAttr(oldElement, 'up-keep');
2798
- if (!doKeep) {
2799
- return;
2800
- }
2801
- let partner;
2802
- let partnerSelector = up.fragment.toTarget(oldElement);
2803
- const lookupOpts = { layer: options.layer };
2804
- if (options.descendantsOnly) {
2805
- partner = up.fragment.get(newElement, partnerSelector, lookupOpts);
2806
- }
2807
- else {
2808
- partner = e.subtreeFirst(newElement, partnerSelector, lookupOpts);
2809
- }
2810
- if (partner && e.booleanAttr(partner, 'up-keep') !== false) {
2811
- const plan = {
2812
- oldElement,
2813
- newElement: partner,
2814
- newData: up.script.data(partner),
2815
- renderOptions: options,
2816
- };
2817
- if (!up.fragment.emitKeep(plan).defaultPrevented) {
2818
- return plan;
2819
- }
2820
- }
2801
+ _welcomeElement(element, step) {
2802
+ this.responseDoc.finalizeElement(element);
2803
+ this.setReloadAttrs(step);
2804
+ up.hello(element, step);
2805
+ this._addToResult(element);
2821
2806
  }
2822
2807
  _preserveDescendantKeepables(step) {
2823
2808
  const descendantKeepPlans = [];
2824
2809
  if (step.keep) {
2825
2810
  for (let keepable of step.oldElement.querySelectorAll('[up-keep]')) {
2826
- let keepPlan = this._findKeepPlan({ ...step, oldElement: keepable, descendantsOnly: true });
2811
+ let keepPlan = up.fragment.keepPlan({ ...step, oldElement: keepable, descendantsOnly: true });
2827
2812
  if (keepPlan) {
2828
2813
  const keepableClone = keepable.cloneNode(true);
2829
2814
  keepable.insertAdjacentElement('beforebegin', keepableClone);
@@ -2838,7 +2823,7 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2838
2823
  keepPlan.newElement.replaceWith(keepable);
2839
2824
  }
2840
2825
  else {
2841
- e.preservingAppend(document.body, keepable);
2826
+ document.body.append(keepable);
2842
2827
  }
2843
2828
  descendantKeepPlans.push(keepPlan);
2844
2829
  }
@@ -2848,12 +2833,7 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2848
2833
  }
2849
2834
  _restoreDescendantKeepables(step) {
2850
2835
  for (let keepPlan of step.descendantKeepPlans) {
2851
- if (this._willChangeBody()) {
2852
- keepPlan.newElement.replaceWith(keepPlan.oldElement);
2853
- }
2854
- else {
2855
- e.preservingReplace(keepPlan.newElement, keepPlan.oldElement);
2856
- }
2836
+ keepPlan.newElement.replaceWith(keepPlan.oldElement);
2857
2837
  for (let reviver of keepPlan.revivers) {
2858
2838
  reviver();
2859
2839
  }
@@ -3077,7 +3057,7 @@ up.Change.FromResponse = (_a = class FromResponse extends up.Change {
3077
3057
  this.options.failTarget = ':none';
3078
3058
  }
3079
3059
  _updateContentFromResponse(finalRenderOptions) {
3080
- if (finalRenderOptions.failPrefixForced) {
3060
+ if (finalRenderOptions.didForceFailOptions) {
3081
3061
  up.puts('up.render()', 'Rendering failed response using fail-prefixed options (https://unpoly.com/failed-responses)');
3082
3062
  }
3083
3063
  this._augmentOptionsFromResponse(finalRenderOptions);
@@ -3244,9 +3224,7 @@ up.Change.FromContent = (_a = class FromContent extends up.Change {
3244
3224
  return this._seekPlan(this._executePlan.bind(this)) || this._cannotMatchPostflightTarget();
3245
3225
  }
3246
3226
  _executePlan(matchedPlan) {
3247
- let result = matchedPlan.execute(this._getResponseDoc(), this._onPlanApplicable.bind(this, matchedPlan));
3248
- result.options = this.options;
3249
- return result;
3227
+ return matchedPlan.execute(this._getResponseDoc(), this._onPlanApplicable.bind(this, matchedPlan));
3250
3228
  }
3251
3229
  _isApplicablePlanError(error) {
3252
3230
  return !(error instanceof up.CannotMatch);
@@ -3320,7 +3298,7 @@ up.Change.FromContent = (_a = class FromContent extends up.Change {
3320
3298
  else if (this._layers.length === 0) {
3321
3299
  this._cannotMatchLayer();
3322
3300
  }
3323
- else if (this.options.failPrefixForced) {
3301
+ else if (this.options.didForceFailOptions) {
3324
3302
  throw new up.CannotMatch('No target selector given for failed responses (https://unpoly.com/failed-responses)');
3325
3303
  }
3326
3304
  else {
@@ -3931,6 +3909,9 @@ up.FieldWatcher = class FieldWatcher {
3931
3909
  if (this._root.matches('input[type=radio]')) {
3932
3910
  fail('Use %s with the container of a radio group, not with an individual radio button (%o)');
3933
3911
  }
3912
+ if (up.form.isField(this._root) && !this._root.name) {
3913
+ fail('%s can only watch fields with a name (%o)');
3914
+ }
3934
3915
  }
3935
3916
  _trackAbort() {
3936
3917
  let guard = ({ target }) => target.contains(this._region);
@@ -4021,9 +4002,9 @@ up.FieldWatcher = class FieldWatcher {
4021
4002
  return up.Params.fromContainer(this._root).toObject();
4022
4003
  }
4023
4004
  _check(event, fieldOptions = {}) {
4024
- up.log.putsEvent(event);
4025
4005
  const values = this._readFieldValues();
4026
4006
  if (this._isNewValues(values)) {
4007
+ up.log.putsEvent(event);
4027
4008
  this._scheduleValues(values, fieldOptions);
4028
4009
  }
4029
4010
  }
@@ -4063,7 +4044,7 @@ up.Switcher = class Switcher {
4063
4044
  _trackNewSwitchees() {
4064
4045
  let filter = (matches) => {
4065
4046
  let scope = this._scope;
4066
- return u.filter(matches, (match) => scope.contains(match));
4047
+ return u.filter(matches, (match) => scope?.contains(match));
4067
4048
  };
4068
4049
  let onSwitcheeAdded = (switchee) => this._switchSwitchee(switchee);
4069
4050
  return up.fragment.trackSelector(this._switcheeSelector, { filter }, onSwitcheeAdded);
@@ -4094,10 +4075,11 @@ up.Switcher = class Switcher {
4094
4075
  }
4095
4076
  }
4096
4077
  let log = ['Switching %o', switchee];
4097
- up.emit(switchee, 'up:form:switch', { field: this._root, tokens: fieldTokens, log });
4078
+ up.emit(switchee, 'up:form:switch', { field: this._root, fieldTokens, log });
4098
4079
  }
4099
4080
  _findSwitchees() {
4100
- return up.fragment.subtree(this._scope, this._switcheeSelector);
4081
+ let scope = this._scope;
4082
+ return scope ? up.fragment.subtree(scope, this._switcheeSelector) : [];
4101
4083
  }
4102
4084
  get _scope() {
4103
4085
  if (this._regionSelector) {
@@ -4111,44 +4093,16 @@ up.Switcher = class Switcher {
4111
4093
  return u.getSimpleTokens(str, { json: true });
4112
4094
  }
4113
4095
  _buildFieldTokens() {
4096
+ let values = up.Params.fromContainer(this._root).values();
4097
+ let tokens = [...values];
4098
+ let anyPresent = u.some(values, u.isPresent);
4099
+ tokens.push(anyPresent ? ':present' : ':blank');
4114
4100
  let fields = up.form.fields(this._root);
4115
- let field = fields[0];
4116
- let value;
4117
- let meta;
4118
- if (field.matches('input[type=checkbox]')) {
4119
- if (field.checked) {
4120
- value = field.value;
4121
- meta = ':checked';
4122
- }
4123
- else {
4124
- meta = ':unchecked';
4125
- }
4126
- }
4127
- else if (field.matches('input[type=radio]')) {
4128
- let checkedButton = up.migrate.checkedRadioButtonForSwitch?.(field) || u.find(fields, 'checked');
4129
- if (checkedButton) {
4130
- meta = ':checked';
4131
- value = checkedButton.value;
4132
- }
4133
- else {
4134
- meta = ':unchecked';
4135
- }
4136
- }
4137
- else {
4138
- value = field.value;
4139
- }
4140
- const values = [];
4141
- if (u.isPresent(value)) {
4142
- values.push(value);
4143
- values.push(':present');
4144
- }
4145
- else {
4146
- values.push(':blank');
4101
+ if (fields[0]?.matches('[type=radio], [type=checkbox]')) {
4102
+ let anyChecked = u.some(fields, 'checked');
4103
+ tokens.push(anyChecked ? ':checked' : ':unchecked');
4147
4104
  }
4148
- if (u.isPresent(meta)) {
4149
- values.push(meta);
4150
- }
4151
- return values;
4105
+ return tokens;
4152
4106
  }
4153
4107
  };
4154
4108
 
@@ -4640,7 +4594,7 @@ up.FragmentPolling = class FragmentPolling {
4640
4594
  if (this._state === 'started') {
4641
4595
  this._clearReloadTimer();
4642
4596
  this._state = 'stopped';
4643
- this.unbindEvents?.();
4597
+ this._unbindEvents?.();
4644
4598
  }
4645
4599
  }
4646
4600
  forceStart(options) {
@@ -4653,8 +4607,8 @@ up.FragmentPolling = class FragmentPolling {
4653
4607
  this.forceStarted = false;
4654
4608
  }
4655
4609
  _ensureEventsBound() {
4656
- if (!this.unbindEvents) {
4657
- this.unbindEvents = up.on('visibilitychange up:layer:opened up:layer:dismissed up:layer:accepted', this._onVisibilityChange.bind(this));
4610
+ if (!this._unbindEvents) {
4611
+ this._unbindEvents = up.on('visibilitychange up:layer:opened up:layer:dismissed up:layer:accepted', this._onVisibilityChange.bind(this));
4658
4612
  }
4659
4613
  }
4660
4614
  _onVisibilityChange() {
@@ -4851,14 +4805,15 @@ up.Layer = class Layer extends up.Record {
4851
4805
  }
4852
4806
  constructor(options = {}) {
4853
4807
  super(options);
4854
- if (!this.mode) {
4855
- throw "missing { mode } option";
4856
- }
4808
+ u.assert(this.mode);
4857
4809
  }
4858
4810
  setupHandlers() {
4859
4811
  up.link.convertClicks(this);
4812
+ this._unbindLocationChanged = up.on('up:location:changed', (event) => this._onBrowserLocationChanged(event));
4813
+ }
4814
+ teardownHandlers() {
4815
+ this._unbindLocationChanged?.();
4860
4816
  }
4861
- teardownHandlers() { }
4862
4817
  mainTargets() {
4863
4818
  return up.layer.mainTargets(this.mode);
4864
4819
  }
@@ -4957,28 +4912,28 @@ up.Layer = class Layer extends up.Record {
4957
4912
  return !this.element.isConnected;
4958
4913
  }
4959
4914
  saveHistory() {
4960
- if (this.history) {
4961
- this.savedTitle = document.title;
4962
- this.savedMetaTags = up.history.findMetaTags();
4963
- this.savedLocation = up.history.location;
4964
- this.savedLang = up.history.getLang();
4965
- }
4915
+ u.assert(this.isFront());
4916
+ if (!this.showsLiveHistory())
4917
+ return;
4918
+ this._savedTitle = this.title;
4919
+ this._savedMetaTags = this.metaTags;
4920
+ this._savedLocation = this.location;
4921
+ this._savedLang = this.lang;
4966
4922
  }
4967
4923
  restoreHistory() {
4968
- if (!this.showsLiveHistory()) {
4924
+ if (!this.showsLiveHistory())
4969
4925
  return;
4926
+ if (this._savedLocation) {
4927
+ up.history.push(this._savedLocation);
4970
4928
  }
4971
- if (this.savedLocation) {
4972
- up.history.push(this.savedLocation);
4973
- }
4974
- if (this.savedTitle) {
4975
- document.title = this.savedTitle;
4929
+ if (this._savedTitle) {
4930
+ document.title = this._savedTitle;
4976
4931
  }
4977
- if (this.savedMetaTags) {
4978
- up.history.updateMetaTags(this.savedMetaTags);
4932
+ if (this._savedMetaTags) {
4933
+ up.history.updateMetaTags(this._savedMetaTags);
4979
4934
  }
4980
- if (u.isString(this.savedLang)) {
4981
- up.history.updateLang(this.savedLang);
4935
+ if (u.isString(this._savedLang)) {
4936
+ up.history.updateLang(this._savedLang);
4982
4937
  }
4983
4938
  }
4984
4939
  asCurrent(fn) {
@@ -4986,32 +4941,37 @@ up.Layer = class Layer extends up.Record {
4986
4941
  }
4987
4942
  updateHistory(options) {
4988
4943
  if (u.isString(options.location)) {
4989
- this.location = options.location;
4944
+ this._updateLocation(options.location, { push: true });
4990
4945
  }
4991
4946
  if (up.history.config.updateMetaTags && u.isList(options.metaTags)) {
4992
4947
  up.migrate?.warnOfHungryMetaTags?.(options.metaTags);
4993
- this.metaTags = options.metaTags;
4948
+ this._updateMetaTags(options.metaTags);
4994
4949
  }
4995
4950
  if (u.isString(options.title)) {
4996
- this.title = options.title;
4951
+ this._updateTitle(options.title);
4997
4952
  }
4998
4953
  if (u.isString(options.lang)) {
4999
- this.lang = options.lang;
4954
+ this._updateLang(options.lang);
5000
4955
  }
5001
4956
  }
5002
4957
  showsLiveHistory() {
5003
4958
  return this.history && this.isFront();
5004
4959
  }
4960
+ _onBrowserLocationChanged({ location }) {
4961
+ if (this.showsLiveHistory()) {
4962
+ this._updateLocation(location, { push: false });
4963
+ }
4964
+ }
5005
4965
  get title() {
5006
4966
  if (this.showsLiveHistory()) {
5007
4967
  return document.title;
5008
4968
  }
5009
4969
  else {
5010
- return this.savedTitle;
4970
+ return this._savedTitle;
5011
4971
  }
5012
4972
  }
5013
- set title(title) {
5014
- this.savedTitle = title;
4973
+ _updateTitle(title) {
4974
+ this._savedTitle = title;
5015
4975
  if (this.showsLiveHistory()) {
5016
4976
  document.title = title;
5017
4977
  }
@@ -5021,11 +4981,11 @@ up.Layer = class Layer extends up.Record {
5021
4981
  return up.history.findMetaTags();
5022
4982
  }
5023
4983
  else {
5024
- return this.savedMetaTags;
4984
+ return this._savedMetaTags;
5025
4985
  }
5026
4986
  }
5027
- set metaTags(metaTags) {
5028
- this.savedMetaTags = metaTags;
4987
+ _updateMetaTags(metaTags) {
4988
+ this._savedMetaTags = metaTags;
5029
4989
  if (this.showsLiveHistory()) {
5030
4990
  up.history.updateMetaTags(metaTags);
5031
4991
  }
@@ -5035,11 +4995,11 @@ up.Layer = class Layer extends up.Record {
5035
4995
  return up.history.getLang();
5036
4996
  }
5037
4997
  else {
5038
- return this.savedLang;
4998
+ return this._savedLang;
5039
4999
  }
5040
5000
  }
5041
- set lang(lang) {
5042
- this.savedLang = lang;
5001
+ _updateLang(lang) {
5002
+ this._savedLang = lang;
5043
5003
  if (this.showsLiveHistory()) {
5044
5004
  up.history.updateLang(lang);
5045
5005
  }
@@ -5049,20 +5009,18 @@ up.Layer = class Layer extends up.Record {
5049
5009
  return up.history.location;
5050
5010
  }
5051
5011
  else {
5052
- return this.savedLocation;
5012
+ return this._savedLocation;
5053
5013
  }
5054
5014
  }
5055
- set location(location) {
5056
- const previousLocation = this.location;
5057
- location = u.normalizeURL(location);
5058
- if (previousLocation !== location || this.opening) {
5059
- this.savedLocation = location;
5060
- if (this.showsLiveHistory()) {
5061
- up.history.push(location);
5062
- }
5063
- if (!this.opening) {
5064
- this.emit('up:layer:location:changed', { location, log: false });
5065
- }
5015
+ _updateLocation(newLocation, { push }) {
5016
+ let prevSavedLocation = this._savedLocation;
5017
+ let liveLocation = up.history.location;
5018
+ this._savedLocation = newLocation;
5019
+ if (newLocation !== liveLocation && this.showsLiveHistory() && push) {
5020
+ up.history.push(newLocation);
5021
+ }
5022
+ if (newLocation !== prevSavedLocation && !this.opening) {
5023
+ this.emit('up:layer:location:changed', { location: newLocation, previousLocation: prevSavedLocation, log: false });
5066
5024
  }
5067
5025
  }
5068
5026
  selector(part) {
@@ -5198,7 +5156,7 @@ up.Layer.Overlay = (_a = class Overlay extends up.Layer {
5198
5156
  });
5199
5157
  }
5200
5158
  else {
5201
- this.unbindParentClicked = this.parent.on('up:click', (event, element) => {
5159
+ this._unbindParentClicked = this.parent.on('up:click', (event, element) => {
5202
5160
  if (!up.layer.isWithinForeignOverlay(element)) {
5203
5161
  const originClicked = this.origin && this.origin.contains(element);
5204
5162
  this._onOutsideClicked(event, originClicked);
@@ -5207,7 +5165,7 @@ up.Layer.Overlay = (_a = class Overlay extends up.Layer {
5207
5165
  }
5208
5166
  }
5209
5167
  if (this._supportsDismissMethod('key')) {
5210
- this.unbindEscapePressed = up.event.onEscape((event) => this.onEscapePressed(event));
5168
+ this._unbindEscapePressed = up.event.onEscape((event) => this.onEscapePressed(event));
5211
5169
  }
5212
5170
  this.registerAttrCloser('up-accept', (value, closeOptions) => {
5213
5171
  this.accept(value, closeOptions);
@@ -5303,8 +5261,8 @@ up.Layer.Overlay = (_a = class Overlay extends up.Layer {
5303
5261
  }
5304
5262
  teardownHandlers() {
5305
5263
  super.teardownHandlers();
5306
- this.unbindParentClicked?.();
5307
- this.unbindEscapePressed?.();
5264
+ this._unbindParentClicked?.();
5265
+ this._unbindEscapePressed?.();
5308
5266
  this.overlayFocus.teardown();
5309
5267
  }
5310
5268
  destroyElements(options) {
@@ -5806,6 +5764,11 @@ up.LinkCurrentURLs = class LinkCurrentURLs {
5806
5764
  this._upHREF === normalizedLocation ||
5807
5765
  this._aliasPattern?.test?.(normalizedLocation, false));
5808
5766
  }
5767
+ isAnyCurrent(normalizedLocations) {
5768
+ return normalizedLocations.some((normalizedLocation) => (this._href === normalizedLocation ||
5769
+ this._upHREF === normalizedLocation ||
5770
+ this._aliasPattern?.test?.(normalizedLocation, false)));
5771
+ }
5809
5772
  };
5810
5773
 
5811
5774
 
@@ -6134,15 +6097,12 @@ up.Params = class Params {
6134
6097
  return formData;
6135
6098
  }
6136
6099
  toQuery() {
6137
- let parts = u.map(this.entries, this._arrayEntryToQuery.bind(this));
6138
- parts = u.compact(parts);
6100
+ let simpleEntries = this.withoutBinaryEntries().entries;
6101
+ let parts = simpleEntries.map(this._arrayEntryToQuery);
6139
6102
  return parts.join('&');
6140
6103
  }
6141
6104
  _arrayEntryToQuery(entry) {
6142
6105
  const { value } = entry;
6143
- if (this._isBinaryValue(value)) {
6144
- return;
6145
- }
6146
6106
  let query = encodeURIComponent(entry.name);
6147
6107
  if (u.isGiven(value)) {
6148
6108
  query += "=";
@@ -6150,12 +6110,15 @@ up.Params = class Params {
6150
6110
  }
6151
6111
  return query;
6152
6112
  }
6153
- _isBinaryValue(value) {
6113
+ _isBinaryEntry({ value }) {
6154
6114
  return value instanceof Blob;
6155
6115
  }
6156
- hasBinaryValues() {
6157
- const values = u.map(this.entries, 'value');
6158
- return u.some(values, this._isBinaryValue);
6116
+ hasBinaryEntries() {
6117
+ return u.some(this.entries, this._isBinaryEntry);
6118
+ }
6119
+ withoutBinaryEntries() {
6120
+ let simpleEntries = u.reject(this.entries, this._isBinaryEntry);
6121
+ return new this.constructor(simpleEntries);
6159
6122
  }
6160
6123
  toURL(base) {
6161
6124
  let parts = [base, this.toQuery()];
@@ -6185,9 +6148,15 @@ up.Params = class Params {
6185
6148
  this._addAllFromObject(raw);
6186
6149
  }
6187
6150
  else {
6188
- up.fail("Unsupport params type: %o", raw);
6151
+ up.fail("Unsupported params type: %o", raw);
6189
6152
  }
6190
6153
  }
6154
+ keys() {
6155
+ return u.map(this.entries, 'name');
6156
+ }
6157
+ values() {
6158
+ return u.map(this.entries, 'value');
6159
+ }
6191
6160
  _addAllFromObject(object) {
6192
6161
  for (let key in object) {
6193
6162
  const value = object[key];
@@ -6240,16 +6209,11 @@ up.Params = class Params {
6240
6209
  return entry?.value;
6241
6210
  }
6242
6211
  getAll(name) {
6243
- if (this._isArrayKey(name)) {
6244
- return this.getAll(name);
6245
- }
6246
- else {
6247
- const entries = u.map(this.entries, this._matchEntryFn(name));
6248
- return u.map(entries, 'value');
6249
- }
6212
+ const entries = u.filter(this.entries, this._matchEntryFn(name));
6213
+ return u.map(entries, 'value');
6250
6214
  }
6251
6215
  _isArrayKey(key) {
6252
- return key.endsWith('[]');
6216
+ return u.evalOption(up.form.config.arrayParam, key);
6253
6217
  }
6254
6218
  [u.isBlank.key]() {
6255
6219
  return this.entries.length === 0;
@@ -6312,11 +6276,15 @@ up.Params = class Params {
6312
6276
  static stripURL(url) {
6313
6277
  return u.normalizeURL(url, { search: false });
6314
6278
  }
6315
- static merge(...objects) {
6316
- return objects.reduce(function (allParams, params) {
6317
- allParams.addAll(params);
6318
- return allParams;
6319
- }, new up.Params());
6279
+ static merge(start, ...objects) {
6280
+ let merged = u.copy(start);
6281
+ for (let object of objects) {
6282
+ let other = u.wrapValue(this, object);
6283
+ for (let key of other.keys())
6284
+ merged.delete(key);
6285
+ merged.addAll(other);
6286
+ }
6287
+ return merged;
6320
6288
  }
6321
6289
  };
6322
6290
 
@@ -6623,19 +6591,22 @@ up.RenderOptions = (function () {
6623
6591
  return overrides;
6624
6592
  }
6625
6593
  function deriveFailOptions(preprocessedOptions) {
6594
+ let markFailure = { didFail: true };
6626
6595
  let overrides = failOverrides(preprocessedOptions);
6627
6596
  if (preprocessedOptions.failOptions) {
6628
6597
  return {
6629
6598
  ...preprocessedOptions.defaults,
6630
6599
  ...u.pick(preprocessedOptions, SHARED_KEYS),
6631
6600
  ...overrides,
6632
- ...{ failPrefixForced: true }
6601
+ didForceFailOptions: true,
6602
+ ...markFailure,
6633
6603
  };
6634
6604
  }
6635
6605
  else {
6636
6606
  return {
6637
6607
  ...preprocessedOptions,
6638
6608
  ...overrides,
6609
+ ...markFailure,
6639
6610
  };
6640
6611
  }
6641
6612
  }
@@ -6661,13 +6632,14 @@ up.RenderResult = class RenderResult extends up.Record {
6661
6632
  'fragments',
6662
6633
  'layer',
6663
6634
  'target',
6664
- 'options',
6635
+ 'renderOptions',
6665
6636
  'finished',
6666
6637
  ];
6667
6638
  }
6668
6639
  defaults() {
6669
6640
  return {
6670
6641
  fragments: [],
6642
+ finished: Promise.resolve(),
6671
6643
  };
6672
6644
  }
6673
6645
  get none() {
@@ -6676,13 +6648,15 @@ up.RenderResult = class RenderResult extends up.Record {
6676
6648
  get fragment() {
6677
6649
  return this.fragments[0];
6678
6650
  }
6651
+ get ok() {
6652
+ return !this.renderOptions.didFail;
6653
+ }
6679
6654
  static both(main, extension, mergeFinished = true) {
6680
- if (!extension)
6655
+ if (!extension) {
6681
6656
  return main;
6657
+ }
6682
6658
  return new this({
6683
- target: main.target,
6684
- layer: main.layer,
6685
- options: main.options,
6659
+ ...main,
6686
6660
  fragments: main.fragments.concat(extension.fragments),
6687
6661
  finished: (mergeFinished && this.mergeFinished(main, extension))
6688
6662
  });
@@ -6690,12 +6664,6 @@ up.RenderResult = class RenderResult extends up.Record {
6690
6664
  static async mergeFinished(main, extension) {
6691
6665
  return this.both(await main.finished, await extension.finished, false);
6692
6666
  }
6693
- static buildNone() {
6694
- return new this({
6695
- target: ':none',
6696
- finished: Promise.resolve(),
6697
- });
6698
- }
6699
6667
  };
6700
6668
 
6701
6669
 
@@ -7164,15 +7132,6 @@ up.Request.Cache = class Cache {
7164
7132
  this._requests.push(request);
7165
7133
  this._limitSize();
7166
7134
  }
7167
- alias(existingCachedRequest, newRequest) {
7168
- existingCachedRequest = this.get(existingCachedRequest);
7169
- if (!existingCachedRequest)
7170
- return;
7171
- newRequest = this._wrap(newRequest);
7172
- this.track(existingCachedRequest, newRequest, { force: true });
7173
- this.put(newRequest);
7174
- return newRequest;
7175
- }
7176
7135
  async track(existingRequest, newRequest, options = {}) {
7177
7136
  newRequest.trackedRequest = existingRequest;
7178
7137
  newRequest.state = 'tracking';
@@ -7266,7 +7225,7 @@ up.Request.Queue = class Queue {
7266
7225
  }
7267
7226
  asap(request) {
7268
7227
  request.runQueuedCallbacks();
7269
- u.always(request, (responseOrError) => this._onRequestSettled(request, responseOrError));
7228
+ u.always(request, () => this._onRequestSettled(request));
7270
7229
  this._scheduleSlowTimer(request);
7271
7230
  this._queueRequest(request);
7272
7231
  queueMicrotask(() => this._poke());
@@ -7306,11 +7265,8 @@ up.Request.Queue = class Queue {
7306
7265
  this._currentRequests.push(request);
7307
7266
  }
7308
7267
  }
7309
- _onRequestSettled(request, responseOrError) {
7268
+ _onRequestSettled(request) {
7310
7269
  u.remove(this._currentRequests, request) || u.remove(this._queuedRequests, request);
7311
- if ((responseOrError instanceof up.Response) && responseOrError.ok) {
7312
- up.network.registerAliasForRedirect(request, responseOrError);
7313
- }
7314
7270
  queueMicrotask(() => this._poke());
7315
7271
  u.task(() => this._checkForRecover());
7316
7272
  }
@@ -7370,7 +7326,7 @@ up.Request.FormRenderer = class FormRenderer {
7370
7326
  this._request = request;
7371
7327
  }
7372
7328
  buildAndSubmit() {
7373
- this.params = u.copy(this._request.params);
7329
+ this.params = this._request.params.withoutBinaryEntries();
7374
7330
  let action = this._request.url;
7375
7331
  let { method } = this._request;
7376
7332
  const paramsFromQuery = up.Params.fromURL(action);
@@ -7447,7 +7403,7 @@ up.Request.XHRRenderer = (_a = class XHRRenderer {
7447
7403
  this._contentType = this._request.contentType;
7448
7404
  if (!this._payload && this._request.allowsPayload()) {
7449
7405
  if (!this._contentType) {
7450
- this._contentType = this._params.hasBinaryValues() ? CONTENT_TYPE_FORM_DATA : CONTENT_TYPE_URL_ENCODED;
7406
+ this._contentType = this._params.hasBinaryEntries() ? CONTENT_TYPE_FORM_DATA : CONTENT_TYPE_URL_ENCODED;
7451
7407
  }
7452
7408
  if (this._contentType === CONTENT_TYPE_FORM_DATA) {
7453
7409
  this._contentType = null;
@@ -7545,6 +7501,20 @@ up.Response = class Response extends up.Record {
7545
7501
  get description() {
7546
7502
  return `HTTP ${this.status} response to ${this.request.description}`;
7547
7503
  }
7504
+ get redirect() {
7505
+ return (this.url !== this.request.url) || (this.method !== this.request.method);
7506
+ }
7507
+ get redirectRequest() {
7508
+ if (!this.redirect)
7509
+ return;
7510
+ let finalRequest = u.variant(this.request, {
7511
+ method: this.method,
7512
+ url: this.url,
7513
+ cacheRoute: null,
7514
+ });
7515
+ up.cache.track(this.request, finalRequest, { force: true });
7516
+ return finalRequest;
7517
+ }
7548
7518
  };
7549
7519
 
7550
7520
 
@@ -8100,7 +8070,7 @@ up.framework = (function () {
8100
8070
  function emitReset() {
8101
8071
  up.emit('up:framework:reset', { log: false });
8102
8072
  }
8103
- function boot() {
8073
+ function boot({ mode = 'manual' } = {}) {
8104
8074
  if (readyState !== 'configuring') {
8105
8075
  console.error('Unpoly has already booted');
8106
8076
  return;
@@ -8108,9 +8078,9 @@ up.framework = (function () {
8108
8078
  let issue = supportIssue();
8109
8079
  if (!issue) {
8110
8080
  readyState = 'booting';
8111
- up.emit('up:framework:boot', { log: false });
8081
+ up.emit('up:framework:boot', { mode, log: false });
8112
8082
  readyState = 'booted';
8113
- up.emit('up:framework:booted', { log: false });
8083
+ up.emit('up:framework:booted', { mode, log: false });
8114
8084
  }
8115
8085
  else {
8116
8086
  console.error("Unpoly cannot boot: %s", issue);
@@ -8134,7 +8104,7 @@ up.framework = (function () {
8134
8104
  console.debug('Call up.boot() after you have configured Unpoly');
8135
8105
  }
8136
8106
  else {
8137
- document.addEventListener('DOMContentLoaded', boot);
8107
+ document.addEventListener('DOMContentLoaded', () => boot({ mode: 'auto' }));
8138
8108
  }
8139
8109
  readyState = 'configuring';
8140
8110
  }
@@ -8565,6 +8535,7 @@ up.script = (function () {
8565
8535
  '[up-expand]': -300,
8566
8536
  '[data-method]': -400,
8567
8537
  '[data-confirm]': -400,
8538
+ '[up-keep]': 9999999999,
8568
8539
  };
8569
8540
  let registeredCompilers = [];
8570
8541
  let registeredMacros = [];
@@ -8841,6 +8812,8 @@ up.history = (function () {
8841
8812
  placeAdoptedHistoryEntry('replaceState', location, trackOptions);
8842
8813
  }
8843
8814
  function push(location, trackOptions) {
8815
+ if (isLocation(location))
8816
+ return;
8844
8817
  placeAdoptedHistoryEntry('pushState', location, trackOptions);
8845
8818
  }
8846
8819
  function placeAdoptedHistoryEntry(method, location, trackOptions) {
@@ -8887,7 +8860,7 @@ up.history = (function () {
8887
8860
  restoreLocation(event.location);
8888
8861
  }
8889
8862
  else if (event.reason === 'hash') {
8890
- up.viewport.revealHash(event.hash, { strong: true });
8863
+ up.viewport.revealHash(location.hash, { strong: true });
8891
8864
  }
8892
8865
  }
8893
8866
  function findMetaTags(head = document.head) {
@@ -8928,14 +8901,13 @@ up.history = (function () {
8928
8901
  adopt();
8929
8902
  }
8930
8903
  }
8931
- up.on('up:framework:boot', function () {
8904
+ up.on('up:framework:boot', function ({ mode }) {
8932
8905
  trackCurrentLocation({ reason: null, alreadyHandled: true });
8933
8906
  patchHistoryAPI();
8934
8907
  adoptInitialHistoryEntry();
8935
- });
8936
- up.on('DOMContentLoaded', function () {
8937
- up.viewport.revealHash({ strong: true });
8938
- u.task(up.viewport.revealHash);
8908
+ if (mode === 'auto') {
8909
+ up.viewport.revealHash(location.hash, { strong: true });
8910
+ }
8939
8911
  });
8940
8912
  up.on(window, 'hashchange, popstate', () => {
8941
8913
  trackCurrentLocation({ reason: 'detect', alreadyHandled: false });
@@ -9063,6 +9035,7 @@ up.fragment = (function () {
9063
9035
  autoScroll: ['hash', 'layer-if-main'],
9064
9036
  autoRevalidate: (response) => response.expired,
9065
9037
  skipResponse: defaultSkipResponse,
9038
+ normalizeKeepHTML: defaultNormalizeKeepHTML
9066
9039
  }));
9067
9040
  u.delegate(config, ['mainTargets'], () => up.layer.config.any);
9068
9041
  function defaultSkipResponse({ response, expiredResponse }) {
@@ -9098,6 +9071,76 @@ up.fragment = (function () {
9098
9071
  const options = parseTargetAndOptions(args);
9099
9072
  return render({ ...options, navigate: true });
9100
9073
  });
9074
+ function emitFragmentKeep(keepPlan) {
9075
+ let { oldElement, newElement: newFragment, newData, renderOptions } = keepPlan;
9076
+ const log = ['Keeping fragment %o', oldElement];
9077
+ const callback = e.callbackAttr(keepPlan.oldElement, 'up-on-keep', { exposedKeys: ['newFragment', 'newData'] });
9078
+ const event = up.event.build('up:fragment:keep', { newFragment, newData, renderOptions });
9079
+ return up.emit(oldElement, event, { log, callback });
9080
+ }
9081
+ function findKeepPlan(options) {
9082
+ if (options.keep === false)
9083
+ return;
9084
+ const { oldElement, newElement } = options;
9085
+ let oldElementMode = keepMode(oldElement);
9086
+ if (!oldElementMode) {
9087
+ return;
9088
+ }
9089
+ let partner;
9090
+ let partnerSelector = up.fragment.toTarget(oldElement);
9091
+ const lookupOpts = { layer: options.layer };
9092
+ if (options.descendantsOnly) {
9093
+ partner = up.fragment.get(newElement, partnerSelector, lookupOpts);
9094
+ }
9095
+ else {
9096
+ partner = e.subtreeFirst(newElement, partnerSelector, lookupOpts);
9097
+ }
9098
+ if (!partner)
9099
+ return;
9100
+ let partnerMode = keepMode(partner);
9101
+ if (partnerMode === false)
9102
+ return;
9103
+ let oldIdentity = keepIdentity(oldElement, oldElementMode);
9104
+ let partnerIdentity = keepIdentity(partner, oldElementMode);
9105
+ if (!u.isEqual(oldIdentity, partnerIdentity))
9106
+ return;
9107
+ const plan = {
9108
+ oldElement,
9109
+ newElement: partner,
9110
+ newData: up.script.data(partner),
9111
+ renderOptions: options,
9112
+ };
9113
+ if (emitFragmentKeep(plan).defaultPrevented)
9114
+ return;
9115
+ return plan;
9116
+ }
9117
+ function keepIdentity(element, mode = keepMode(element)) {
9118
+ return element.upKeepIdentity ??= u.copy(buildKeepIdentity(element, mode));
9119
+ }
9120
+ function defaultNormalizeKeepHTML(html) {
9121
+ let tagPattern = /<[^<]+>/g;
9122
+ let attrs = ['up-etag', 'up-source', 'up-time', 'nonce', ...up.script.config.nonceableAttributes];
9123
+ let attrPattern = new RegExp(`\\s+(${attrs.join('|')})="[^"]*"`, 'g');
9124
+ let cleanTag = (match) => match.replace(attrPattern, '');
9125
+ html = html.replace(tagPattern, cleanTag);
9126
+ html = html.replace(/^[ \t]+/mg, '');
9127
+ return html;
9128
+ }
9129
+ function buildKeepIdentity(element, mode) {
9130
+ if (mode === 'same-html') {
9131
+ return config.normalizeKeepHTML(element.outerHTML);
9132
+ }
9133
+ else if (mode === 'same-data') {
9134
+ return up.data(element);
9135
+ }
9136
+ else {
9137
+ return true;
9138
+ }
9139
+ }
9140
+ up.macro('[up-keep]', (element) => keepIdentity(element));
9141
+ function keepMode(element) {
9142
+ return e.booleanOrStringAttr(element, 'up-keep');
9143
+ }
9101
9144
  function emitFragmentInserted(element) {
9102
9145
  if (element.upInserted)
9103
9146
  return;
@@ -9106,13 +9149,6 @@ up.fragment = (function () {
9106
9149
  log: ['Inserted fragment %o', element],
9107
9150
  });
9108
9151
  }
9109
- function emitFragmentKeep(keepPlan) {
9110
- let { oldElement, newElement: newFragment, newData, renderOptions } = keepPlan;
9111
- const log = ['Keeping fragment %o', oldElement];
9112
- const callback = e.callbackAttr(keepPlan.oldElement, 'up-on-keep', { exposedKeys: ['newFragment', 'newData'] });
9113
- const event = up.event.build('up:fragment:keep', { newFragment, newData, renderOptions });
9114
- return up.emit(oldElement, event, { log, callback });
9115
- }
9116
9152
  function emitFragmentDestroyed(fragment, options) {
9117
9153
  const log = options.log ?? ['Destroyed fragment %o', fragment];
9118
9154
  const parent = options.parent || document;
@@ -9570,8 +9606,8 @@ up.fragment = (function () {
9570
9606
  }
9571
9607
  up.on('up:framework:boot', function () {
9572
9608
  const { documentElement } = document;
9573
- documentElement.setAttribute('up-source', normalizeSource(location.href));
9574
9609
  up.hello(documentElement);
9610
+ documentElement.setAttribute('up-source', normalizeSource(location.href));
9575
9611
  if (!up.browser.canPushState()) {
9576
9612
  return up.warn('Cannot push history changes. Next render pass with history will load a full page.');
9577
9613
  }
@@ -9595,6 +9631,8 @@ up.fragment = (function () {
9595
9631
  emitInserted: emitFragmentInserted,
9596
9632
  emitDestroyed: emitFragmentDestroyed,
9597
9633
  emitKeep: emitFragmentKeep,
9634
+ keepPlan: findKeepPlan,
9635
+ defaultNormalizeKeepHTML,
9598
9636
  successKey,
9599
9637
  failKey,
9600
9638
  expandTargets,
@@ -9710,7 +9748,7 @@ up.viewport = (function () {
9710
9748
  function revealHash(...args) {
9711
9749
  return revealHashFn(...args)?.();
9712
9750
  }
9713
- function revealHashFn(hash = location.hash, { strong, layer, origin, behavior = 'instant' } = {}) {
9751
+ function revealHashFn(hash, { strong, layer, origin, behavior = 'instant' } = {}) {
9714
9752
  if (!hash)
9715
9753
  return;
9716
9754
  let match = firstHashTarget(hash, { layer, origin });
@@ -10010,7 +10048,7 @@ up.motion = (function () {
10010
10048
  }
10011
10049
  function animateNow(element, lastFrame, options) {
10012
10050
  if (up.migrate.loaded)
10013
- lastFrame = up.migrate.fixStyleProps(lastFrame);
10051
+ lastFrame = up.migrate.fixSetStyleProps(lastFrame);
10014
10052
  options = { ...options, finishEvent: motionController.finishEvent };
10015
10053
  const cssTransition = new up.CSSTransition(element, lastFrame, options);
10016
10054
  return cssTransition.start();
@@ -10132,8 +10170,7 @@ up.motion = (function () {
10132
10170
  }
10133
10171
  function registerOpacityAnimation(name, from, to) {
10134
10172
  namedAnimations.put(name, function (element, options) {
10135
- element.style.opacity = 0;
10136
- e.setStyle(element, { opacity: from });
10173
+ element.style.opacity = from;
10137
10174
  return animateNow(element, { opacity: to }, options);
10138
10175
  });
10139
10176
  }
@@ -10279,20 +10316,25 @@ up.network = (function () {
10279
10316
  u.always(request, function (responseOrError) {
10280
10317
  cache.expire(responseOrError.expireCache ?? false, { except: request });
10281
10318
  cache.evict(responseOrError.evictCache ?? false, { except: request });
10282
- let hasCacheEntry = cache.get(request);
10283
10319
  let isResponse = responseOrError instanceof up.Response;
10284
10320
  let isNetworkError = !isResponse;
10285
10321
  let isSuccessResponse = isResponse && responseOrError.ok;
10286
10322
  let isErrorResponse = isResponse && !responseOrError.ok;
10287
10323
  let isEmptyResponse = isResponse && responseOrError.none;
10324
+ let redirectRequest = isResponse && responseOrError.redirectRequest;
10288
10325
  if (isErrorResponse) {
10289
10326
  cache.evict(request.url);
10290
10327
  }
10291
10328
  else if (isNetworkError || isEmptyResponse) {
10292
10329
  cache.evict(request);
10293
10330
  }
10294
- else if (isSuccessResponse && hasCacheEntry) {
10295
- cache.put(request);
10331
+ else if (isSuccessResponse) {
10332
+ if (cache.get(request)) {
10333
+ cache.put(request);
10334
+ }
10335
+ if (redirectRequest && (redirectRequest.willCache() || cache.get(redirectRequest))) {
10336
+ cache.put(redirectRequest);
10337
+ }
10296
10338
  }
10297
10339
  });
10298
10340
  }
@@ -10306,16 +10348,6 @@ up.network = (function () {
10306
10348
  up.migrate.preprocessAbortArgs?.(args);
10307
10349
  queue.abort(...args);
10308
10350
  }
10309
- function registerAliasForRedirect(request, response) {
10310
- if (request.cache && response.url && request.url !== response.url) {
10311
- const newRequest = u.variant(request, {
10312
- method: response.method,
10313
- url: response.url,
10314
- cacheRoute: null,
10315
- });
10316
- cache.alias(request, newRequest);
10317
- }
10318
- }
10319
10351
  function isSafeMethod(method) {
10320
10352
  return u.contains(['GET', 'OPTIONS', 'HEAD'], u.normalizeMethod(method));
10321
10353
  }
@@ -10337,7 +10369,6 @@ up.network = (function () {
10337
10369
  isSafeMethod,
10338
10370
  config,
10339
10371
  abort: abortRequests,
10340
- registerAliasForRedirect,
10341
10372
  queue,
10342
10373
  loadPage,
10343
10374
  };
@@ -10967,6 +10998,7 @@ up.form = (function () {
10967
10998
  watchInputDelay: 0,
10968
10999
  watchChangeEvents: ['change'],
10969
11000
  watchableEvents: ['input', 'change'],
11001
+ arrayParam: (name) => name.endsWith('[]'),
10970
11002
  }));
10971
11003
  function fieldSelector(suffix = '') {
10972
11004
  return config.fieldSelectors.map((field) => field + suffix).join();
@@ -11048,6 +11080,8 @@ up.form = (function () {
11048
11080
  parser.string('url');
11049
11081
  parser.string('method', { normalize: u.normalizeMethod });
11050
11082
  parser.boolean('batch', { default: config.validateBatch });
11083
+ parser.json('params');
11084
+ parser.json('headers');
11051
11085
  parser.include(watchOptions, { defaults: { event: 'change' } });
11052
11086
  return options;
11053
11087
  }
@@ -11113,8 +11147,7 @@ up.form = (function () {
11113
11147
  options.method ||= submitButton.getAttribute('formmethod');
11114
11148
  options.url ||= submitButton.getAttribute('formaction');
11115
11149
  }
11116
- params.addAll(options.params);
11117
- options.params = params;
11150
+ options.params = up.Params.merge(params, options.params);
11118
11151
  parser.string('url', { attr: 'action', default: up.fragment.source(form) });
11119
11152
  parser.string('method', {
11120
11153
  attr: ['up-method', 'data-method', 'method'],
@@ -11207,10 +11240,10 @@ up.form = (function () {
11207
11240
  function trackFields(...args) {
11208
11241
  let [root, { guard }, callback] = u.args(args, 'val', 'options', 'callback');
11209
11242
  let filter = function (fields) {
11210
- let scope = getRegion(root);
11243
+ let region = getRegion(root);
11211
11244
  return u.filter(fields, function (field) {
11212
- return (root === scope || root.contains(field))
11213
- && (getForm(field) === scope)
11245
+ return (root === region || root.contains(field))
11246
+ && (getRegion(field) === region)
11214
11247
  && (!guard || guard(field));
11215
11248
  });
11216
11249
  };
@@ -11287,29 +11320,38 @@ up.status = (function () {
11287
11320
  noNavSelectors: ['[up-nav=false]'],
11288
11321
  }));
11289
11322
  let namedPreviewFns = new up.Registry('preview');
11290
- function reset() {
11291
- up.layer.root.feedbackLocation = null;
11292
- }
11293
11323
  const SELECTOR_LINK = 'a, [up-href]';
11294
11324
  function linkCurrentURLs(link) {
11295
11325
  return link.upCurrentURLs ||= new up.LinkCurrentURLs(link);
11296
11326
  }
11297
- function updateFragment(fragment, { layer } = {}) {
11298
- layer ||= up.layer.get(fragment);
11299
- let layerLocation = getMatchableLayerLocation(layer);
11300
- const navSelector = config.selector('navSelectors');
11301
- const navLinkSelector = `${navSelector} :is(${SELECTOR_LINK}), ${navSelector}:is(${SELECTOR_LINK})`;
11302
- const links = up.fragment.all(navLinkSelector, { layer });
11303
- for (let link of links) {
11304
- const isCurrent = linkCurrentURLs(link).isCurrent(layerLocation);
11305
- for (let currentClass of config.currentClasses) {
11306
- link.classList.toggle(currentClass, isCurrent);
11327
+ function getNavLocations(nav) {
11328
+ let layerRef = e.attr(nav, 'up-layer') || 'origin';
11329
+ let layers = up.layer.getAll(layerRef, { origin: nav });
11330
+ return u.compact(layers.map(getMatchableLayerLocation));
11331
+ }
11332
+ function updateNav(nav, links, { newLinks, anyLocationChanged }) {
11333
+ let currentLocations = (!anyLocationChanged && nav.upNavLocations) || getNavLocations(nav);
11334
+ if (newLinks || !u.isEqual(nav.upNavLocations, currentLocations)) {
11335
+ for (let link of links) {
11336
+ const isCurrent = linkCurrentURLs(link).isAnyCurrent(currentLocations);
11337
+ for (let currentClass of config.currentClasses) {
11338
+ link.classList.toggle(currentClass, isCurrent);
11339
+ }
11340
+ e.setAttrPresence(link, 'aria-current', 'page', isCurrent);
11307
11341
  }
11308
- e.setAttrPresence(link, 'aria-current', 'page', isCurrent);
11342
+ nav.upNavLocations = currentLocations;
11343
+ }
11344
+ }
11345
+ function updateNavsAround(root, opts) {
11346
+ const navSelector = config.selector('navSelectors');
11347
+ const fullNavs = e.around(root, navSelector);
11348
+ for (let fullNav of fullNavs) {
11349
+ let links = e.subtree(fullNav, SELECTOR_LINK);
11350
+ updateNav(fullNav, links, opts);
11309
11351
  }
11310
11352
  }
11311
11353
  function getMatchableLayerLocation(layer) {
11312
- return layer.feedbackLocation || u.matchableURL(layer.location);
11354
+ return u.matchableURL(layer.location);
11313
11355
  }
11314
11356
  function findActivatableArea(element) {
11315
11357
  return e.ancestor(element, SELECTOR_LINK) || element;
@@ -11393,30 +11435,12 @@ up.status = (function () {
11393
11435
  parser.string('placeholder');
11394
11436
  return options;
11395
11437
  }
11396
- function updateLayerIfLocationChanged(layer) {
11397
- const processedLocation = layer.feedbackLocation;
11398
- const layerLocation = getMatchableLayerLocation(layer.location);
11399
- if (!processedLocation || (processedLocation !== layerLocation)) {
11400
- layer.feedbackLocation = layerLocation;
11401
- updateFragment(layer.element, { layer });
11402
- }
11403
- }
11404
- function onBrowserLocationChanged() {
11405
- const frontLayer = up.layer.front;
11406
- if (frontLayer.showsLiveHistory()) {
11407
- updateLayerIfLocationChanged(frontLayer);
11408
- }
11409
- }
11410
- up.on('up:location:changed', (_event) => {
11411
- onBrowserLocationChanged();
11412
- });
11413
11438
  up.on('up:fragment:compile', (_event, newFragment) => {
11414
- updateFragment(newFragment);
11439
+ updateNavsAround(newFragment, { newLinks: true, anyLocationChanged: false });
11415
11440
  });
11416
- up.on('up:layer:location:changed', (event) => {
11417
- updateLayerIfLocationChanged(event.layer);
11441
+ up.on('up:layer:location:changed up:layer:opened up:layer:dismissed up:layer:accepted', () => {
11442
+ updateNavsAround(document.body, { newLinks: false, anyLocationChanged: true });
11418
11443
  });
11419
- up.on('up:framework:reset', reset);
11420
11444
  return {
11421
11445
  config,
11422
11446
  preview: namedPreviewFns.put,
@@ -11586,7 +11610,7 @@ up.radio = (function () {
11586
11610
  /******/
11587
11611
  /************************************************************************/
11588
11612
  var __webpack_exports__ = {};
11589
- // This entry need to be wrapped in an IIFE because it need to be isolated against other modules in the chunk.
11613
+ // This entry needs to be wrapped in an IIFE because it needs to be isolated against other modules in the chunk.
11590
11614
  (() => {
11591
11615
  __webpack_require__(1);
11592
11616
  __webpack_require__(2);