unpoly-rails 3.7.3.1 → 3.8.0.rc1

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.7.3'
8
+ version: '3.8.0-rc1'
9
9
  };
10
10
 
11
11
 
@@ -229,11 +229,6 @@ up.util = (function () {
229
229
  function isArguments(value) {
230
230
  return Object.prototype.toString.call(value) === '[object Arguments]';
231
231
  }
232
- function nullToUndefined(value) {
233
- if (!isNull(value)) {
234
- return value;
235
- }
236
- }
237
232
  function wrapList(value) {
238
233
  if (isList(value)) {
239
234
  return value;
@@ -337,9 +332,6 @@ up.util = (function () {
337
332
  function compact(array) {
338
333
  return filterList(array, isGiven);
339
334
  }
340
- function filterMap(list, mapping) {
341
- return filterList(map(list, mapping), isDefined);
342
- }
343
335
  function compactObject(object) {
344
336
  return pickBy(object, isGiven);
345
337
  }
@@ -389,9 +381,6 @@ up.util = (function () {
389
381
  function queueTask(task) {
390
382
  return setTimeout(task);
391
383
  }
392
- function queueMicrotask(task) {
393
- return Promise.resolve().then(task);
394
- }
395
384
  function last(value) {
396
385
  return value[value.length - 1];
397
386
  }
@@ -399,6 +388,9 @@ up.util = (function () {
399
388
  let indexOf = value.indexOf || Array.prototype.indexOf;
400
389
  return indexOf.call(value, subValue) >= 0;
401
390
  }
391
+ function containsAll(values, subValues) {
392
+ return every(subValues, (subValue) => contains(values, subValue));
393
+ }
402
394
  function objectContains(object, subObject) {
403
395
  const reducedValue = pick(object, Object.keys(subObject));
404
396
  return isEqual(subObject, reducedValue);
@@ -582,6 +574,14 @@ up.util = (function () {
582
574
  function reverse(list) {
583
575
  return copy(list).reverse();
584
576
  }
577
+ function replaceValue(value, matchValue, replacementValue) {
578
+ if (value === matchValue) {
579
+ return replacementValue;
580
+ }
581
+ else {
582
+ return value;
583
+ }
584
+ }
585
585
  function renameKeys(object, renameKeyFn) {
586
586
  const renamed = {};
587
587
  for (let key in object) {
@@ -732,7 +732,6 @@ up.util = (function () {
732
732
  normalizeMethod,
733
733
  methodAllowsPayload,
734
734
  copy,
735
- copyArrayLike,
736
735
  merge,
737
736
  mergeDefined,
738
737
  options: newOptions,
@@ -746,7 +745,6 @@ up.util = (function () {
746
745
  every,
747
746
  find: findInList,
748
747
  filter: filterList,
749
- filterMap: filterMap,
750
748
  reject,
751
749
  intersect,
752
750
  compact,
@@ -778,6 +776,7 @@ up.util = (function () {
778
776
  isRegExp,
779
777
  timer: scheduleTimer,
780
778
  contains,
779
+ containsAll,
781
780
  objectContains,
782
781
  toArray,
783
782
  pick,
@@ -804,7 +803,6 @@ up.util = (function () {
804
803
  isBasicObjectProperty,
805
804
  isCrossOrigin,
806
805
  task: queueTask,
807
- microtask: queueMicrotask,
808
806
  isEqual,
809
807
  parseTokens,
810
808
  wrapList,
@@ -815,10 +813,9 @@ up.util = (function () {
815
813
  delegate: defineDelegates,
816
814
  reverse,
817
815
  camelToKebabCase,
818
- nullToUndefined,
816
+ replaceValue,
819
817
  sprintf,
820
818
  renameKeys,
821
- negate,
822
819
  memoizeMethod,
823
820
  safeStringifyJSON,
824
821
  variant,
@@ -1179,7 +1176,7 @@ up.element = (function () {
1179
1176
  }
1180
1177
  }
1181
1178
  function idSelector(id) {
1182
- if (id.match(/^[a-z0-9\-_]+$/i)) {
1179
+ if (id.match(/^[a-z][a-z0-9\-_]*$/i)) {
1183
1180
  return `#${id}`;
1184
1181
  }
1185
1182
  else {
@@ -1269,9 +1266,6 @@ up.element = (function () {
1269
1266
  element.appendChild(wrapper);
1270
1267
  return wrapper;
1271
1268
  }
1272
- function isWrapper(element) {
1273
- return element.matches('up-wrapper');
1274
- }
1275
1269
  function preservingFocus(fn) {
1276
1270
  const oldFocusElement = document.activeElement;
1277
1271
  try {
@@ -1284,7 +1278,8 @@ up.element = (function () {
1284
1278
  }
1285
1279
  }
1286
1280
  function stringAttr(element, attribute) {
1287
- return u.nullToUndefined(element.getAttribute(attribute));
1281
+ let value = element.getAttribute(attribute);
1282
+ return u.replaceValue(value, null, undefined);
1288
1283
  }
1289
1284
  function booleanAttr(element, attribute, pass) {
1290
1285
  if (!element.hasAttribute(attribute))
@@ -1309,14 +1304,15 @@ up.element = (function () {
1309
1304
  }
1310
1305
  }
1311
1306
  }
1312
- function booleanOrStringAttr(element, attribute) {
1313
- return booleanAttr(element, attribute, true);
1307
+ function booleanOrStringAttr(element, attribute, trueValue = true) {
1308
+ let value = booleanAttr(element, attribute, true);
1309
+ return value === true ? trueValue : value;
1314
1310
  }
1315
1311
  function numberAttr(element, attribute) {
1316
1312
  let value = element.getAttribute(attribute);
1317
1313
  if (value) {
1318
1314
  value = value.replace(/_/g, '');
1319
- if (value.match(/^[\d.]+$/)) {
1315
+ if (value.match(/^-?[\d.]+$/)) {
1320
1316
  return parseFloat(value);
1321
1317
  }
1322
1318
  }
@@ -1352,10 +1348,6 @@ up.element = (function () {
1352
1348
  element.classList.add(klass);
1353
1349
  return () => element.classList.remove(klass);
1354
1350
  }
1355
- function setTemporaryAttr(element, attr, value) {
1356
- element.setAttribute(attr, value);
1357
- return () => element.removeAttribute(element, attr);
1358
- }
1359
1351
  function computedStyle(element, props) {
1360
1352
  const style = window.getComputedStyle(element);
1361
1353
  return extractFromStyleObject(style, props);
@@ -1441,6 +1433,18 @@ up.element = (function () {
1441
1433
  function crossOriginSelector(attr) {
1442
1434
  return `[${attr}*="//"]:not([${attr}*="//${location.host}/"])`;
1443
1435
  }
1436
+ function isIntersectingWindow(element, { margin = 0 } = {}) {
1437
+ const rect = up.Rect.fromElement(element);
1438
+ rect.grow(margin);
1439
+ return (rect.bottom > 0) && (rect.top < window.innerHeight) &&
1440
+ (rect.right > 0) && (rect.left < window.innerWidth);
1441
+ }
1442
+ function unionSelector(includes, excludes) {
1443
+ let selector = `:is(${includes.join()})`;
1444
+ if (u.isPresent(excludes))
1445
+ selector += `:not(${excludes.join()})`;
1446
+ return selector;
1447
+ }
1444
1448
  return {
1445
1449
  subtree,
1446
1450
  contains,
@@ -1475,7 +1479,6 @@ up.element = (function () {
1475
1479
  setMissingAttr,
1476
1480
  unwrap,
1477
1481
  wrapChildren,
1478
- isWrapper,
1479
1482
  attr: stringAttr,
1480
1483
  booleanAttr,
1481
1484
  numberAttr,
@@ -1492,11 +1495,12 @@ up.element = (function () {
1492
1495
  upClasses,
1493
1496
  toggleAttr,
1494
1497
  addTemporaryClass,
1495
- setTemporaryAttr,
1496
1498
  cleanJQuery,
1497
1499
  parseSelector,
1498
1500
  isEmpty,
1499
1501
  crossOriginSelector,
1502
+ isIntersectingWindow,
1503
+ unionSelector,
1500
1504
  };
1501
1505
  })();
1502
1506
 
@@ -1609,6 +1613,7 @@ up.Record = class Record {
1609
1613
  /***/ (() => {
1610
1614
 
1611
1615
  const u = up.util;
1616
+ const e = up.element;
1612
1617
  up.Config = class Config {
1613
1618
  constructor(blueprintFn = (() => ({}))) {
1614
1619
  this._blueprintFn = blueprintFn;
@@ -1624,10 +1629,7 @@ up.Config = class Config {
1624
1629
  selector(prop) {
1625
1630
  let includes = this[prop];
1626
1631
  let excludes = this['no' + u.upperCaseFirst(prop)];
1627
- let selector = `:is(${includes.join()})`;
1628
- if (u.isPresent(excludes))
1629
- selector += `:not(${excludes.join()})`;
1630
- return selector;
1632
+ return e.unionSelector(includes, excludes);
1631
1633
  }
1632
1634
  selectorFn(prop) {
1633
1635
  return () => this.selector(prop);
@@ -1797,6 +1799,12 @@ up.Rect = class Rect extends up.Record {
1797
1799
  get right() {
1798
1800
  return this.left + this.width;
1799
1801
  }
1802
+ grow(padding) {
1803
+ this.left -= padding;
1804
+ this.top -= padding;
1805
+ this.width += padding * 2;
1806
+ this.height += padding * 2;
1807
+ }
1800
1808
  static fromElement(element) {
1801
1809
  return new (this)(element.getBoundingClientRect());
1802
1810
  }
@@ -1808,64 +1816,46 @@ up.Rect = class Rect extends up.Record {
1808
1816
  /***/ (() => {
1809
1817
 
1810
1818
  const e = up.element;
1819
+ const SHIFT_CLASS = 'up-scrollbar-away';
1811
1820
  up.BodyShifter = class BodyShifter {
1812
1821
  constructor() {
1813
- this._unshiftFns = [];
1814
1822
  this._anchoredElements = new Set();
1815
1823
  this._stack = 0;
1816
1824
  }
1817
1825
  lowerStack() {
1818
- this._stack--;
1819
- if (this._stack === 0) {
1826
+ if (--this._stack === 0)
1820
1827
  this._unshiftNow();
1821
- }
1822
1828
  }
1823
1829
  raiseStack() {
1824
- this._stack++;
1825
- if (this._stack === 1) {
1830
+ if (++this._stack === 1)
1826
1831
  this._shiftNow();
1827
- }
1828
1832
  }
1829
1833
  onAnchoredElementInserted(element) {
1830
1834
  this._anchoredElements.add(element);
1831
- if (this._isShifted()) {
1832
- this._shiftAnchoredElement(element);
1833
- }
1835
+ this._shiftElement(element, 'right');
1834
1836
  return () => this._anchoredElements.delete(element);
1835
1837
  }
1836
1838
  _isShifted() {
1837
- return this._scrollbarTookSpace && this._stack > 0;
1839
+ return this._rootScrollbarWidth && this._stack > 0;
1838
1840
  }
1839
1841
  _shiftNow() {
1840
- this._scrollbarWidth = up.viewport.scrollbarWidth();
1841
- this._scrollbarTookSpace = up.viewport.rootHasReducedWidthFromScrollbar();
1842
- if (!this._scrollbarTookSpace)
1843
- return;
1844
- this._shiftBody();
1842
+ this._rootScrollbarWidth = up.viewport.rootScrollbarWidth();
1843
+ e.root.style.setProperty('--up-scrollbar-width', this._rootScrollbarWidth + 'px');
1844
+ this._shiftElement(document.body, 'padding-right');
1845
1845
  for (let element of this._anchoredElements) {
1846
- this._shiftAnchoredElement(element);
1846
+ this._shiftElement(element, 'right');
1847
1847
  }
1848
1848
  }
1849
- _shiftBody() {
1850
- const overflowElement = up.viewport.rootOverflowElement();
1851
- this._changeStyle(overflowElement, { overflowY: 'hidden' });
1852
- const { body } = document;
1853
- const bodyRightPadding = e.styleNumber(body, 'paddingRight');
1854
- const bodyRightShift = this._scrollbarWidth + bodyRightPadding;
1855
- this._changeStyle(body, { paddingRight: bodyRightShift });
1856
- }
1857
- _shiftAnchoredElement(element) {
1858
- const elementRight = e.styleNumber(element, 'right');
1859
- const elementRightShift = this._scrollbarWidth + elementRight;
1860
- this._changeStyle(element, { right: elementRightShift });
1861
- }
1862
- _changeStyle(element, styles) {
1863
- this._unshiftFns.push(e.setTemporaryStyle(element, styles));
1849
+ _shiftElement(element, styleProp) {
1850
+ if (!this._isShifted())
1851
+ return;
1852
+ let originalValue = e.style(element, styleProp);
1853
+ element.style.setProperty('--up-original-' + styleProp, originalValue);
1854
+ element.classList.add(SHIFT_CLASS);
1864
1855
  }
1865
1856
  _unshiftNow() {
1866
- let unshiftFn;
1867
- while (unshiftFn = this._unshiftFns.pop()) {
1868
- unshiftFn();
1857
+ for (let element of [document.body, ...this._anchoredElements]) {
1858
+ element.classList.remove(SHIFT_CLASS);
1869
1859
  }
1870
1860
  }
1871
1861
  };
@@ -3058,6 +3048,7 @@ up.Change.FromContent = (_a = class FromContent extends up.Change {
3058
3048
  }
3059
3049
  this.options.title = this.improveHistoryValue(this.options.title, responseDoc.title);
3060
3050
  this.options.metaTags = this.improveHistoryValue(this.options.metaTags, responseDoc.metaTags);
3051
+ this.options.lang = this.improveHistoryValue(this.options.lang, responseDoc.lang);
3061
3052
  }
3062
3053
  _defaultPlacement() {
3063
3054
  if (!this.options.document && !this.options.fragment) {
@@ -3773,6 +3764,7 @@ up.FieldWatcher = class FieldWatcher {
3773
3764
  /***/ (() => {
3774
3765
 
3775
3766
  const u = up.util;
3767
+ const e = up.element;
3776
3768
  up.FormValidator = class FormValidator {
3777
3769
  constructor(form) {
3778
3770
  this._form = form;
@@ -3832,7 +3824,7 @@ up.FormValidator = class FormValidator {
3832
3824
  }
3833
3825
  }
3834
3826
  _getTargetSelectorSolutions({ target, origin }) {
3835
- if (u.isString(target) && target) {
3827
+ if (u.isString(target)) {
3836
3828
  up.puts('up.validate()', 'Validating target "%s"', target);
3837
3829
  let simpleSelectors = up.fragment.splitTarget(target);
3838
3830
  return u.compact(simpleSelectors.map(function (simpleSelector) {
@@ -3861,7 +3853,7 @@ up.FormValidator = class FormValidator {
3861
3853
  _getValidateAttrSolutions(field) {
3862
3854
  let containerWithAttr = field.closest('[up-validate]');
3863
3855
  if (containerWithAttr) {
3864
- let target = containerWithAttr.getAttribute('up-validate');
3856
+ let target = e.booleanOrStringAttr(containerWithAttr, 'up-validate');
3865
3857
  return this._getTargetSelectorSolutions({ target, origin: field });
3866
3858
  }
3867
3859
  }
@@ -3907,7 +3899,8 @@ up.FormValidator = class FormValidator {
3907
3899
  options.guardEvent = up.event.build('up:form:validate', {
3908
3900
  fields: dirtyFields,
3909
3901
  log: 'Validating form',
3910
- params: options.params
3902
+ params: options.params,
3903
+ form: this._form,
3911
3904
  });
3912
3905
  this._rendering = true;
3913
3906
  let renderingPromise = this._nextRenderPromise;
@@ -4548,6 +4541,7 @@ up.Layer = class Layer extends up.Record {
4548
4541
  this.savedTitle = document.title;
4549
4542
  this.savedMetaTags = up.history.findMetaTags();
4550
4543
  this.savedLocation = up.history.location;
4544
+ this.savedLang = up.history.getLang();
4551
4545
  }
4552
4546
  }
4553
4547
  restoreHistory() {
@@ -4563,6 +4557,9 @@ up.Layer = class Layer extends up.Record {
4563
4557
  if (this.savedMetaTags) {
4564
4558
  up.history.updateMetaTags(this.savedMetaTags);
4565
4559
  }
4560
+ if (u.isString(this.savedLang)) {
4561
+ up.history.updateLang(this.savedLang);
4562
+ }
4566
4563
  }
4567
4564
  asCurrent(fn) {
4568
4565
  return this.stack.asCurrent(this, fn);
@@ -4578,6 +4575,9 @@ up.Layer = class Layer extends up.Record {
4578
4575
  if (u.isString(options.title)) {
4579
4576
  this.title = options.title;
4580
4577
  }
4578
+ if (u.isString(options.lang)) {
4579
+ this.lang = options.lang;
4580
+ }
4581
4581
  }
4582
4582
  showsLiveHistory() {
4583
4583
  return this.history && this.isFront();
@@ -4610,6 +4610,20 @@ up.Layer = class Layer extends up.Record {
4610
4610
  up.history.updateMetaTags(metaTags);
4611
4611
  }
4612
4612
  }
4613
+ get lang() {
4614
+ if (this.showsLiveHistory()) {
4615
+ return up.history.getLang();
4616
+ }
4617
+ else {
4618
+ return this.savedLang;
4619
+ }
4620
+ }
4621
+ set lang(lang) {
4622
+ this.savedLang = lang;
4623
+ if (this.showsLiveHistory()) {
4624
+ up.history.updateLang(lang);
4625
+ }
4626
+ }
4613
4627
  get location() {
4614
4628
  if (this.showsLiveHistory()) {
4615
4629
  return up.history.location;
@@ -5321,7 +5335,10 @@ up.LinkFeedbackURLs = class LinkFeedbackURLs {
5321
5335
  }
5322
5336
  }
5323
5337
  isCurrent(normalizedLocation) {
5324
- return this._isSafe && !!(this.href === normalizedLocation ||
5338
+ if (!normalizedLocation) {
5339
+ return false;
5340
+ }
5341
+ return !!(this.href === normalizedLocation ||
5325
5342
  this._upHREF === normalizedLocation ||
5326
5343
  this._aliasPattern?.test?.(normalizedLocation, false));
5327
5344
  }
@@ -5334,60 +5351,40 @@ up.LinkFeedbackURLs = class LinkFeedbackURLs {
5334
5351
 
5335
5352
  const u = up.util;
5336
5353
  const e = up.element;
5337
- up.LinkPreloader = class LinkPreloader {
5338
- watchLink(link) {
5339
- if (!up.link.preloadIssue(link)) {
5340
- this._on(link, 'mouseenter', (event) => this._considerPreload(event, true));
5341
- this._on(link, 'mousedown touchstart', (event) => this._considerPreload(event));
5342
- this._on(link, 'mouseleave', (event) => this._stopPreload(event));
5343
- }
5344
- }
5345
- _on(link, eventTypes, callback) {
5346
- up.on(link, eventTypes, { passive: true }, callback);
5347
- }
5348
- _considerPreload(event, applyDelay) {
5349
- const link = event.target;
5350
- if (link !== this._currentLink) {
5351
- this.reset();
5352
- this._currentLink = link;
5353
- if (up.link.shouldFollowEvent(event, link)) {
5354
- if (applyDelay) {
5355
- this._preloadAfterDelay(event, link);
5356
- }
5357
- else {
5358
- this._preloadNow(event, link);
5359
- }
5360
- }
5361
- }
5354
+ up.LinkFollowIntent = class LinkFollowIntent {
5355
+ constructor(link, callback) {
5356
+ this._link = link;
5357
+ this._callback = callback;
5358
+ this._on('mouseenter mousedown touchstart', (event) => this._scheduleCallback(event));
5359
+ this._on('mouseleave', () => this._unscheduleCallback());
5360
+ up.fragment.onAborted(this._link, () => this._unscheduleCallback());
5362
5361
  }
5363
- _stopPreload(event) {
5364
- if (event.target === this._currentLink) {
5365
- return this.reset();
5366
- }
5362
+ _on(eventType, callback) {
5363
+ up.on(this._link, eventType, { passive: true }, callback);
5367
5364
  }
5368
- reset() {
5369
- if (!this._currentLink) {
5365
+ _scheduleCallback(event) {
5366
+ if (!up.link.shouldFollowEvent(event, this._link))
5370
5367
  return;
5368
+ this._unscheduleCallback();
5369
+ const applyDelay = (event.type === 'mouseenter');
5370
+ if (applyDelay) {
5371
+ let delay = this._parseDelay();
5372
+ this._timer = u.timer(delay, () => this._runCallback(event));
5371
5373
  }
5372
- clearTimeout(this._timer);
5373
- if (this._currentRequest?.background) {
5374
- this._currentRequest.abort();
5374
+ else {
5375
+ this._runCallback(event);
5375
5376
  }
5376
- this._currentLink = undefined;
5377
- this._currentRequest = undefined;
5378
5377
  }
5379
- _preloadAfterDelay(event, link) {
5380
- const delay = e.numberAttr(link, 'up-preload-delay') ?? up.link.config.preloadDelay;
5381
- this._timer = u.timer(delay, () => this._preloadNow(event, link));
5378
+ _unscheduleCallback() {
5379
+ clearTimeout(this._timer);
5380
+ up.network.abort((request) => (request.origin === this._link) && request.background);
5381
+ }
5382
+ _parseDelay() {
5383
+ return e.numberAttr(this._link, 'up-preload-delay') ?? up.link.config.preloadDelay;
5382
5384
  }
5383
- _preloadNow(event, link) {
5384
- if (!link.isConnected) {
5385
- this.reset();
5386
- return;
5387
- }
5388
- const onQueued = request => { return this._currentRequest = request; };
5385
+ _runCallback(event) {
5389
5386
  up.log.putsEvent(event);
5390
- up.error.muteUncriticalRejection(up.link.preload(link, { onQueued }));
5387
+ up.error.muteUncriticalRejection(this._callback());
5391
5388
  }
5392
5389
  };
5393
5390
 
@@ -6130,7 +6127,6 @@ up.Request = (_a = class Request extends up.Record {
6130
6127
  'wrapMethod',
6131
6128
  'contentType',
6132
6129
  'payload',
6133
- 'onQueued',
6134
6130
  'onLoading',
6135
6131
  'fail',
6136
6132
  'abortable',
@@ -6164,7 +6160,7 @@ up.Request = (_a = class Request extends up.Record {
6164
6160
  }
6165
6161
  this.deferred = u.newDeferred();
6166
6162
  this.badResponseTime ??= u.evalOption(up.network.config.badResponseTime, this);
6167
- this._addAutoHeaders();
6163
+ this._setAutoHeaders();
6168
6164
  }
6169
6165
  get xhr() {
6170
6166
  return this._xhr ??= new XMLHttpRequest();
@@ -6173,7 +6169,7 @@ up.Request = (_a = class Request extends up.Record {
6173
6169
  if (this._fragments) {
6174
6170
  return this._fragments;
6175
6171
  }
6176
- else if (this.target) {
6172
+ else {
6177
6173
  let steps = up.fragment.parseTargetSteps(this.target);
6178
6174
  let selectors = u.map(steps, 'selector');
6179
6175
  let lookupOpts = { origin: this.origin, layer: this.layer };
@@ -6228,7 +6224,6 @@ up.Request = (_a = class Request extends up.Record {
6228
6224
  }
6229
6225
  runQueuedCallbacks() {
6230
6226
  u.always(this, () => this._evictExpensiveAttrs());
6231
- this.onQueued?.(this);
6232
6227
  }
6233
6228
  load() {
6234
6229
  if (this.state !== 'new')
@@ -6372,9 +6367,6 @@ up.Request = (_a = class Request extends up.Record {
6372
6367
  return this.method + ' ' + this.url;
6373
6368
  }
6374
6369
  isPartOfSubtree(subtreeElements) {
6375
- if (!this.fragments || !subtreeElements) {
6376
- return false;
6377
- }
6378
6370
  subtreeElements = u.wrapList(subtreeElements);
6379
6371
  return u.some(this.fragments, function (fragment) {
6380
6372
  return u.some(subtreeElements, (subtreeElement) => subtreeElement.contains(fragment));
@@ -6386,17 +6378,20 @@ up.Request = (_a = class Request extends up.Record {
6386
6378
  header(name) {
6387
6379
  return this.headers[name];
6388
6380
  }
6389
- _addAutoHeaders() {
6381
+ _setAutoHeaders() {
6390
6382
  for (let key of ['target', 'failTarget', 'mode', 'failMode', 'context', 'failContext']) {
6391
- this._addAutoHeader(up.protocol.headerize(key), this[key]);
6383
+ this._setPropertyHeader(key);
6392
6384
  }
6393
6385
  let csrfHeader, csrfToken;
6394
6386
  if ((csrfHeader = this.csrfHeader()) && (csrfToken = this.csrfToken())) {
6395
- this._addAutoHeader(csrfHeader, csrfToken);
6387
+ this._setAutoHeader(csrfHeader, csrfToken);
6396
6388
  }
6397
- this._addAutoHeader(up.protocol.headerize('version'), up.version);
6389
+ this._setAutoHeader(up.protocol.headerize('version'), up.version);
6398
6390
  }
6399
- _addAutoHeader(name, value) {
6391
+ _setPropertyHeader(key) {
6392
+ this._setAutoHeader(up.protocol.headerize(key), this[key]);
6393
+ }
6394
+ _setAutoHeader(name, value) {
6400
6395
  if (u.isMissing(value)) {
6401
6396
  return;
6402
6397
  }
@@ -6405,6 +6400,16 @@ up.Request = (_a = class Request extends up.Record {
6405
6400
  }
6406
6401
  this.headers[name] = value;
6407
6402
  }
6403
+ mergeIfUnsent(trackingRequest) {
6404
+ if (this.state !== 'new')
6405
+ return;
6406
+ if (!this.target || !trackingRequest.target)
6407
+ return;
6408
+ let targetAtoms = up.fragment.splitTarget(this.target + ',' + trackingRequest.target);
6409
+ this.target = u.uniq(targetAtoms).join(', ');
6410
+ this._setPropertyHeader('target');
6411
+ this._fragments = u.uniq([...this.fragments, ...trackingRequest.fragments]);
6412
+ }
6408
6413
  static tester(condition, { except } = {}) {
6409
6414
  let testFn;
6410
6415
  if (u.isFunction(condition)) {
@@ -6439,74 +6444,82 @@ up.Request = (_a = class Request extends up.Record {
6439
6444
  /***/ (() => {
6440
6445
 
6441
6446
  const u = up.util;
6447
+ class Route {
6448
+ constructor() {
6449
+ this.varyHeaders = new Set();
6450
+ this.requests = [];
6451
+ }
6452
+ matchBest(newRequest) {
6453
+ let matches = this.requests.filter((cachedRequest) => this.satisfies(cachedRequest, newRequest));
6454
+ return u.last(matches);
6455
+ }
6456
+ delete(request) {
6457
+ u.remove(this.requests, request);
6458
+ }
6459
+ put(request) {
6460
+ this.requests.push(request);
6461
+ }
6462
+ updateVary(response) {
6463
+ for (let headerName of response.varyHeaderNames) {
6464
+ this.varyHeaders.add(headerName);
6465
+ }
6466
+ }
6467
+ satisfies(cachedRequest, newRequest) {
6468
+ if (cachedRequest === newRequest)
6469
+ return true;
6470
+ return u.every(this.varyHeaders, (varyHeader) => {
6471
+ let cachedValue = cachedRequest.header(varyHeader);
6472
+ let newValue = newRequest.header(varyHeader);
6473
+ if (varyHeader === 'X-Up-Target' || varyHeader === 'X-Up-Fail-Target') {
6474
+ if (!cachedValue)
6475
+ return true;
6476
+ if (!newValue)
6477
+ return false;
6478
+ let cachedTokens = u.parseTokens(cachedValue, { separator: 'comma' });
6479
+ let newTokens = u.parseTokens(newValue, { separator: 'comma' });
6480
+ return u.containsAll(cachedTokens, newTokens);
6481
+ }
6482
+ else {
6483
+ return cachedValue === newValue;
6484
+ }
6485
+ });
6486
+ }
6487
+ }
6442
6488
  up.Request.Cache = class Cache {
6443
6489
  constructor() {
6444
6490
  this.reset();
6445
6491
  }
6446
6492
  reset() {
6447
- this._varyInfo = {};
6448
- this._map = new Map();
6449
- }
6450
- _cacheKey(request) {
6451
- let influencingHeaders = this._getPreviousInfluencingHeaders(request);
6452
- let varyPart = u.flatMap(influencingHeaders, (headerName) => [headerName, request.header(headerName)]);
6453
- return [request.description, ...varyPart].join(':');
6454
- }
6455
- _getPreviousInfluencingHeaders(request) {
6456
- return (this._varyInfo[request.description] ||= new Set());
6493
+ this._routes = {};
6494
+ this._requests = [];
6457
6495
  }
6458
6496
  get(request) {
6459
6497
  request = this._wrap(request);
6460
- let cacheKey = this._cacheKey(request);
6461
- let cachedRequest = this._map.get(cacheKey);
6498
+ let route = this._getRoute(request);
6499
+ let cachedRequest = route.matchBest(request);
6462
6500
  if (cachedRequest) {
6463
6501
  if (this._isUsable(cachedRequest)) {
6464
6502
  return cachedRequest;
6465
6503
  }
6466
6504
  else {
6467
- this._map.delete(cacheKey);
6505
+ this._delete(request, route);
6468
6506
  }
6469
6507
  }
6470
6508
  }
6471
- get _capacity() {
6472
- return up.network.config.cacheSize;
6473
- }
6474
- _isUsable(request) {
6475
- return request.age < up.network.config.cacheEvictAge;
6476
- }
6477
6509
  async put(request) {
6478
6510
  request = this._wrap(request);
6479
- this._makeRoom();
6480
- let cacheKey = this._updateCacheKey(request);
6481
- this._map.set(cacheKey, request);
6482
- }
6483
- _updateCacheKey(request) {
6484
- let oldCacheKey = this._cacheKey(request);
6511
+ let route = this._getRoute(request);
6485
6512
  let { response } = request;
6486
- if (response) {
6487
- this._mergePreviousHeaderNames(request, response);
6488
- let newCacheKey = this._cacheKey(request);
6489
- this._renameMapKey(oldCacheKey, newCacheKey);
6490
- return newCacheKey;
6491
- }
6492
- else {
6493
- return oldCacheKey;
6494
- }
6495
- }
6496
- _renameMapKey(oldKey, newKey) {
6497
- if (oldKey !== newKey && this._map.has(oldKey)) {
6498
- this._map.set(newKey, this._map.get(oldKey));
6499
- this._map.delete(oldKey);
6500
- }
6501
- }
6502
- _mergePreviousHeaderNames(request, response) {
6503
- let headersInfluencingResponse = response.ownInfluncingHeaders;
6504
- if (headersInfluencingResponse.length) {
6505
- let previousInfluencingHeaders = this._getPreviousInfluencingHeaders(request);
6506
- for (let headerName of headersInfluencingResponse) {
6507
- previousInfluencingHeaders.add(headerName);
6508
- }
6513
+ if (response)
6514
+ route.updateVary(response);
6515
+ let superseded = route.requests.filter((oldRequest) => route.satisfies(request, oldRequest));
6516
+ for (let r of superseded) {
6517
+ this._delete(r);
6509
6518
  }
6519
+ request.cacheRoute = route;
6520
+ route.put(request);
6521
+ this._requests.push(request);
6522
+ this._limitSize();
6510
6523
  }
6511
6524
  alias(existingCachedRequest, newRequest) {
6512
6525
  existingCachedRequest = this.get(existingCachedRequest);
@@ -6522,7 +6535,7 @@ up.Request.Cache = class Cache {
6522
6535
  newRequest.state = 'tracking';
6523
6536
  let value = await u.always(existingRequest);
6524
6537
  if (value instanceof up.Response) {
6525
- if (options.force || this._isCacheCompatible(existingRequest, newRequest)) {
6538
+ if (options.force || existingRequest.cacheRoute.satisfies(existingRequest, newRequest)) {
6526
6539
  newRequest.fromCache = true;
6527
6540
  value = u.variant(value, { request: newRequest });
6528
6541
  newRequest.respondWith(value);
@@ -6542,31 +6555,43 @@ up.Request.Cache = class Cache {
6542
6555
  willHaveSameResponse(existingRequest, newRequest) {
6543
6556
  return existingRequest === newRequest || existingRequest === newRequest.trackedRequest;
6544
6557
  }
6545
- _delete(request) {
6546
- request = this._wrap(request);
6547
- let cacheKey = this._cacheKey(request);
6548
- this._map.delete(cacheKey);
6549
- }
6550
6558
  evict(condition = true, testerOptions) {
6551
6559
  this._eachMatch(condition, testerOptions, (request) => this._delete(request));
6552
6560
  }
6553
6561
  expire(condition = true, testerOptions) {
6554
6562
  this._eachMatch(condition, testerOptions, (request) => request.expired = true);
6555
6563
  }
6556
- _makeRoom() {
6557
- while (this._map.size >= this._capacity) {
6558
- let oldestKey = this._map.keys().next().value;
6559
- this._map.delete(oldestKey);
6564
+ reindex(request) {
6565
+ this._delete(request);
6566
+ this.put(request);
6567
+ }
6568
+ _delete(request) {
6569
+ u.remove(this._requests, request);
6570
+ request.cacheRoute?.delete(request);
6571
+ delete request.cacheRoute;
6572
+ }
6573
+ _getRoute(request) {
6574
+ return request.cacheRoute || (this._routes[request.description] ||= new Route());
6575
+ }
6576
+ _isUsable(request) {
6577
+ return request.age < up.network.config.cacheEvictAge;
6578
+ }
6579
+ get _size() {
6580
+ return this._requests.length;
6581
+ }
6582
+ get _capacity() {
6583
+ return up.network.config.cacheSize;
6584
+ }
6585
+ _limitSize() {
6586
+ for (let i = 0; i < (this._size - this._capacity); i++) {
6587
+ this._delete(this._requests[0]);
6560
6588
  }
6561
6589
  }
6562
6590
  _eachMatch(condition = true, testerOptions, fn) {
6563
6591
  let tester = up.Request.tester(condition, testerOptions);
6564
- let results = u.filter(this._map.values(), tester);
6592
+ let results = u.filter(this._requests, tester);
6565
6593
  u.each(results, fn);
6566
6594
  }
6567
- _isCacheCompatible(request1, request2) {
6568
- return this._cacheKey(request1) === this._cacheKey(request2);
6569
- }
6570
6595
  _wrap(requestOrOptions) {
6571
6596
  return u.wrapValue(up.Request, requestOrOptions);
6572
6597
  }
@@ -6595,7 +6620,7 @@ up.Request.Queue = class Queue {
6595
6620
  u.always(request, responseOrError => this._onRequestSettled(request, responseOrError));
6596
6621
  this._scheduleSlowTimer(request);
6597
6622
  this._queueRequest(request);
6598
- u.microtask(() => this._poke());
6623
+ queueMicrotask(() => this._poke());
6599
6624
  }
6600
6625
  promoteToForeground(request) {
6601
6626
  if (request.background) {
@@ -6636,7 +6661,7 @@ up.Request.Queue = class Queue {
6636
6661
  up.network.registerAliasForRedirect(request, responseOrError);
6637
6662
  }
6638
6663
  this._checkLate();
6639
- u.microtask(() => this._poke());
6664
+ queueMicrotask(() => this._poke());
6640
6665
  }
6641
6666
  _poke() {
6642
6667
  let request;
@@ -6830,15 +6855,12 @@ up.Response = class Response extends up.Record {
6830
6855
  get none() {
6831
6856
  return !this.text;
6832
6857
  }
6833
- isCacheable() {
6834
- return this.ok && !this.none;
6835
- }
6836
6858
  header(name) {
6837
6859
  return this.headers[name] || this.xhr?.getResponseHeader(name);
6838
6860
  }
6839
- get ownInfluncingHeaders() {
6840
- let influencingHeaders = up.protocol.influencingHeadersFromResponse(this);
6841
- return u.filter(influencingHeaders, (headerName) => this.request.header(headerName));
6861
+ get varyHeaderNames() {
6862
+ let varyHeaderValue = this.header('Vary');
6863
+ return u.parseTokens(varyHeaderValue, { separator: 'comma' });
6842
6864
  }
6843
6865
  get contentType() {
6844
6866
  return this.header('Content-Type');
@@ -6879,6 +6901,7 @@ up.Response = class Response extends up.Record {
6879
6901
  var _a;
6880
6902
  const u = up.util;
6881
6903
  const e = up.element;
6904
+ const FULL_DOCUMENT_PATTERN = /^\s*<(html|!DOCTYPE)\b/i;
6882
6905
  up.ResponseDoc = (_a = class ResponseDoc {
6883
6906
  constructor({ document, fragment, content, target, origin, cspNonces, match }) {
6884
6907
  if (document) {
@@ -6899,43 +6922,43 @@ up.ResponseDoc = (_a = class ResponseDoc {
6899
6922
  }
6900
6923
  this._match = match;
6901
6924
  }
6902
- _parseDocument(document) {
6903
- document = this._parse(document, e.createBrokenDocumentFromHTML);
6904
- this._isDocumentBroken = true;
6905
- this._useParseResult(document);
6906
- }
6907
- _parseFragment(fragment) {
6908
- fragment = this._parse(fragment, e.createFromHTML);
6909
- this._useParseResult(fragment);
6910
- }
6911
- _parseContent(content, target) {
6912
- if (!target)
6913
- up.fail("must pass a { target } when passing { content }");
6914
- target = u.map(up.fragment.parseTargetSteps(target), 'selector').join();
6915
- const matchingElement = e.createFromSelector(target);
6916
- if (u.isString(content)) {
6917
- matchingElement.innerHTML = content;
6925
+ _parseDocument(value) {
6926
+ if (value instanceof Document) {
6927
+ this._document = value;
6928
+ this._isFullDocument = true;
6929
+ }
6930
+ else if (u.isString(value)) {
6931
+ this._document = e.createBrokenDocumentFromHTML(value);
6932
+ this._isFullDocument = FULL_DOCUMENT_PATTERN.test(value);
6933
+ this._isDocumentBroken = true;
6918
6934
  }
6919
6935
  else {
6920
- matchingElement.appendChild(content);
6936
+ this._document = this._buildFauxDocument(value);
6937
+ this._isFullDocument = value.matches('html');
6921
6938
  }
6922
- this._useParseResult(matchingElement);
6923
6939
  }
6924
- _parse(value, parseFn) {
6925
- if (u.isString(value)) {
6926
- value = parseFn(value);
6927
- }
6928
- return value;
6940
+ _parseFragment(value) {
6941
+ let parsed = u.isString(value) ? e.createFromHTML(value) : value;
6942
+ this._document = this._buildFauxDocument(parsed);
6929
6943
  }
6930
- _useParseResult(node) {
6931
- if (node instanceof Document) {
6932
- this._document = node;
6944
+ _parseContent(value, target) {
6945
+ if (!target)
6946
+ up.fail("must pass a { target } when passing { content }");
6947
+ let simplifiedTarget = u.map(up.fragment.parseTargetSteps(target), 'selector').join();
6948
+ const matchingElement = e.createFromSelector(simplifiedTarget);
6949
+ if (u.isString(value)) {
6950
+ matchingElement.innerHTML = value;
6933
6951
  }
6934
6952
  else {
6935
- this._document = document.createElement('up-document');
6936
- this._document.append(node);
6937
- this._document.documentElement = node;
6953
+ matchingElement.appendChild(value);
6938
6954
  }
6955
+ this._document = this._buildFauxDocument(matchingElement);
6956
+ }
6957
+ _buildFauxDocument(node) {
6958
+ let fauxDocument = document.createElement('up-document');
6959
+ fauxDocument.append(node);
6960
+ fauxDocument.documentElement = node;
6961
+ return fauxDocument;
6939
6962
  }
6940
6963
  rootSelector() {
6941
6964
  return up.fragment.toTarget(this._document.documentElement);
@@ -6944,9 +6967,8 @@ up.ResponseDoc = (_a = class ResponseDoc {
6944
6967
  return this._fromHead(this._getTitleText);
6945
6968
  }
6946
6969
  _getHead() {
6947
- let { head } = this._document;
6948
- if (head && head.childNodes.length > 0) {
6949
- return head;
6970
+ if (this._isFullDocument) {
6971
+ return this._document.head;
6950
6972
  }
6951
6973
  }
6952
6974
  _fromHead(fn) {
@@ -6959,6 +6981,11 @@ up.ResponseDoc = (_a = class ResponseDoc {
6959
6981
  get assets() {
6960
6982
  return this._fromHead(up.script.findAssets);
6961
6983
  }
6984
+ get lang() {
6985
+ if (this._isFullDocument) {
6986
+ return up.history.getLang(this._document);
6987
+ }
6988
+ }
6962
6989
  _getTitleText(head) {
6963
6990
  return head.querySelector('title')?.textContent;
6964
6991
  }
@@ -7056,7 +7083,7 @@ up.RevealMotion = class RevealMotion {
7056
7083
  const maxPixels = u.evalOption(this._max, this._element);
7057
7084
  elementRect.height = Math.min(elementRect.height, maxPixels);
7058
7085
  }
7059
- this._addPadding(elementRect);
7086
+ elementRect.grow(this._padding);
7060
7087
  this._substractObstructions(viewportRect);
7061
7088
  if (viewportRect.height < 0) {
7062
7089
  up.fail('Viewport has no visible area');
@@ -7095,10 +7122,6 @@ up.RevealMotion = class RevealMotion {
7095
7122
  return up.Rect.fromElement(this._viewport);
7096
7123
  }
7097
7124
  }
7098
- _addPadding(elementRect) {
7099
- elementRect.top -= this._padding;
7100
- elementRect.height += 2 * this._padding;
7101
- }
7102
7125
  _selectObstructions(selector) {
7103
7126
  let elements = up.fragment.all(selector, { layer: this._obstructionsLayer });
7104
7127
  return u.filter(elements, e.isVisible);
@@ -7646,10 +7669,6 @@ up.protocol = (function () {
7646
7669
  function locationFromXHR(xhr) {
7647
7670
  return extractHeader(xhr, 'location') || xhr.responseURL;
7648
7671
  }
7649
- function influencingHeadersFromResponse(response) {
7650
- let varyHeaderValue = response.header('Vary');
7651
- return u.parseTokens(varyHeaderValue, { separator: 'comma' });
7652
- }
7653
7672
  const config = new up.Config(() => ({
7654
7673
  methodParam: '_method',
7655
7674
  csrfParam() { return e.metaContent('csrf-param'); },
@@ -7710,7 +7729,6 @@ up.protocol = (function () {
7710
7729
  headerize,
7711
7730
  wrapMethod,
7712
7731
  cspNoncesFromHeader,
7713
- influencingHeadersFromResponse,
7714
7732
  };
7715
7733
  })();
7716
7734
 
@@ -7846,44 +7864,61 @@ up.script = (function () {
7846
7864
  let registeredCompilers = [];
7847
7865
  let registeredMacros = [];
7848
7866
  function registerCompiler(...args) {
7849
- const compiler = buildCompiler(args);
7850
- return insertCompiler(registeredCompilers, compiler);
7867
+ registerProcessor(args);
7851
7868
  }
7852
7869
  function registerMacro(...args) {
7853
- const macro = buildCompiler(args);
7854
- if (up.framework.evaling) {
7855
- macro.priority ||= detectSystemMacroPriority(macro.selector) ||
7856
- up.fail('Unregistered priority for system macro %o', macro.selector);
7857
- }
7858
- return insertCompiler(registeredMacros, macro);
7870
+ registerProcessor(args, { macro: true });
7871
+ }
7872
+ function registerAttrCompiler(...args) {
7873
+ let [attr, options, valueCallback] = parseProcessorArgs(args);
7874
+ let selector = `[${attr}]`;
7875
+ let callback = (element) => {
7876
+ let value = e.booleanOrStringAttr(element, attr, options.defaultValue);
7877
+ if (!value)
7878
+ return;
7879
+ return valueCallback(element, value);
7880
+ };
7881
+ registerProcessor([selector, options, callback]);
7859
7882
  }
7860
7883
  function detectSystemMacroPriority(macroSelector) {
7861
7884
  macroSelector = u.evalOption(macroSelector);
7862
7885
  for (let substr in SYSTEM_MACRO_PRIORITIES) {
7863
- const priority = SYSTEM_MACRO_PRIORITIES[substr];
7864
7886
  if (macroSelector.indexOf(substr) >= 0) {
7865
- return priority;
7887
+ return SYSTEM_MACRO_PRIORITIES[substr];
7888
+ }
7889
+ }
7890
+ up.fail('Unregistered priority for system macro %o', macroSelector);
7891
+ }
7892
+ function registerProcessor(args, overrides = {}) {
7893
+ let processor = buildProcessor(args, overrides);
7894
+ if (processor.macro) {
7895
+ if (up.framework.evaling) {
7896
+ processor.priority ||= detectSystemMacroPriority(processor.selector);
7866
7897
  }
7898
+ insertProcessor(registeredMacros, processor);
7899
+ }
7900
+ else {
7901
+ insertProcessor(registeredCompilers, processor);
7867
7902
  }
7868
7903
  }
7869
- const parseCompilerArgs = function (args) {
7904
+ const parseProcessorArgs = function (args) {
7870
7905
  const defaults = u.extractOptions(args);
7871
7906
  const selector = args.shift();
7872
7907
  const callback = args.pop();
7873
7908
  const options = { ...defaults, ...u.extractOptions(args) };
7874
7909
  return [selector, options, callback];
7875
7910
  };
7876
- function buildCompiler(args) {
7877
- let [selector, options, callback] = parseCompilerArgs(args);
7911
+ function buildProcessor(args, overrides) {
7912
+ let [selector, options, callback] = parseProcessorArgs(args);
7878
7913
  options = u.options(options, {
7879
7914
  selector,
7880
7915
  isDefault: up.framework.evaling,
7881
7916
  priority: 0,
7882
7917
  batch: false,
7883
7918
  });
7884
- return Object.assign(callback, options);
7919
+ return Object.assign(callback, options, overrides);
7885
7920
  }
7886
- function insertCompiler(queue, newCompiler) {
7921
+ function insertProcessor(queue, newCompiler) {
7887
7922
  let existingCompiler;
7888
7923
  let index = 0;
7889
7924
  while ((existingCompiler = queue[index]) && (existingCompiler.priority >= newCompiler.priority)) {
@@ -7981,6 +8016,7 @@ up.script = (function () {
7981
8016
  config,
7982
8017
  compiler: registerCompiler,
7983
8018
  macro: registerMacro,
8019
+ attrCompiler: registerAttrCompiler,
7984
8020
  destructor: registerDestructor,
7985
8021
  hello,
7986
8022
  clean,
@@ -7995,6 +8031,7 @@ up.destructor = up.script.destructor;
7995
8031
  up.macro = up.script.macro;
7996
8032
  up.data = up.script.data;
7997
8033
  up.hello = up.script.hello;
8034
+ up.attribute = up.script.attrCompiler;
7998
8035
 
7999
8036
 
8000
8037
  /***/ }),
@@ -8136,6 +8173,15 @@ up.history = (function () {
8136
8173
  document.head.append(newMetaTag);
8137
8174
  }
8138
8175
  }
8176
+ function getLang(doc = document) {
8177
+ let { documentElement } = doc;
8178
+ if (documentElement.matches('html')) {
8179
+ return doc.documentElement.lang;
8180
+ }
8181
+ }
8182
+ function updateLang(newLang) {
8183
+ e.toggleAttr(e.root, 'lang', newLang, !!newLang);
8184
+ }
8139
8185
  up.macro('a[up-back], [up-href][up-back]', function (link) {
8140
8186
  if (previousLocation) {
8141
8187
  e.setMissingAttrs(link, {
@@ -8157,6 +8203,8 @@ up.history = (function () {
8157
8203
  isLocation,
8158
8204
  findMetaTags,
8159
8205
  updateMetaTags,
8206
+ getLang,
8207
+ updateLang,
8160
8208
  };
8161
8209
  })();
8162
8210
 
@@ -8639,6 +8687,25 @@ up.fragment = (function () {
8639
8687
  up.destructor(fragment, unsubscribe);
8640
8688
  return unsubscribe;
8641
8689
  }
8690
+ function onFirstIntersect(origin, callback, { margin = 0 } = {}) {
8691
+ if (e.isIntersectingWindow(origin, { margin })) {
8692
+ callback();
8693
+ return;
8694
+ }
8695
+ function processIntersectEntries(entries) {
8696
+ for (let entry of entries) {
8697
+ if (entry.isIntersecting) {
8698
+ disconnect();
8699
+ callback();
8700
+ return;
8701
+ }
8702
+ }
8703
+ }
8704
+ let observer = new IntersectionObserver(processIntersectEntries, { rootMargin: `${margin}px` });
8705
+ let disconnect = () => observer.disconnect();
8706
+ observer.observe(origin);
8707
+ onAborted(origin, disconnect);
8708
+ }
8642
8709
  up.on('up:framework:boot', function () {
8643
8710
  const { documentElement } = document;
8644
8711
  documentElement.setAttribute('up-source', u.normalizeURL(location.href, { hash: false }));
@@ -8679,6 +8746,7 @@ up.fragment = (function () {
8679
8746
  shouldRevalidate,
8680
8747
  abort,
8681
8748
  onAborted,
8749
+ onFirstIntersect,
8682
8750
  splitTarget,
8683
8751
  parseTargetSteps,
8684
8752
  isAlive,
@@ -8810,33 +8878,9 @@ up.viewport = (function () {
8810
8878
  function isRoot(element) {
8811
8879
  return element === getRoot();
8812
8880
  }
8813
- function rootHasReducedWidthFromScrollbar() {
8814
- return window.innerWidth > document.documentElement.offsetWidth;
8815
- }
8816
- function rootOverflowElement() {
8817
- const { body } = document;
8818
- const html = document.documentElement;
8819
- const element = u.find([html, body], wasChosenAsOverflowingElement);
8820
- return element || getRoot();
8821
- }
8822
- function wasChosenAsOverflowingElement(element) {
8823
- const overflowY = e.style(element, 'overflow-y');
8824
- return overflowY === 'auto' || overflowY === 'scroll';
8825
- }
8826
- const scrollbarWidth = u.memoize(function () {
8827
- const outerStyle = {
8828
- position: 'absolute',
8829
- top: '0',
8830
- left: '0',
8831
- width: '100px',
8832
- height: '100px',
8833
- overflowY: 'scroll'
8834
- };
8835
- const outer = up.element.affix(document.body, '[up-viewport]', { style: outerStyle });
8836
- const width = outer.offsetWidth - outer.clientWidth;
8837
- outer.remove();
8838
- return width;
8839
- });
8881
+ function rootScrollbarWidth() {
8882
+ return window.innerWidth - rootWidth();
8883
+ }
8840
8884
  function scrollTopKey(viewport) {
8841
8885
  return up.fragment.tryToTarget(viewport);
8842
8886
  }
@@ -9013,10 +9057,8 @@ up.viewport = (function () {
9013
9057
  get root() { return getRoot(); },
9014
9058
  rootWidth,
9015
9059
  rootHeight,
9016
- rootHasReducedWidthFromScrollbar,
9017
- rootOverflowElement,
9018
9060
  isRoot,
9019
- scrollbarWidth,
9061
+ rootScrollbarWidth,
9020
9062
  saveScroll,
9021
9063
  restoreScroll,
9022
9064
  resetScroll,
@@ -9100,8 +9142,8 @@ up.motion = (function () {
9100
9142
  return cssTransition.start();
9101
9143
  }
9102
9144
  function applyConfig(options) {
9103
- options.easing ||= config.easing;
9104
- options.duration ||= config.duration;
9145
+ options.easing ??= config.easing;
9146
+ options.duration ??= config.duration;
9105
9147
  }
9106
9148
  function findNamedAnimation(name) {
9107
9149
  return namedAnimations[name] || up.fail("Unknown animation %o", name);
@@ -9368,6 +9410,7 @@ up.network = (function () {
9368
9410
  if (!newRequest.background) {
9369
9411
  queue.promoteToForeground(cachedRequest);
9370
9412
  }
9413
+ cachedRequest.mergeIfUnsent(newRequest);
9371
9414
  cache.track(cachedRequest, newRequest, { onIncompatible: processRequest });
9372
9415
  return true;
9373
9416
  }
@@ -9380,7 +9423,7 @@ up.network = (function () {
9380
9423
  function handleCaching(request) {
9381
9424
  if (request.willCache()) {
9382
9425
  cache.put(request);
9383
- request.onLoading = () => cache.put(request);
9426
+ request.onLoading = () => cache.reindex(request);
9384
9427
  }
9385
9428
  u.always(request, function (responseOrError) {
9386
9429
  let expireCache = responseOrError.expireCache ?? request.expireCache ?? u.evalOption(config.expireCache, request, responseOrError);
@@ -9391,12 +9434,21 @@ up.network = (function () {
9391
9434
  if (evictCache) {
9392
9435
  cache.evict(evictCache, { except: request });
9393
9436
  }
9394
- if (cache.get(request)) {
9395
- cache.put(request);
9437
+ let hasCacheEntry = cache.get(request);
9438
+ let isResponse = responseOrError instanceof up.Response;
9439
+ let isNetworkError = !isResponse;
9440
+ let isSuccessResponse = isResponse && responseOrError.ok;
9441
+ let isErrorResponse = isResponse && !responseOrError.ok;
9442
+ let isEmptyResponse = isResponse && responseOrError.none;
9443
+ if (isErrorResponse) {
9444
+ cache.evict(request.url);
9396
9445
  }
9397
- if (!responseOrError.isCacheable?.()) {
9446
+ else if (isNetworkError || isEmptyResponse) {
9398
9447
  cache.evict(request);
9399
9448
  }
9449
+ else if (isSuccessResponse && hasCacheEntry) {
9450
+ cache.put(request);
9451
+ }
9400
9452
  });
9401
9453
  }
9402
9454
  function isBusy() {
@@ -9413,7 +9465,8 @@ up.network = (function () {
9413
9465
  if (request.cache && response.url && request.url !== response.url) {
9414
9466
  const newRequest = u.variant(request, {
9415
9467
  method: response.method,
9416
- url: response.url
9468
+ url: response.url,
9469
+ cacheRoute: null,
9417
9470
  });
9418
9471
  cache.alias(request, newRequest);
9419
9472
  }
@@ -9717,7 +9770,6 @@ __webpack_require__(96);
9717
9770
  up.link = (function () {
9718
9771
  const u = up.util;
9719
9772
  const e = up.element;
9720
- const linkPreloader = new up.LinkPreloader();
9721
9773
  let lastMousedownTarget = null;
9722
9774
  const LINKS_WITH_LOCAL_HTML = ['a[up-content]', 'a[up-fragment]', 'a[up-document]'];
9723
9775
  const LINKS_WITH_REMOTE_HTML = ['a[href]', '[up-href]'];
@@ -9751,10 +9803,9 @@ up.link = (function () {
9751
9803
  }
9752
9804
  function reset() {
9753
9805
  lastMousedownTarget = null;
9754
- linkPreloader.reset();
9755
9806
  }
9756
- const follow = up.mockable(function (link, options) {
9757
- return up.render(followOptions(link, options));
9807
+ const follow = up.mockable(function (link, options, parserOptions) {
9808
+ return up.render(followOptions(link, options, parserOptions));
9758
9809
  });
9759
9810
  function parseRequestOptions(link, options, parserOptions) {
9760
9811
  options = u.options(options);
@@ -9829,10 +9880,9 @@ up.link = (function () {
9829
9880
  parser.booleanOrString('location');
9830
9881
  parser.booleanOrString('title');
9831
9882
  parser.boolean('metaTags');
9883
+ parser.booleanOrString('lang');
9832
9884
  parser.include(up.motion.motionOptions);
9833
- if (!options.guardEvent) {
9834
- options.guardEvent = up.event.build('up:link:follow', { log: 'Following link' });
9835
- }
9885
+ options.guardEvent ??= up.event.build('up:link:follow', { log: ['Following link %o', link] });
9836
9886
  return options;
9837
9887
  }
9838
9888
  function preload(link, options) {
@@ -9876,9 +9926,10 @@ up.link = (function () {
9876
9926
  if (link.matches('a[href], button')) {
9877
9927
  return;
9878
9928
  }
9929
+ let role = link.matches('a') ? 'link' : 'button';
9879
9930
  e.setMissingAttrs(link, {
9880
9931
  tabindex: '0',
9881
- role: 'link',
9932
+ role,
9882
9933
  'up-clickable': ''
9883
9934
  });
9884
9935
  link.addEventListener('keydown', function (event) {
@@ -9938,6 +9989,42 @@ up.link = (function () {
9938
9989
  const method = followMethod(link);
9939
9990
  return up.network.isSafeMethod(method);
9940
9991
  }
9992
+ function onLoadCondition(condition, link, callback) {
9993
+ switch (condition) {
9994
+ case 'insert':
9995
+ callback();
9996
+ break;
9997
+ case 'reveal': {
9998
+ let margin = e.numberAttr(link, 'up-intersect-margin');
9999
+ up.fragment.onFirstIntersect(link, callback, { margin });
10000
+ break;
10001
+ }
10002
+ case 'hover':
10003
+ new up.LinkFollowIntent(link, callback);
10004
+ break;
10005
+ case 'manual':
10006
+ break;
10007
+ }
10008
+ }
10009
+ function loadDeferred(link, options) {
10010
+ let guardEvent = up.event.build('up:deferred:load', { log: ['Loading deferred %o', link] });
10011
+ let forcedOptions = {
10012
+ navigate: false,
10013
+ guardEvent,
10014
+ ...options,
10015
+ };
10016
+ let defaults = {
10017
+ target: ':origin',
10018
+ cache: 'auto',
10019
+ revalidate: 'auto',
10020
+ feedback: true,
10021
+ };
10022
+ return follow(link, forcedOptions, { defaults });
10023
+ }
10024
+ up.attribute('up-defer', { defaultValue: 'insert' }, function (link, condition) {
10025
+ let doLoad = () => up.error.muteUncriticalRejection(loadDeferred(link));
10026
+ onLoadCondition(condition, link, doLoad);
10027
+ });
9941
10028
  up.on('up:click', config.selectorFn('followSelectors'), function (event, link) {
9942
10029
  if (shouldFollowEvent(event, link)) {
9943
10030
  up.event.halt(event, { log: true });
@@ -9945,21 +10032,22 @@ up.link = (function () {
9945
10032
  up.error.muteUncriticalRejection(follow(link));
9946
10033
  }
9947
10034
  });
9948
- up.macro('[up-expand]', function (area) {
9949
- const selector = area.getAttribute('up-expand') || 'a, [up-href]';
9950
- let childLink = e.get(area, selector);
10035
+ up.attribute('up-expand', { defaultValue: 'a, [up-href]', macro: true }, function (area, childLinkSelector) {
10036
+ let childLink = e.get(area, childLinkSelector);
9951
10037
  if (childLink) {
9952
- const areaAttrs = e.upAttrs(childLink);
9953
- areaAttrs['up-href'] ||= childLink.getAttribute('href');
9954
- e.setMissingAttrs(area, areaAttrs);
9955
- const areaClasses = e.upClasses(childLink);
9956
- area.classList.add(...areaClasses);
10038
+ e.setMissingAttrs(area, {
10039
+ 'up-href': e.attr(childLink, 'href'),
10040
+ ...e.upAttrs(childLink)
10041
+ });
10042
+ area.classList.add(...e.upClasses(childLink));
9957
10043
  makeFollowable(area);
9958
10044
  }
9959
10045
  });
9960
10046
  up.compiler(config.selectorFn('preloadSelectors'), function (link) {
9961
10047
  if (!isPreloadDisabled(link)) {
9962
- linkPreloader.watchLink(link);
10048
+ let doPreload = () => up.error.muteUncriticalRejection(preload(link));
10049
+ let condition = e.booleanOrStringAttr(link, 'up-preload', null) ?? 'hover';
10050
+ onLoadCondition(condition, link, doPreload);
9963
10051
  }
9964
10052
  });
9965
10053
  up.on('up:framework:reset', reset);
@@ -9968,18 +10056,17 @@ up.link = (function () {
9968
10056
  followOptions,
9969
10057
  preload,
9970
10058
  makeFollowable,
9971
- makeClickable,
9972
10059
  isSafe,
9973
10060
  isFollowable,
9974
10061
  shouldFollowEvent,
9975
- followMethod,
9976
10062
  convertClicks,
9977
10063
  config,
9978
10064
  combineFollowableSelectors,
9979
- preloadIssue,
10065
+ loadDeferred,
9980
10066
  };
9981
10067
  })();
9982
10068
  up.follow = up.link.follow;
10069
+ up.deferred = { load: up.link.loadDeferred };
9983
10070
 
9984
10071
 
9985
10072
  /***/ }),
@@ -10055,7 +10142,8 @@ up.form = (function () {
10055
10142
  options.guardEvent ||= up.event.build('up:form:submit', {
10056
10143
  submitButton: options.submitButton,
10057
10144
  log: 'Submitting form',
10058
- params: options.params
10145
+ params: options.params,
10146
+ form,
10059
10147
  });
10060
10148
  options.origin ||= up.viewport.focusedElementWithin(form) || options.submitButton || form;
10061
10149
  parser.include(up.link.followOptions);
@@ -10328,7 +10416,9 @@ up.form = (function () {
10328
10416
  validator.watchContainer(fieldOrForm);
10329
10417
  });
10330
10418
  function validatingFieldSelector() {
10331
- return config.fieldSelectors.map((selector) => `${selector}[up-validate], [up-validate] ${selector}`).join(', ');
10419
+ let includes = config.fieldSelectors.map((selector) => `${selector}[up-validate], [up-validate] ${selector}`);
10420
+ let excludes = ['[up-validate=false]'];
10421
+ return e.unionSelector(includes, excludes);
10332
10422
  }
10333
10423
  up.compiler('[up-switch]', (switcher) => {
10334
10424
  switchTargets(switcher);
@@ -10339,8 +10429,8 @@ up.form = (function () {
10339
10429
  up.compiler('[up-show-for]:not(.up-switched), [up-hide-for]:not(.up-switched)', (element) => {
10340
10430
  switchTarget(element);
10341
10431
  });
10342
- up.compiler('[up-watch]', (formOrField) => watch(formOrField));
10343
- up.compiler('[up-autosubmit]', (formOrField) => autosubmit(formOrField));
10432
+ up.attribute('up-watch', (formOrField) => watch(formOrField));
10433
+ up.attribute('up-autosubmit', (formOrField) => autosubmit(formOrField));
10344
10434
  return {
10345
10435
  config,
10346
10436
  submit,
@@ -10382,6 +10472,7 @@ up.feedback = (function () {
10382
10472
  const config = new up.Config(() => ({
10383
10473
  currentClasses: ['up-current'],
10384
10474
  navSelectors: ['[up-nav]', 'nav'],
10475
+ noNavSelectors: ['[up-nav=false]'],
10385
10476
  }));
10386
10477
  function reset() {
10387
10478
  up.layer.root.feedbackLocation = null;
@@ -10389,9 +10480,6 @@ up.feedback = (function () {
10389
10480
  const CLASS_ACTIVE = 'up-active';
10390
10481
  const CLASS_LOADING = 'up-loading';
10391
10482
  const SELECTOR_LINK = 'a, [up-href]';
10392
- function navSelector() {
10393
- return config.selector('navSelectors');
10394
- }
10395
10483
  function normalizeURL(url) {
10396
10484
  if (url) {
10397
10485
  return u.normalizeURL(url, { trailingSlash: false, hash: false });
@@ -10400,40 +10488,23 @@ up.feedback = (function () {
10400
10488
  function linkURLs(link) {
10401
10489
  return link.upFeedbackURLs ||= new up.LinkFeedbackURLs(link);
10402
10490
  }
10403
- function updateFragment(fragment) {
10404
- const layerOption = { layer: up.layer.get(fragment) };
10405
- if (up.fragment.closest(fragment, navSelector(), layerOption)) {
10406
- const links = up.fragment.subtree(fragment, SELECTOR_LINK, layerOption);
10407
- updateLinks(links, layerOption);
10408
- }
10409
- else {
10410
- updateLinksWithinNavs(fragment, layerOption);
10491
+ function updateFragment(fragment, { layer } = {}) {
10492
+ layer ||= up.layer.get(fragment);
10493
+ let layerLocation = getNormalizedLayerLocation(layer);
10494
+ const navSelector = config.selector('navSelectors');
10495
+ const navLinkSelector = `${navSelector} :is(${SELECTOR_LINK}), ${navSelector}:is(${SELECTOR_LINK})`;
10496
+ const links = up.fragment.all(navLinkSelector, { layer });
10497
+ for (let link of links) {
10498
+ const isCurrent = linkURLs(link).isCurrent(layerLocation);
10499
+ for (let currentClass of config.currentClasses) {
10500
+ link.classList.toggle(currentClass, isCurrent);
10501
+ }
10502
+ e.toggleAttr(link, 'aria-current', 'page', isCurrent);
10411
10503
  }
10412
10504
  }
10413
- function updateLinksWithinNavs(fragment, options) {
10414
- const navs = up.fragment.subtree(fragment, navSelector(), options);
10415
- const links = u.flatMap(navs, nav => e.subtree(nav, SELECTOR_LINK));
10416
- updateLinks(links, options);
10417
- }
10418
10505
  function getNormalizedLayerLocation(layer) {
10419
10506
  return layer.feedbackLocation || normalizeURL(layer.location);
10420
10507
  }
10421
- function updateLinks(links, options = {}) {
10422
- if (!links.length) {
10423
- return;
10424
- }
10425
- const layer = options.layer || up.layer.get(links[0]);
10426
- let layerLocation = getNormalizedLayerLocation(layer);
10427
- if (layerLocation) {
10428
- for (let link of links) {
10429
- const isCurrent = linkURLs(link).isCurrent(layerLocation);
10430
- for (let currentClass of config.currentClasses) {
10431
- link.classList.toggle(currentClass, isCurrent);
10432
- }
10433
- e.toggleAttr(link, 'aria-current', 'page', isCurrent);
10434
- }
10435
- }
10436
- }
10437
10508
  function findActivatableArea(element) {
10438
10509
  return e.ancestor(element, SELECTOR_LINK) || element;
10439
10510
  }
@@ -10461,7 +10532,7 @@ up.feedback = (function () {
10461
10532
  const layerLocation = getNormalizedLayerLocation(layer.location);
10462
10533
  if (!processedLocation || (processedLocation !== layerLocation)) {
10463
10534
  layer.feedbackLocation = layerLocation;
10464
- updateLinksWithinNavs(layer.element, { layer });
10535
+ updateFragment(layer.element, { layer });
10465
10536
  }
10466
10537
  }
10467
10538
  function onBrowserLocationChanged() {
@@ -10496,6 +10567,7 @@ up.radio = (function () {
10496
10567
  const e = up.element;
10497
10568
  const config = new up.Config(() => ({
10498
10569
  hungrySelectors: ['[up-hungry]'],
10570
+ noHungrySelectors: ['[up-hungry=false]'],
10499
10571
  pollInterval: 30000,
10500
10572
  }));
10501
10573
  function hungrySteps(renderOptions) {
@@ -10552,7 +10624,7 @@ up.radio = (function () {
10552
10624
  parser.string('ifLayer', { default: 'front' });
10553
10625
  return options;
10554
10626
  }
10555
- up.compiler('[up-poll]:not([up-poll=false])', function (fragment) {
10627
+ up.attribute('up-poll', function (fragment) {
10556
10628
  up.FragmentPolling.forFragment(fragment).onPollAttributeObserved();
10557
10629
  });
10558
10630
  up.macro('[up-flashes]', function (fragment) {