unpoly-rails 3.7.3.2 → 3.8.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
@@ -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) {