unpoly-rails 3.11.0 → 3.12.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -5,7 +5,7 @@
5
5
  /***/ (() => {
6
6
 
7
7
  window.up = {
8
- version: '3.11.0'
8
+ version: '3.12.0'
9
9
  };
10
10
 
11
11
 
@@ -394,6 +394,9 @@ up.util = (function () {
394
394
  return setTimeout(callback, millis);
395
395
  }
396
396
  function queueTask(task) {
397
+ setTimeout(task);
398
+ }
399
+ function queueFastTask(task) {
397
400
  const channel = new MessageChannel();
398
401
  channel.port1.onmessage = () => task();
399
402
  channel.port2.postMessage(0);
@@ -775,7 +778,7 @@ up.util = (function () {
775
778
  function scanFunctions(...values) {
776
779
  return values.flat().filter(isFunction);
777
780
  }
778
- function cleaner() {
781
+ function cleaner(order = 'lifo') {
779
782
  let fns = [];
780
783
  let track = function (values, transform) {
781
784
  values = scanFunctions(...values).map(transform);
@@ -788,9 +791,9 @@ up.util = (function () {
788
791
  track(values, up.error.guardFn);
789
792
  };
790
793
  api.clean = function (...args) {
791
- let { length } = fns;
792
- for (let i = length - 1; i >= 0; i--)
793
- fns[i](...args);
794
+ if (order === 'lifo')
795
+ fns.reverse();
796
+ fns.forEach((fn) => fn(...args));
794
797
  fns = [];
795
798
  };
796
799
  return api;
@@ -966,6 +969,7 @@ up.util = (function () {
966
969
  isBasicObjectProperty,
967
970
  isCrossOrigin,
968
971
  task: queueTask,
972
+ fastTask: queueFastTask,
969
973
  isEqual,
970
974
  getSimpleTokens,
971
975
  getComplexTokens,
@@ -1031,6 +1035,9 @@ up.error = (function () {
1031
1035
  reportError(error);
1032
1036
  }
1033
1037
  }
1038
+ function guardPromise(promise) {
1039
+ return promise.catch(reportError);
1040
+ }
1034
1041
  function guardFn(fn) {
1035
1042
  return (...args) => guard(() => fn(...args));
1036
1043
  }
@@ -1040,6 +1047,7 @@ up.error = (function () {
1040
1047
  muteUncriticalRejection,
1041
1048
  muteUncriticalSync,
1042
1049
  guard,
1050
+ guardPromise,
1043
1051
  guardFn,
1044
1052
  };
1045
1053
  })();
@@ -1353,6 +1361,10 @@ up.element = (function () {
1353
1361
  return `.${klass}`;
1354
1362
  }
1355
1363
  function createBrokenDocumentFromHTML(html) {
1364
+ const firstTag = firstTagNameInHTML(html);
1365
+ if (['TR', 'TD', 'TH', 'THEAD', 'TBODY'].includes(firstTag)) {
1366
+ html = `<table>${html}</table>`;
1367
+ }
1356
1368
  return new DOMParser().parseFromString(html, 'text/html');
1357
1369
  }
1358
1370
  function revivedClone(element) {
@@ -1641,6 +1653,14 @@ up.element = (function () {
1641
1653
  return [element.parentElement, 'beforeend'];
1642
1654
  }
1643
1655
  }
1656
+ const FIRST_TAG_NAME_PATTERN = /^\s*(<!--[^-]*.*?-->\s*)*<([a-z-!]+)\b/i;
1657
+ function firstTagNameInHTML(html) {
1658
+ return FIRST_TAG_NAME_PATTERN.exec(html)?.[2].toUpperCase();
1659
+ }
1660
+ function isFullDocumentHTML(html) {
1661
+ let firstTag = firstTagNameInHTML(html);
1662
+ return firstTag === 'HTML' || firstTag === '!DOCTYPE';
1663
+ }
1644
1664
  return {
1645
1665
  subtree,
1646
1666
  subtreeFirst,
@@ -1709,6 +1729,8 @@ up.element = (function () {
1709
1729
  matchSelectorMap,
1710
1730
  elementLikeMatches,
1711
1731
  documentPosition,
1732
+ firstTagNameInHTML,
1733
+ isFullDocumentHTML,
1712
1734
  };
1713
1735
  })();
1714
1736
 
@@ -1917,7 +1939,7 @@ up.OptionsParser = class OptionsParser {
1917
1939
  this._fail = parserOptions.fail;
1918
1940
  this._closest = parserOptions.closest;
1919
1941
  this._attrPrefix = parserOptions.attrPrefix || 'up-';
1920
- this._defaults = parserOptions.defaults || {};
1942
+ this._defaults = parserOptions.defaults ?? {};
1921
1943
  }
1922
1944
  string(key, keyOptions) {
1923
1945
  this.parse(e.attr, key, keyOptions);
@@ -1947,7 +1969,9 @@ up.OptionsParser = class OptionsParser {
1947
1969
  for (let attrName of attrNames) {
1948
1970
  value ??= this._parseFromAttr(attrValueFn, this._element, attrName);
1949
1971
  }
1950
- value ??= keyOptions.default ?? this._defaults[key];
1972
+ if (this._defaults !== false) {
1973
+ value ??= keyOptions.default ?? this._defaults[key];
1974
+ }
1951
1975
  if (u.isDefined(value)) {
1952
1976
  let normalizeFn = keyOptions.normalize;
1953
1977
  if (normalizeFn) {
@@ -2045,6 +2069,14 @@ up.Rect = class Rect extends up.Record {
2045
2069
  static fromElement(element) {
2046
2070
  return new (this)(element.getBoundingClientRect());
2047
2071
  }
2072
+ static fromElementAsFixed(element) {
2073
+ let fixedClone = element.cloneNode(true);
2074
+ element.after(fixedClone);
2075
+ fixedClone.style.position = 'fixed';
2076
+ let rect = this.fromElement(fixedClone);
2077
+ fixedClone.remove();
2078
+ return rect;
2079
+ }
2048
2080
  };
2049
2081
 
2050
2082
 
@@ -2119,6 +2151,11 @@ up.Change = class Change {
2119
2151
  deriveFailOptions() {
2120
2152
  return up.RenderOptions.deriveFailOptions(this.options);
2121
2153
  }
2154
+ ensureLayerAlive(layer = this.layer) {
2155
+ if (!layer.isAlive()) {
2156
+ throw new up.Aborted("Layer is already " + layer.state);
2157
+ }
2158
+ }
2122
2159
  };
2123
2160
 
2124
2161
 
@@ -2139,18 +2176,18 @@ up.Change.Addition = class Addition extends up.Change {
2139
2176
  handleLayerChangeRequests() {
2140
2177
  if (this.layer.isOverlay()) {
2141
2178
  this._tryAcceptLayerFromServer();
2142
- this.abortWhenLayerClosed();
2179
+ this.ensureLayerAlive();
2143
2180
  this.layer.tryAcceptForLocation(this._responseOptions());
2144
- this.abortWhenLayerClosed();
2181
+ this.ensureLayerAlive();
2145
2182
  this._tryDismissLayerFromServer();
2146
- this.abortWhenLayerClosed();
2183
+ this.ensureLayerAlive();
2147
2184
  this.layer.tryDismissForLocation(this._responseOptions());
2148
- this.abortWhenLayerClosed();
2185
+ this.ensureLayerAlive();
2149
2186
  }
2150
2187
  this.layer.asCurrent(() => {
2151
2188
  for (let eventPlan of this._eventPlans) {
2152
2189
  up.emit({ ...eventPlan, ...this._responseOptions() });
2153
- this.abortWhenLayerClosed();
2190
+ this.ensureLayerAlive();
2154
2191
  }
2155
2192
  });
2156
2193
  }
@@ -2164,11 +2201,6 @@ up.Change.Addition = class Addition extends up.Change {
2164
2201
  this.layer.dismiss(this._dismissLayer, this._responseOptions());
2165
2202
  }
2166
2203
  }
2167
- abortWhenLayerClosed(layer = this.layer) {
2168
- if (layer.isClosed()) {
2169
- throw new up.Aborted('Layer was closed');
2170
- }
2171
- }
2172
2204
  _setSource({ oldElement, newElement, source }) {
2173
2205
  if (source === 'keep') {
2174
2206
  source = (oldElement && up.fragment.source(oldElement));
@@ -2282,16 +2314,16 @@ up.RenderJob = (_a = class RenderJob {
2282
2314
  }
2283
2315
  }
2284
2316
  }
2285
- _handleAbortOption(request) {
2317
+ _handleAbortOption({ bindFragments, bindLayer, origin, layer, request }) {
2286
2318
  let { abort } = this.renderOptions;
2287
- if (!abort || !up.network.isBusy())
2319
+ if (!abort)
2288
2320
  return;
2289
- let { bindFragments, bindLayer, origin, layer } = this._getChange().getPreflightProps();
2290
2321
  let abortOptions = {
2291
2322
  except: request,
2292
2323
  logOnce: ['up.render()', 'Change with { abort } option will abort other requests'],
2293
2324
  newLayer: (layer === 'new'),
2294
2325
  origin,
2326
+ jid: this.renderOptions.jid,
2295
2327
  };
2296
2328
  if (abort === 'target') {
2297
2329
  up.fragment.abort(bindFragments, { ...abortOptions });
@@ -2417,7 +2449,7 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2417
2449
  else {
2418
2450
  this._content = this.responseDoc.select(this.target);
2419
2451
  }
2420
- if (!this._content || this._baseLayer.isClosed()) {
2452
+ if (!this._content || !this._baseLayer.isAlive()) {
2421
2453
  throw new up.CannotMatch();
2422
2454
  }
2423
2455
  }
@@ -2438,7 +2470,7 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2438
2470
  this.layer.setContent(this._content);
2439
2471
  this.responseDoc.finalizeElement(this._content);
2440
2472
  this.setReloadAttrs({ newElement: this._content, source: this.options.source });
2441
- up.hello(this.layer.element, { ...this.options, layer: this.layer, dataRoot: this._content });
2473
+ this._helloPromise = up.hello(this.layer.element, { ...this.options, layer: this.layer, dataRoot: this._content });
2442
2474
  this._newOverlayResult = new up.RenderResult({
2443
2475
  layer: this.layer,
2444
2476
  fragments: [this._content],
@@ -2447,9 +2479,9 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2447
2479
  });
2448
2480
  this._handleScroll();
2449
2481
  this._newOverlayResult.finished = this._finish();
2450
- this.layer.opening = false;
2482
+ this.layer.state = 'opened';
2451
2483
  this._emitOpenedEvent();
2452
- this.abortWhenLayerClosed();
2484
+ this.ensureLayerAlive();
2453
2485
  }
2454
2486
  _renderOtherLayers() {
2455
2487
  if (this._otherLayersResult)
@@ -2462,16 +2494,16 @@ up.Change.OpenLayer = class OpenLayer extends up.Change.Addition {
2462
2494
  }
2463
2495
  async _finish() {
2464
2496
  await this.layer.startOpenAnimation();
2465
- this.abortWhenLayerClosed();
2497
+ this.ensureLayerAlive();
2466
2498
  this._handleFocus();
2499
+ await this._helloPromise;
2467
2500
  return this._newOverlayResult;
2468
2501
  }
2469
2502
  _buildLayer() {
2470
- const buildOptions = { ...this.options, opening: true };
2471
2503
  const beforeNew = (optionsWithLayerDefaults) => {
2472
2504
  return this.options = up.RenderOptions.finalize(optionsWithLayerDefaults);
2473
2505
  };
2474
- return up.layer.build(buildOptions, beforeNew);
2506
+ return up.layer.build(this.options, beforeNew);
2475
2507
  }
2476
2508
  _handleHistory() {
2477
2509
  if (this.layer.history === 'auto') {
@@ -2595,7 +2627,10 @@ up.Change.UpdateLayer = (_a = class UpdateLayer extends up.Change.Addition {
2595
2627
  this.layer.peel({ history: !this._hasHistory() });
2596
2628
  }
2597
2629
  if (this.options.abort !== false) {
2598
- up.fragment.abort(this._getFragments(), { reason: 'Fragment is being replaced' });
2630
+ up.fragment.abort(this._getFragments(), {
2631
+ reason: 'Fragment is being replaced',
2632
+ jid: this.options.jid,
2633
+ });
2599
2634
  }
2600
2635
  Object.assign(this.layer.context, this._context);
2601
2636
  if (this._hasHistory()) {
@@ -2699,6 +2734,7 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2699
2734
  this._steps = u.copy(u.assert(options.steps));
2700
2735
  this._passRenderOptions = u.assert(options.passRenderOptions);
2701
2736
  this._noneOptions = options.noneOptions || {};
2737
+ this._finishDelays = [];
2702
2738
  }
2703
2739
  execute(responseDoc) {
2704
2740
  this.responseDoc = responseDoc;
@@ -2715,15 +2751,20 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2715
2751
  }
2716
2752
  else {
2717
2753
  this._steps.reverse();
2718
- const motionEndPromises = this._steps.map((step) => this._executeStep(step));
2719
- this.renderResult.finished = this._finish(motionEndPromises);
2754
+ up.fragment.mutate(() => {
2755
+ const motionEndPromises = this._steps.map((step) => this._executeStep(step));
2756
+ this.renderResult.finished = this._finish(motionEndPromises);
2757
+ });
2720
2758
  }
2721
2759
  return this.renderResult;
2722
2760
  }
2723
- async _finish(motionEndPromises) {
2724
- await Promise.all(motionEndPromises);
2761
+ _delayFinish(delay) {
2762
+ this._finishDelays.push(delay);
2763
+ }
2764
+ async _finish() {
2765
+ await Promise.all(this._finishDelays);
2725
2766
  for (let step of this._steps) {
2726
- this.abortWhenLayerClosed(step.layer);
2767
+ this.ensureLayerAlive(step.layer);
2727
2768
  }
2728
2769
  return this.renderResult;
2729
2770
  }
@@ -2738,7 +2779,8 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2738
2779
  if (keepPlan) {
2739
2780
  this._handleFocus(step.oldElement, step);
2740
2781
  this._handleScroll(step.oldElement, step);
2741
- return Promise.resolve();
2782
+ up.fragment.emitKept(keepPlan);
2783
+ break;
2742
2784
  }
2743
2785
  else {
2744
2786
  this._preserveDescendantKeepables(step);
@@ -2759,13 +2801,15 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2759
2801
  afterDetach() {
2760
2802
  e.cleanJQuery();
2761
2803
  up.fragment.emitDestroyed(step.oldElement, { parent, log: false });
2804
+ step.afterDetach?.();
2762
2805
  },
2763
2806
  scrollNew: () => {
2764
2807
  this._handleFocus(step.newElement, step);
2765
2808
  this._handleScroll(step.newElement, step);
2766
2809
  }
2767
2810
  };
2768
- return up.morph(step.oldElement, step.newElement, step.transition, morphOptions);
2811
+ this._delayFinish(up.morph(step.oldElement, step.newElement, step.transition, morphOptions));
2812
+ break;
2769
2813
  }
2770
2814
  }
2771
2815
  case 'content': {
@@ -2776,12 +2820,14 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2776
2820
  placement: 'swap',
2777
2821
  oldElement: oldWrapper,
2778
2822
  newElement: newWrapper,
2779
- focus: false
2823
+ focus: false,
2824
+ afterDetach: () => {
2825
+ e.unwrap(newWrapper);
2826
+ this._handleFocus(step.oldElement, step);
2827
+ },
2780
2828
  };
2781
- return this._executeStep(wrapperStep).then(() => {
2782
- e.unwrap(newWrapper);
2783
- this._handleFocus(step.oldElement, step);
2784
- });
2829
+ this._executeStep(wrapperStep);
2830
+ break;
2785
2831
  }
2786
2832
  case 'before':
2787
2833
  case 'after': {
@@ -2791,7 +2837,8 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2791
2837
  this._welcomeElement(wrapper, step);
2792
2838
  this._handleFocus(wrapper, step);
2793
2839
  this._handleScroll(wrapper, step);
2794
- return up.animate(wrapper, step.animation, step).then(() => e.unwrap(wrapper));
2840
+ this._delayFinish(up.animate(wrapper, step.animation, step).then(() => e.unwrap(wrapper)));
2841
+ break;
2795
2842
  }
2796
2843
  default: {
2797
2844
  up.fail('Unknown placement: %o', step.placement);
@@ -2801,7 +2848,7 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2801
2848
  _welcomeElement(element, step) {
2802
2849
  this.responseDoc.finalizeElement(element);
2803
2850
  this.setReloadAttrs(step);
2804
- up.hello(element, step);
2851
+ this._delayFinish(up.hello(element, step));
2805
2852
  this._addToResult(element);
2806
2853
  }
2807
2854
  _preserveDescendantKeepables(step) {
@@ -2842,6 +2889,7 @@ up.Change.UpdateSteps = class UpdateSteps extends up.Change.Addition {
2842
2889
  _finalizeDescendantKeepables(step) {
2843
2890
  for (let keepPlan of step.descendantKeepPlans) {
2844
2891
  keepPlan.oldElement.classList.remove('up-keeping');
2892
+ up.fragment.emitKept(keepPlan);
2845
2893
  }
2846
2894
  }
2847
2895
  _willChangeBody() {
@@ -2875,7 +2923,7 @@ up.Change.CloseLayer = class CloseLayer extends up.Change {
2875
2923
  constructor(options) {
2876
2924
  super(options);
2877
2925
  this._verb = options.verb;
2878
- this._layer = up.layer.get(options);
2926
+ this.layer = up.layer.get(options);
2879
2927
  this._origin = options.origin;
2880
2928
  this._value = options.value;
2881
2929
  this._preventable = options.preventable ?? true;
@@ -2883,61 +2931,62 @@ up.Change.CloseLayer = class CloseLayer extends up.Change {
2883
2931
  this._history = options.history ?? true;
2884
2932
  }
2885
2933
  execute() {
2886
- if (!this._layer.isOpen())
2887
- return;
2934
+ this.ensureLayerAlive();
2888
2935
  up.browser.assertConfirmed(this.options);
2889
2936
  if (this._emitCloseEvent().defaultPrevented && this._preventable) {
2890
2937
  throw new up.Aborted('Close event was prevented');
2891
2938
  }
2892
2939
  this._emitClosingEvent();
2893
- up.fragment.abort({ reason: 'Layer is closing', layer: this._layer });
2894
- const { parent } = this._layer;
2895
- this._layer.peel();
2896
- this._layer.stack.remove(this._layer);
2940
+ up.fragment.abort({ reason: 'Layer is closing', layer: this.layer });
2941
+ this.layer.state = 'closing';
2942
+ const { parent } = this.layer;
2943
+ this.layer.peel();
2944
+ this._handleFocus(parent);
2945
+ this.layer.teardownHandlers();
2946
+ this.layer.destroyElements(this.options);
2947
+ this.layer.stack.remove(this.layer);
2897
2948
  if (this._history) {
2898
2949
  parent.restoreHistory();
2899
2950
  }
2900
- this._handleFocus(parent);
2901
- this._layer.teardownHandlers();
2902
- this._layer.destroyElements(this.options);
2951
+ this.layer.state = 'closed';
2903
2952
  this._emitClosedEvent(parent);
2904
2953
  }
2905
2954
  _emitCloseEvent() {
2906
- let event = this._layer.emit(this._buildEvent(`up:layer:${this._verb}`), {
2907
- callback: this._layer.callback(`on${u.upperCaseFirst(this._verb)}`),
2908
- log: [`Will ${this._verb} ${this._layer} with value %o`, this._value]
2955
+ let event = this.layer.emit(this._buildEvent(`up:layer:${this._verb}`), {
2956
+ callback: this.layer.callback(`on${u.upperCaseFirst(this._verb)}`),
2957
+ log: [`Will ${this._verb} ${this.layer} with value %o`, this._value]
2909
2958
  });
2910
2959
  this._value = event.value;
2911
2960
  return event;
2912
2961
  }
2913
2962
  _emitClosingEvent() {
2914
2963
  let event = this._buildEvent(`up:layer:${this._verb}ing`);
2915
- this._layer.emit(event, { log: false });
2964
+ this.layer.emit(event, { log: false });
2916
2965
  }
2917
2966
  _emitClosedEvent(formerParent) {
2918
2967
  const verbPast = `${this._verb}ed`;
2919
2968
  const verbPastUpperCaseFirst = u.upperCaseFirst(verbPast);
2920
- return this._layer.emit(this._buildEvent(`up:layer:${verbPast}`), {
2969
+ return this.layer.emit(this._buildEvent(`up:layer:${verbPast}`), {
2921
2970
  baseLayer: formerParent,
2922
- callback: this._layer.callback(`on${verbPastUpperCaseFirst}`),
2971
+ callback: this.layer.callback(`on${verbPastUpperCaseFirst}`),
2923
2972
  ensureBubbles: true,
2924
- log: [`${verbPastUpperCaseFirst} ${this._layer} with value %o`, this._value]
2973
+ log: [`${verbPastUpperCaseFirst} ${this.layer} with value %o`, this._value]
2925
2974
  });
2926
2975
  }
2927
2976
  _buildEvent(name) {
2928
2977
  return up.event.build(name, {
2929
- layer: this._layer,
2978
+ layer: this.layer,
2930
2979
  value: this._value,
2931
2980
  origin: this._origin,
2932
2981
  response: this._response,
2933
2982
  });
2934
2983
  }
2935
2984
  _handleFocus(formerParent) {
2936
- let hadFocus = this._layer.hasFocus();
2937
- this._layer.overlayFocus.teardown();
2985
+ let hadFocus = this.layer.hasFocus();
2986
+ this.layer.overlayFocus.teardown();
2938
2987
  formerParent.overlayFocus?.moveToFront();
2939
2988
  if (hadFocus) {
2940
- let newFocusElement = this._layer.origin || formerParent.element;
2989
+ let newFocusElement = this.layer.origin || formerParent.element;
2941
2990
  up.focus(newFocusElement, { preventScroll: true });
2942
2991
  }
2943
2992
  }
@@ -2958,11 +3007,12 @@ up.Change.FromURL = (_a = class FromURL extends up.Change {
2958
3007
  up.network.loadPage(this.options);
2959
3008
  return u.unresolvablePromise();
2960
3009
  }
2961
- let request = this.request = up.request(this._getRequestAttrs());
3010
+ let requestAttrs = this._getRequestAttrs();
3011
+ let request = this.request = up.request(requestAttrs);
2962
3012
  this.options.onRequestKnown?.(request);
2963
3013
  if (this.options.preload)
2964
3014
  return request;
2965
- this.options.handleAbort?.(request);
3015
+ this.options.handleAbort({ request, ...requestAttrs });
2966
3016
  request.runPreviews(this.options);
2967
3017
  return await u.always(request, (responseOrError) => this._onRequestSettled(responseOrError));
2968
3018
  }
@@ -3044,6 +3094,7 @@ up.Change.FromResponse = (_a = class FromResponse extends up.Change {
3044
3094
  log: ['Loaded fragment from %s', this._response.description],
3045
3095
  skip: () => this._skip()
3046
3096
  });
3097
+ this._assetRenderableResponse();
3047
3098
  }
3048
3099
  let fail = u.evalOption(this.options.fail, this._response) ?? !this._response.ok;
3049
3100
  if (fail) {
@@ -3051,6 +3102,11 @@ up.Change.FromResponse = (_a = class FromResponse extends up.Change {
3051
3102
  }
3052
3103
  return this._updateContentFromResponse(this.options);
3053
3104
  }
3105
+ _assetRenderableResponse() {
3106
+ if (!up.fragment.config.renderableResponse(this._response)) {
3107
+ throw new up.CannotParse(['Cannot render response with content-type "%s" (configure with up.fragment.config.renderableResponse)', this._response.contentType]);
3108
+ }
3109
+ }
3054
3110
  _skip() {
3055
3111
  up.puts('up.render()', 'Skipping ' + this._response.description);
3056
3112
  this.options.target = ':none';
@@ -3200,7 +3256,7 @@ up.Change.FromContent = (_a = class FromContent extends up.Change {
3200
3256
  return plans;
3201
3257
  }
3202
3258
  _isRenderableLayer(layer) {
3203
- return (layer === 'new') || layer.isOpen();
3259
+ return (layer === 'new') || layer.isAlive();
3204
3260
  }
3205
3261
  _filterLayers() {
3206
3262
  this._layers = u.filter(this._layers, this._isRenderableLayer);
@@ -3238,7 +3294,7 @@ up.Change.FromContent = (_a = class FromContent extends up.Change {
3238
3294
  if (assets) {
3239
3295
  up.script.assertAssetsOK(assets, plan.options);
3240
3296
  }
3241
- this.options.handleAbort?.(null);
3297
+ this.options.handleAbort(this.getPreflightProps());
3242
3298
  }
3243
3299
  _getResponseDoc() {
3244
3300
  if (this._preflight)
@@ -3345,6 +3401,7 @@ up.CompilerPass = class CompilerPass {
3345
3401
  this._data = data;
3346
3402
  this._dataRoot = dataRoot || root;
3347
3403
  this._dataMap = dataMap;
3404
+ this._compilePromises = [];
3348
3405
  meta ||= {};
3349
3406
  meta.layer = layer;
3350
3407
  this._meta = meta;
@@ -3356,6 +3413,7 @@ up.CompilerPass = class CompilerPass {
3356
3413
  this._runCompiler(compiler);
3357
3414
  }
3358
3415
  });
3416
+ return Promise.all(this._compilePromises);
3359
3417
  }
3360
3418
  setCompileData() {
3361
3419
  if (this._data) {
@@ -3385,7 +3443,7 @@ up.CompilerPass = class CompilerPass {
3385
3443
  this._compileOneElement(compiler, match);
3386
3444
  }
3387
3445
  }
3388
- return up.migrate.postCompile?.(matches, compiler);
3446
+ up.migrate.postCompile?.(matches, compiler);
3389
3447
  }
3390
3448
  _compileOneElement(compiler, element) {
3391
3449
  const compileArgs = [element];
@@ -3393,8 +3451,8 @@ up.CompilerPass = class CompilerPass {
3393
3451
  const data = up.script.data(element);
3394
3452
  compileArgs.push(data, this._meta);
3395
3453
  }
3396
- const result = this._applyCompilerFunction(compiler, element, compileArgs);
3397
- up.destructor(element, result);
3454
+ let onDestructor = (destructor) => up.destructor(element, destructor);
3455
+ this._applyCompilerFunction(compiler, element, compileArgs, onDestructor);
3398
3456
  }
3399
3457
  _compileBatch(compiler, elements) {
3400
3458
  const compileArgs = [elements];
@@ -3402,26 +3460,38 @@ up.CompilerPass = class CompilerPass {
3402
3460
  const dataList = u.map(elements, up.script.data);
3403
3461
  compileArgs.push(dataList, this._meta);
3404
3462
  }
3405
- const result = this._applyCompilerFunction(compiler, elements, compileArgs);
3406
- if (result) {
3407
- up.fail('Compilers with { batch: true } cannot return destructors');
3463
+ let onDestructor = () => this._reportBatchCompilerWithDestructor(compiler);
3464
+ this._applyCompilerFunction(compiler, elements, compileArgs, onDestructor);
3465
+ }
3466
+ async _applyCompilerFunction(compiler, elementOrElements, compileArgs, onDestructor) {
3467
+ let maybeDestructor = up.error.guard(() => compiler.apply(elementOrElements, compileArgs));
3468
+ if (u.isPromise(maybeDestructor)) {
3469
+ let guardedPromise = up.error.guardPromise(maybeDestructor);
3470
+ this._compilePromises.push(guardedPromise);
3471
+ maybeDestructor = await guardedPromise;
3408
3472
  }
3473
+ if (maybeDestructor)
3474
+ onDestructor(maybeDestructor);
3409
3475
  }
3410
- _applyCompilerFunction(compiler, elementOrElements, compileArgs) {
3411
- return up.error.guard(() => compiler.apply(elementOrElements, compileArgs));
3476
+ _reportBatchCompilerWithDestructor(compiler) {
3477
+ let error = new up.Error(['Batch compiler (%s) cannot return a destructor', compiler.selector]);
3478
+ reportError(error);
3412
3479
  }
3413
3480
  _select(selector) {
3414
3481
  return up.fragment.subtree(this._root, u.evalOption(selector), { layer: this._layer });
3415
3482
  }
3416
3483
  _selectOnce(compiler) {
3417
3484
  let matches = this._select(compiler.selector);
3418
- return u.filter(matches, (element) => {
3419
- let appliedCompilers = (element.upAppliedCompilers ||= new Set());
3420
- if (!appliedCompilers.has(compiler)) {
3421
- appliedCompilers.add(compiler);
3422
- return true;
3423
- }
3424
- });
3485
+ if (!compiler.rerun) {
3486
+ matches = u.filter(matches, (element) => {
3487
+ let appliedCompilers = (element.upAppliedCompilers ||= new Set());
3488
+ if (!appliedCompilers.has(compiler)) {
3489
+ appliedCompilers.add(compiler);
3490
+ return true;
3491
+ }
3492
+ });
3493
+ }
3494
+ return matches;
3425
3495
  }
3426
3496
  };
3427
3497
 
@@ -3842,19 +3912,24 @@ up.SelectorTracker = class SelectorTracker {
3842
3912
  this._addCallback = addCallback;
3843
3913
  this._layer = options.layer || 'any';
3844
3914
  this._filter = options.filter || u.identity;
3845
- this._live = options.live ?? true;
3846
3915
  this._knownMatches = new Map();
3916
+ this._syncScheduled = false;
3847
3917
  }
3848
3918
  start() {
3849
- this._sync();
3919
+ this._scheduleSync();
3850
3920
  return u.sequence(this._trackFragments(), () => this._removeAllMatches());
3851
3921
  }
3852
3922
  _trackFragments() {
3853
- if (this._live) {
3854
- return up.on('up:fragment:inserted up:fragment:destroyed', () => this._sync());
3923
+ return up.on('up:fragment:inserted up:fragment:destroyed', () => this._scheduleSync());
3924
+ }
3925
+ _scheduleSync() {
3926
+ if (!this._syncScheduled) {
3927
+ this._syncScheduled = true;
3928
+ up.fragment.afterMutate(() => this._sync());
3855
3929
  }
3856
3930
  }
3857
3931
  _sync() {
3932
+ this._syncScheduled = false;
3858
3933
  let removeMap = new Map(this._knownMatches);
3859
3934
  this._knownMatches.clear();
3860
3935
  for (let newMatch of this._currentMatches) {
@@ -3899,7 +3974,7 @@ up.FieldWatcher = class FieldWatcher {
3899
3974
  this._processedValues = this._readFieldValues();
3900
3975
  this._currentTimer = null;
3901
3976
  this._callbackRunning = false;
3902
- return u.sequence(up.form.trackFields(this._root, (field) => this._watchField(field)), this._trackAbort(), this._trackReset(), () => this._abort());
3977
+ return u.sequence(this._trackFields(), this._trackAbort(), this._trackReset(), () => this._abort());
3903
3978
  }
3904
3979
  _ensureWatchable() {
3905
3980
  const fail = (message) => up.fail(message, this._logPrefix, this._root);
@@ -3913,6 +3988,14 @@ up.FieldWatcher = class FieldWatcher {
3913
3988
  fail('%s can only watch fields with a name (%o)');
3914
3989
  }
3915
3990
  }
3991
+ _trackFields() {
3992
+ if (up.form.isField(this._root)) {
3993
+ return this._watchField(this._root);
3994
+ }
3995
+ else {
3996
+ return up.form.trackFields(this._root, (field) => this._watchField(field));
3997
+ }
3998
+ }
3916
3999
  _trackAbort() {
3917
4000
  let guard = ({ target }) => target.contains(this._region);
3918
4001
  return up.on('up:fragment:aborted', { guard }, () => this._abort());
@@ -4558,35 +4641,47 @@ up.FragmentFocus = class FragmentFocus extends up.FragmentProcessor {
4558
4641
  /***/ (() => {
4559
4642
 
4560
4643
  const e = up.element;
4644
+ const u = up.util;
4561
4645
  up.FragmentPolling = class FragmentPolling {
4646
+ static forFragment(fragment) {
4647
+ return fragment.upPolling ||= new this(fragment);
4648
+ }
4562
4649
  constructor(fragment) {
4563
4650
  this._options = up.radio.pollOptions(fragment);
4564
4651
  this._fragment = fragment;
4565
4652
  up.destructor(fragment, this._onFragmentDestroyed.bind(this));
4566
- up.fragment.onAborted(fragment, this._onFragmentAborted.bind(this));
4567
- this._state = 'initialized';
4568
- this._abortable = true;
4653
+ this._state = 'stopped';
4654
+ this._forceIntent = null;
4655
+ this._reloadJID = u.uid();
4569
4656
  this._loading = false;
4570
4657
  this._satisfyInterval();
4571
4658
  }
4572
- static forFragment(fragment) {
4573
- return fragment.upPolling ||= new this(fragment);
4574
- }
4575
4659
  onPollAttributeObserved() {
4576
4660
  this._start();
4577
4661
  }
4662
+ _onFragmentAborted({ newLayer, jid }) {
4663
+ const isOurAbort = (jid === this._reloadJID);
4664
+ if (isOurAbort || newLayer)
4665
+ return;
4666
+ this._stop();
4667
+ }
4668
+ _onFragmentKept() {
4669
+ if (this._forceIntent !== 'stop') {
4670
+ this._start();
4671
+ }
4672
+ }
4578
4673
  _onFragmentDestroyed() {
4579
4674
  this._stop();
4580
4675
  }
4581
4676
  _start(options) {
4582
4677
  Object.assign(this._options, options);
4583
- if (this._state !== 'started') {
4678
+ if (this._state === 'stopped') {
4584
4679
  if (!up.fragment.isTargetable(this._fragment)) {
4585
4680
  up.warn('[up-poll]', 'Cannot poll untargetable fragment %o', this._fragment);
4586
4681
  return;
4587
4682
  }
4588
4683
  this._state = 'started';
4589
- this._ensureEventsBound();
4684
+ this._bindEvents();
4590
4685
  this._scheduleRemainingTime();
4591
4686
  }
4592
4687
  }
@@ -4594,28 +4689,31 @@ up.FragmentPolling = class FragmentPolling {
4594
4689
  if (this._state === 'started') {
4595
4690
  this._clearReloadTimer();
4596
4691
  this._state = 'stopped';
4597
- this._unbindEvents?.();
4598
4692
  }
4599
4693
  }
4600
4694
  forceStart(options) {
4601
4695
  Object.assign(this._options, options);
4602
- this.forceStarted = true;
4696
+ this._forceIntent = 'start';
4603
4697
  this._start();
4604
4698
  }
4605
4699
  forceStop() {
4700
+ this._forceIntent = 'stop';
4606
4701
  this._stop();
4607
- this.forceStarted = false;
4608
4702
  }
4609
- _ensureEventsBound() {
4610
- if (!this._unbindEvents) {
4611
- this._unbindEvents = up.on('visibilitychange up:layer:opened up:layer:dismissed up:layer:accepted', this._onVisibilityChange.bind(this));
4612
- }
4703
+ _bindEvents() {
4704
+ if (this._eventsBound)
4705
+ return;
4706
+ this._eventsBound = true;
4707
+ up.destructor(this._fragment, up.on('visibilitychange up:layer:opened up:layer:dismissed up:layer:accepted', this._onVisibilityChange.bind(this)));
4708
+ up.fragment.onAborted(this._fragment, this._onFragmentAborted.bind(this));
4709
+ up.fragment.onKept(this._fragment, this._onFragmentKept.bind(this));
4613
4710
  }
4614
4711
  _onVisibilityChange() {
4615
4712
  if (this._isFragmentVisible()) {
4616
4713
  this._scheduleRemainingTime();
4617
4714
  }
4618
4715
  else {
4716
+ this._clearReloadTimer();
4619
4717
  }
4620
4718
  }
4621
4719
  _isFragmentVisible() {
@@ -4627,17 +4725,17 @@ up.FragmentPolling = class FragmentPolling {
4627
4725
  this._reloadTimer = null;
4628
4726
  }
4629
4727
  _scheduleRemainingTime() {
4630
- if (!this._reloadTimer && !this._loading) {
4631
- this._clearReloadTimer();
4632
- this._reloadTimer = setTimeout(this._onTimerReached.bind(this), this._getRemainingDelay());
4633
- }
4728
+ if (this._reloadTimer || this._loading || this._state === 'stopped')
4729
+ return;
4730
+ this._clearReloadTimer();
4731
+ this._reloadTimer = setTimeout(this._onTimerReached.bind(this), this._getRemainingDelay());
4634
4732
  }
4635
4733
  _onTimerReached() {
4636
4734
  this._reloadTimer = null;
4637
4735
  this._tryReload();
4638
4736
  }
4639
4737
  _tryReload() {
4640
- if (this._state !== 'started') {
4738
+ if (this._state === 'stopped') {
4641
4739
  return;
4642
4740
  }
4643
4741
  if (!up.fragment.isAlive(this._fragment)) {
@@ -4666,24 +4764,10 @@ up.FragmentPolling = class FragmentPolling {
4666
4764
  }
4667
4765
  _reloadNow() {
4668
4766
  this._clearReloadTimer();
4669
- let oldAbortable = this._abortable;
4670
- try {
4671
- this._abortable = false;
4672
- this._loading = true;
4673
- up.reload(this._fragment, this._reloadOptions()).then(this._onReloadSuccess.bind(this), this._onReloadFailure.bind(this));
4674
- }
4675
- finally {
4676
- this._abortable = oldAbortable;
4677
- }
4678
- }
4679
- _reloadOptions() {
4767
+ this._loading = true;
4680
4768
  let guardEvent = up.event.build('up:fragment:poll', { log: ['Polling fragment', this._fragment] });
4681
- return { ...this._options, guardEvent };
4682
- }
4683
- _onFragmentAborted({ newLayer }) {
4684
- if (this._abortable && !newLayer) {
4685
- this._stop();
4686
- }
4769
+ let reloadOptions = { ...this._options, guardEvent, jid: this._reloadJID };
4770
+ up.reload(this._fragment, reloadOptions).then(this._onReloadSuccess.bind(this), this._onReloadFailure.bind(this));
4687
4771
  }
4688
4772
  _onReloadSuccess({ fragment }) {
4689
4773
  this._loading = false;
@@ -4696,8 +4780,7 @@ up.FragmentPolling = class FragmentPolling {
4696
4780
  }
4697
4781
  }
4698
4782
  _onFragmentSwapped(newFragment) {
4699
- this._stop();
4700
- if (this.forceStarted && up.fragment.matches(this._fragment, newFragment)) {
4783
+ if (this._forceIntent === 'start' && up.fragment.matches(this._fragment, newFragment)) {
4701
4784
  this.constructor.forFragment(newFragment).forceStart(this._options);
4702
4785
  }
4703
4786
  }
@@ -4843,11 +4926,8 @@ up.Layer = class Layer extends up.Record {
4843
4926
  isOverlay() {
4844
4927
  return this.stack.isOverlay(this);
4845
4928
  }
4846
- isOpen() {
4847
- return this.stack.isOpen(this);
4848
- }
4849
- isClosed() {
4850
- return this.stack.isClosed(this);
4929
+ isAlive() {
4930
+ throw new up.NotImplemented();
4851
4931
  }
4852
4932
  get parent() {
4853
4933
  return this.stack.parentOf(this);
@@ -5019,7 +5099,7 @@ up.Layer = class Layer extends up.Record {
5019
5099
  if (newLocation !== liveLocation && this.showsLiveHistory() && push) {
5020
5100
  up.history.push(newLocation);
5021
5101
  }
5022
- if (newLocation !== prevSavedLocation && !this.opening) {
5102
+ if (newLocation !== prevSavedLocation && this.state !== 'opening') {
5023
5103
  this.emit('up:layer:location:changed', { location: newLocation, previousLocation: prevSavedLocation, log: false });
5024
5104
  }
5025
5105
  }
@@ -5069,11 +5149,11 @@ up.Layer.Overlay = (_a = class Overlay extends up.Layer {
5069
5149
  'dismissEvent',
5070
5150
  'acceptLocation',
5071
5151
  'dismissLocation',
5072
- 'opening'
5073
5152
  ];
5074
5153
  }
5075
5154
  constructor(options) {
5076
5155
  super(options);
5156
+ this.state = 'opening';
5077
5157
  if (this.dismissable === true) {
5078
5158
  this.dismissable = ['button', 'key', 'outside'];
5079
5159
  }
@@ -5263,7 +5343,6 @@ up.Layer.Overlay = (_a = class Overlay extends up.Layer {
5263
5343
  super.teardownHandlers();
5264
5344
  this._unbindParentClicked?.();
5265
5345
  this._unbindEscapePressed?.();
5266
- this.overlayFocus.teardown();
5267
5346
  }
5268
5347
  destroyElements(options) {
5269
5348
  const animation = () => this.startCloseAnimation(options);
@@ -5302,9 +5381,12 @@ up.Layer.Overlay = (_a = class Overlay extends up.Layer {
5302
5381
  boxAnimation,
5303
5382
  backdropAnimation,
5304
5383
  easing: options.easing || this.closeEasing,
5305
- duration: options.duration || this.closeDuration
5384
+ duration: options.duration || this.closeDuration,
5306
5385
  });
5307
5386
  }
5387
+ isAlive() {
5388
+ return ['opening', 'opened'].includes(this.state);
5389
+ }
5308
5390
  accept(value = null, options = {}) {
5309
5391
  return this._executeCloseChange('accept', value, options);
5310
5392
  }
@@ -5372,7 +5454,7 @@ up.Layer.OverlayWithTether = class OverlayWithTether extends up.Layer.Overlay {
5372
5454
  this._tether.stop();
5373
5455
  }
5374
5456
  sync() {
5375
- if (this.isOpen()) {
5457
+ if (this.isAlive()) {
5376
5458
  if (this.isDetached() || this._tether.isDetached()) {
5377
5459
  this.dismiss(':detached', {
5378
5460
  animation: false,
@@ -5409,7 +5491,7 @@ up.Layer.OverlayWithViewport = class OverlayWithViewport extends up.Layer.Overla
5409
5491
  up.viewport.bodyShifter.lowerStack();
5410
5492
  }
5411
5493
  sync() {
5412
- if (this.isDetached() && this.isOpen()) {
5494
+ if (this.isDetached() && this.isAlive()) {
5413
5495
  this.constructor.getParentElement().appendChild(this.element);
5414
5496
  }
5415
5497
  }
@@ -5445,6 +5527,9 @@ up.Layer.Root = (_a = class Root extends up.Layer {
5445
5527
  sync() {
5446
5528
  this.setupHandlers();
5447
5529
  }
5530
+ isAlive() {
5531
+ return true;
5532
+ }
5448
5533
  accept() {
5449
5534
  this._cannotCloseRoot();
5450
5535
  }
@@ -5647,12 +5732,6 @@ up.LayerStack = class LayerStack {
5647
5732
  this._currentOverrides = [];
5648
5733
  this.root.reset();
5649
5734
  }
5650
- isOpen(layer) {
5651
- return u.contains(this.layers, layer);
5652
- }
5653
- isClosed(layer) {
5654
- return !this.isOpen(layer);
5655
- }
5656
5735
  parentOf(layer) {
5657
5736
  return this.layers[layer.index - 1];
5658
5737
  }
@@ -6506,6 +6585,7 @@ up.RenderOptions = (function () {
6506
6585
  'fallback',
6507
6586
  'abort',
6508
6587
  'abortable',
6588
+ 'handleAbort',
6509
6589
  'confirm',
6510
6590
  'feedback',
6511
6591
  'disable',
@@ -7429,6 +7509,7 @@ up.Request.XHRRenderer = (_a = class XHRRenderer {
7429
7509
  /***/ (() => {
7430
7510
 
7431
7511
  const u = up.util;
7512
+ const HTML_CONTENT_TYPE = /^(text\/html|application\/xhtml\+xml) *(;|$)/i;
7432
7513
  up.Response = class Response extends up.Record {
7433
7514
  keys() {
7434
7515
  return [
@@ -7474,6 +7555,9 @@ up.Response = class Response extends up.Record {
7474
7555
  get contentType() {
7475
7556
  return this.header('Content-Type');
7476
7557
  }
7558
+ isHTML() {
7559
+ return HTML_CONTENT_TYPE.test(this.contentType);
7560
+ }
7477
7561
  get cspInfo() {
7478
7562
  let policy = this.header('Content-Security-Policy') || this.header('Content-Security-Policy-Report-Only');
7479
7563
  return up.protocol.cspInfoFromHeader(policy);
@@ -7525,7 +7609,6 @@ up.Response = class Response extends up.Record {
7525
7609
  var _a;
7526
7610
  const u = up.util;
7527
7611
  const e = up.element;
7528
- const FULL_DOCUMENT_PATTERN = /^\s*(<!--[^-]*.*?-->\s*)*<(html|!DOCTYPE)\b/i;
7529
7612
  up.ResponseDoc = (_a = class ResponseDoc {
7530
7613
  constructor({ document, fragment, content, target, origin, data, cspInfo, match }) {
7531
7614
  if (document) {
@@ -7552,7 +7635,7 @@ up.ResponseDoc = (_a = class ResponseDoc {
7552
7635
  this._isFullDocument = true;
7553
7636
  }
7554
7637
  else if (u.isString(value)) {
7555
- this._isFullDocument = FULL_DOCUMENT_PATTERN.test(value);
7638
+ this._isFullDocument = e.isFullDocumentHTML(value);
7556
7639
  let htmlParser = (html) => [e.createBrokenDocumentFromHTML(html)];
7557
7640
  let nodes = up.fragment.provideNodes(value, { origin, data, htmlParser });
7558
7641
  if (nodes[0] instanceof Document) {
@@ -7775,21 +7858,27 @@ up.RevealMotion = class RevealMotion {
7775
7858
  return up.Rect.fromElement(this._viewport);
7776
7859
  }
7777
7860
  }
7778
- _selectObstructions(selector) {
7861
+ _computeObstructionRects(selector) {
7779
7862
  let elements = up.fragment.all(selector, { layer: this._obstructionsLayer });
7780
- return u.filter(elements, e.isVisible);
7863
+ elements = u.filter(elements, e.isVisible);
7864
+ return u.map(elements, (element) => {
7865
+ if (e.style(element, 'position') === 'sticky') {
7866
+ return up.Rect.fromElementAsFixed(element);
7867
+ }
7868
+ else {
7869
+ return up.Rect.fromElement(element);
7870
+ }
7871
+ });
7781
7872
  }
7782
7873
  _substractObstructions(viewportRect) {
7783
- for (let obstruction of this._selectObstructions(this._topObstructionSelector)) {
7784
- let obstructionRect = up.Rect.fromElement(obstruction);
7874
+ for (let obstructionRect of this._computeObstructionRects(this._topObstructionSelector)) {
7785
7875
  let diff = obstructionRect.bottom - viewportRect.top;
7786
7876
  if (diff > 0) {
7787
7877
  viewportRect.top += diff;
7788
7878
  viewportRect.height -= diff;
7789
7879
  }
7790
7880
  }
7791
- for (let obstruction of this._selectObstructions(this._bottomObstructionSelector)) {
7792
- let obstructionRect = up.Rect.fromElement(obstruction);
7881
+ for (let obstructionRect of this._computeObstructionRects(this._bottomObstructionSelector)) {
7793
7882
  let diff = viewportRect.bottom - obstructionRect.top;
7794
7883
  if (diff > 0) {
7795
7884
  viewportRect.height -= diff;
@@ -8086,12 +8175,11 @@ up.framework = (function () {
8086
8175
  console.error("Unpoly cannot boot: %s", issue);
8087
8176
  }
8088
8177
  }
8089
- function mustBootManually() {
8090
- let unpolyScript = document.currentScript;
8091
- if (unpolyScript?.async) {
8178
+ function mustManualBoot() {
8179
+ if (document.currentScript?.async) {
8092
8180
  return true;
8093
8181
  }
8094
- if (unpolyScript?.getAttribute('up-boot') === 'manual') {
8182
+ if (document.querySelector('[up-boot=manual]:is(html, script)')) {
8095
8183
  return true;
8096
8184
  }
8097
8185
  if (document.readyState === 'complete') {
@@ -8100,7 +8188,7 @@ up.framework = (function () {
8100
8188
  }
8101
8189
  function onEvaled() {
8102
8190
  up.emit('up:framework:evaled', { log: false });
8103
- if (mustBootManually()) {
8191
+ if (mustManualBoot()) {
8104
8192
  console.debug('Call up.boot() after you have configured Unpoly');
8105
8193
  }
8106
8194
  else {
@@ -8167,6 +8255,12 @@ up.event = (function () {
8167
8255
  function on(...args) {
8168
8256
  return buildListenerGroup(args).bind();
8169
8257
  }
8258
+ function onAncestor(fragment, eventType, callback) {
8259
+ let guard = (event) => event.target.contains(fragment);
8260
+ let unsubscribe = up.on(eventType, { guard }, callback);
8261
+ up.destructor(fragment, unsubscribe);
8262
+ return unsubscribe;
8263
+ }
8170
8264
  function off(...args) {
8171
8265
  return buildListenerGroup(args).unbind();
8172
8266
  }
@@ -8248,6 +8342,7 @@ up.event = (function () {
8248
8342
  on('up:framework:reset', reset);
8249
8343
  return {
8250
8344
  on,
8345
+ onAncestor,
8251
8346
  off,
8252
8347
  build,
8253
8348
  emit,
@@ -8590,6 +8685,7 @@ up.script = (function () {
8590
8685
  isDefault: up.framework.evaling,
8591
8686
  priority: 0,
8592
8687
  batch: false,
8688
+ rerun: false,
8593
8689
  });
8594
8690
  return Object.assign(callback, options, overrides);
8595
8691
  }
@@ -8616,25 +8712,35 @@ up.script = (function () {
8616
8712
  up.emit(fragment, 'up:fragment:compile', { log: false });
8617
8713
  let compilers = options.compilers || registeredMacros.concat(registeredCompilers);
8618
8714
  const pass = new up.CompilerPass(fragment, compilers, options);
8619
- pass.run();
8715
+ return pass.run();
8620
8716
  }
8621
- function registerDestructor(element, destructor) {
8622
- let fns = u.scanFunctions(destructor);
8717
+ function registerDestructor(element, value) {
8718
+ let fns = u.scanFunctions(value);
8623
8719
  if (!fns.length)
8624
8720
  return;
8625
- let registry = (element.upDestructors ||= buildDestructorRegistry(element));
8626
- registry.guard(fns);
8721
+ if (element.isConnected) {
8722
+ let registry = (element.upDestructors ||= buildDestructorRegistry(element));
8723
+ registry.guard(fns);
8724
+ }
8725
+ else {
8726
+ up.puts('up.destructor()', 'Immediately calling destructor for detached element (%o)', element);
8727
+ for (let fn of fns)
8728
+ up.error.guard(fn, element);
8729
+ }
8627
8730
  }
8628
8731
  function buildDestructorRegistry(element) {
8629
8732
  let registry = u.cleaner();
8630
8733
  registry(e.addClassTemp(element, 'up-can-clean'));
8631
8734
  return registry;
8632
8735
  }
8633
- function hello(element, options = {}) {
8736
+ async function hello(element, options = {}) {
8634
8737
  element = up.fragment.get(element, options);
8635
8738
  up.puts('up.hello()', "Compiling fragment %o", element);
8636
- compile(element, options);
8637
- up.fragment.emitInserted(element);
8739
+ await up.fragment.mutate(async () => {
8740
+ let compilePromise = compile(element, options);
8741
+ up.fragment.emitInserted(element);
8742
+ await compilePromise;
8743
+ });
8638
8744
  return element;
8639
8745
  }
8640
8746
  function clean(fragment, options = {}) {
@@ -8760,30 +8866,44 @@ up.history = (function () {
8760
8866
  let nextPreviousLocation;
8761
8867
  let nextTrackOptions;
8762
8868
  let adoptedBases = new up.FIFOCache({ capacity: 100, normalizeKey: getBase });
8763
- function isAdopted(location) {
8764
- return !!adoptedBases.get(location);
8765
- }
8766
8869
  function reset() {
8767
8870
  previousLocation = undefined;
8768
8871
  nextPreviousLocation = undefined;
8769
8872
  nextTrackOptions = undefined;
8770
8873
  adoptedBases.clear();
8771
8874
  trackCurrentLocation({ reason: null, alreadyHandled: true });
8772
- adopt();
8875
+ adoptBase();
8773
8876
  }
8774
8877
  function currentLocation() {
8775
8878
  return u.normalizeURL(location.href);
8776
8879
  }
8880
+ function withTrackOptions(trackOptions, fn) {
8881
+ try {
8882
+ nextTrackOptions = trackOptions;
8883
+ fn();
8884
+ }
8885
+ finally {
8886
+ nextTrackOptions = undefined;
8887
+ }
8888
+ }
8777
8889
  function trackCurrentLocation(trackOptions) {
8778
- let { reason, alreadyHandled } = nextTrackOptions || trackOptions;
8890
+ let { reason, alreadyHandled, pauseTracking } = nextTrackOptions || trackOptions;
8891
+ if (pauseTracking)
8892
+ return;
8779
8893
  let location = currentLocation();
8894
+ if (isAdoptedState()) {
8895
+ adoptBase(location);
8896
+ }
8897
+ else if (isAdoptedBase(location)) {
8898
+ adoptState();
8899
+ }
8780
8900
  if (nextPreviousLocation !== location) {
8781
8901
  previousLocation = nextPreviousLocation;
8782
8902
  nextPreviousLocation = location;
8783
8903
  if (reason === 'detect') {
8784
8904
  reason = (getBase(location) === getBase(previousLocation)) ? 'hash' : 'pop';
8785
8905
  }
8786
- let willHandle = !alreadyHandled && isAdopted(location);
8906
+ let willHandle = !alreadyHandled && isAdoptedBase(location);
8787
8907
  let locationChangedEvent = up.event.build('up:location:changed', {
8788
8908
  reason,
8789
8909
  location,
@@ -8817,17 +8937,33 @@ up.history = (function () {
8817
8937
  placeAdoptedHistoryEntry('pushState', location, trackOptions);
8818
8938
  }
8819
8939
  function placeAdoptedHistoryEntry(method, location, trackOptions) {
8820
- adopt(location);
8940
+ adoptBase(location);
8821
8941
  if (config.enabled) {
8822
- nextTrackOptions = trackOptions;
8823
- history[method](null, '', location);
8824
- nextTrackOptions = undefined;
8942
+ withTrackOptions(trackOptions, function () {
8943
+ history[method](null, { up: true }, location);
8944
+ });
8825
8945
  }
8826
8946
  }
8827
- function adopt(location = currentLocation()) {
8947
+ function isAdoptedBase(location) {
8948
+ return !!adoptedBases.get(location);
8949
+ }
8950
+ function adoptBase(location = currentLocation()) {
8828
8951
  location = u.normalizeURL(location);
8829
8952
  adoptedBases.set(location, true);
8830
8953
  }
8954
+ function isAdoptedState() {
8955
+ return history.state?.up;
8956
+ }
8957
+ function adoptState() {
8958
+ let { state } = history;
8959
+ if (isAdoptedState())
8960
+ return;
8961
+ if (u.isBlank(state) || u.isObject(state)) {
8962
+ withTrackOptions({ pauseTracking: true }, function () {
8963
+ history.replaceState({ ...state, up: true }, '');
8964
+ });
8965
+ }
8966
+ }
8831
8967
  function restoreLocation(location) {
8832
8968
  up.error.muteUncriticalRejection(up.render({
8833
8969
  guardEvent: up.event.build('up:location:restore', { location, log: `Restoring location ${location}` }),
@@ -8898,7 +9034,8 @@ up.history = (function () {
8898
9034
  }
8899
9035
  function adoptInitialHistoryEntry() {
8900
9036
  if (up.protocol.initialRequestMethod() === 'GET') {
8901
- adopt();
9037
+ adoptState();
9038
+ adoptBase();
8902
9039
  }
8903
9040
  }
8904
9041
  up.on('up:framework:boot', function ({ mode }) {
@@ -9035,6 +9172,7 @@ up.fragment = (function () {
9035
9172
  autoScroll: ['hash', 'layer-if-main'],
9036
9173
  autoRevalidate: (response) => response.expired,
9037
9174
  skipResponse: defaultSkipResponse,
9175
+ renderableResponse: (response) => response.isHTML(),
9038
9176
  normalizeKeepHTML: defaultNormalizeKeepHTML
9039
9177
  }));
9040
9178
  u.delegate(config, ['mainTargets'], () => up.layer.config.any);
@@ -9071,12 +9209,13 @@ up.fragment = (function () {
9071
9209
  const options = parseTargetAndOptions(args);
9072
9210
  return render({ ...options, navigate: true });
9073
9211
  });
9074
- function emitFragmentKeep(keepPlan) {
9075
- let { oldElement, newElement: newFragment, newData, renderOptions } = keepPlan;
9212
+ function emitFragmentKeep({ oldElement, newElement: newFragment, newData, renderOptions }) {
9076
9213
  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 });
9214
+ const callback = e.callbackAttr(oldElement, 'up-on-keep', { exposedKeys: ['newFragment', 'newData'] });
9215
+ return up.emit(oldElement, 'up:fragment:keep', { log, callback, newFragment, newData, renderOptions });
9216
+ }
9217
+ function emitFragmentKept({ oldElement, newElement: newFragment, newData }) {
9218
+ return up.emit(oldElement, 'up:fragment:kept', { log: true, newFragment, newData });
9080
9219
  }
9081
9220
  function findKeepPlan(options) {
9082
9221
  if (options.keep === false)
@@ -9170,9 +9309,7 @@ up.fragment = (function () {
9170
9309
  }
9171
9310
  return new up.FragmentFinder({
9172
9311
  selector,
9173
- origin: options.origin,
9174
- layer: options.layer,
9175
- match: options.match,
9312
+ ...u.pick(options, ['layer', 'match', 'origin', 'destroying']),
9176
9313
  }).find();
9177
9314
  }
9178
9315
  function getFirstDescendant(...args) {
@@ -9210,8 +9347,9 @@ up.fragment = (function () {
9210
9347
  }
9211
9348
  function destroy(...args) {
9212
9349
  const options = parseTargetAndOptions(args);
9213
- if (options.element = getSmart(options.target, options)) {
9214
- new up.Change.DestroyFragment(options).execute();
9350
+ const element = getSmart(options.target, options);
9351
+ if (element) {
9352
+ new up.Change.DestroyFragment({ ...options, element }).execute();
9215
9353
  }
9216
9354
  return up.migrate.formerlyAsync?.('up.destroy()');
9217
9355
  }
@@ -9394,7 +9532,7 @@ up.fragment = (function () {
9394
9532
  targets.unshift(...replaced);
9395
9533
  }
9396
9534
  else if (LAYER_PSEUDO.test(target)) {
9397
- if (layer === 'new' || layer.opening)
9535
+ if (layer === 'new' || layer.state === 'opening')
9398
9536
  continue;
9399
9537
  let firstSwappableTarget = toTarget(layer.getFirstSwappableElement(), options);
9400
9538
  targets.unshift(target.replace(LAYER_PSEUDO, firstSwappableTarget));
@@ -9508,7 +9646,7 @@ up.fragment = (function () {
9508
9646
  function abort(...args) {
9509
9647
  let options = parseTargetAndOptions(args);
9510
9648
  let testFn;
9511
- let { reason, newLayer } = options;
9649
+ let { reason, newLayer, jid } = options;
9512
9650
  let elements;
9513
9651
  if (options.target) {
9514
9652
  elements = getAll(options.target, options);
@@ -9524,14 +9662,14 @@ up.fragment = (function () {
9524
9662
  let testFnWithAbortable = (request) => request.abortable && testFn(request);
9525
9663
  up.network.abort(testFnWithAbortable, { ...options, reason });
9526
9664
  for (let element of elements) {
9527
- up.emit(element, 'up:fragment:aborted', { reason, newLayer, log: false });
9665
+ up.emit(element, 'up:fragment:aborted', { reason, newLayer, jid, log: reason });
9528
9666
  }
9529
9667
  }
9530
9668
  function onAborted(fragment, callback) {
9531
- let guard = (event) => event.target.contains(fragment);
9532
- let unsubscribe = up.on('up:fragment:aborted', { guard }, callback);
9533
- up.destructor(fragment, unsubscribe);
9534
- return unsubscribe;
9669
+ return up.event.onAncestor(fragment, 'up:fragment:aborted', callback);
9670
+ }
9671
+ function onKept(fragment, callback) {
9672
+ return up.event.onAncestor(fragment, 'up:fragment:kept', callback);
9535
9673
  }
9536
9674
  function onFirstIntersect(element, callback, { margin = 0 } = {}) {
9537
9675
  if (e.isIntersectingWindow(element, { margin })) {
@@ -9604,6 +9742,26 @@ up.fragment = (function () {
9604
9742
  let tracker = new up.SelectorTracker(...parsedArgs);
9605
9743
  return tracker.start();
9606
9744
  }
9745
+ let mutateLevel = 0;
9746
+ let afterMutateCleaner = u.cleaner('fifo');
9747
+ function mutate(callback) {
9748
+ try {
9749
+ mutateLevel++;
9750
+ return callback();
9751
+ }
9752
+ finally {
9753
+ if (--mutateLevel === 0)
9754
+ afterMutateCleaner.clean();
9755
+ }
9756
+ }
9757
+ function afterMutate(callback) {
9758
+ if (mutateLevel === 0) {
9759
+ callback();
9760
+ }
9761
+ else {
9762
+ afterMutateCleaner(callback);
9763
+ }
9764
+ }
9607
9765
  up.on('up:framework:boot', function () {
9608
9766
  const { documentElement } = document;
9609
9767
  up.hello(documentElement);
@@ -9631,6 +9789,7 @@ up.fragment = (function () {
9631
9789
  emitInserted: emitFragmentInserted,
9632
9790
  emitDestroyed: emitFragmentDestroyed,
9633
9791
  emitKeep: emitFragmentKeep,
9792
+ emitKept: emitFragmentKept,
9634
9793
  keepPlan: findKeepPlan,
9635
9794
  defaultNormalizeKeepHTML,
9636
9795
  successKey,
@@ -9647,6 +9806,7 @@ up.fragment = (function () {
9647
9806
  shouldRevalidate,
9648
9807
  abort,
9649
9808
  onAborted,
9809
+ onKept,
9650
9810
  onFirstIntersect,
9651
9811
  splitTarget,
9652
9812
  parseTargetSteps,
@@ -9659,6 +9819,8 @@ up.fragment = (function () {
9659
9819
  provideNodes,
9660
9820
  cloneTemplate,
9661
9821
  trackSelector,
9822
+ mutate,
9823
+ afterMutate,
9662
9824
  };
9663
9825
  })();
9664
9826
  up.reload = up.fragment.reload;
@@ -9756,7 +9918,7 @@ up.viewport = (function () {
9756
9918
  return () => {
9757
9919
  let doReveal = () => reveal(match, { top: true, behavior });
9758
9920
  if (strong)
9759
- u.task(doReveal);
9921
+ u.fastTask(doReveal);
9760
9922
  return doReveal();
9761
9923
  };
9762
9924
  }
@@ -10938,6 +11100,7 @@ up.link = (function () {
10938
11100
  'up-href': e.attr(childLink, 'href'),
10939
11101
  ...e.upAttrs(childLink)
10940
11102
  });
11103
+ childLink.upExpandedPair = area.upExpandedPair = [area, childLink];
10941
11104
  e.addClasses(area, e.upClasses(childLink));
10942
11105
  makeFollowable(area);
10943
11106
  }
@@ -11039,6 +11202,7 @@ up.form = (function () {
11039
11202
  return up.render(submitOptions(form, options));
11040
11203
  });
11041
11204
  function submitOptions(form, options, parserOptions) {
11205
+ form = up.fragment.get(form);
11042
11206
  form = getForm(form);
11043
11207
  options = u.options(options);
11044
11208
  let parser = new up.OptionsParser(form, options, parserOptions);
@@ -11053,6 +11217,7 @@ up.form = (function () {
11053
11217
  options.origin ||= up.viewport.focusedElementWithin(form) || options.submitButton || form;
11054
11218
  options.activeElements = u.uniq([options.origin, options.submitButton, form].filter(u.isElement));
11055
11219
  parser.include(up.link.followOptions);
11220
+ Object.assign(options, submitButtonOverrides(options.submitButton));
11056
11221
  return options;
11057
11222
  }
11058
11223
  function watchOptions(field, options, parserOptions = {}) {
@@ -11140,14 +11305,22 @@ up.form = (function () {
11140
11305
  const parser = new up.OptionsParser(form, options, parserOptions);
11141
11306
  parser.string('contentType', { attr: 'enctype' });
11142
11307
  parser.json('headers');
11143
- const params = up.Params.fromForm(form);
11308
+ const paramParts = [
11309
+ up.Params.fromForm(form),
11310
+ e.jsonAttr(form, 'up-params'),
11311
+ ];
11312
+ const headerParts = [
11313
+ e.jsonAttr(form, 'up-headers'),
11314
+ ];
11144
11315
  const submitButton = (options.submitButton ??= findSubmitButtons(form)[0]);
11145
11316
  if (submitButton) {
11146
- params.addField(submitButton);
11317
+ paramParts.push(up.Params.fromFields(submitButton), e.jsonAttr(submitButton, 'up-params'));
11318
+ headerParts.push(e.jsonAttr(submitButton, 'up-headers'));
11147
11319
  options.method ||= submitButton.getAttribute('formmethod');
11148
11320
  options.url ||= submitButton.getAttribute('formaction');
11149
11321
  }
11150
- options.params = up.Params.merge(params, options.params);
11322
+ options.params = up.Params.merge(...paramParts, options.params);
11323
+ options.headers = u.merge(...headerParts, options.headers);
11151
11324
  parser.string('url', { attr: 'action', default: up.fragment.source(form) });
11152
11325
  parser.string('method', {
11153
11326
  attr: ['up-method', 'data-method', 'method'],
@@ -11159,6 +11332,12 @@ up.form = (function () {
11159
11332
  }
11160
11333
  return options;
11161
11334
  }
11335
+ function submitButtonOverrides(submitButton) {
11336
+ if (!submitButton)
11337
+ return {};
11338
+ let followOptions = up.link.followOptions(submitButton, {}, { defaults: false });
11339
+ return u.omit(followOptions, ['method', 'url', 'guardEvent', 'origin', 'params', 'headers']);
11340
+ }
11162
11341
  function watch(...args) {
11163
11342
  let [root, options, callback] = u.args(args, 'val', 'options', 'callback');
11164
11343
  root = up.element.get(root);
@@ -11216,9 +11395,22 @@ up.form = (function () {
11216
11395
  }
11217
11396
  return options;
11218
11397
  }
11219
- function getForm(elementOrSelector, options = {}) {
11220
- const element = up.fragment.get(elementOrSelector, options);
11221
- return element.form || element.closest('form');
11398
+ function getForm(element) {
11399
+ return getLiteralForm(element) || getClosestForm(element) || getAssociatedForm(element);
11400
+ }
11401
+ function getLiteralForm(element) {
11402
+ if (element instanceof HTMLFormElement)
11403
+ return element;
11404
+ }
11405
+ function getClosestForm(element) {
11406
+ return element.closest('form');
11407
+ }
11408
+ function getAssociatedForm(element) {
11409
+ let formID = element.getAttribute('form');
11410
+ if (formID) {
11411
+ let selector = 'form' + e.idSelector(formID);
11412
+ return up.fragment.get(selector, { layer: element });
11413
+ }
11222
11414
  }
11223
11415
  function getFormValidator(form) {
11224
11416
  return form.upFormValidator ||= setupFormValidator(form);
@@ -11229,15 +11421,15 @@ up.form = (function () {
11229
11421
  up.destructor(form, stop);
11230
11422
  return validator;
11231
11423
  }
11232
- function getRegion(origin, options) {
11424
+ function getRegion(origin) {
11425
+ return getOriginRegion(origin) || up.layer.current.element;
11426
+ }
11427
+ function getOriginRegion(origin) {
11233
11428
  if (origin) {
11234
- return getForm(origin, options) || up.layer.get(origin).element;
11235
- }
11236
- else {
11237
- return up.layer.current.element;
11429
+ return getForm(origin) || up.layer.get(origin)?.element;
11238
11430
  }
11239
11431
  }
11240
- function trackFields(...args) {
11432
+ const trackFields = up.mockable(function (...args) {
11241
11433
  let [root, { guard }, callback] = u.args(args, 'val', 'options', 'callback');
11242
11434
  let filter = function (fields) {
11243
11435
  let region = getRegion(root);
@@ -11247,9 +11439,8 @@ up.form = (function () {
11247
11439
  && (!guard || guard(field));
11248
11440
  });
11249
11441
  };
11250
- const live = true;
11251
- return up.fragment.trackSelector(fieldSelector(), { filter, live }, callback);
11252
- }
11442
+ return up.fragment.trackSelector(fieldSelector(), { filter }, callback);
11443
+ });
11253
11444
  function focusedField() {
11254
11445
  return u.presence(document.activeElement, isField);
11255
11446
  }
@@ -11258,14 +11449,18 @@ up.form = (function () {
11258
11449
  return config.matches(form, 'submitSelectors');
11259
11450
  }
11260
11451
  up.on('submit', config.selectorFn('submitSelectors'), function (event, form) {
11452
+ const submitButton = u.presence(event.submitter, isSubmitButton);
11453
+ if (submitButton?.getAttribute('up-submit') === 'false')
11454
+ return;
11261
11455
  if (event.defaultPrevented)
11262
11456
  return;
11263
- const submitButton = u.presence(event.submitter, isSubmitButton);
11264
11457
  up.event.halt(event, { log: true });
11265
11458
  up.error.muteUncriticalRejection(submit(form, { submitButton }));
11266
11459
  });
11267
- up.compiler('form', function (form) {
11268
- getFormValidator(form);
11460
+ up.compiler('[up-validate]', { rerun: true }, function (element) {
11461
+ let form = getForm(element);
11462
+ if (form)
11463
+ getFormValidator(form);
11269
11464
  });
11270
11465
  up.compiler('[up-switch]', (switcher) => {
11271
11466
  return new up.Switcher(switcher).start();
@@ -11277,6 +11472,7 @@ up.form = (function () {
11277
11472
  submit,
11278
11473
  submitOptions,
11279
11474
  destinationOptions,
11475
+ submitButtonOverrides,
11280
11476
  watchOptions,
11281
11477
  validateOptions,
11282
11478
  isSubmittable,
@@ -11353,8 +11549,8 @@ up.status = (function () {
11353
11549
  function getMatchableLayerLocation(layer) {
11354
11550
  return u.matchableURL(layer.location);
11355
11551
  }
11356
- function findActivatableArea(element) {
11357
- return e.ancestor(element, SELECTOR_LINK) || element;
11552
+ function findActivatableAreas(element) {
11553
+ return element.upExpandedPair || [element];
11358
11554
  }
11359
11555
  function runPreviews(request, renderOptions) {
11360
11556
  let { bindLayer } = request;
@@ -11415,7 +11611,7 @@ up.status = (function () {
11415
11611
  }
11416
11612
  function getActiveElements({ origin, activeElements }) {
11417
11613
  activeElements ||= u.wrapList(origin);
11418
- return activeElements.map(findActivatableArea);
11614
+ return u.flatMap(activeElements, findActivatableAreas);
11419
11615
  }
11420
11616
  function getFeedbackClassesPreviewFn(feedbackOption, fragments) {
11421
11617
  if (!feedbackOption)