@pacem/pacem-core 1.0.0-bernoulli → 1.0.0-binet
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.
- package/dist/browser/pacem-core.js +919 -32
- package/dist/browser/pacem-core.js.map +1 -1
- package/dist/browser/pacem-core.min.js +2 -3
- package/dist/bundle/pacem-core.min.mjs +6 -6
- package/dist/bundle/pacem-core.mjs +510 -22
- package/dist/bundle/pacem-core.mjs.map +2 -2
- package/dist/docs/pacem-core.json +40109 -0
- package/dist/esm/adapter.js +2 -1
- package/dist/esm/command.js +6 -1
- package/dist/esm/comparers.js +1 -1
- package/dist/esm/converters.js +6 -1
- package/dist/esm/decorators-next.js +1 -1
- package/dist/esm/decorators.js +94 -1
- package/dist/esm/defaults.js +2 -1
- package/dist/esm/events.js +55 -1
- package/dist/esm/eventtarget.js +11 -1
- package/dist/esm/expression.js +37 -1
- package/dist/esm/index-animations.js +1 -1
- package/dist/esm/index-behaviors.js +1 -1
- package/dist/esm/index-components.js +1 -1
- package/dist/esm/index-iife.js +1 -1
- package/dist/esm/index-logging.js +1 -1
- package/dist/esm/index-net.js +1 -1
- package/dist/esm/index-root.js +1 -1
- package/dist/esm/index-ui.js +1 -1
- package/dist/esm/index.js +1 -1
- package/dist/esm/logger.js +8 -1
- package/dist/esm/prefix.js +2 -1
- package/dist/esm/promise.js +11 -1
- package/dist/esm/regex.js +3 -1
- package/dist/esm/storage.js +14 -1
- package/dist/esm/template.js +2 -1
- package/dist/esm/transforms.js +53 -1
- package/dist/esm/types.js +6 -1
- package/dist/esm/utils-customelement.js +165 -6
- package/dist/esm/utils-customevent.js +27 -1
- package/dist/esm/utils.js +96 -1
- package/package.json +2 -1
- package/typings/index.d.ts +1105 -20
- package/dist/browser/pacem-core.min.js.map +0 -1
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
/*!
|
|
2
|
-
* @pacem/pacem-core v1.0.0-
|
|
2
|
+
* @pacem/pacem-core v1.0.0-binet (https://js.pacem.it)
|
|
3
3
|
* Pacem (https://pacem.it)
|
|
4
4
|
* Licensed under Apache-2.0
|
|
5
5
|
*/
|
|
@@ -24,22 +24,39 @@
|
|
|
24
24
|
output['css'] = css;
|
|
25
25
|
return output;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Collection of ready-made {@link Easing} functions, each paired with its CSS timing-function equivalent.
|
|
29
|
+
*/
|
|
27
30
|
const Easings = {
|
|
31
|
+
/** No easing: progress is proportional to elapsed time. */
|
|
28
32
|
linear: assembleEasing(EASINGS.linear, "linear"),
|
|
33
|
+
/** Sine ease-in: starts slow, accelerates towards the end. */
|
|
29
34
|
sineIn: assembleEasing(EASINGS.sineIn, 'cubic-bezier(0.12, 0, 0.39, 0)'),
|
|
35
|
+
/** Sine ease-out: starts fast, decelerates towards the end. */
|
|
30
36
|
sineOut: assembleEasing(EASINGS.sineOut, 'cubic-bezier(0.61, 1, 0.88, 1)'),
|
|
37
|
+
/** Sine ease-in-out: accelerates then decelerates, symmetric around the midpoint. */
|
|
31
38
|
sineInOut: assembleEasing(EASINGS.sineInOut, 'cubic-bezier(0.37, 0, 0.63, 1)'),
|
|
32
39
|
// TODO: set correct pairing
|
|
40
|
+
/** Exponential ease-in: starts very slow, accelerates sharply towards the end. */
|
|
33
41
|
expoIn: assembleEasing(EASINGS.expoIn, 'cubic-bezier(0.7, 0, 0.84, 0)'),
|
|
42
|
+
/** Exponential ease-out: starts very fast, decelerates sharply towards the end. */
|
|
34
43
|
expoOut: assembleEasing(EASINGS.expoOut, 'cubic-bezier(0.16, 1, 0.3, 1)'),
|
|
44
|
+
/** Exponential ease-in-out: sharp acceleration then sharp deceleration, symmetric around the midpoint. */
|
|
35
45
|
expoInOut: assembleEasing(EASINGS.expoInOut, 'cubic-bezier(0.87, 0, 0.13, 1)'),
|
|
36
46
|
};
|
|
37
47
|
//}
|
|
38
48
|
|
|
39
49
|
// namespace Pacem {
|
|
50
|
+
/**
|
|
51
|
+
* Recursively clones plain objects, arrays, `Date`s and `RegExp`s, preserving reference cycles/shared
|
|
52
|
+
* references within a single `clone` call (via an internal `WeakMap`). DOM `Element`s, `FileSystemHandle`s
|
|
53
|
+
* and `File`s are returned as-is (not cloned) - they aren't meaningfully deep-cloneable and are typically
|
|
54
|
+
* meant to be referenced, not duplicated.
|
|
55
|
+
*/
|
|
40
56
|
class DeepCloner {
|
|
41
57
|
constructor() {
|
|
42
58
|
}
|
|
59
|
+
/** Deep-clones `obj`; see {@link DeepCloner} for what gets cloned vs. passed through by reference. */
|
|
43
60
|
static clone(obj) {
|
|
44
61
|
return new DeepCloner()._clone(obj);
|
|
45
62
|
}
|
|
@@ -110,13 +127,27 @@
|
|
|
110
127
|
this._jsonFnWeakMap.delete(fn);
|
|
111
128
|
}
|
|
112
129
|
}
|
|
130
|
+
/**
|
|
131
|
+
* A `JSON.stringify`/`JSON.parse` superset able to round-trip values plain JSON can't represent: circular/
|
|
132
|
+
* shared object references (`$#ref_N` markers), `RegExp` instances, DOM `Element`s (serialized by `id`
|
|
133
|
+
* and revived via `document.getElementById`, so they must have one), and - depending on
|
|
134
|
+
* {@link JsonSerializationOptions.functions} - functions themselves. The source object is deep-cloned
|
|
135
|
+
* first (via {@link DeepCloner}) so serialization never mutates the input.
|
|
136
|
+
*/
|
|
113
137
|
class Json {
|
|
114
138
|
constructor(_options) {
|
|
115
139
|
this._options = _options;
|
|
116
140
|
}
|
|
141
|
+
/**
|
|
142
|
+
* Serializes `obj` to a JSON string, extended to support circular/shared references, `RegExp`s,
|
|
143
|
+
* `Element`s (by `id`) and, per `options`, functions.
|
|
144
|
+
* @param obj Value to serialize.
|
|
145
|
+
* @param options Controls how function-valued properties are handled.
|
|
146
|
+
*/
|
|
117
147
|
static serialize(obj, options) {
|
|
118
148
|
return new Json(options)._serialize(obj);
|
|
119
149
|
}
|
|
150
|
+
/** Deserializes a string produced by {@link serialize}, restoring references, `RegExp`s, `Element`s and functions. */
|
|
120
151
|
static deserialize(json) {
|
|
121
152
|
return new Json()._deserialize(json);
|
|
122
153
|
}
|
|
@@ -240,6 +271,12 @@
|
|
|
240
271
|
//}
|
|
241
272
|
|
|
242
273
|
//namespace Pacem {
|
|
274
|
+
/**
|
|
275
|
+
* A `PromiseLike<T>` whose resolution is externalized: unlike a plain `Promise`, the executor isn't
|
|
276
|
+
* passed a callback up front - instead, `resolve`/`reject` are exposed for the caller to invoke later,
|
|
277
|
+
* via the {@link defer} static factory. Useful when the completion of an operation is driven by code
|
|
278
|
+
* that doesn't naturally live inside a `Promise` executor (e.g. event callbacks, decorator plumbing).
|
|
279
|
+
*/
|
|
243
280
|
class DeferPromise {
|
|
244
281
|
constructor() {
|
|
245
282
|
this.deferred = null;
|
|
@@ -248,6 +285,7 @@
|
|
|
248
285
|
me.deferred = { 'resolve': resolve, 'reject': reject, 'promise': me };
|
|
249
286
|
});
|
|
250
287
|
}
|
|
288
|
+
/** Standard `PromiseLike.then`, delegating to the underlying native `Promise`. */
|
|
251
289
|
then(onCompleted, onFailed) {
|
|
252
290
|
return this.promise.then(onCompleted, onFailed);
|
|
253
291
|
}
|
|
@@ -259,14 +297,17 @@
|
|
|
259
297
|
this.promise.then(callback, callback);
|
|
260
298
|
return this;
|
|
261
299
|
}
|
|
300
|
+
/** Registers a fulfillment-only callback (fluent, chainable alternative to `then(callback)`). */
|
|
262
301
|
success(callback) {
|
|
263
302
|
this.promise.then(callback, null);
|
|
264
303
|
return this;
|
|
265
304
|
}
|
|
305
|
+
/** Registers a rejection-only callback (fluent, chainable alternative to `then(null, callback)`). */
|
|
266
306
|
error(callback) {
|
|
267
307
|
this.promise.then(null, callback);
|
|
268
308
|
return this;
|
|
269
309
|
}
|
|
310
|
+
/** Creates a new deferred promise and returns its controller: `{ resolve, reject, promise }`. */
|
|
270
311
|
static defer() {
|
|
271
312
|
var q = new DeferPromise();
|
|
272
313
|
return q.deferred;
|
|
@@ -277,40 +318,56 @@
|
|
|
277
318
|
// namespace Pacem {
|
|
278
319
|
const PACEM_CORE_DEFAULT = 'pacem';
|
|
279
320
|
const DEFAULT_DOWNLOAD_FILENAME = 'download';
|
|
321
|
+
/** Ready-to-use event listener that only calls `evt.stopPropagation()`; handy as an `addEventListener` callback without allocating a closure. */
|
|
280
322
|
const stopPropagationHandler = (evt) => {
|
|
281
323
|
evt.stopPropagation();
|
|
282
324
|
};
|
|
325
|
+
/** Ready-to-use event listener that only calls `evt.preventDefault()`; handy as an `addEventListener` callback without allocating a closure. */
|
|
283
326
|
const preventDefaultHandler = (evt) => {
|
|
284
327
|
evt.preventDefault();
|
|
285
328
|
};
|
|
329
|
+
/** Ready-to-use event listener that calls both `evt.preventDefault()` and `evt.stopPropagation()`. */
|
|
286
330
|
const avoidHandler = (evt) => {
|
|
287
331
|
evt.preventDefault();
|
|
288
332
|
evt.stopPropagation();
|
|
289
333
|
};
|
|
290
334
|
const SEGMENT_PATTERN = /^\/[^\/]+/;
|
|
291
335
|
const ROUTE_SEGMENT_PATTERN = /\/\{([a-z\$_][\w]*)\??\}/;
|
|
336
|
+
/**
|
|
337
|
+
* The framework's static utility grab-bag: general helpers plus namespaced sub-modules (`Json`, `Dates`,
|
|
338
|
+
* `Css`, `Strings`, `Blobs`, `Images`, `Browsers`, `Cookies`, `URIs`) re-exposing (and in some cases
|
|
339
|
+
* extending) functionality from `@pacem/pacem-foundation`. Also the home of `Utils.core`, the global
|
|
340
|
+
* registry object used for cross-cutting framework state (e.g. registered {@link Transformer}s).
|
|
341
|
+
*/
|
|
292
342
|
class Utils {
|
|
343
|
+
/** The framework's global scratch/registry object (`window[id]`, where `id` defaults to `'pacem'` and is configurable via `window.Pacem.Configuration.core`); created on first access. Used e.g. to store {@link Transformer}-registered transform functions. */
|
|
293
344
|
static get core() {
|
|
294
345
|
const root = window['Pacem'];
|
|
295
346
|
var id = (root && root.Configuration && root.Configuration.core) || PACEM_CORE_DEFAULT;
|
|
296
347
|
return window[id] = window[id] || {};
|
|
297
348
|
}
|
|
349
|
+
/** The platform's (native or polyfilled) `customElements` registry. */
|
|
298
350
|
static get customElements() {
|
|
299
351
|
return window['customElements'];
|
|
300
352
|
}
|
|
301
353
|
// #region GENERAL
|
|
354
|
+
/** Generates a unique numeric-ish id string. */
|
|
302
355
|
static uniqueId() {
|
|
303
356
|
return pacemFoundation.Keys.uniqueId();
|
|
304
357
|
}
|
|
358
|
+
/** Generates a unique short code string. */
|
|
305
359
|
static uniqueCode() {
|
|
306
360
|
return pacemFoundation.Keys.uniqueCode();
|
|
307
361
|
}
|
|
362
|
+
/** Generates a random string of the given `length`. */
|
|
308
363
|
static randomString(length) {
|
|
309
364
|
return pacemFoundation.Keys.randomString(length);
|
|
310
365
|
}
|
|
366
|
+
/** Parses a date-like value (ISO string, `Date`, or timestamp) into a `Date`. */
|
|
311
367
|
static parseDate(input) {
|
|
312
368
|
return pacemFoundation.Dates.parse(input);
|
|
313
369
|
}
|
|
370
|
+
/** Writes `input` to the system clipboard, using the async Clipboard API when available and falling back to `document.execCommand('copy')` otherwise. */
|
|
314
371
|
static copyToClipboard(input) {
|
|
315
372
|
if (navigator.clipboard) {
|
|
316
373
|
return navigator.clipboard.writeText(input);
|
|
@@ -378,6 +435,7 @@
|
|
|
378
435
|
return filter;
|
|
379
436
|
}
|
|
380
437
|
}
|
|
438
|
+
/** Escapes `input` for safe use as a CSS identifier/selector fragment. */
|
|
381
439
|
static cssEscape(input) {
|
|
382
440
|
return escape(input).replace('%', '\\');
|
|
383
441
|
}
|
|
@@ -385,9 +443,11 @@
|
|
|
385
443
|
static { this.Css = {
|
|
386
444
|
colorize: Utils.colorize,
|
|
387
445
|
escape: Utils.cssEscape,
|
|
446
|
+
/** Reads a CSS custom property's current value off the document root (`:root`/`html`). */
|
|
388
447
|
getVariableValue(name) {
|
|
389
448
|
return getComputedStyle(document.documentElement).getPropertyValue(name);
|
|
390
449
|
},
|
|
450
|
+
/** Sets a CSS custom property's value on the document root (`:root`/`html`). */
|
|
391
451
|
setVariable(name, value) {
|
|
392
452
|
getComputedStyle(document.documentElement).setProperty(name, value);
|
|
393
453
|
},
|
|
@@ -417,6 +477,7 @@
|
|
|
417
477
|
}
|
|
418
478
|
return false;
|
|
419
479
|
},
|
|
480
|
+
/** Parses an element's (or style declaration's) computed `transform: matrix(...)` CSS value into its 2D affine matrix components; identity if unset. */
|
|
420
481
|
deserializeTransform(element) {
|
|
421
482
|
const style = element instanceof Element ? getComputedStyle(element) : element;
|
|
422
483
|
let a = 1, b = 0, c = 0, d = 1, x = 0, y = 0, matches = /^matrix\((.*)\)$/.exec(style.transform), mij;
|
|
@@ -443,6 +504,7 @@
|
|
|
443
504
|
leftPad: pacemFoundation.Strings.leftPad,
|
|
444
505
|
format: pacemFoundation.Strings.format,
|
|
445
506
|
diff: pacemFoundation.Strings.diff,
|
|
507
|
+
/** Strips HTML markup from `html`, returning its plain-text content. */
|
|
446
508
|
stripHTML: (html) => {
|
|
447
509
|
let doc = new DOMParser().parseFromString(html, 'text/html');
|
|
448
510
|
return doc.body.textContent || '';
|
|
@@ -461,6 +523,7 @@
|
|
|
461
523
|
load: pacemFoundation.Imaging.loadImage,
|
|
462
524
|
resize: pacemFoundation.Imaging.resizeImage
|
|
463
525
|
}; }
|
|
526
|
+
/** Loads an image from `src` and resolves with the `HTMLImageElement` once ready (or rejects on error). */
|
|
464
527
|
static loadImage(src) {
|
|
465
528
|
return new Promise((resolve, reject) => {
|
|
466
529
|
const img = new Image();
|
|
@@ -478,15 +541,19 @@
|
|
|
478
541
|
}
|
|
479
542
|
});
|
|
480
543
|
}
|
|
544
|
+
/** Converts a `Blob` to a `data:` URL string. */
|
|
481
545
|
static blobToDataURL(blob) {
|
|
482
546
|
return Utils.Blobs.toDataURL(blob);
|
|
483
547
|
}
|
|
548
|
+
/** Converts a `data:` URL string back into a `Blob`. */
|
|
484
549
|
static dataURLToBlob(dataURL) {
|
|
485
550
|
return Utils.Blobs.fromDataURL(dataURL);
|
|
486
551
|
}
|
|
552
|
+
/** Reads a `Blob`'s content as text, using the given `encoding` (default UTF-8). */
|
|
487
553
|
static blobToText(blob, encoding) {
|
|
488
554
|
return Utils.Blobs.toText(blob, encoding);
|
|
489
555
|
}
|
|
556
|
+
/** Wraps `content` in a `Blob` of the given MIME `type` (default `'text/plain'`). */
|
|
490
557
|
static textToBlob(content, type = 'text/plain') {
|
|
491
558
|
return Utils.Blobs.fromText(content, type);
|
|
492
559
|
}
|
|
@@ -572,6 +639,7 @@
|
|
|
572
639
|
el.src = url;
|
|
573
640
|
return deferred.promise;
|
|
574
641
|
}
|
|
642
|
+
/** Returns the list of vendor-prefixed `getUserMedia` implementations available on `navigator`, if any. */
|
|
575
643
|
static getUserMediaFunctions() {
|
|
576
644
|
var _getUserMedia = [];
|
|
577
645
|
let methods = [navigator['getUserMedia'], navigator['webkitGetUserMedia'], navigator['msGetUserMedia'], navigator['mozGetUserMedia']];
|
|
@@ -818,10 +886,12 @@
|
|
|
818
886
|
}
|
|
819
887
|
// #endregion
|
|
820
888
|
// #region DOM
|
|
889
|
+
/** Whether the document (including module scripts) has fully finished loading (`document.readyState === 'complete'`). */
|
|
821
890
|
static isDOMReady() {
|
|
822
891
|
// Must exclude 'interactive' state since module script haven't been processed yet.
|
|
823
892
|
return /* document.readyState === 'interactive' ||*/ document.readyState === 'complete';
|
|
824
893
|
}
|
|
894
|
+
/** Registers `listener` to run once the document (and its resources, e.g. images) has fully loaded (the `window` `'load'` event). */
|
|
825
895
|
static onDOMReady(listener) {
|
|
826
896
|
//window.addEventListener('DOMContentLoaded', listener, false);
|
|
827
897
|
// window 'load' event fires after 'DOMCOntentLoaded' since includes images load.
|
|
@@ -850,13 +920,16 @@
|
|
|
850
920
|
}
|
|
851
921
|
return dom;
|
|
852
922
|
}
|
|
923
|
+
/** Cross-browser `Element.matches(selector)`, falling back to vendor-prefixed variants. */
|
|
853
924
|
static is(el, selector) {
|
|
854
925
|
return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector)
|
|
855
926
|
.call(el, selector);
|
|
856
927
|
}
|
|
928
|
+
/** Resolves the effective language for `el`: its own `lang` attribute, else the document's, else the browser's. */
|
|
857
929
|
static lang(el) {
|
|
858
930
|
return el.lang || document.documentElement.lang || navigator.language;
|
|
859
931
|
}
|
|
932
|
+
/** JSON-stringifies `obj` (via {@link Json}), or passes through `null`/`undefined` unchanged. */
|
|
860
933
|
static jsonSortStringify(obj) {
|
|
861
934
|
if (Utils.isNull(obj)) {
|
|
862
935
|
return obj;
|
|
@@ -866,6 +939,7 @@
|
|
|
866
939
|
static { this.Browsers = {
|
|
867
940
|
detect: pacemFoundation.detect
|
|
868
941
|
}; }
|
|
942
|
+
/** Parses a `document.cookie`-style string (or `document.cookie` itself, by default) into a name/value map. */
|
|
869
943
|
static cookies(cookie) {
|
|
870
944
|
const cookies = (cookie ?? document.cookie).split(';'), retval = {};
|
|
871
945
|
for (var pair of cookies) {
|
|
@@ -881,7 +955,9 @@
|
|
|
881
955
|
}
|
|
882
956
|
return retval;
|
|
883
957
|
}
|
|
958
|
+
/** Cookie helpers, preferring the native `cookieStore` API when available and falling back to `document.cookie` parsing otherwise. */
|
|
884
959
|
static { this.Cookies = {
|
|
960
|
+
/** Retrieves all cookies (or just `cookieName`, if given) as {@link CookieData} entries. */
|
|
885
961
|
getAll: async (cookieName) => {
|
|
886
962
|
const store = 'cookieStore' in window ? window.cookieStore : null;
|
|
887
963
|
const retval = [];
|
|
@@ -899,6 +975,7 @@
|
|
|
899
975
|
return await store.getAll(cookieName);
|
|
900
976
|
}
|
|
901
977
|
},
|
|
978
|
+
/** Serializes a {@link CookieData} descriptor into a `document.cookie`-assignable string (including `domain`/`path`/`max-age`/`expires`/`Partitioned`/`SameSite`/`Secure` attributes, as provided). */
|
|
902
979
|
stringify: (data) => {
|
|
903
980
|
let retval = `${data.name}=${encodeURIComponent(data.value ?? '')}`;
|
|
904
981
|
if (!Utils.isNullOrEmpty(data.domain)) {
|
|
@@ -925,6 +1002,7 @@
|
|
|
925
1002
|
return retval;
|
|
926
1003
|
}
|
|
927
1004
|
}; }
|
|
1005
|
+
/** Tests whether `el` is currently visible (via the `checkVisibility` API when available, falling back to a CSS-visibility/size heuristic). */
|
|
928
1006
|
static isVisible(el) {
|
|
929
1007
|
if ('checkVisibility' in el) {
|
|
930
1008
|
return el.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true });
|
|
@@ -932,6 +1010,7 @@
|
|
|
932
1010
|
return getComputedStyle(el).visibility !== 'hidden' && (el.clientWidth > 0 || el.clientHeight > 0);
|
|
933
1011
|
}
|
|
934
1012
|
//#region classList DOMTokenList (css)
|
|
1013
|
+
/** Tests whether `el` has `className` set, using `classList` when available (with a regex-based fallback). */
|
|
935
1014
|
static hasClass(el, className) {
|
|
936
1015
|
if (el.classList) {
|
|
937
1016
|
return el.classList.contains(className);
|
|
@@ -940,6 +1019,7 @@
|
|
|
940
1019
|
return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
|
|
941
1020
|
}
|
|
942
1021
|
}
|
|
1022
|
+
/** Adds one or more (space-separated) classes to `el`, using `classList` when available. */
|
|
943
1023
|
static addClass(el, className) {
|
|
944
1024
|
const css = className.trim();
|
|
945
1025
|
if (el.classList) {
|
|
@@ -949,6 +1029,7 @@
|
|
|
949
1029
|
el.setAttribute('class', el.className + ' ' + css);
|
|
950
1030
|
}
|
|
951
1031
|
}
|
|
1032
|
+
/** Removes one or more (space-separated) classes from `el`, using `classList` when available. */
|
|
952
1033
|
static removeClass(el, className) {
|
|
953
1034
|
const css = className.trim();
|
|
954
1035
|
if (el.classList) {
|
|
@@ -958,6 +1039,7 @@
|
|
|
958
1039
|
el.setAttribute('class', el.className.replace(new RegExp('(^|\\b)' + css.split(' ').join('|') + '(\\b|$)', 'gi'), ' '));
|
|
959
1040
|
}
|
|
960
1041
|
}
|
|
1042
|
+
/** Adds or removes `className` on `el` depending on `on`; see {@link addClass}/{@link removeClass}. */
|
|
961
1043
|
static toggleClass(el, className, on) {
|
|
962
1044
|
if (on) {
|
|
963
1045
|
Utils.addClass(el, className);
|
|
@@ -968,15 +1050,19 @@
|
|
|
968
1050
|
}
|
|
969
1051
|
//#endregion
|
|
970
1052
|
//#region part DOMTokenList (shadow-DOM)
|
|
1053
|
+
/** Tests whether `el`'s `part` attribute (CSS Shadow Parts) contains `partName`. */
|
|
971
1054
|
static hasPart(el, partName) {
|
|
972
1055
|
return el.part.contains(partName);
|
|
973
1056
|
}
|
|
1057
|
+
/** Adds one or more (space-separated) shadow-part names to `el`'s `part` attribute. */
|
|
974
1058
|
static addPart(el, partName) {
|
|
975
1059
|
DOMTokenList.prototype.add.apply(el.part, partName.trim().split(' '));
|
|
976
1060
|
}
|
|
1061
|
+
/** Removes one or more (space-separated) shadow-part names from `el`'s `part` attribute. */
|
|
977
1062
|
static removePart(el, partName) {
|
|
978
1063
|
DOMTokenList.prototype.remove.apply(el.part, partName.trim().split(' '));
|
|
979
1064
|
}
|
|
1065
|
+
/** Adds or removes `partName` on `el`'s `part` attribute depending on `on`; see {@link addPart}/{@link removePart}. */
|
|
980
1066
|
static togglePart(el, partName, on) {
|
|
981
1067
|
if (on) {
|
|
982
1068
|
Utils.addPart(el, partName);
|
|
@@ -1012,9 +1098,11 @@
|
|
|
1012
1098
|
height: rect.height
|
|
1013
1099
|
};
|
|
1014
1100
|
}
|
|
1101
|
+
/** Sums `offsetTop` up the `offsetParent` chain, giving `el`'s vertical position relative to the document rather than just its nearest positioned ancestor. */
|
|
1015
1102
|
static naturalOffsetTop(el) {
|
|
1016
1103
|
return (el?.offsetTop ?? 0) + ((el?.offsetParent && el.offsetParent instanceof HTMLElement) ? this.naturalOffsetTop(el.offsetParent) : 0);
|
|
1017
1104
|
}
|
|
1105
|
+
/** Sums `offsetTop` up the `offsetParent` chain (note: mirrors {@link naturalOffsetTop}'s calculation, not `offsetLeft`), giving `el`'s horizontal position relative to the document. */
|
|
1018
1106
|
static naturalOffsetLeft(el) {
|
|
1019
1107
|
return (el?.offsetTop ?? 0) + ((el?.offsetParent && el.offsetParent instanceof HTMLElement) ? this.naturalOffsetLeft(el.offsetParent) : 0);
|
|
1020
1108
|
}
|
|
@@ -1042,18 +1130,30 @@
|
|
|
1042
1130
|
static deserializeTransform(obj) {
|
|
1043
1131
|
return Utils.Css.deserializeTransform(obj);
|
|
1044
1132
|
}
|
|
1133
|
+
/** Sets the window's vertical scroll position, keeping the current horizontal position. */
|
|
1045
1134
|
static set scrollTop(y) {
|
|
1046
1135
|
window.scrollTo(Utils.scrollLeft, y);
|
|
1047
1136
|
}
|
|
1137
|
+
/** Sets the window's horizontal scroll position, keeping the current vertical position. */
|
|
1048
1138
|
static set scrollLeft(x) {
|
|
1049
1139
|
window.scrollTo(x, Utils.scrollTop);
|
|
1050
1140
|
}
|
|
1141
|
+
static scrollTo(x, y = true, smooth = true) {
|
|
1142
|
+
const yFirst = Utils.isNull(y) || typeof y === 'boolean';
|
|
1143
|
+
const left = yFirst ? Utils.scrollLeft : x;
|
|
1144
|
+
const top = yFirst ? x : y;
|
|
1145
|
+
const behavior = (yFirst ? y : smooth) ? 'smooth' : 'instant';
|
|
1146
|
+
window.scrollTo({ behavior, left, top });
|
|
1147
|
+
}
|
|
1148
|
+
/** The window's current vertical scroll position, with fallbacks for older browsers. */
|
|
1051
1149
|
static get scrollTop() {
|
|
1052
1150
|
return window.scrollY /*window.pageYOffset*/ || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
|
1053
1151
|
}
|
|
1152
|
+
/** The window's current horizontal scroll position, with fallbacks for older browsers. */
|
|
1054
1153
|
static get scrollLeft() {
|
|
1055
1154
|
return window.scrollX /*window.pageXOffset*/ || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
|
|
1056
1155
|
}
|
|
1156
|
+
/** The window's current viewport size (`{ width, height }`), with fallbacks for older browsers. */
|
|
1057
1157
|
static get windowSize() {
|
|
1058
1158
|
let win = window;
|
|
1059
1159
|
return {
|
|
@@ -1129,16 +1229,20 @@
|
|
|
1129
1229
|
}
|
|
1130
1230
|
// #endregion
|
|
1131
1231
|
// #region other
|
|
1232
|
+
/** Tests whether `obj` is "empty" (per {@link NullChecker.isEmpty}: `null`/`undefined`/`''`/an empty array or object), excluding DOM `Node`s (which are never considered empty here). */
|
|
1132
1233
|
static isEmpty(obj) {
|
|
1133
1234
|
return pacemFoundation.NullChecker.isEmpty(obj) && !(obj instanceof Node);
|
|
1134
1235
|
}
|
|
1236
|
+
/** Type-guarding test for `obj` being `null` or `undefined`. */
|
|
1135
1237
|
static isNull(obj) {
|
|
1136
1238
|
return pacemFoundation.NullChecker.isNull(obj);
|
|
1137
1239
|
}
|
|
1138
1240
|
// legacy
|
|
1241
|
+
/** Type-guarding test for `val` being an array. */
|
|
1139
1242
|
static isArray(val) {
|
|
1140
1243
|
return Array.isArray(val);
|
|
1141
1244
|
}
|
|
1245
|
+
/** Tests whether `val` is `null`/`undefined` or "empty"; see {@link isNull}/{@link isEmpty}. */
|
|
1142
1246
|
static isNullOrEmpty(val) {
|
|
1143
1247
|
return Utils.isNull(val) || Utils.isEmpty(val);
|
|
1144
1248
|
}
|
|
@@ -1157,6 +1261,7 @@
|
|
|
1157
1261
|
}
|
|
1158
1262
|
/** Legacy alias for Object.assign(). */
|
|
1159
1263
|
static { this.extend = Object.assign; }
|
|
1264
|
+
/** Deep-clones `obj`; see {@link DeepCloner}. */
|
|
1160
1265
|
static clone(obj) {
|
|
1161
1266
|
if (obj === undefined)
|
|
1162
1267
|
return undefined;
|
|
@@ -1166,6 +1271,7 @@
|
|
|
1166
1271
|
static fromResult(v) {
|
|
1167
1272
|
return Promise.resolve(v);
|
|
1168
1273
|
}
|
|
1274
|
+
/** Runs `task` on the next animation frame and wraps its result in a `Promise`. */
|
|
1169
1275
|
static fromResultAsync(task) {
|
|
1170
1276
|
return new Promise(resolve => requestAnimationFrame(() => resolve(task())));
|
|
1171
1277
|
}
|
|
@@ -1189,7 +1295,15 @@
|
|
|
1189
1295
|
}
|
|
1190
1296
|
//#endregion
|
|
1191
1297
|
//#region Net
|
|
1298
|
+
/** URL/route parsing and formatting helpers, chiefly backing the router (`{segment}`/`{segment?}` template resolution, query-string parsing, route metadata). */
|
|
1192
1299
|
static { this.URIs = {
|
|
1300
|
+
/**
|
|
1301
|
+
* Substitutes each `{name}`/`{name?}` template segment in `url` with the matching entry of `parameters`
|
|
1302
|
+
* (URL-encoded), then strips out any remaining unmatched *optional* segments.
|
|
1303
|
+
* @param url URL/route template containing `{name}`/`{name?}` segments.
|
|
1304
|
+
* @param parameters Values to substitute, keyed by segment name.
|
|
1305
|
+
* @param removeMatchedParameters When `true`, consumed entries are deleted from `parameters`.
|
|
1306
|
+
*/
|
|
1193
1307
|
format: function (url, parameters, removeMatchedParameters) {
|
|
1194
1308
|
// replace segments
|
|
1195
1309
|
for (let name in parameters || {}) {
|
|
@@ -1212,6 +1326,14 @@
|
|
|
1212
1326
|
}
|
|
1213
1327
|
return url;
|
|
1214
1328
|
},
|
|
1329
|
+
/**
|
|
1330
|
+
* Appends `parameters` to `url`'s query string (or hash fragment, when `hash` is `true`), URL-encoding
|
|
1331
|
+
* names and values, and correctly choosing `?`/`&` (or `#`/`&`) as the joining separator.
|
|
1332
|
+
* @param url Base URL.
|
|
1333
|
+
* @param parameters Values to append, keyed by parameter name.
|
|
1334
|
+
* @param removeNullEntries When `true`, entries whose value is `null`/`undefined` are omitted instead of appended as empty.
|
|
1335
|
+
* @param hash When `true`, appends to the URL's hash fragment instead of its query string.
|
|
1336
|
+
*/
|
|
1215
1337
|
appendQuery: function (url, parameters, removeNullEntries = false, hash = false) {
|
|
1216
1338
|
const char = hash ? '#' : '?', pattern = hash ? /#/ : /\?/;
|
|
1217
1339
|
const query = (pattern.test(url) ? '&' : char) +
|
|
@@ -1220,6 +1342,7 @@
|
|
|
1220
1342
|
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(parameters[k] ?? '')).join('&');
|
|
1221
1343
|
return url + (query.length > 1 ? query : '');
|
|
1222
1344
|
},
|
|
1345
|
+
/** Parses a query string (or a `Location`'s `.search`, defaulting to `window.location`) into a name/value map. */
|
|
1223
1346
|
parseQuery: (query = window.location) => {
|
|
1224
1347
|
if (typeof query !== 'string') {
|
|
1225
1348
|
query = query.search;
|
|
@@ -1230,9 +1353,11 @@
|
|
|
1230
1353
|
});
|
|
1231
1354
|
return obj;
|
|
1232
1355
|
},
|
|
1356
|
+
/** Tests whether `url` contains at least one non-optional `{name}` template segment (as opposed to only `{name?}` ones). */
|
|
1233
1357
|
hasMandatoryTemplateSegments: function (url) {
|
|
1234
1358
|
return /(^|\/)\{\.*\}(\/|$)/.test(url);
|
|
1235
1359
|
},
|
|
1360
|
+
/** Parses a route template's `{name}`/`{name?}` segments into an ordered list of {@link RouteMetadata} descriptors, consumed by {@link parseState}. */
|
|
1236
1361
|
parseRoute: function (pathTemplate) {
|
|
1237
1362
|
let tmpl = pathTemplate;
|
|
1238
1363
|
const trunks = [];
|
|
@@ -1252,6 +1377,7 @@
|
|
|
1252
1377
|
}
|
|
1253
1378
|
return trunks;
|
|
1254
1379
|
},
|
|
1380
|
+
/** Splits `url` into its `protocol`, `domain`, `path`, `query` (including leading `?`) and `hash` (including leading `#`) parts, or `null` if it can't be parsed. */
|
|
1255
1381
|
split: function (url) {
|
|
1256
1382
|
const URL_PATTERN = /^((https?:)?\/\/[^\/]+)?([^\?#]+)(\?[^#]*)?(#[^#]*)?$/;
|
|
1257
1383
|
const regArr = URL_PATTERN.exec(url);
|
|
@@ -1260,6 +1386,16 @@
|
|
|
1260
1386
|
}
|
|
1261
1387
|
return { domain: regArr[2] ?? '', protocol: regArr[1] ?? '', path: regArr[3] ?? '', query: regArr[4] ?? '', hash: regArr[5] ?? '' };
|
|
1262
1388
|
},
|
|
1389
|
+
/**
|
|
1390
|
+
* Builds a {@link RouterState} for `url`, extracting named route segments per `metadata` (see
|
|
1391
|
+
* {@link parseRoute}) as top-level properties, alongside the standard `$path`/`$pathname`/`$querystring`/
|
|
1392
|
+
* `$query`/`$hash` keys. Throws if a non-optional route segment declared in `metadata` isn't present in `path`.
|
|
1393
|
+
* @param metadata Route segment descriptors, as produced by {@link parseRoute}.
|
|
1394
|
+
* @param url Full URL/path being navigated to.
|
|
1395
|
+
* @param path Path component override; parsed out of `url` via {@link split} when omitted.
|
|
1396
|
+
* @param query Query-string override (including leading `?`); parsed out of `url` when omitted.
|
|
1397
|
+
* @param hash Hash-fragment override (including leading `#`); parsed out of `url` when omitted.
|
|
1398
|
+
*/
|
|
1263
1399
|
parseState: function (metadata, url, path, query, hash) {
|
|
1264
1400
|
let fullPath = url;
|
|
1265
1401
|
if (Utils.isNullOrEmpty(path)) {
|
|
@@ -1344,6 +1480,7 @@
|
|
|
1344
1480
|
}
|
|
1345
1481
|
// }
|
|
1346
1482
|
|
|
1483
|
+
/** String tokens accepted by {@link CustomEventUtils.matchModifiers} to describe a keyboard/mouse modifier. */
|
|
1347
1484
|
var EventKeyModifier;
|
|
1348
1485
|
(function (EventKeyModifier) {
|
|
1349
1486
|
EventKeyModifier["AltKey"] = "Alt";
|
|
@@ -1351,11 +1488,24 @@
|
|
|
1351
1488
|
EventKeyModifier["ShiftKey"] = "Shift";
|
|
1352
1489
|
EventKeyModifier["MetaKey"] = "Cmd";
|
|
1353
1490
|
})(EventKeyModifier || (EventKeyModifier = {}));
|
|
1491
|
+
/** Static helpers for inspecting and normalizing native/custom events (coordinates, keyboard modifiers, type checks). */
|
|
1354
1492
|
class CustomEventUtils {
|
|
1493
|
+
/**
|
|
1494
|
+
* Type-guarding check for whether `evt` is an instance of the given event `type`.
|
|
1495
|
+
* @param evt Event to test.
|
|
1496
|
+
* @param type Event constructor to test against.
|
|
1497
|
+
* @returns `true` (narrowing `evt` to `TEvent`) if `evt instanceof type`.
|
|
1498
|
+
*/
|
|
1355
1499
|
static isInstanceOf(evt, type) {
|
|
1356
1500
|
return evt instanceof type;
|
|
1357
1501
|
// || CustomElementUtils.getAttachedPropertyValue(evt, 'pacem:custom-event') === evt.type;
|
|
1358
1502
|
}
|
|
1503
|
+
/**
|
|
1504
|
+
* Extracts page/client/screen coordinates from a mouse or (single-)touch event, using the first
|
|
1505
|
+
* active touch (`touches[0]`, falling back to `changedTouches[0]`) for `TouchEvent`s.
|
|
1506
|
+
* @param evt Source mouse/touch event, or an already-extracted {@link UIEventLike}.
|
|
1507
|
+
* @returns The event's position in all three coordinate systems.
|
|
1508
|
+
*/
|
|
1359
1509
|
static getEventCoordinates(evt) {
|
|
1360
1510
|
var src;
|
|
1361
1511
|
if ('touches' in evt) {
|
|
@@ -1375,6 +1525,11 @@
|
|
|
1375
1525
|
client: { x: src.clientX, y: src.clientY }
|
|
1376
1526
|
};
|
|
1377
1527
|
}
|
|
1528
|
+
/**
|
|
1529
|
+
* Extracts the keyboard-modifier flags (and, for mouse events, the pressed button as `which`) from a native event.
|
|
1530
|
+
* @param evt Source keyboard/mouse/touch event, or an already-extracted {@link KeyEventLike}.
|
|
1531
|
+
* @returns The captured modifier flags.
|
|
1532
|
+
*/
|
|
1378
1533
|
static getEventKeyModifiers(evt) {
|
|
1379
1534
|
var which = evt.which;
|
|
1380
1535
|
if (evt instanceof MouseEvent) {
|
|
@@ -1395,6 +1550,13 @@
|
|
|
1395
1550
|
}
|
|
1396
1551
|
return true;
|
|
1397
1552
|
}
|
|
1553
|
+
/**
|
|
1554
|
+
* Works around a legacy Edge/ChakraCore bug (see {@link https://github.com/Microsoft/ChakraCore/issues/3952})
|
|
1555
|
+
* where an instance built via `super(...)` from a `CustomEvent` subclass constructor doesn't correctly
|
|
1556
|
+
* pass `instanceof` checks against that subclass; forcibly resets its prototype when so.
|
|
1557
|
+
* @param evt Event instance to fix up.
|
|
1558
|
+
* @param type The subclass constructor `evt` is supposed to be an instance of.
|
|
1559
|
+
*/
|
|
1398
1560
|
static fixEdgeCustomEventSubClassInstance(evt, type) {
|
|
1399
1561
|
// this an optimistic BUGGED workaround due to a damn' Edge bug: https://github.com/Microsoft/ChakraCore/issues/3952
|
|
1400
1562
|
if (!(evt instanceof type)) {
|
|
@@ -1406,7 +1568,17 @@
|
|
|
1406
1568
|
|
|
1407
1569
|
/// <reference path="utils-customevent.ts" />
|
|
1408
1570
|
// namespace Pacem {
|
|
1571
|
+
/**
|
|
1572
|
+
* Base class for framework custom events whose `detail` payload is strongly typed as `TDetail`.
|
|
1573
|
+
* Thin wrapper around the native `CustomEvent<TDetail>` that also works around an old Edge bug
|
|
1574
|
+
* (see {@link CustomEventUtils.fixEdgeCustomEventSubClassInstance}) affecting `CustomEvent` subclasses.
|
|
1575
|
+
*/
|
|
1409
1576
|
class CustomTypedEvent extends CustomEvent /* new in TypeScript v2.7.1 -> */ {
|
|
1577
|
+
/**
|
|
1578
|
+
* @param type Event name.
|
|
1579
|
+
* @param detail Strongly-typed payload, exposed as `event.detail`.
|
|
1580
|
+
* @param eventInit Standard `CustomEvent` initialization options (`bubbles`, `cancelable`, `composed`).
|
|
1581
|
+
*/
|
|
1410
1582
|
constructor(type, detail, eventInit) {
|
|
1411
1583
|
super(type, Utils.extend({ detail: detail }, eventInit || {}));
|
|
1412
1584
|
CustomEventUtils.fixEdgeCustomEventSubClassInstance(this, this.constructor);
|
|
@@ -1425,7 +1597,20 @@
|
|
|
1425
1597
|
return false;
|
|
1426
1598
|
}
|
|
1427
1599
|
// TODO: make inherit from CustomTypedEvent
|
|
1600
|
+
/**
|
|
1601
|
+
* Base class for UI-originated custom events: like {@link CustomTypedEvent}, but additionally captures
|
|
1602
|
+
* the mouse/touch/keyboard modifiers and coordinates of the originating native event (when supplied),
|
|
1603
|
+
* exposing them through the {@link UIEventLike} surface (`which`, `altKey`, `pageX`, `clientY`, ...).
|
|
1604
|
+
* Useful for events that are re-dispatched/synthesized (e.g. drag & drop) from an original pointer or
|
|
1605
|
+
* keyboard event, so that consumers can still inspect that original event's modifiers/position.
|
|
1606
|
+
*/
|
|
1428
1607
|
class CustomUIEvent extends CustomEvent {
|
|
1608
|
+
/**
|
|
1609
|
+
* @param type Event name.
|
|
1610
|
+
* @param detail Either the strongly-typed payload, or (when it satisfies `CustomEventInit`, i.e. has a `detail` key) the full event-init object.
|
|
1611
|
+
* @param eventInit Either standard `CustomEvent` init options, or the original native event this custom event derives from (in which case its coordinates/modifiers get captured).
|
|
1612
|
+
* @param orig The original native mouse/touch/keyboard event this custom event derives from, when `eventInit` is itself the init options.
|
|
1613
|
+
*/
|
|
1429
1614
|
constructor(type, detail, eventInit, orig) {
|
|
1430
1615
|
super(type, Utils.extend((isEventInit(detail || {}) ? detail : { detail: detail }), (!(eventInit instanceof Event) && eventInit) || {}));
|
|
1431
1616
|
const originalEvent = orig || eventInit;
|
|
@@ -1478,30 +1663,70 @@
|
|
|
1478
1663
|
return this.#coords?.screen.y;
|
|
1479
1664
|
}
|
|
1480
1665
|
}
|
|
1666
|
+
/** Event name dispatched whenever an observed attribute changes; see {@link AttributeChangeEvent}. */
|
|
1481
1667
|
const AttributeChangeEventName = 'attributechange';
|
|
1668
|
+
/**
|
|
1669
|
+
* Dispatched by elements registered with the {@link CustomElement} decorator whenever one of their
|
|
1670
|
+
* observed attributes changes, mirroring the native `attributeChangedCallback` as a DOM event.
|
|
1671
|
+
*/
|
|
1482
1672
|
class AttributeChangeEvent extends CustomTypedEvent {
|
|
1483
1673
|
constructor(args) {
|
|
1484
1674
|
super(AttributeChangeEventName, args);
|
|
1485
1675
|
}
|
|
1486
1676
|
}
|
|
1677
|
+
/** Event name dispatched whenever a `@Watch`-decorated property changes value; see {@link PropertyChangeEvent}. */
|
|
1487
1678
|
const PropertyChangeEventName = 'propertychange';
|
|
1679
|
+
/**
|
|
1680
|
+
* Dispatched whenever a property decorated with {@link Watch} changes value (unless that decorator's
|
|
1681
|
+
* `emit` option is set to `false`). This is the mechanism data-bound expressions listen to in order to
|
|
1682
|
+
* re-evaluate themselves when one of their dependencies changes.
|
|
1683
|
+
*/
|
|
1488
1684
|
class PropertyChangeEvent extends CustomTypedEvent {
|
|
1489
1685
|
constructor(args) {
|
|
1490
1686
|
super(PropertyChangeEventName, args);
|
|
1491
1687
|
}
|
|
1492
1688
|
}
|
|
1689
|
+
// #region NAVIGATING
|
|
1690
|
+
/** Event name dispatched by a router *before* a navigation is committed; see {@link RouterNavigatingEvent}. Cancelable: calling `preventDefault()` aborts the navigation. */
|
|
1691
|
+
const RouterNavigatingEventName = 'navigating';
|
|
1692
|
+
/** Event name dispatched by a router *after* a navigation has been committed; see {@link RouterNavigateEvent}. */
|
|
1693
|
+
const RouterNavigateEventName = 'navigate';
|
|
1694
|
+
/** Dispatched by a router once it has navigated to a new `path` (not cancelable — the navigation already happened). */
|
|
1695
|
+
class RouterNavigateEvent extends CustomTypedEvent {
|
|
1696
|
+
/** @param path Either the destination path alone, or the full navigation args (path, title, referrer). */
|
|
1697
|
+
constructor(path) {
|
|
1698
|
+
super(RouterNavigateEventName, typeof path === 'string' ? { path } : path, { cancelable: false, bubbles: false });
|
|
1699
|
+
}
|
|
1700
|
+
}
|
|
1701
|
+
/** Dispatched by a router right before navigating to `path`; cancelable, so a listener can call `preventDefault()` to abort the navigation. */
|
|
1702
|
+
class RouterNavigatingEvent extends CustomTypedEvent {
|
|
1703
|
+
/** @param path Destination path about to be navigated to. */
|
|
1704
|
+
constructor(path) {
|
|
1705
|
+
super(RouterNavigatingEventName, path, { cancelable: true, bubbles: false });
|
|
1706
|
+
}
|
|
1707
|
+
}
|
|
1708
|
+
// #endregion
|
|
1493
1709
|
//}
|
|
1494
1710
|
|
|
1711
|
+
/**
|
|
1712
|
+
* `step` event dispatched on every animation frame of a running tween, carrying the current progress and value.
|
|
1713
|
+
*/
|
|
1495
1714
|
class AnimationEvent extends CustomTypedEvent {
|
|
1496
1715
|
constructor(args) {
|
|
1497
1716
|
super("step", args);
|
|
1498
1717
|
}
|
|
1499
1718
|
}
|
|
1719
|
+
/**
|
|
1720
|
+
* `end` event dispatched once a tween/animation completes.
|
|
1721
|
+
*/
|
|
1500
1722
|
class AnimationEndEvent extends CustomEvent {
|
|
1501
1723
|
constructor() {
|
|
1502
1724
|
super('end');
|
|
1503
1725
|
}
|
|
1504
1726
|
}
|
|
1727
|
+
/**
|
|
1728
|
+
* `start` event dispatched when a tween/animation begins.
|
|
1729
|
+
*/
|
|
1505
1730
|
class AnimationStartEvent extends CustomEvent {
|
|
1506
1731
|
constructor() {
|
|
1507
1732
|
super('start');
|
|
@@ -1510,7 +1735,20 @@
|
|
|
1510
1735
|
//}
|
|
1511
1736
|
|
|
1512
1737
|
//namespace Pacem.Animations {
|
|
1738
|
+
/**
|
|
1739
|
+
* Runs `requestAnimationFrame`-driven numeric tweens between two values, invoking a callback on each frame.
|
|
1740
|
+
*/
|
|
1513
1741
|
class TweenService {
|
|
1742
|
+
/**
|
|
1743
|
+
* Tweens a numeric value from `from` to `to` over `duration` milliseconds, invoking `callback` on every animation frame.
|
|
1744
|
+
* @param from Starting value.
|
|
1745
|
+
* @param to Ending value.
|
|
1746
|
+
* @param duration Tween duration, in milliseconds.
|
|
1747
|
+
* @param delay Delay before the tween starts, in milliseconds (default `0`).
|
|
1748
|
+
* @param easing Easing function applied to the normalized `[0,1]` time (defaults to {@link Easings.linear}).
|
|
1749
|
+
* @param callback Invoked on every frame (and once more with `time === 1.0`) with the normalized time and the current value.
|
|
1750
|
+
* @returns A promise resolved when the tween completes.
|
|
1751
|
+
*/
|
|
1514
1752
|
run(from, to, duration, delay = 0, easing = Easings.linear, callback = null) {
|
|
1515
1753
|
return new Promise((resolve, _) => {
|
|
1516
1754
|
const now = (performance && performance.now()) || Date.now();
|
|
@@ -1543,6 +1781,10 @@
|
|
|
1543
1781
|
//}
|
|
1544
1782
|
|
|
1545
1783
|
// namespace Pacem.Animations {
|
|
1784
|
+
/**
|
|
1785
|
+
* `requestAnimationFrame`-driven timer that invokes a callback at (approximately) a given frame rate.
|
|
1786
|
+
* Instances are created via the static {@link Timer.run} factory rather than `new`.
|
|
1787
|
+
*/
|
|
1546
1788
|
class Timer {
|
|
1547
1789
|
#fps;
|
|
1548
1790
|
#callback;
|
|
@@ -1563,9 +1805,15 @@
|
|
|
1563
1805
|
this.#handle = requestAnimationFrame(this._run);
|
|
1564
1806
|
}
|
|
1565
1807
|
#then;
|
|
1808
|
+
/**
|
|
1809
|
+
* Creates and starts a new {@link Timer}.
|
|
1810
|
+
* @param callback Invoked on every tick with the current frame timestamp.
|
|
1811
|
+
* @param fps Target frame rate, in frames per second (default `60`).
|
|
1812
|
+
*/
|
|
1566
1813
|
static run(callback, fps = 60) {
|
|
1567
1814
|
return new Timer(callback, fps);
|
|
1568
1815
|
}
|
|
1816
|
+
/** Stops the timer, cancelling any pending animation frame. */
|
|
1569
1817
|
stop() {
|
|
1570
1818
|
cancelAnimationFrame(this.#handle);
|
|
1571
1819
|
}
|
|
@@ -1583,6 +1831,7 @@
|
|
|
1583
1831
|
});
|
|
1584
1832
|
|
|
1585
1833
|
//namespace Pacem.Net {
|
|
1834
|
+
/** HTTP verbs accepted by {@link Fetcher.method} / {@link Http}. */
|
|
1586
1835
|
var HttpMethod;
|
|
1587
1836
|
(function (HttpMethod) {
|
|
1588
1837
|
HttpMethod["Get"] = "GET";
|
|
@@ -1595,10 +1844,15 @@
|
|
|
1595
1844
|
HttpMethod["Connect"] = "CONNECT";
|
|
1596
1845
|
HttpMethod["Trace"] = "TRACE";
|
|
1597
1846
|
})(HttpMethod || (HttpMethod = {}));
|
|
1847
|
+
/** Event name dispatched with the raw fetch result payload; see {@link Fetcher.result}. */
|
|
1598
1848
|
const FetchResultEventName = 'fetchresult';
|
|
1849
|
+
/** Event name dispatched when a {@link Fetcher}'s request fails. */
|
|
1599
1850
|
const FetchErrorEventName = 'error';
|
|
1851
|
+
/** Event name dispatched when a {@link Fetcher}'s request completes successfully. */
|
|
1600
1852
|
const FetchSuccessEventName = 'success';
|
|
1853
|
+
/** Static helpers for working with fetch `Response`s. */
|
|
1601
1854
|
class Fetcher {
|
|
1855
|
+
/** Tests whether a response carries no body (HTTP 204, or an explicit `Content-Length: 0`). */
|
|
1602
1856
|
static isEmpty(r) {
|
|
1603
1857
|
return r.status === 204 || r.headers.get("Content-Length") === "0";
|
|
1604
1858
|
}
|
|
@@ -1608,17 +1862,22 @@
|
|
|
1608
1862
|
/// <reference path="../promise.ts" />
|
|
1609
1863
|
// namespace Pacem.Net {
|
|
1610
1864
|
const noop = () => { };
|
|
1865
|
+
/** Error thrown by {@link Http.request} when a request fails outright or completes with a non-2xx status. */
|
|
1611
1866
|
class StatusCodeError extends Error {
|
|
1867
|
+
/** @param statusCode HTTP status code (`500` for network-level failures). @param statusText Status text / error message. */
|
|
1612
1868
|
constructor(statusCode, statusText) {
|
|
1613
1869
|
super(statusText);
|
|
1614
1870
|
this.statusCode = statusCode;
|
|
1615
1871
|
}
|
|
1872
|
+
/** The HTTP status code that caused this error. */
|
|
1616
1873
|
get status() {
|
|
1617
1874
|
return this.statusCode;
|
|
1618
1875
|
}
|
|
1619
1876
|
}
|
|
1620
|
-
/** @
|
|
1877
|
+
/** Wraps a completed `XMLHttpRequest` produced by {@link Http.request}, exposing its status, headers and body in convenient forms.
|
|
1878
|
+
* @deprecated */
|
|
1621
1879
|
class Response {
|
|
1880
|
+
/** @param req The completed `XMLHttpRequest`. @param processTime Elapsed request time, in milliseconds. */
|
|
1622
1881
|
constructor(req, processTime) {
|
|
1623
1882
|
if (!req)
|
|
1624
1883
|
return;
|
|
@@ -1629,20 +1888,25 @@
|
|
|
1629
1888
|
this._allHeadersRaw = req.getAllResponseHeaders();
|
|
1630
1889
|
this._processTime = processTime;
|
|
1631
1890
|
}
|
|
1891
|
+
/** Elapsed request time, in milliseconds. */
|
|
1632
1892
|
get processTime() {
|
|
1633
1893
|
return this._processTime;
|
|
1634
1894
|
}
|
|
1895
|
+
/** Response headers, parsed into a name/value map (lazily, on first access). */
|
|
1635
1896
|
get headers() {
|
|
1636
1897
|
if (this._headers === undefined && this._allHeadersRaw)
|
|
1637
1898
|
this._parseHeaders();
|
|
1638
1899
|
return this._headers;
|
|
1639
1900
|
}
|
|
1901
|
+
/** The `Content-Length` response header, parsed as a number. */
|
|
1640
1902
|
get size() {
|
|
1641
1903
|
return +this.headers['Content-Length'];
|
|
1642
1904
|
}
|
|
1905
|
+
/** The `Content-Type` response header. */
|
|
1643
1906
|
get mime() {
|
|
1644
1907
|
return this.headers['Content-Type'];
|
|
1645
1908
|
}
|
|
1909
|
+
/** The `Date` response header, parsed into a `Date`. */
|
|
1646
1910
|
get date() {
|
|
1647
1911
|
return new Date(Date.parse(this.headers['Date']));
|
|
1648
1912
|
}
|
|
@@ -1658,9 +1922,13 @@
|
|
|
1658
1922
|
});
|
|
1659
1923
|
this._headers = headers;
|
|
1660
1924
|
}
|
|
1925
|
+
/** HTTP status code. */
|
|
1661
1926
|
get status() { return this._status; }
|
|
1927
|
+
/** Raw response body as text. */
|
|
1662
1928
|
get text() { return this._text; }
|
|
1929
|
+
/** Response body, typed per `XMLHttpRequest.responseType` (see {@link type}). */
|
|
1663
1930
|
get content() { return this._body; }
|
|
1931
|
+
/** The `XMLHttpRequest.responseType` used for this request. */
|
|
1664
1932
|
get type() { return this._type; }
|
|
1665
1933
|
/**
|
|
1666
1934
|
* Short-hand utility for getting or parsing json content (if any).
|
|
@@ -1677,7 +1945,11 @@
|
|
|
1677
1945
|
}
|
|
1678
1946
|
}
|
|
1679
1947
|
}
|
|
1680
|
-
/**
|
|
1948
|
+
/**
|
|
1949
|
+
* Legacy `XMLHttpRequest`-based HTTP client, superseded by the standard `fetch` API and {@link Fetcher}.
|
|
1950
|
+
* Kept for backward compatibility.
|
|
1951
|
+
* @deprecated
|
|
1952
|
+
*/
|
|
1681
1953
|
class Http {
|
|
1682
1954
|
constructor() { }
|
|
1683
1955
|
/**
|
|
@@ -1823,6 +2095,9 @@
|
|
|
1823
2095
|
/** Terminating the drag-drop activities. */
|
|
1824
2096
|
DragDropEventType["End"] = "dragend";
|
|
1825
2097
|
})(DragDropEventType || (DragDropEventType = {}));
|
|
2098
|
+
/**
|
|
2099
|
+
* Base class shared by the drag & drop event-args classes; wraps a {@link DragDropEventArgs} builder and exposes its common, read-only facets.
|
|
2100
|
+
*/
|
|
1826
2101
|
class DragDropEventArgsBaseClass {
|
|
1827
2102
|
constructor(_builder) {
|
|
1828
2103
|
this._builder = _builder;
|
|
@@ -1849,32 +2124,46 @@
|
|
|
1849
2124
|
return pacemFoundation.Point.add(args.initialDelta, /* included in `initialDelta` for perf sake => { x: this._builder.scroll.left, y: this._builder.scroll.top },*/ pacemFoundation.Point.subtract(args.origin, args.currentPosition));
|
|
1850
2125
|
}
|
|
1851
2126
|
}
|
|
2127
|
+
/**
|
|
2128
|
+
* Event args dispatched with the {@link DragDropEventType.Init} event, mutable so listeners can override
|
|
2129
|
+
* the `placeholder` and `data` before the drag actually starts.
|
|
2130
|
+
*/
|
|
1852
2131
|
class DragDropInitEventArgsClass extends DragDropEventArgsBaseClass {
|
|
1853
2132
|
constructor(_builder) {
|
|
1854
2133
|
super(_builder);
|
|
1855
2134
|
this.placeholder = _builder.placeholder;
|
|
1856
2135
|
this.data = _builder.data;
|
|
1857
2136
|
}
|
|
2137
|
+
/** Builds a new {@link DragDropInitEventArgsClass} out of a plain {@link DragDropEventArgs} builder. */
|
|
1858
2138
|
static fromArgs(builder) {
|
|
1859
2139
|
return new DragDropInitEventArgsClass(Utils.extend({}, builder));
|
|
1860
2140
|
}
|
|
1861
2141
|
}
|
|
2142
|
+
/**
|
|
2143
|
+
* Read-only event args dispatched with the drag/drop lifecycle events other than `draginit` (start, drag, drop, over, out, end).
|
|
2144
|
+
*/
|
|
1862
2145
|
class DragDropEventArgsClass extends DragDropEventArgsBaseClass {
|
|
1863
2146
|
constructor(_builder, _placeholder = _builder.placeholder, _data = _builder.data) {
|
|
1864
2147
|
super(_builder);
|
|
1865
2148
|
this._placeholder = _placeholder;
|
|
1866
2149
|
this._data = _data;
|
|
1867
2150
|
}
|
|
2151
|
+
/** @readonly Gets the element used as a dragging placeholder. */
|
|
1868
2152
|
get placeholder() {
|
|
1869
2153
|
return this._placeholder;
|
|
1870
2154
|
}
|
|
2155
|
+
/** @readonly Gets the logical payload being dragged. */
|
|
1871
2156
|
get data() {
|
|
1872
2157
|
return this._data;
|
|
1873
2158
|
}
|
|
2159
|
+
/** Builds a new {@link DragDropEventArgsClass} out of a plain {@link DragDropEventArgs} builder. */
|
|
1874
2160
|
static fromArgs(builder) {
|
|
1875
2161
|
return new DragDropEventArgsClass(Utils.extend({}, builder));
|
|
1876
2162
|
}
|
|
1877
2163
|
}
|
|
2164
|
+
/**
|
|
2165
|
+
* Custom UI event dispatched throughout the drag & drop lifecycle (see {@link DragDropEventType}).
|
|
2166
|
+
*/
|
|
1878
2167
|
class DragDropEvent extends CustomUIEvent {
|
|
1879
2168
|
constructor(type, args, eventInit, evt) {
|
|
1880
2169
|
super(type, args, eventInit, evt);
|
|
@@ -1883,40 +2172,65 @@
|
|
|
1883
2172
|
//}
|
|
1884
2173
|
|
|
1885
2174
|
//namespace Pacem.UI {
|
|
2175
|
+
/**
|
|
2176
|
+
* Enumerates the resize handles a {@link Rescaler} can expose around a rescalable element.
|
|
2177
|
+
*/
|
|
1886
2178
|
var RescaleHandle;
|
|
1887
2179
|
(function (RescaleHandle) {
|
|
2180
|
+
/** All four sides/corners are enabled. */
|
|
1888
2181
|
RescaleHandle["All"] = "all";
|
|
2182
|
+
/** The top edge (and its corners) is enabled. */
|
|
1889
2183
|
RescaleHandle["Top"] = "top";
|
|
2184
|
+
/** The left edge (and its corners) is enabled. */
|
|
1890
2185
|
RescaleHandle["Left"] = "left";
|
|
2186
|
+
/** The right edge (and its corners) is enabled. */
|
|
1891
2187
|
RescaleHandle["Right"] = "right";
|
|
2188
|
+
/** The bottom edge (and its corners) is enabled. */
|
|
1892
2189
|
RescaleHandle["Bottom"] = "bottom";
|
|
1893
2190
|
})(RescaleHandle || (RescaleHandle = {}));
|
|
2191
|
+
/**
|
|
2192
|
+
* Enumerates the events dispatched throughout the rescale gesture lifecycle.
|
|
2193
|
+
*/
|
|
1894
2194
|
var RescaleEventType;
|
|
1895
2195
|
(function (RescaleEventType) {
|
|
2196
|
+
/** First rescaling act. */
|
|
1896
2197
|
RescaleEventType["Start"] = "rescalestart";
|
|
2198
|
+
/** Any rescaling act. */
|
|
1897
2199
|
RescaleEventType["Rescale"] = "rescale";
|
|
2200
|
+
/** Terminating the rescaling activities. */
|
|
1898
2201
|
RescaleEventType["End"] = "rescaleend";
|
|
1899
2202
|
})(RescaleEventType || (RescaleEventType = {}));
|
|
2203
|
+
/**
|
|
2204
|
+
* Read-only event args dispatched with the rescale gesture lifecycle events.
|
|
2205
|
+
*/
|
|
1900
2206
|
class RescaleEventArgsClass {
|
|
1901
2207
|
constructor(_builder) {
|
|
1902
2208
|
this._builder = _builder;
|
|
1903
2209
|
}
|
|
2210
|
+
/** @readonly Gets the current pointer position. */
|
|
1904
2211
|
get currentPosition() {
|
|
1905
2212
|
return this._builder.currentPosition;
|
|
1906
2213
|
}
|
|
2214
|
+
/** @readonly Gets the rectangle the element is being resized to. */
|
|
1907
2215
|
get targetRect() {
|
|
1908
2216
|
return this._builder.targetRect;
|
|
1909
2217
|
}
|
|
2218
|
+
/** @readonly Gets the element being resized. */
|
|
1910
2219
|
get element() {
|
|
1911
2220
|
return this._builder.element;
|
|
1912
2221
|
}
|
|
2222
|
+
/** @readonly Gets the handle being dragged (see {@link RescaleHandle}). */
|
|
1913
2223
|
get handle() {
|
|
1914
2224
|
return this._builder.handle;
|
|
1915
2225
|
}
|
|
2226
|
+
/** Builds a new {@link RescaleEventArgsClass} out of a plain {@link RescaleEventArgs} builder. */
|
|
1916
2227
|
static fromArgs(builder) {
|
|
1917
2228
|
return new RescaleEventArgsClass(Utils.extend({}, builder));
|
|
1918
2229
|
}
|
|
1919
2230
|
}
|
|
2231
|
+
/**
|
|
2232
|
+
* Custom UI event dispatched throughout the rescale gesture lifecycle (see {@link RescaleEventType}).
|
|
2233
|
+
*/
|
|
1920
2234
|
class RescaleEvent extends CustomUIEvent {
|
|
1921
2235
|
constructor(type, args, eventInit, evt) {
|
|
1922
2236
|
super(type, args, eventInit, evt);
|
|
@@ -1924,32 +2238,49 @@
|
|
|
1924
2238
|
}
|
|
1925
2239
|
//}
|
|
1926
2240
|
|
|
2241
|
+
/**
|
|
2242
|
+
* Enumerates the events dispatched throughout the rotate gesture lifecycle.
|
|
2243
|
+
*/
|
|
1927
2244
|
var RotateEventType;
|
|
1928
2245
|
(function (RotateEventType) {
|
|
2246
|
+
/** First rotating act. */
|
|
1929
2247
|
RotateEventType["Start"] = "rotatestart";
|
|
2248
|
+
/** Any rotating act. */
|
|
1930
2249
|
RotateEventType["Rotate"] = "rotate";
|
|
2250
|
+
/** Terminating the rotating activities. */
|
|
1931
2251
|
RotateEventType["End"] = "rotateend";
|
|
1932
2252
|
})(RotateEventType || (RotateEventType = {}));
|
|
2253
|
+
/**
|
|
2254
|
+
* Read-only event args dispatched with the rotate gesture lifecycle events.
|
|
2255
|
+
*/
|
|
1933
2256
|
class RotateEventArgsClass {
|
|
1934
2257
|
constructor(_builder) {
|
|
1935
2258
|
this._builder = _builder;
|
|
1936
2259
|
}
|
|
2260
|
+
/** @readonly Gets the current pointer position. */
|
|
1937
2261
|
get currentPosition() {
|
|
1938
2262
|
return this._builder.currentPosition;
|
|
1939
2263
|
}
|
|
2264
|
+
/** @readonly Gets the current rotation, in radians. */
|
|
1940
2265
|
get rotation() {
|
|
1941
2266
|
return this._builder.rotation;
|
|
1942
2267
|
}
|
|
2268
|
+
/** @readonly Gets the element being rotated. */
|
|
1943
2269
|
get element() {
|
|
1944
2270
|
return this._builder.element;
|
|
1945
2271
|
}
|
|
2272
|
+
/** @readonly Gets the pivot point the element is rotating around. */
|
|
1946
2273
|
get center() {
|
|
1947
2274
|
return this._builder.center;
|
|
1948
2275
|
}
|
|
2276
|
+
/** Builds a new {@link RotateEventArgsClass} out of a plain {@link RotateEventArgs} builder. */
|
|
1949
2277
|
static fromArgs(builder) {
|
|
1950
2278
|
return new RotateEventArgsClass(Utils.extend({}, builder));
|
|
1951
2279
|
}
|
|
1952
2280
|
}
|
|
2281
|
+
/**
|
|
2282
|
+
* Custom UI event dispatched throughout the rotate gesture lifecycle (see {@link RotateEventType}).
|
|
2283
|
+
*/
|
|
1953
2284
|
class RotateEvent extends CustomUIEvent {
|
|
1954
2285
|
constructor(type, args, eventInit, evt) {
|
|
1955
2286
|
super(type, args, eventInit, evt);
|
|
@@ -1957,21 +2288,38 @@
|
|
|
1957
2288
|
}
|
|
1958
2289
|
//}
|
|
1959
2290
|
|
|
2291
|
+
/**
|
|
2292
|
+
* Enumerates the events dispatched throughout the swipe gesture lifecycle.
|
|
2293
|
+
*/
|
|
1960
2294
|
var SwipeEventType;
|
|
1961
2295
|
(function (SwipeEventType) {
|
|
2296
|
+
/** Dispatched when the swipe gesture ends (release). */
|
|
1962
2297
|
SwipeEventType["Swipe"] = "swipe";
|
|
2298
|
+
/** Dispatched while the pointer is dragging the element around. */
|
|
1963
2299
|
SwipeEventType["Pan"] = "pan";
|
|
2300
|
+
/** Dispatched when the swipe ends and its resolved direction is leftward. */
|
|
1964
2301
|
SwipeEventType["SwipeLeft"] = "swipeleft";
|
|
2302
|
+
/** Dispatched when the swipe ends and its resolved direction is rightward. */
|
|
1965
2303
|
SwipeEventType["SwipeRight"] = "swiperight";
|
|
2304
|
+
/** Dispatched when the swipe ends and its resolved direction is upward. */
|
|
1966
2305
|
SwipeEventType["SwipeUp"] = "swipeup";
|
|
2306
|
+
/** Dispatched when the swipe ends and its resolved direction is downward. */
|
|
1967
2307
|
SwipeEventType["SwipeDown"] = "swipedown";
|
|
1968
2308
|
// end of swipe animation
|
|
2309
|
+
/** Dispatched when the snap/bounce-back animation following a swipe completes. */
|
|
1969
2310
|
SwipeEventType["SwipeAnimationEnd"] = "swipeanimationend";
|
|
2311
|
+
/** Dispatched when the snap/bounce-back animation following a leftward swipe completes. */
|
|
1970
2312
|
SwipeEventType["SwipeLeftAnimationEnd"] = "swipeleftanimationend";
|
|
2313
|
+
/** Dispatched when the snap/bounce-back animation following a rightward swipe completes. */
|
|
1971
2314
|
SwipeEventType["SwipeRightAnimationEnd"] = "swiperightanimationend";
|
|
2315
|
+
/** Dispatched when the snap/bounce-back animation following an upward swipe completes. */
|
|
1972
2316
|
SwipeEventType["SwipeUpAnimationEnd"] = "swipeupanimationend";
|
|
2317
|
+
/** Dispatched when the snap/bounce-back animation following a downward swipe completes. */
|
|
1973
2318
|
SwipeEventType["SwipeDownAnimationEnd"] = "swipedownanimationend";
|
|
1974
2319
|
})(SwipeEventType || (SwipeEventType = {}));
|
|
2320
|
+
/**
|
|
2321
|
+
* Read-only event args dispatched with the swipe gesture lifecycle events.
|
|
2322
|
+
*/
|
|
1975
2323
|
class SwipeEventArgsClass {
|
|
1976
2324
|
constructor(_builder) {
|
|
1977
2325
|
this._builder = _builder;
|
|
@@ -1981,6 +2329,7 @@
|
|
|
1981
2329
|
const isVertical = vertical ?? (Math.abs(horizontalspeed) < Math.abs(verticalspeed));
|
|
1982
2330
|
return isVertical;
|
|
1983
2331
|
}
|
|
2332
|
+
/** @readonly Gets the resolved swipe direction (`'up'`, `'down'`, `'left'` or `'right'`). */
|
|
1984
2333
|
get direction() {
|
|
1985
2334
|
return this.#isVertical
|
|
1986
2335
|
? (this._builder.verticalspeed > 0 ? 'down' : 'up')
|
|
@@ -2012,13 +2361,18 @@
|
|
|
2012
2361
|
const inertia = this._builder.inertia ?? 8;
|
|
2013
2362
|
return -Math.log(.01 /* v/v0 where v ~ 0 <=> 1% of v0 */) / inertia;
|
|
2014
2363
|
}
|
|
2364
|
+
/** @readonly Gets the element being swiped. */
|
|
2015
2365
|
get element() {
|
|
2016
2366
|
return this._builder.element;
|
|
2017
2367
|
}
|
|
2368
|
+
/** Builds a new {@link SwipeEventArgsClass} out of a plain {@link SwipeEventArgs} builder. */
|
|
2018
2369
|
static fromArgs(builder) {
|
|
2019
2370
|
return new SwipeEventArgsClass(Utils.extend({}, builder));
|
|
2020
2371
|
}
|
|
2021
2372
|
}
|
|
2373
|
+
/**
|
|
2374
|
+
* Custom typed event dispatched throughout the swipe gesture lifecycle (see {@link SwipeEventType}).
|
|
2375
|
+
*/
|
|
2022
2376
|
class SwipeEvent extends CustomTypedEvent {
|
|
2023
2377
|
constructor(type, args, eventInit) {
|
|
2024
2378
|
super(type, args, eventInit);
|
|
@@ -2047,6 +2401,7 @@
|
|
|
2047
2401
|
get SwipeEventType () { return SwipeEventType; }
|
|
2048
2402
|
});
|
|
2049
2403
|
|
|
2404
|
+
/** Dispatched by an {@link Iterative} container whenever its `index` changes. */
|
|
2050
2405
|
class CurrentIndexChangeEvent extends CustomTypedEvent {
|
|
2051
2406
|
constructor(args) {
|
|
2052
2407
|
super('currentindexchange', args);
|
|
@@ -2055,11 +2410,16 @@
|
|
|
2055
2410
|
//}
|
|
2056
2411
|
|
|
2057
2412
|
/// <reference path="events.ts" />
|
|
2413
|
+
/** Runtime value (aliasing the built-in `Function`) usable where a constructor reference is needed as a value, paired with the {@link Type} interface for typing it. */
|
|
2058
2414
|
const Type = Function;
|
|
2059
2415
|
// #region CONSTS
|
|
2416
|
+
/** Key used to attach, on an element's constructor, the list of its `@Watch`-decorated (watched) property descriptors. */
|
|
2060
2417
|
const WATCH_PROPS_VAR = 'pacem:properties';
|
|
2418
|
+
/** Key used to attach, on an element instance, the list of attribute names currently holding a pending (not-yet-evaluable) binding expression. */
|
|
2061
2419
|
const INSTANCE_BINDINGS_VAR = 'pacem:custom-element:bindings';
|
|
2420
|
+
/** Key used to attach, on a templated node, a reference to the custom element instance ("host") whose template produced it — used to resolve binding expression scope. */
|
|
2062
2421
|
const INSTANCE_HOST_VAR = 'pacem:custom-element:host';
|
|
2422
|
+
/** Key used to attach, on an element instance, its binding-expression evaluation scope object. */
|
|
2063
2423
|
const INSTANCE_SCOPE_VAR = 'pacem:custom-element:scope';
|
|
2064
2424
|
// #endregion
|
|
2065
2425
|
//}
|
|
@@ -2068,37 +2428,66 @@
|
|
|
2068
2428
|
var root;
|
|
2069
2429
|
/** the overall customelement prefix */
|
|
2070
2430
|
const P = (root = window['Pacem'])?.Configuration?.prefix ?? 'pacem';
|
|
2431
|
+
/** The prefix used for CSS custom properties and classes (e.g. `--{PCSS}-font-main`); configurable independently of the element tag prefix {@link P} via `window.Pacem.Configuration.css`, defaults to {@link P}. */
|
|
2071
2432
|
const PCSS = root?.Configuration?.css ?? P;
|
|
2072
2433
|
//}
|
|
2073
2434
|
|
|
2074
2435
|
//namespace Pacem {
|
|
2436
|
+
/** Marker base class for elements that behave like `<template>` (e.g. a custom template-proxy element), so template-detection logic can treat them the same as a native `HTMLTemplateElement`. */
|
|
2075
2437
|
class TemplateElement extends HTMLElement {
|
|
2076
2438
|
}
|
|
2077
2439
|
//}
|
|
2078
2440
|
|
|
2079
|
-
/// <reference path="expression.ts" />
|
|
2080
|
-
/// <reference path="promise.ts" />
|
|
2081
|
-
/// <reference path="prefix.ts" />
|
|
2082
|
-
/// <reference path="trees/json.ts" />
|
|
2083
|
-
/// <reference path="utils.ts" />
|
|
2084
2441
|
// namespace Pacem {
|
|
2085
2442
|
const PACEM_BAG = '__pacem__';
|
|
2086
2443
|
// {{ binding.expression }}
|
|
2087
2444
|
const bindingPattern = /^\{\{(.|\n)+\}\}$/;
|
|
2088
2445
|
// TODO: CSP-proof binding (no unsafe-eval)
|
|
2089
2446
|
// binding syntax example: `{ binding: { source: ^item.property.path, binder: $pacem.func, parameters: [ ^index, 'foo', : host._baz], mode: 'twoway' } }`
|
|
2447
|
+
/**
|
|
2448
|
+
* Static grab-bag of low-level helpers underpinning the {@link CustomElement}/{@link Watch} decorators
|
|
2449
|
+
* and the binding-expression engine: attached-property storage (a per-instance, non-enumerable "bag"
|
|
2450
|
+
* used instead of expando properties), attribute/property name conversion, DOM tree traversal, host/
|
|
2451
|
+
* scope resolution for templated content, and binding-attribute parsing.
|
|
2452
|
+
*/
|
|
2090
2453
|
class CustomElementUtils {
|
|
2454
|
+
/** `true` when the platform's `customElements` registry is the Custom Elements polyfill rather than a native implementation. */
|
|
2091
2455
|
static get polyfilling() {
|
|
2092
2456
|
return customElements instanceof window['CustomElementRegistry'];
|
|
2093
2457
|
}
|
|
2458
|
+
/** Converts a camelCase property name to its kebab-case attribute-name equivalent (e.g. `myProp` → `my-prop`). */
|
|
2094
2459
|
static camelToKebab(camelCased) {
|
|
2095
2460
|
return camelCased && camelCased.replace(/([A-Z])/g, '-$1').toLowerCase();
|
|
2096
2461
|
}
|
|
2462
|
+
/** Converts a kebab-case attribute name to its camelCase property-name equivalent (e.g. `my-prop` → `myProp`). */
|
|
2097
2463
|
static kebabToCamel(kebabCased) {
|
|
2098
2464
|
return kebabCased && kebabCased.replace(/(-[a-z])/g, (m) => {
|
|
2099
2465
|
return m[1].toUpperCase();
|
|
2100
2466
|
});
|
|
2101
2467
|
}
|
|
2468
|
+
/** Waits for the specified alement to be ready. */
|
|
2469
|
+
static waitForReady(element) {
|
|
2470
|
+
return new Promise((resolve, reject) => {
|
|
2471
|
+
if (Utils.isNull(element)) {
|
|
2472
|
+
reject('Not found.');
|
|
2473
|
+
}
|
|
2474
|
+
// if (element.isConnected && !(element instanceof PacemEventTarget)){
|
|
2475
|
+
// reject('Invalid element.');
|
|
2476
|
+
// }
|
|
2477
|
+
if (element.isReady) {
|
|
2478
|
+
resolve();
|
|
2479
|
+
}
|
|
2480
|
+
const callback = () => {
|
|
2481
|
+
element.removeEventListener('load', callback);
|
|
2482
|
+
resolve();
|
|
2483
|
+
};
|
|
2484
|
+
element.addEventListener('load', callback);
|
|
2485
|
+
});
|
|
2486
|
+
}
|
|
2487
|
+
/** Waits for the specified element to be ready, then executes the provided callback. */
|
|
2488
|
+
static whenReady(element, callback) {
|
|
2489
|
+
CustomElementUtils.waitForReady(element).then(callback);
|
|
2490
|
+
}
|
|
2102
2491
|
static getWatchedProperties(target, includeInherited = true) {
|
|
2103
2492
|
var properties = [];
|
|
2104
2493
|
var chain = target instanceof HTMLElement ? target.constructor : target;
|
|
@@ -2109,9 +2498,16 @@
|
|
|
2109
2498
|
} while (includeInherited && (chain = Object.getPrototypeOf(chain)));
|
|
2110
2499
|
return properties;
|
|
2111
2500
|
}
|
|
2501
|
+
/** Retrieves a single `@Watch`-decorated property descriptor by name; see {@link getWatchedProperties}. */
|
|
2112
2502
|
static getWatchedProperty(target, name) {
|
|
2113
2503
|
return this.getWatchedProperties(target, true).find(p => p.name === name);
|
|
2114
2504
|
}
|
|
2505
|
+
/**
|
|
2506
|
+
* Loads a `<script>` tag once (deduplicated by `src`, cached via {@link import}), resolving when it fires `load`.
|
|
2507
|
+
* @param src Script URL.
|
|
2508
|
+
* @param integrity Optional Subresource Integrity hash.
|
|
2509
|
+
* @param crossorigin Whether to set `crossorigin` on the tag.
|
|
2510
|
+
*/
|
|
2115
2511
|
static importjs(src, integrity = null, crossorigin = false) {
|
|
2116
2512
|
var attrs = { 'type': 'text\/javascript', 'src': src };
|
|
2117
2513
|
if (!Utils.isNullOrEmpty(integrity))
|
|
@@ -2120,6 +2516,12 @@
|
|
|
2120
2516
|
Utils.extend(attrs, { 'crossorigin': '' });
|
|
2121
2517
|
return CustomElementUtils.import(src, 'script', attrs, (document.head || document.getElementsByTagName("head")[0]));
|
|
2122
2518
|
}
|
|
2519
|
+
/**
|
|
2520
|
+
* Loads a `<link rel="stylesheet">` tag once (deduplicated by `src`, cached via {@link import}), resolving when it fires `load`.
|
|
2521
|
+
* @param src Stylesheet URL.
|
|
2522
|
+
* @param integrity Optional Subresource Integrity hash.
|
|
2523
|
+
* @param crossorigin Whether to set `crossorigin="anonymous"` on the tag.
|
|
2524
|
+
*/
|
|
2123
2525
|
static importcss(src, integrity = null, crossorigin = false) {
|
|
2124
2526
|
var attrs = { 'rel': 'stylesheet', 'href': src };
|
|
2125
2527
|
if (!Utils.isNullOrEmpty(integrity))
|
|
@@ -2128,6 +2530,15 @@
|
|
|
2128
2530
|
Utils.extend(attrs, { 'crossorigin': 'anonymous' });
|
|
2129
2531
|
return CustomElementUtils.import(src, 'link', attrs, (document.head || document.getElementsByTagName("head")[0]));
|
|
2130
2532
|
}
|
|
2533
|
+
/**
|
|
2534
|
+
* Creates (or reuses an already-present, matching) element of `tagName` with the given attributes and
|
|
2535
|
+
* appends it to the DOM, returning a promise that resolves once it loads (or rejects on error). Results
|
|
2536
|
+
* are cached per `key` (under `Utils.core.imports`) so repeated calls with the same key are a no-op.
|
|
2537
|
+
* @param key Cache/dedupe key (typically the resource URL).
|
|
2538
|
+
* @param tagName Tag name of the element to create (e.g. `'script'`, `'link'`).
|
|
2539
|
+
* @param attrs Attributes to set on the element, also used to build a matching CSS selector to detect a pre-existing element.
|
|
2540
|
+
* @param appendTo Node to append the newly created element to, if none was found. Defaults to `document.body`.
|
|
2541
|
+
*/
|
|
2131
2542
|
static import(key, tagName, attrs, appendTo = document.body) {
|
|
2132
2543
|
const _p = Utils.core;
|
|
2133
2544
|
var _imports = _p['imports'] = _p['imports'] || {};
|
|
@@ -2157,7 +2568,9 @@
|
|
|
2157
2568
|
}
|
|
2158
2569
|
});
|
|
2159
2570
|
}
|
|
2571
|
+
/** Theming-related helpers. */
|
|
2160
2572
|
static { this.Theme = {
|
|
2573
|
+
/** Downloads the web fonts referenced by the current theme's `--{@link PCSS}-font-*` CSS custom properties, resolving once at least one font face is ready (or after a short timeout). */
|
|
2161
2574
|
importWebFonts() {
|
|
2162
2575
|
function cleanupQuotes(input) {
|
|
2163
2576
|
// check if the string is wrapped around quotes to escape ';' chars
|
|
@@ -2195,6 +2608,14 @@
|
|
|
2195
2608
|
return Promise.all(promises);
|
|
2196
2609
|
}
|
|
2197
2610
|
}; }
|
|
2611
|
+
/**
|
|
2612
|
+
* Stores an arbitrary value "attached" to `target` under `name`, in a private, non-enumerable bag
|
|
2613
|
+
* (rather than as a plain expando property) so it doesn't show up in `for...in`/`Object.keys`/JSON
|
|
2614
|
+
* serialization. Setting `value` to `null`/`undefined` removes the entry. See {@link getAttachedPropertyValue}.
|
|
2615
|
+
* @param target Object to attach the value to.
|
|
2616
|
+
* @param name Key under which the value is stored.
|
|
2617
|
+
* @param value Value to store, or `null`/`undefined` to remove the entry.
|
|
2618
|
+
*/
|
|
2198
2619
|
static setAttachedPropertyValue(target, name, value) {
|
|
2199
2620
|
/*(target[PACEM_BAG] = target[PACEM_BAG] || {})[name] = value;*/
|
|
2200
2621
|
if (Utils.isNull(target))
|
|
@@ -2209,9 +2630,16 @@
|
|
|
2209
2630
|
else
|
|
2210
2631
|
bag[name] = value;
|
|
2211
2632
|
}
|
|
2633
|
+
/** Removes a value previously stored via {@link setAttachedPropertyValue}. */
|
|
2212
2634
|
static deleteAttachedPropertyValue(target, name) {
|
|
2213
2635
|
CustomElementUtils.setAttachedPropertyValue(target, name, undefined);
|
|
2214
2636
|
}
|
|
2637
|
+
/**
|
|
2638
|
+
* Reads a value previously stored via {@link setAttachedPropertyValue}.
|
|
2639
|
+
* @param target Object the value is attached to.
|
|
2640
|
+
* @param name Key the value is stored under.
|
|
2641
|
+
* @param ensureValue If provided and no value is currently stored, this value is stored and returned (lazy-initialization).
|
|
2642
|
+
*/
|
|
2215
2643
|
static getAttachedPropertyValue(target, name, ensureValue) {
|
|
2216
2644
|
const propKey = Object.getOwnPropertyDescriptor(target, PACEM_BAG), bag = propKey && propKey.value;
|
|
2217
2645
|
if (Utils.isNull(bag && bag[name]) && !Utils.isNull(ensureValue)) {
|
|
@@ -2257,6 +2685,17 @@
|
|
|
2257
2685
|
}
|
|
2258
2686
|
return { target: { value: ref, name: name }, parent: { value: parent || scope, path: path.substring(0, ndx) }, root: { value: root, property: core } };
|
|
2259
2687
|
}
|
|
2688
|
+
/**
|
|
2689
|
+
* Writes `value` at the given dotted `path`, rooted at `element` (see {@link resolvePath}), triggering
|
|
2690
|
+
* the appropriate `propertychange` notification. For paths nested more than one level deep (and not
|
|
2691
|
+
* targeting an `HTMLElement`, which fires change notifications autonomously), the whole root property
|
|
2692
|
+
* is cloned, mutated and reassigned so that a `propertychange` fires with meaningful old/new values -
|
|
2693
|
+
* a plain nested assignment wouldn't otherwise be observed. Repeater-bound `item` paths are special-cased
|
|
2694
|
+
* to update the owning {@link Repeater}'s `datasource` array in place.
|
|
2695
|
+
* @param element Root element the path is resolved against.
|
|
2696
|
+
* @param path Dotted path (e.g. `'foo.bar[0].baz'`) to the target property.
|
|
2697
|
+
* @param value New value to assign.
|
|
2698
|
+
*/
|
|
2260
2699
|
static set(element, path, value) {
|
|
2261
2700
|
var obj = CustomElementUtils.resolvePath(path, element);
|
|
2262
2701
|
var current = obj.target.value;
|
|
@@ -2287,6 +2726,7 @@
|
|
|
2287
2726
|
}
|
|
2288
2727
|
}
|
|
2289
2728
|
}
|
|
2729
|
+
/** Reads the value at the given dotted `path`, rooted at `element`; see {@link resolvePath}. */
|
|
2290
2730
|
static get(element, path) {
|
|
2291
2731
|
var obj = CustomElementUtils.resolvePath(path, element);
|
|
2292
2732
|
return obj && obj.target && obj.target.value;
|
|
@@ -2307,12 +2747,27 @@
|
|
|
2307
2747
|
// logFn(`Element "${element.constructor.name}" isn't attached to any context yet.`);
|
|
2308
2748
|
return retval;
|
|
2309
2749
|
}
|
|
2750
|
+
/**
|
|
2751
|
+
* Finds the `Document` or `ShadowRoot` that `element` belongs to - used as the root for resolving
|
|
2752
|
+
* scoped binding expressions (e.g. `$document`/`$host` lookups). Falls back to the previously-attached
|
|
2753
|
+
* scope value (see `INSTANCE_SCOPE_VAR`, set on `connectedCallback`) when `element` is currently
|
|
2754
|
+
* disconnected, and to a legacy manual-walk implementation as a last resort (needed under Jasmine tests,
|
|
2755
|
+
* where `element.isConnected` never reports `true`).
|
|
2756
|
+
* @param element Element to resolve the containing document/shadow-root scope for.
|
|
2757
|
+
*/
|
|
2310
2758
|
static findScopeContext(element) {
|
|
2311
2759
|
let retval = (element.isConnected) ?
|
|
2312
2760
|
element.getRootNode()
|
|
2313
2761
|
: CustomElementUtils.getAttachedPropertyValue(element, INSTANCE_SCOPE_VAR);
|
|
2314
2762
|
return retval ?? /* in Jasmine TESTS `element.isConnected` always returns false (attached to #document-fragment) */ CustomElementUtils._findScopeContextLegacy(element);
|
|
2315
2763
|
}
|
|
2764
|
+
/**
|
|
2765
|
+
* Walks up from `element` (crossing shadow-root boundaries via the host element) looking for the
|
|
2766
|
+
* first ancestor matching `predicate`.
|
|
2767
|
+
* @param element Element to start the upward search from.
|
|
2768
|
+
* @param predicate Test invoked (as `this`-bound and argument-passed) against each ancestor.
|
|
2769
|
+
* @returns The first matching ancestor, or `undefined` if none matches.
|
|
2770
|
+
*/
|
|
2316
2771
|
static findAncestor(element, predicate) {
|
|
2317
2772
|
let el = element;
|
|
2318
2773
|
let retval;
|
|
@@ -2326,12 +2781,20 @@
|
|
|
2326
2781
|
}
|
|
2327
2782
|
return retval;
|
|
2328
2783
|
}
|
|
2784
|
+
/** Finds the nearest ancestor "shell" element - matching `.{@link PCSS}-body` - or falls back to `document.body`; see {@link findAncestor}. */
|
|
2329
2785
|
static findAncestorShell(element) {
|
|
2330
2786
|
return CustomElementUtils.findAncestor(element, el => Utils.is(el, `.${PCSS}-body`) || el === document.body);
|
|
2331
2787
|
}
|
|
2788
|
+
/** Finds the nearest ancestor that is an instance of `ctor`; see {@link findAncestor}. */
|
|
2332
2789
|
static findAncestorOfType(element, ctor) {
|
|
2333
2790
|
return CustomElementUtils.findAncestor(element, el => el instanceof ctor);
|
|
2334
2791
|
}
|
|
2792
|
+
/**
|
|
2793
|
+
* Collects the descendants of `element` matching `predicate`, via a `TreeWalker` over element nodes.
|
|
2794
|
+
* @param element Root element to search from (exclusive - the walker starts on its descendants).
|
|
2795
|
+
* @param predicate Test invoked against each descendant element.
|
|
2796
|
+
* @param firstOnly When `true`, stops and returns as soon as one match is found.
|
|
2797
|
+
*/
|
|
2335
2798
|
static findDescendants(element, predicate, firstOnly = false) {
|
|
2336
2799
|
const walker = document.createTreeWalker(element, NodeFilter.SHOW_ELEMENT);
|
|
2337
2800
|
var retval = [];
|
|
@@ -2346,10 +2809,16 @@
|
|
|
2346
2809
|
}
|
|
2347
2810
|
return retval;
|
|
2348
2811
|
}
|
|
2812
|
+
/** Finds the first descendant of `element` matching `predicate`, or `undefined` if none matches; see {@link findDescendants}. */
|
|
2349
2813
|
static findFirstDescendant(element, predicate) {
|
|
2350
2814
|
const any = CustomElementUtils.findDescendants(element, predicate, true);
|
|
2351
2815
|
return Utils.isNullOrEmpty(any) ? void 0 : any[0];
|
|
2352
2816
|
}
|
|
2817
|
+
/**
|
|
2818
|
+
* Queries the whole document for elements matching `selector`, additionally filtered by `filter`.
|
|
2819
|
+
* @param selector CSS selector to query for. Defaults to `'[pacem]'` (every Pacem custom element, which all carry that attribute once connected).
|
|
2820
|
+
* @param filter Extra predicate applied to each matched element. Defaults to accepting everything.
|
|
2821
|
+
*/
|
|
2353
2822
|
static findAll(selector = '[pacem]', filter = (e) => true) {
|
|
2354
2823
|
const retval = [];
|
|
2355
2824
|
document.querySelectorAll(selector).forEach((e, i, arr) => {
|
|
@@ -2411,6 +2880,14 @@
|
|
|
2411
2880
|
} while (el != null);
|
|
2412
2881
|
return retval;
|
|
2413
2882
|
}
|
|
2883
|
+
/**
|
|
2884
|
+
* Tags every element/document-fragment node inside `template`'s content (that isn't already tagged)
|
|
2885
|
+
* with `host` under `INSTANCE_HOST_VAR`, so that once cloned and inserted into the DOM, binding
|
|
2886
|
+
* expressions in that fragment can resolve back to the custom element instance ("host") that owns
|
|
2887
|
+
* this template. See {@link findHostContext}.
|
|
2888
|
+
* @param host The custom element instance whose template this is.
|
|
2889
|
+
* @param template Template whose content's descendants should be tagged.
|
|
2890
|
+
*/
|
|
2414
2891
|
static assignHostContext(host, template) {
|
|
2415
2892
|
// navigate through the DOM hierarchy
|
|
2416
2893
|
const walker = document.createTreeWalker(template.content, NodeFilter.SHOW_ELEMENT + /* traverse templates' content */ NodeFilter.SHOW_DOCUMENT_FRAGMENT);
|
|
@@ -2423,6 +2900,13 @@
|
|
|
2423
2900
|
}
|
|
2424
2901
|
}
|
|
2425
2902
|
}
|
|
2903
|
+
/**
|
|
2904
|
+
* Walks up from `element` looking for the nearest ancestor (inclusive) tagged with a host reference
|
|
2905
|
+
* via {@link assignHostContext}; this is the custom element instance whose template produced `element`,
|
|
2906
|
+
* used to resolve `$host`/scope references in binding expressions. Returns `undefined` (with a console
|
|
2907
|
+
* warning) if `element` is disconnected or isn't part of any templated component.
|
|
2908
|
+
* @param element Element to resolve the owning host for.
|
|
2909
|
+
*/
|
|
2426
2910
|
static findHostContext(element) {
|
|
2427
2911
|
let el = element;
|
|
2428
2912
|
let retval;
|
|
@@ -2445,6 +2929,7 @@
|
|
|
2445
2929
|
logFn(`Element "${element.constructor.name}" isn't a descendant of a templated component.`);
|
|
2446
2930
|
return retval;
|
|
2447
2931
|
}
|
|
2932
|
+
/** Tests whether an attribute's raw string value has binding-expression syntax (`{{ ... }}`). */
|
|
2448
2933
|
static isBindingAttribute(attr) {
|
|
2449
2934
|
/*if (/^\s*\{\s*binding\s+.*\}\s*$/.test(attr)) {
|
|
2450
2935
|
|
|
@@ -2464,6 +2949,7 @@
|
|
|
2464
2949
|
else*/
|
|
2465
2950
|
return bindingPattern.test(attr);
|
|
2466
2951
|
}
|
|
2952
|
+
/** Strips the `{{ }}` delimiters off a binding attribute's raw value, returning the bare expression text. Throws if `attr` isn't a valid binding attribute; see {@link isBindingAttribute}. */
|
|
2467
2953
|
static extractBindingAttributeExpression(attr) {
|
|
2468
2954
|
if (CustomElementUtils.isBindingAttribute(attr)) {
|
|
2469
2955
|
return attr.substr(2, attr.length - 4);
|
|
@@ -2472,6 +2958,13 @@
|
|
|
2472
2958
|
throw `Invalid attribute: incorrect binding syntax.`;
|
|
2473
2959
|
}
|
|
2474
2960
|
}
|
|
2961
|
+
/**
|
|
2962
|
+
* Parses a `{{ ... }}` binding attribute value into an {@link Expression}, recognizing the optional
|
|
2963
|
+
* trailing `, twoway` / `, once` modifier and stamping it onto the expression's dependencies (`twoway`
|
|
2964
|
+
* only applies when the expression has exactly one dependency that itself allows two-way binding).
|
|
2965
|
+
* @param attr Raw binding attribute value, including the `{{ }}` delimiters.
|
|
2966
|
+
* @param element Element the expression is evaluated/bound against (used to resolve scope references).
|
|
2967
|
+
*/
|
|
2475
2968
|
static parseBindingAttribute(attr, element) {
|
|
2476
2969
|
// loose syntax {{ ... }}
|
|
2477
2970
|
let expression = CustomElementUtils.extractBindingAttributeExpression(attr);
|
|
@@ -2492,6 +2985,14 @@
|
|
|
2492
2985
|
});
|
|
2493
2986
|
return expr;
|
|
2494
2987
|
}
|
|
2988
|
+
/**
|
|
2989
|
+
* Defines an own property `name` on `o` with `attributes`, unless `o` already has an own property
|
|
2990
|
+
* descriptor for that name (in which case the existing descriptor is returned untouched).
|
|
2991
|
+
* @param o Object to define the property on.
|
|
2992
|
+
* @param name Property name.
|
|
2993
|
+
* @param attributes Property descriptor to apply if the property isn't already defined.
|
|
2994
|
+
* @returns The (possibly pre-existing) property descriptor.
|
|
2995
|
+
*/
|
|
2495
2996
|
static ensureMember(o, name, attributes) {
|
|
2496
2997
|
let original = Object.getOwnPropertyDescriptor(o, name);
|
|
2497
2998
|
if (original != undefined)
|
|
@@ -2552,6 +3053,7 @@
|
|
|
2552
3053
|
const GET_VAL$6 = CustomElementUtils.getAttachedPropertyValue;
|
|
2553
3054
|
const SET_VAL$5 = CustomElementUtils.setAttachedPropertyValue;
|
|
2554
3055
|
const REPEATERITEM_PLACEHOLDER = 'pacem:repeater-item';
|
|
3056
|
+
/** Static lookup helpers for locating the {@link RepeaterItem} context a given element belongs to. */
|
|
2555
3057
|
class Repeater {
|
|
2556
3058
|
/**
|
|
2557
3059
|
* Seeks for a PacemRepeaterItem element upwards through the DOM tree, given a starting element and the number of nesting levels.
|
|
@@ -2566,7 +3068,22 @@
|
|
|
2566
3068
|
return retval;
|
|
2567
3069
|
}
|
|
2568
3070
|
}
|
|
3071
|
+
/**
|
|
3072
|
+
* Base class tracking a single rendered repetition of a {@link Repeater}'s template: it associates a
|
|
3073
|
+
* DOM placeholder node with the `item`/`index` scope values that template's binding expressions
|
|
3074
|
+
* evaluate against, and is itself attached to that placeholder (see {@link getRepeaterItem}) so
|
|
3075
|
+
* descendant elements can find their way back to it via {@link findUpwards}.
|
|
3076
|
+
*/
|
|
2569
3077
|
let RepeaterItem$1 = class RepeaterItem {
|
|
3078
|
+
/**
|
|
3079
|
+
* Seeks upwards from `element` (previous siblings, then parent, repeated) for the nearest ancestor
|
|
3080
|
+
* placeholder tagged as a {@link RepeaterItem}, skipping `upLevels` matches before returning one -
|
|
3081
|
+
* this is how deeply-nested repeaters resolve which repetition (of which ancestor repeater) an
|
|
3082
|
+
* expression's `^item`/`^^item`/... token refers to.
|
|
3083
|
+
* @param element Element to start the upward search from.
|
|
3084
|
+
* @param upLevels 0-based number of matching ancestors to skip before returning one (`0` = nearest).
|
|
3085
|
+
* @param logFn Called with a warning message if no match is found and `element` is connected to the DOM.
|
|
3086
|
+
*/
|
|
2570
3087
|
static findUpwards(element, upLevels = 0, logFn = console.warn) {
|
|
2571
3088
|
if (Utils.isNull(element) || element.localName === 'template' || element instanceof /* template-like element? */ TemplateElement) {
|
|
2572
3089
|
return null;
|
|
@@ -2579,16 +3096,20 @@
|
|
|
2579
3096
|
}
|
|
2580
3097
|
return item;
|
|
2581
3098
|
}
|
|
3099
|
+
/** Tests whether `node` is the placeholder of some {@link RepeaterItem}. */
|
|
2582
3100
|
static isRepeaterItem(node) {
|
|
2583
3101
|
return !Utils.isNull(GET_VAL$6(node, REPEATERITEM_PLACEHOLDER));
|
|
2584
3102
|
}
|
|
3103
|
+
/** Retrieves the {@link RepeaterItem} attached to `node`, if `node` is a repeater-item placeholder; see {@link isRepeaterItem}. */
|
|
2585
3104
|
static getRepeaterItem(node) {
|
|
2586
3105
|
return GET_VAL$6(node, REPEATERITEM_PLACEHOLDER);
|
|
2587
3106
|
}
|
|
3107
|
+
/** @param _placeholder DOM node (typically a comment) that stands in for this repetition and gets tagged so {@link getRepeaterItem} can retrieve this instance back from it. */
|
|
2588
3108
|
constructor(_placeholder) {
|
|
2589
3109
|
this._placeholder = _placeholder;
|
|
2590
3110
|
SET_VAL$5(_placeholder, REPEATERITEM_PLACEHOLDER, this);
|
|
2591
3111
|
}
|
|
3112
|
+
/** The DOM placeholder node standing in for this repetition. */
|
|
2592
3113
|
get placeholder() {
|
|
2593
3114
|
return this._placeholder;
|
|
2594
3115
|
}
|
|
@@ -2634,32 +3155,53 @@
|
|
|
2634
3155
|
// * ...
|
|
2635
3156
|
// */
|
|
2636
3157
|
//const purePropertyPathPattern = /^[\w\.\$]+/;
|
|
3158
|
+
/**
|
|
3159
|
+
* A parsed, compiled binding expression (the content of a `{{ ... }}` attribute value). Parsing
|
|
3160
|
+
* (see {@link parse}) rewrites special tokens found in the source text - `:host.`, `^item`/`^^item`/...,
|
|
3161
|
+
* `^index`/`^^index`/..., `#id`, `::hostProp` - into references resolvable at evaluation time, and
|
|
3162
|
+
* records each of them as a {@link PropertyDependency} so the owning element can be notified (via
|
|
3163
|
+
* {@link PropertyChangeEvent}) and re-evaluate the expression whenever a dependency changes - this is
|
|
3164
|
+
* what powers the two-way/live data binding wired up by the {@link CustomElement} decorator.
|
|
3165
|
+
* Expressions with no dependencies are flagged `independent` (effectively a constant) and can be
|
|
3166
|
+
* evaluated immediately without waiting for the DOM/scope to be ready.
|
|
3167
|
+
*/
|
|
2637
3168
|
class Expression {
|
|
2638
3169
|
constructor() {
|
|
2639
3170
|
this._pending = false;
|
|
2640
3171
|
this._independent = false;
|
|
2641
3172
|
}
|
|
3173
|
+
/** The resolved property dependencies (element + property + path) this expression re-evaluates on change. Empty for {@link independent} expressions. */
|
|
2642
3174
|
get dependencies() {
|
|
2643
3175
|
return this._dependencies;
|
|
2644
3176
|
}
|
|
3177
|
+
/** The compiled function body used by {@link evaluate}/{@link evaluateAsync} (a `return <expr>;` statement, guarded by a try/catch). */
|
|
2645
3178
|
get functionBody() {
|
|
2646
3179
|
return this._fnBody;
|
|
2647
3180
|
}
|
|
3181
|
+
/** The compiled function body used by {@link evaluatePlus} (a bare, non-`return`ing statement, guarded by a try/catch) - suited for expressions run for their side effects, e.g. `on-*` event handlers. */
|
|
2648
3182
|
get voidBody() {
|
|
2649
3183
|
return this._voidBody;
|
|
2650
3184
|
}
|
|
3185
|
+
/**
|
|
3186
|
+
* `true` when this expression couldn't be resolved against a scope yet (the element wasn't connected
|
|
3187
|
+
* to a document/shadow-root at parse time) - a shared, inert placeholder singleton returned by
|
|
3188
|
+
* {@link parse} in that case; evaluating it is a no-op. Callers should re-parse once the element is ready.
|
|
3189
|
+
*/
|
|
2651
3190
|
get pending() {
|
|
2652
3191
|
return this._pending;
|
|
2653
3192
|
}
|
|
3193
|
+
/** `true` when the expression has no dependencies (a constant) and can be evaluated immediately, without a resolved scope. */
|
|
2654
3194
|
get independent() {
|
|
2655
3195
|
return this._independent;
|
|
2656
3196
|
}
|
|
3197
|
+
/** Evaluates the expression and returns its result, swallowing any runtime error (returning `undefined`). */
|
|
2657
3198
|
evaluate() {
|
|
2658
3199
|
if (!this._fn) {
|
|
2659
3200
|
this._fn = new Function('$pacem', CONTEXT_PREFIX, this._fnBody);
|
|
2660
3201
|
}
|
|
2661
3202
|
return this._fn.apply(null, [Utils.core, this._args]);
|
|
2662
3203
|
}
|
|
3204
|
+
/** Promise-wrapped variant of {@link evaluate} (resolves synchronously with the same result - kept for call-site uniformity with genuinely async evaluation paths). */
|
|
2663
3205
|
evaluateAsync() {
|
|
2664
3206
|
const deferred = DeferPromise.defer();
|
|
2665
3207
|
if (!this._fn) {
|
|
@@ -2668,6 +3210,12 @@
|
|
|
2668
3210
|
deferred.resolve(this._fn.apply(null, [Utils.core, this._args]));
|
|
2669
3211
|
return deferred.promise;
|
|
2670
3212
|
}
|
|
3213
|
+
/**
|
|
3214
|
+
* Executes the expression for its side effects (using {@link voidBody}, so no value is returned),
|
|
3215
|
+
* with extra named variables merged into scope alongside the expression's own dependencies -
|
|
3216
|
+
* used to expose e.g. `$event` to `on-*` event handler expressions.
|
|
3217
|
+
* @param args Extra name/value pairs made available to the expression, in addition to its resolved dependencies.
|
|
3218
|
+
*/
|
|
2671
3219
|
evaluatePlus(args = {}) {
|
|
2672
3220
|
if (!this._fn) {
|
|
2673
3221
|
let argNames = [CONTEXT_PREFIX];
|
|
@@ -2692,6 +3240,15 @@
|
|
|
2692
3240
|
}
|
|
2693
3241
|
return Expression._pendingExpression;
|
|
2694
3242
|
}
|
|
3243
|
+
/**
|
|
3244
|
+
* Parses a binding expression's source text (the bare content of a `{{ ... }}` attribute, without the
|
|
3245
|
+
* delimiters - see {@link CustomElementUtils.extractBindingAttributeExpression}) into an {@link Expression},
|
|
3246
|
+
* resolving its special tokens against `element`'s scope (see {@link CustomElementUtils.findScopeContext}).
|
|
3247
|
+
* Throws if the expression matches the unsafe-code guard (`new`, `window.`, `document.`, `eval(`).
|
|
3248
|
+
* @param expression Bare expression source text.
|
|
3249
|
+
* @param element Element the expression is bound to; provides the scope special tokens (`:host.`, `#id`, `^item`, ...) resolve against.
|
|
3250
|
+
* @returns A constant expression if it has no dependencies; a shared pending placeholder if `element` isn't attached to a scope yet; otherwise a fully resolved expression.
|
|
3251
|
+
*/
|
|
2695
3252
|
static parse(expression, element) {
|
|
2696
3253
|
var context = CustomElementUtils.findScopeContext(element);
|
|
2697
3254
|
var args = { '$this': element };
|
|
@@ -2822,13 +3379,20 @@
|
|
|
2822
3379
|
// }
|
|
2823
3380
|
|
|
2824
3381
|
//namespace Pacem.Logging {
|
|
3382
|
+
/** Severity levels accepted by {@link Logger.log}, and used by {@link PacemEventTarget.log}. */
|
|
2825
3383
|
var LogLevel;
|
|
2826
3384
|
(function (LogLevel) {
|
|
3385
|
+
/** Fine-grained diagnostic detail. */
|
|
2827
3386
|
LogLevel["Trace"] = "trace";
|
|
3387
|
+
/** Development-time diagnostic detail. */
|
|
2828
3388
|
LogLevel["Debug"] = "debug";
|
|
3389
|
+
/** Recoverable but noteworthy condition. */
|
|
2829
3390
|
LogLevel["Warn"] = "warn";
|
|
3391
|
+
/** Unrecoverable or unexpected failure. */
|
|
2830
3392
|
LogLevel["Error"] = "error";
|
|
3393
|
+
/** General informational message. */
|
|
2831
3394
|
LogLevel["Info"] = "info";
|
|
3395
|
+
/** Generic/default-severity message. */
|
|
2832
3396
|
LogLevel["Log"] = "log";
|
|
2833
3397
|
})(LogLevel || (LogLevel = {}));
|
|
2834
3398
|
//}
|
|
@@ -2882,6 +3446,11 @@
|
|
|
2882
3446
|
convert: (attr) => pacemFoundation.Rect.parse(attr),
|
|
2883
3447
|
convertBack: (prop) => `${prop.x || 0} ${prop.y || 0} ${prop.width || 0} ${prop.height || 0}`
|
|
2884
3448
|
};
|
|
3449
|
+
/**
|
|
3450
|
+
* Built-in {@link PropertyConverter} implementations, keyed by the type they convert to/from. Passed as
|
|
3451
|
+
* the `converter` option of a {@link Watch}-decorated property (e.g. `@Watch({ converter: PropertyConverters.Number })`)
|
|
3452
|
+
* to control how that property's attribute string is parsed and, on change, reflected back.
|
|
3453
|
+
*/
|
|
2885
3454
|
const PropertyConverters /*: { [name: string]: PropertyConverter }*/ = {
|
|
2886
3455
|
None: NonePropertyConverter,
|
|
2887
3456
|
String: StringPropertyConverter,
|
|
@@ -3029,6 +3598,31 @@
|
|
|
3029
3598
|
}
|
|
3030
3599
|
return promises;
|
|
3031
3600
|
}
|
|
3601
|
+
/**
|
|
3602
|
+
* Class decorator that turns a class extending `HTMLElement` (typically {@link PacemEventTarget}) into a
|
|
3603
|
+
* fully-fledged Pacem custom element: it registers the tag with the custom elements registry and wraps
|
|
3604
|
+
* the standard `connectedCallback`/`disconnectedCallback`/`attributeChangedCallback`/`propertyChangedCallback`
|
|
3605
|
+
* lifecycle hooks (calling through to any hooks already defined on the class) so that:
|
|
3606
|
+
*
|
|
3607
|
+
* - `observedAttributes` is computed automatically from the class's {@link Watch}-decorated properties
|
|
3608
|
+
* (in kebab-case), merged with any statically-declared `observedAttributes`.
|
|
3609
|
+
* - On first connection, `config.template`/`config.templateUrl` (if provided) is fetched, parsed and
|
|
3610
|
+
* injected — into a Shadow Root when `config.shadow` is `true`, otherwise as light-DOM children,
|
|
3611
|
+
* splicing any pre-existing light-DOM content into a `<pacem-content>` placeholder if present.
|
|
3612
|
+
* - Attribute changes are converted to property values via each watched property's {@link PropertyConverter},
|
|
3613
|
+
* and dispatched as an {@link AttributeChangeEvent}.
|
|
3614
|
+
* - Property changes are dispatched as a {@link PropertyChangeEvent} (unless the property's `@Watch`
|
|
3615
|
+
* config sets `emit: false`) and, when `reflectBack` is enabled, written back onto the corresponding
|
|
3616
|
+
* attribute via the converter's `convertBack`.
|
|
3617
|
+
* - Binding expression attributes (`{{ ... }}`) are parsed and kept live: dependent properties are
|
|
3618
|
+
* re-evaluated whenever one of the expression's upstream properties fires a {@link PropertyChangeEvent}.
|
|
3619
|
+
* - Once the template (and all descendant templated custom elements) have finished rendering, the
|
|
3620
|
+
* `cloak` attribute is removed and `viewActivatedCallback` fires — this is the right place for logic
|
|
3621
|
+
* that depends on the element's full DOM (light or shadow) being in place.
|
|
3622
|
+
*
|
|
3623
|
+
* @param config Tag name, optional native `customElements.define` options, and optional template/templateUrl/shadow settings.
|
|
3624
|
+
* @returns A class decorator to apply to the custom element's class declaration.
|
|
3625
|
+
*/
|
|
3032
3626
|
const CustomElement = (config) => {
|
|
3033
3627
|
return (target) => {
|
|
3034
3628
|
// might be already registered
|
|
@@ -3363,6 +3957,27 @@
|
|
|
3363
3957
|
// #endregion
|
|
3364
3958
|
// #region WATCH PROPERTIES
|
|
3365
3959
|
const DefaultPropertyConverter = PropertyConverters.None;
|
|
3960
|
+
/**
|
|
3961
|
+
* Property decorator that turns a plain class field (or accessor) into a Pacem "watched" property:
|
|
3962
|
+
* a property whose changes are detected (via reference/value comparison, see {@link DefaultComparer}),
|
|
3963
|
+
* optionally converted from/to its corresponding attribute string via a {@link PropertyConverter}
|
|
3964
|
+
* (see {@link PropertyConverters}), and notified through `propertyChangedCallback` plus a
|
|
3965
|
+
* {@link PropertyChangeEvent} (unless `config.emit` is `false`).
|
|
3966
|
+
*
|
|
3967
|
+
* Must be paired with the {@link CustomElement} class decorator, which is what actually reads the list
|
|
3968
|
+
* of watched properties (via `WATCH_PROPS_VAR`) to compute `observedAttributes` and wire up attribute/
|
|
3969
|
+
* property change plumbing — `@Watch` alone, on a class not decorated with `@CustomElement`, only sets
|
|
3970
|
+
* up the getter/setter and change notification, with no attribute involvement.
|
|
3971
|
+
*
|
|
3972
|
+
* Array-valued properties are additionally instrumented: their mutating methods (`push`, `pop`, `splice`,
|
|
3973
|
+
* `shift`, `unshift`, `copyWithin`) are patched so in-place mutations also trigger a change notification.
|
|
3974
|
+
*
|
|
3975
|
+
* @param config Optional behavior: `converter` (attribute↔property conversion, default `PropertyConverters.None`),
|
|
3976
|
+
* `emit` (whether to dispatch a {@link PropertyChangeEvent}, default `true`), `debounce` (delay the
|
|
3977
|
+
* change notification, either by a fixed number of milliseconds or `true` for one animation frame),
|
|
3978
|
+
* and `reflectBack` (write the property value back onto the attribute on change, default `false`).
|
|
3979
|
+
* @returns A property decorator to apply to the watched field/accessor.
|
|
3980
|
+
*/
|
|
3366
3981
|
function Watch(config) {
|
|
3367
3982
|
return (target, prop, descriptor) => {
|
|
3368
3983
|
var watchableProperties = GET_VAL$5(target.constructor, WATCH_PROPS_VAR, []);
|
|
@@ -3481,6 +4096,17 @@
|
|
|
3481
4096
|
}
|
|
3482
4097
|
};
|
|
3483
4098
|
}
|
|
4099
|
+
/**
|
|
4100
|
+
* Property decorator that turns a class field into a lazily-resolved, cached reference to a descendant
|
|
4101
|
+
* element matching `selector`. Resolution happens on first read (via `shadowRoot.querySelectorAll` when
|
|
4102
|
+
* available, falling back to the element itself), and only nodes whose `INSTANCE_HOST_VAR` points back
|
|
4103
|
+
* at `this` are considered — this keeps the lookup scoped to the current element's own template output,
|
|
4104
|
+
* skipping over descendants that belong to a nested custom element's own template. The cache is reset
|
|
4105
|
+
* whenever the host element reconnects to the DOM (`connectedCallback`, see the {@link CustomElement} decorator).
|
|
4106
|
+
*
|
|
4107
|
+
* @param selector CSS selector identifying the child element to bind to.
|
|
4108
|
+
* @returns A property decorator to apply to the view-child field (read-only; no setter is defined).
|
|
4109
|
+
*/
|
|
3484
4110
|
function ViewChild(selector) {
|
|
3485
4111
|
return (target, prop, descriptor) => {
|
|
3486
4112
|
const key = `${prop}_${Utils.uniqueCode()}`;
|
|
@@ -3513,6 +4139,15 @@
|
|
|
3513
4139
|
}
|
|
3514
4140
|
// #endregion
|
|
3515
4141
|
// #region CONCURRENT/DEBOUNCE EXECUTION
|
|
4142
|
+
/**
|
|
4143
|
+
* Method decorator that serializes calls to an async (promise-returning) method on a per-instance basis:
|
|
4144
|
+
* while an invocation is in flight, further calls are queued (their arguments buffered) instead of running
|
|
4145
|
+
* concurrently, and each queued call gets its own promise that resolves/rejects once it eventually runs.
|
|
4146
|
+
* Once the in-flight call settles, the oldest buffered call (if any) is dequeued and executed next.
|
|
4147
|
+
* Methods that don't return a promise-like value are simply invoked synchronously with no queuing.
|
|
4148
|
+
*
|
|
4149
|
+
* @returns A method decorator to apply to an async instance method.
|
|
4150
|
+
*/
|
|
3516
4151
|
function Concurrent() {
|
|
3517
4152
|
function isPromiseLike(obj) {
|
|
3518
4153
|
return obj && typeof obj.then === 'function';
|
|
@@ -3563,6 +4198,15 @@
|
|
|
3563
4198
|
};
|
|
3564
4199
|
};
|
|
3565
4200
|
}
|
|
4201
|
+
/**
|
|
4202
|
+
* Method decorator that debounces calls to an instance method: repeated calls within the debounce window
|
|
4203
|
+
* cancel the previously scheduled invocation and reschedule it, so only the last call in a rapid burst
|
|
4204
|
+
* actually runs (with its own arguments), after the window elapses.
|
|
4205
|
+
*
|
|
4206
|
+
* @param msecs Debounce delay in milliseconds, or `true` to debounce to the next animation frame (`requestAnimationFrame`) instead of a fixed delay. Defaults to `50`.
|
|
4207
|
+
* @param keyFactory Computes the per-instance timer key from the method name and call arguments; override to scope debouncing per argument (e.g. debounce separately per id) instead of per method. Defaults to a single shared key per method.
|
|
4208
|
+
* @returns A method decorator to apply to the instance method to debounce.
|
|
4209
|
+
*/
|
|
3566
4210
|
function Debounce(msecs = 50, keyFactory = (key, ...args) => `pacem:debouncer:${key}`) {
|
|
3567
4211
|
return function (target /* type, actually */, key, descriptor) {
|
|
3568
4212
|
const originalMethod = descriptor.value;
|
|
@@ -3588,6 +4232,16 @@
|
|
|
3588
4232
|
}
|
|
3589
4233
|
};
|
|
3590
4234
|
}
|
|
4235
|
+
/**
|
|
4236
|
+
* Method decorator that throttles calls to an instance method: the first call in a window runs immediately;
|
|
4237
|
+
* further calls arriving before the window elapses are not dropped but coalesced — only the most recent
|
|
4238
|
+
* set of arguments is remembered and, once the window elapses, one trailing call runs with those arguments
|
|
4239
|
+
* (mirroring a classic "leading + trailing" throttle).
|
|
4240
|
+
*
|
|
4241
|
+
* @param msecs Throttle window in milliseconds, or `true` to use a single animation frame (`requestAnimationFrame`) as the window. Defaults to `50`.
|
|
4242
|
+
* @param keyFactory Computes the per-instance timer key from the method name and call arguments; override to scope throttling per argument instead of per method. Defaults to a single shared key per method.
|
|
4243
|
+
* @returns A method decorator to apply to the instance method to throttle.
|
|
4244
|
+
*/
|
|
3591
4245
|
function Throttle(msecs = 50, keyFactory = (key, ...args) => `pacem:throttler:${key}`) {
|
|
3592
4246
|
return function (target /* type, actually */, key, descriptor) {
|
|
3593
4247
|
const originalMethod = descriptor.value;
|
|
@@ -3637,6 +4291,14 @@
|
|
|
3637
4291
|
}
|
|
3638
4292
|
}
|
|
3639
4293
|
};
|
|
4294
|
+
/**
|
|
4295
|
+
* Method decorator that registers a static/instance method as a globally-named {@link TransformFunction},
|
|
4296
|
+
* making it callable by name from template binding expressions (e.g. `{{ ^value | myTransform }}`).
|
|
4297
|
+
* The registration is global (attached under `Utils.core`, dot-segmented by `name`), not per-instance.
|
|
4298
|
+
*
|
|
4299
|
+
* @param name Dot-separated registration path (e.g. `'strings.upper'`); segments before the last create/reuse nested namespaces under `Utils.core`. Defaults to the decorated method's own name when omitted.
|
|
4300
|
+
* @returns A method decorator to apply to the transform function.
|
|
4301
|
+
*/
|
|
3640
4302
|
function Transformer(name) {
|
|
3641
4303
|
return function (target, key, descriptor) {
|
|
3642
4304
|
const method = descriptor.value;
|
|
@@ -3654,6 +4316,16 @@
|
|
|
3654
4316
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
3655
4317
|
};
|
|
3656
4318
|
//namespace Pacem.Components {
|
|
4319
|
+
/**
|
|
4320
|
+
* Base class for (almost) every Pacem custom element. Extends `HTMLElement` with the framework's
|
|
4321
|
+
* common lifecycle/event plumbing: `on-{event-type}` attribute handlers (see {@link emit}), a
|
|
4322
|
+
* {@link disabled} property that suppresses both outgoing (`dispatchEvent`) and incoming
|
|
4323
|
+
* (`emitHandler`) events, an optional {@link logger} sink, and an {@link isReady} flag toggled once
|
|
4324
|
+
* `viewActivatedCallback` has fired (see the {@link CustomElement} decorator for when that happens).
|
|
4325
|
+
* Concrete elements are expected to be decorated with `@CustomElement` and typically override
|
|
4326
|
+
* `propertyChangedCallback`/`viewActivatedCallback`/`connectedCallback`/`disconnectedCallback`,
|
|
4327
|
+
* calling `super.xxxCallback()` first/last as documented on each.
|
|
4328
|
+
*/
|
|
3657
4329
|
class PacemEventTarget extends HTMLElement {
|
|
3658
4330
|
constructor() {
|
|
3659
4331
|
super();
|
|
@@ -3836,12 +4508,14 @@
|
|
|
3836
4508
|
return this._aria.attrs;
|
|
3837
4509
|
}
|
|
3838
4510
|
}
|
|
4511
|
+
/** Common base class for Pacem custom elements: adds ARIA support, CSS class/part/style bags, visibility, tab order and behaviors. */
|
|
3839
4512
|
class PacemElement extends PacemEventTarget {
|
|
3840
4513
|
constructor(role, aria) {
|
|
3841
4514
|
super();
|
|
3842
4515
|
this.#cssBag = [];
|
|
3843
4516
|
this.#parts = [];
|
|
3844
4517
|
this._tabIndex = -1;
|
|
4518
|
+
/** Gets or sets the list of behaviors to attach to this element. */
|
|
3845
4519
|
this.behaviors = [];
|
|
3846
4520
|
this._aria = new ElementAria(this, role, aria);
|
|
3847
4521
|
}
|
|
@@ -4026,6 +4700,7 @@
|
|
|
4026
4700
|
__decorate$H([
|
|
4027
4701
|
Watch({ emit: false })
|
|
4028
4702
|
], PacemElement.prototype, "behaviors", void 0);
|
|
4703
|
+
/** Base class for elements whose (sanitized) inner content is driven by a `content` property, restoring the original markup on disconnect. */
|
|
4029
4704
|
class PacemContentElement extends PacemElement {
|
|
4030
4705
|
propertyChangedCallback(name, old, val, first) {
|
|
4031
4706
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -4069,11 +4744,13 @@
|
|
|
4069
4744
|
__decorate$H([
|
|
4070
4745
|
Watch({ emit: false, converter: PropertyConverters.String })
|
|
4071
4746
|
], PacemContentElement.prototype, "content", void 0);
|
|
4747
|
+
/** {@link PacemContentElement} variant that renders `content` verbatim, without any sanitization. */
|
|
4072
4748
|
class PacemUnsafeContentElement extends PacemContentElement {
|
|
4073
4749
|
sanitize(html) {
|
|
4074
4750
|
return html;
|
|
4075
4751
|
}
|
|
4076
4752
|
}
|
|
4753
|
+
/** {@link PacemContentElement} variant that strips out `<script>` blocks (and, for `Node` content, `<script>` elements) before rendering `content`. */
|
|
4077
4754
|
class PacemSafeContentElement extends PacemContentElement {
|
|
4078
4755
|
_sanitizeLegacy(html) {
|
|
4079
4756
|
if (Utils.isNullOrEmpty(html)) {
|
|
@@ -4105,6 +4782,7 @@
|
|
|
4105
4782
|
}
|
|
4106
4783
|
//}
|
|
4107
4784
|
|
|
4785
|
+
/** Base class for pluggable adapters that drive the focus/navigation behavior of a {@link PacemIterativeElement}. */
|
|
4108
4786
|
class PacemAdapter extends PacemElement {
|
|
4109
4787
|
constructor() {
|
|
4110
4788
|
super(...arguments);
|
|
@@ -4233,6 +4911,10 @@
|
|
|
4233
4911
|
const current = this._getAvailable();
|
|
4234
4912
|
return ndx === this._getNextAvailable(current);
|
|
4235
4913
|
}
|
|
4914
|
+
/**
|
|
4915
|
+
* Sets the master's index to the closest available item to the provided one.
|
|
4916
|
+
* @param ndx Desired index
|
|
4917
|
+
*/
|
|
4236
4918
|
select(ndx) {
|
|
4237
4919
|
this._element.index = this._getAvailable(ndx);
|
|
4238
4920
|
}
|
|
@@ -4256,6 +4938,7 @@
|
|
|
4256
4938
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4257
4939
|
};
|
|
4258
4940
|
//namespace Pacem.Components {
|
|
4941
|
+
/** Removes itself from the DOM as soon as it gets connected; handy to strip out placeholder markup at runtime. */
|
|
4259
4942
|
let PacemAnnihilatorElement = class PacemAnnihilatorElement extends HTMLElement {
|
|
4260
4943
|
constructor() {
|
|
4261
4944
|
super();
|
|
@@ -4276,6 +4959,7 @@
|
|
|
4276
4959
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4277
4960
|
};
|
|
4278
4961
|
//namespace Pacem.Components {
|
|
4962
|
+
/** Warns the user (native `beforeunload` prompt or router-driven confirm dialog) before leaving the page. */
|
|
4279
4963
|
let PacemBeforeunloadElement = class PacemBeforeunloadElement extends PacemEventTarget {
|
|
4280
4964
|
constructor() {
|
|
4281
4965
|
super(...arguments);
|
|
@@ -4322,9 +5006,11 @@
|
|
|
4322
5006
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4323
5007
|
};
|
|
4324
5008
|
//namespace Pacem.Components {
|
|
5009
|
+
/** Wraps a {@link BroadcastChannel}, sending/receiving messages to/from other same-origin browsing contexts. */
|
|
4325
5010
|
let PacemBroadcastChannelProxyElement = class PacemBroadcastChannelProxyElement extends PacemEventTarget {
|
|
4326
5011
|
constructor() {
|
|
4327
5012
|
super(...arguments);
|
|
5013
|
+
/** Gets or sets whether to automatically send `message` as soon as it changes (default `true`). */
|
|
4328
5014
|
this.autosend = true;
|
|
4329
5015
|
this._messageHandler = (evt) => {
|
|
4330
5016
|
this.result = evt.data;
|
|
@@ -4360,6 +5046,10 @@
|
|
|
4360
5046
|
this._disposeChannel();
|
|
4361
5047
|
super.disconnectedCallback();
|
|
4362
5048
|
}
|
|
5049
|
+
/**
|
|
5050
|
+
* Posts a message through the channel.
|
|
5051
|
+
* @param message Message to send; defaults to the current `message` property
|
|
5052
|
+
*/
|
|
4363
5053
|
send(message = this.message) {
|
|
4364
5054
|
if (this.disabled) {
|
|
4365
5055
|
return;
|
|
@@ -4412,6 +5102,7 @@
|
|
|
4412
5102
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4413
5103
|
};
|
|
4414
5104
|
//namespace Pacem.Components {
|
|
5105
|
+
/** Detects the name and version of the current (or a provided) browser user agent. */
|
|
4415
5106
|
let PacemBrowserDetectElement = class PacemBrowserDetectElement extends PacemEventTarget {
|
|
4416
5107
|
viewActivatedCallback() {
|
|
4417
5108
|
super.viewActivatedCallback();
|
|
@@ -4425,9 +5116,11 @@
|
|
|
4425
5116
|
}
|
|
4426
5117
|
#browser;
|
|
4427
5118
|
#version;
|
|
5119
|
+
/** @readonly Gets the detected browser name. */
|
|
4428
5120
|
get browser() {
|
|
4429
5121
|
return this.#browser;
|
|
4430
5122
|
}
|
|
5123
|
+
/** @readonly Gets the detected browser version. */
|
|
4431
5124
|
get version() {
|
|
4432
5125
|
return this.#version;
|
|
4433
5126
|
}
|
|
@@ -4454,6 +5147,12 @@
|
|
|
4454
5147
|
|
|
4455
5148
|
//namespace Pacem {
|
|
4456
5149
|
const EXPIRATION_FIELD = '_exp';
|
|
5150
|
+
/**
|
|
5151
|
+
* JSON-serializing wrapper around `sessionStorage`/`localStorage` that transparently supports per-key
|
|
5152
|
+
* expiration: values stored with a `duration` are stamped with an expiry timestamp and evicted lazily
|
|
5153
|
+
* (checked on the next {@link getPropertyValue} call for that key, not via a timer). Session storage and
|
|
5154
|
+
* local storage are treated as a single logical namespace - storing a key in one clears any same-named entry in the other.
|
|
5155
|
+
*/
|
|
4457
5156
|
class Storage {
|
|
4458
5157
|
_getStorage(persistent) {
|
|
4459
5158
|
return !!persistent ? window.localStorage : window.sessionStorage;
|
|
@@ -4488,10 +5187,16 @@
|
|
|
4488
5187
|
storage = this._getStorage(!persistent);
|
|
4489
5188
|
storage.removeItem(name);
|
|
4490
5189
|
}
|
|
5190
|
+
/**
|
|
5191
|
+
* Retrieves a previously stored value by `name`, checking session storage first, then local storage.
|
|
5192
|
+
* Returns `null` if not found, or if found but past its expiration.
|
|
5193
|
+
* @param name The unique key.
|
|
5194
|
+
*/
|
|
4491
5195
|
getPropertyValue(name) {
|
|
4492
5196
|
return this._parseValue(this._getStorage(false), name)
|
|
4493
5197
|
|| this._parseValue(this._getStorage(true), name);
|
|
4494
5198
|
}
|
|
5199
|
+
/** Clears both session and local storage entirely. */
|
|
4495
5200
|
clear() {
|
|
4496
5201
|
var storage = this._getStorage(true);
|
|
4497
5202
|
storage.clear();
|
|
@@ -4499,6 +5204,7 @@
|
|
|
4499
5204
|
storage.clear();
|
|
4500
5205
|
}
|
|
4501
5206
|
;
|
|
5207
|
+
/** Removes a single entry by `name` from both session and local storage. */
|
|
4502
5208
|
removeProperty(name) {
|
|
4503
5209
|
var storage = this._getStorage(true);
|
|
4504
5210
|
storage.removeItem(name);
|
|
@@ -4515,6 +5221,7 @@
|
|
|
4515
5221
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4516
5222
|
};
|
|
4517
5223
|
//namespace Pacem.Components {
|
|
5224
|
+
/** Holds an arbitrary data model, optionally persisting it to local storage and debouncing/throttling change notifications. */
|
|
4518
5225
|
let PacemDataElement = class PacemDataElement extends PacemEventTarget {
|
|
4519
5226
|
constructor(storage = new Storage()) {
|
|
4520
5227
|
super();
|
|
@@ -4710,6 +5417,7 @@
|
|
|
4710
5417
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
4711
5418
|
};
|
|
4712
5419
|
//namespace Pacem.Components {
|
|
5420
|
+
/** Base class for elements that move their own content to another DOM location, restoring nothing on disconnect. */
|
|
4713
5421
|
class PacemTransferProxyElement extends PacemEventTarget {
|
|
4714
5422
|
constructor() {
|
|
4715
5423
|
super(...arguments);
|
|
@@ -4815,6 +5523,7 @@
|
|
|
4815
5523
|
return PropertyConverters.Element.convertBack(prop, element);
|
|
4816
5524
|
}
|
|
4817
5525
|
};
|
|
5526
|
+
/** Listens for an arbitrary DOM event on a `target` element (or `window`) and re-dispatches it as its own `emit` event. */
|
|
4818
5527
|
let PacemEventProxyElement = class PacemEventProxyElement extends PacemEventTarget {
|
|
4819
5528
|
constructor() {
|
|
4820
5529
|
super(...arguments);
|
|
@@ -4887,11 +5596,13 @@
|
|
|
4887
5596
|
};
|
|
4888
5597
|
//namespace Pacem.Components {
|
|
4889
5598
|
const ABORT_MESSAGE = "New incoming request overrides the ongoing one. Aborting.";
|
|
5599
|
+
/** Declarative wrapper around the `fetch()` API, debouncing/aborting requests as its properties change and exposing the parsed `result`. */
|
|
4890
5600
|
let PacemFetchElement = class PacemFetchElement extends PacemEventTarget {
|
|
4891
5601
|
constructor() {
|
|
4892
5602
|
super(...arguments);
|
|
4893
5603
|
/** Gets or sets whether to trigger a fetch whenever a significant property has changed (default: true). */
|
|
4894
5604
|
this.autofetch = true;
|
|
5605
|
+
/** Gets or sets the debounce delay (in milliseconds) applied before auto-triggered fetches (default `100`). */
|
|
4895
5606
|
this.debounce = 100;
|
|
4896
5607
|
}
|
|
4897
5608
|
viewActivatedCallback() {
|
|
@@ -5150,6 +5861,7 @@
|
|
|
5150
5861
|
super(type, { detail });
|
|
5151
5862
|
}
|
|
5152
5863
|
}
|
|
5864
|
+
/** Keeps track of an undo/redo-capable history of an arbitrary state. */
|
|
5153
5865
|
let PacemHistoryElement = class PacemHistoryElement extends PacemEventTarget {
|
|
5154
5866
|
#history;
|
|
5155
5867
|
propertyChangedCallback(name, old, val, first) {
|
|
@@ -5183,6 +5895,7 @@
|
|
|
5183
5895
|
this.#history = new pacemFoundation.HistoryService(val, this.maxlength);
|
|
5184
5896
|
}
|
|
5185
5897
|
}
|
|
5898
|
+
/** Moves the state forward one step, if `canRedo` is `true`. */
|
|
5186
5899
|
redo() {
|
|
5187
5900
|
const h = this.#history;
|
|
5188
5901
|
if (!Utils.isNull(h)) {
|
|
@@ -5191,6 +5904,7 @@
|
|
|
5191
5904
|
this._synchronize('redo');
|
|
5192
5905
|
}
|
|
5193
5906
|
}
|
|
5907
|
+
/** Clears the history stack, keeping only the current state. */
|
|
5194
5908
|
reset() {
|
|
5195
5909
|
const h = this.#history;
|
|
5196
5910
|
if (!Utils.isNull(h)) {
|
|
@@ -5199,6 +5913,7 @@
|
|
|
5199
5913
|
this._synchronize('reset');
|
|
5200
5914
|
}
|
|
5201
5915
|
}
|
|
5916
|
+
/** Moves the state back one step, if `canUndo` is `true`. */
|
|
5202
5917
|
undo() {
|
|
5203
5918
|
const h = this.#history;
|
|
5204
5919
|
if (!Utils.isNull(h)) {
|
|
@@ -5240,6 +5955,7 @@
|
|
|
5240
5955
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5241
5956
|
};
|
|
5242
5957
|
//namespace Pacem.Components {
|
|
5958
|
+
/** Sets (or removes) an attribute on the document's root `<html>` element. */
|
|
5243
5959
|
let PacemHtmlProxyElement = class PacemHtmlProxyElement extends PacemEventTarget {
|
|
5244
5960
|
constructor() {
|
|
5245
5961
|
super(...arguments);
|
|
@@ -5288,6 +6004,7 @@
|
|
|
5288
6004
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5289
6005
|
};
|
|
5290
6006
|
//namespace Pacem.Components {
|
|
6007
|
+
/** Conditionally shows or clears its own inner content, restoring the original markup whenever `match` becomes truthy again. */
|
|
5291
6008
|
let PacemIfElement = class PacemIfElement extends HTMLElement {
|
|
5292
6009
|
#innerHTML;
|
|
5293
6010
|
propertyChangedCallback(name, old, val, first) {
|
|
@@ -5318,6 +6035,7 @@
|
|
|
5318
6035
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5319
6036
|
};
|
|
5320
6037
|
//namespace Pacem.Components {
|
|
6038
|
+
/** Base class for elements that register themselves against an ancestor {@link PacemItemsContainerElement}. */
|
|
5321
6039
|
class PacemItemElement extends PacemElement {
|
|
5322
6040
|
get container() {
|
|
5323
6041
|
return this._container;
|
|
@@ -5423,6 +6141,7 @@
|
|
|
5423
6141
|
__decorate$u([
|
|
5424
6142
|
Watch( /* can only be databound or assigned at runtime */)
|
|
5425
6143
|
], PacemCrossItemsContainerElement.prototype, "items", void 0);
|
|
6144
|
+
/** Base class for container elements holding a registry of {@link PacemItemElement} children. */
|
|
5426
6145
|
class PacemItemsContainerElement extends PacemElement {
|
|
5427
6146
|
constructor(role, aria) {
|
|
5428
6147
|
super(role, aria);
|
|
@@ -5455,6 +6174,7 @@
|
|
|
5455
6174
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5456
6175
|
};
|
|
5457
6176
|
//namespace Pacem.Components {
|
|
6177
|
+
/** Base class for items belonging to a {@link PacemIterativeElement}, forwarding their property changes to the master's `adapter`. */
|
|
5458
6178
|
class PacemIterableElement extends PacemItemElement {
|
|
5459
6179
|
/** @overrides */
|
|
5460
6180
|
findContainer() {
|
|
@@ -5470,6 +6190,7 @@
|
|
|
5470
6190
|
&& iter.adapter.itemPropertyChangedCallback(index, name, old, val, first);
|
|
5471
6191
|
}
|
|
5472
6192
|
}
|
|
6193
|
+
/** Base class for container elements whose items are driven by a pluggable {@link PacemAdapter}, tracking a `current` `index`. */
|
|
5473
6194
|
class PacemIterativeElement extends PacemItemsContainerElement {
|
|
5474
6195
|
propertyChangedCallback(name, old, val, first) {
|
|
5475
6196
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -5527,6 +6248,7 @@
|
|
|
5527
6248
|
//namespace Pacem.Components {
|
|
5528
6249
|
const REMOVE_VALUE = 'false';
|
|
5529
6250
|
const EMPTY_VALUE = 'true';
|
|
6251
|
+
/** Sets (or removes) an attribute on the closest ancestor shell element, e.g. to drive layout-wide CSS state. */
|
|
5530
6252
|
let PacemLayoutProxyElement = class PacemLayoutProxyElement extends PacemEventTarget {
|
|
5531
6253
|
#shell;
|
|
5532
6254
|
connectedCallback() {
|
|
@@ -5580,6 +6302,7 @@
|
|
|
5580
6302
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5581
6303
|
};
|
|
5582
6304
|
//namespace Pacem.Components {
|
|
6305
|
+
/** Creates, updates or removes a `<meta>` element in the document `<head>`, keyed by either `name` or `itemprop`. */
|
|
5583
6306
|
let PacemMetaProxyElement = class PacemMetaProxyElement extends PacemEventTarget {
|
|
5584
6307
|
propertyChangedCallback(name, old, val, first) {
|
|
5585
6308
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -5641,6 +6364,7 @@
|
|
|
5641
6364
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5642
6365
|
};
|
|
5643
6366
|
//namespace Pacem.Components {
|
|
6367
|
+
/** Tracks the browser's network connectivity (`navigator.onLine`), updating `online` on `online`/`offline` events. */
|
|
5644
6368
|
let PacemOnlineStatusElement = class PacemOnlineStatusElement extends PacemEventTarget {
|
|
5645
6369
|
constructor() {
|
|
5646
6370
|
super(...arguments);
|
|
@@ -5683,6 +6407,7 @@
|
|
|
5683
6407
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5684
6408
|
};
|
|
5685
6409
|
//namespace Pacem.Components {
|
|
6410
|
+
/** Plain generic-purpose container element, useful as a databinding/behaviors host wherever a `<div>` would do. */
|
|
5686
6411
|
let PacemPanelElement = class PacemPanelElement extends PacemSafeContentElement {
|
|
5687
6412
|
};
|
|
5688
6413
|
PacemPanelElement = __decorate$p([
|
|
@@ -5692,7 +6417,12 @@
|
|
|
5692
6417
|
|
|
5693
6418
|
/// <reference path="events.ts" />
|
|
5694
6419
|
//namespace Pacem {
|
|
6420
|
+
/** Event name dispatched to signal a generic, named command invocation; see {@link CommandEvent}. */
|
|
5695
6421
|
const CommandEventName = 'command';
|
|
6422
|
+
/**
|
|
6423
|
+
* Bubbling, cancelable event representing a generic UI command (e.g. a button's `command`/`command-argument`
|
|
6424
|
+
* attributes), letting ancestor elements handle named actions without wiring a dedicated listener per command.
|
|
6425
|
+
*/
|
|
5696
6426
|
class CommandEvent extends CustomTypedEvent {
|
|
5697
6427
|
constructor(args) {
|
|
5698
6428
|
super(CommandEventName, args, { bubbles: true, cancelable: true });
|
|
@@ -5707,6 +6437,7 @@
|
|
|
5707
6437
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
5708
6438
|
};
|
|
5709
6439
|
//namespace Pacem.Components {
|
|
6440
|
+
/** Redirects consumers (e.g. {@link PacemRepeaterElement}) to an external `<template>` element, firing a `templatechange` event when it changes. */
|
|
5710
6441
|
let PacemTemplateProxyElement = class PacemTemplateProxyElement extends /*PacemEventTarget*/ /*PacemEventTarget*/ TemplateElement {
|
|
5711
6442
|
propertyChangedCallback(name, old, val, first) {
|
|
5712
6443
|
// super.propertyChangedCallback(name, old, val, first);
|
|
@@ -5761,6 +6492,8 @@
|
|
|
5761
6492
|
}
|
|
5762
6493
|
//
|
|
5763
6494
|
/**
|
|
6495
|
+
* Repeats its `<template>` content once per `datasource` item, keeping the rendered items in sync as the datasource changes.
|
|
6496
|
+
* @example
|
|
5764
6497
|
* <pacem-repeater datasource="[{id:'first', items:['a','b','c']}, ...]">
|
|
5765
6498
|
* <ol>
|
|
5766
6499
|
* <li>
|
|
@@ -5792,6 +6525,7 @@
|
|
|
5792
6525
|
this._databind();
|
|
5793
6526
|
};
|
|
5794
6527
|
}
|
|
6528
|
+
/** Removes the rendered item at the given index. */
|
|
5795
6529
|
removeItem(index) {
|
|
5796
6530
|
this._removeItems(index, index);
|
|
5797
6531
|
}
|
|
@@ -5820,6 +6554,7 @@
|
|
|
5820
6554
|
item.remove();
|
|
5821
6555
|
}
|
|
5822
6556
|
}
|
|
6557
|
+
/** Forces a full re-render of the items against the current `datasource`. */
|
|
5823
6558
|
databind() {
|
|
5824
6559
|
this._databind();
|
|
5825
6560
|
}
|
|
@@ -6017,6 +6752,7 @@
|
|
|
6017
6752
|
}
|
|
6018
6753
|
}
|
|
6019
6754
|
const BORDER_BOX = { box: 'border-box' };
|
|
6755
|
+
/** Observes size (and, optionally, position) changes of itself or a `target` element, firing a `resize` event accordingly. */
|
|
6020
6756
|
let PacemResizeElement = class PacemResizeElement extends PacemEventTarget {
|
|
6021
6757
|
constructor() {
|
|
6022
6758
|
super(...arguments);
|
|
@@ -6115,6 +6851,7 @@
|
|
|
6115
6851
|
this._dispatchResize();
|
|
6116
6852
|
}
|
|
6117
6853
|
}
|
|
6854
|
+
/** @readonly Gets the last measured size (and position, if `watchPosition` is set) of the observed target. */
|
|
6118
6855
|
get currentSize() {
|
|
6119
6856
|
var args = { height: this._previousHeight, width: this._previousWidth };
|
|
6120
6857
|
if (this.watchPosition) {
|
|
@@ -6152,33 +6889,34 @@
|
|
|
6152
6889
|
else for (var i = decorators.length - 1; i >= 0; i--) if (d = decorators[i]) r = (c < 3 ? d(r) : c > 3 ? d(target, key, r) : d(target, key)) || r;
|
|
6153
6890
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6154
6891
|
};
|
|
6155
|
-
//namespace Pacem {
|
|
6156
|
-
const RouterNavigatingEventName = 'navigating';
|
|
6157
|
-
const RouterNavigateEventName = 'navigate';
|
|
6158
|
-
class RouterNavigateEvent extends CustomTypedEvent {
|
|
6159
|
-
constructor(path) {
|
|
6160
|
-
super(RouterNavigateEventName, typeof path === 'string' ? { path } : path, { cancelable: false, bubbles: false });
|
|
6161
|
-
}
|
|
6162
|
-
}
|
|
6163
|
-
class RouterNavigatingEvent extends CustomTypedEvent {
|
|
6164
|
-
constructor(path) {
|
|
6165
|
-
super(RouterNavigatingEventName, path, { cancelable: true, bubbles: false });
|
|
6166
|
-
}
|
|
6167
|
-
}
|
|
6168
|
-
//}
|
|
6169
6892
|
//namespace Pacem.Components {
|
|
6170
6893
|
const CHECK_PATTERN = /^([\w\.]:)?\/\/[^\/]+/;
|
|
6894
|
+
/** Client-side router: parses/generates paths against a template and drives the browser History API. */
|
|
6171
6895
|
let PacemRouterElement = class PacemRouterElement extends PacemEventTarget {
|
|
6172
6896
|
constructor() {
|
|
6173
6897
|
super(...arguments);
|
|
6898
|
+
this._onScroll = (e) => {
|
|
6899
|
+
const scrollRestoration = this.scrollRestoration ?? true;
|
|
6900
|
+
if (!scrollRestoration) {
|
|
6901
|
+
return;
|
|
6902
|
+
}
|
|
6903
|
+
const y = Math.floor(Utils.scrollTop);
|
|
6904
|
+
window.history.replaceState(Utils.extend({}, window.history.state, { $scrollY: y }), document.title);
|
|
6905
|
+
};
|
|
6174
6906
|
this._onPopState = (evt) => {
|
|
6175
6907
|
if (!Utils.isNull(evt.state) && !this.#cancelingNavigation) {
|
|
6176
|
-
|
|
6908
|
+
const state = evt.state;
|
|
6909
|
+
// navigate and scroll, just in case
|
|
6910
|
+
this.path = state.$path;
|
|
6177
6911
|
}
|
|
6178
6912
|
};
|
|
6179
6913
|
}
|
|
6180
6914
|
#cancelingNavigation;
|
|
6181
6915
|
#template;
|
|
6916
|
+
/**
|
|
6917
|
+
* Merges the provided partial state into the current one and returns the resulting parsed {@link RouterState}, without navigating.
|
|
6918
|
+
* @param state Piece of new state to be merged
|
|
6919
|
+
*/
|
|
6182
6920
|
parseState(state = {}) {
|
|
6183
6921
|
const path = this._stringifyMergedState(state);
|
|
6184
6922
|
const segments = this._segmentateUrl(path);
|
|
@@ -6225,14 +6963,18 @@
|
|
|
6225
6963
|
// exiting
|
|
6226
6964
|
return;
|
|
6227
6965
|
}
|
|
6966
|
+
const newState = !poppingState
|
|
6967
|
+
? Utils.extend({ $scrollY: 0 }, state)
|
|
6968
|
+
: Utils.extend({ $scrollY: 0 }, window.history.state /* < may include `$scrollY` */, state);
|
|
6228
6969
|
// this one actually does trigger the navigation in the outer world
|
|
6229
|
-
|
|
6970
|
+
// 'cause it is usually bound to a PacemViewElement 'url'
|
|
6971
|
+
this.state = newState;
|
|
6972
|
+
window.history.scrollRestoration = (this.scrollRestoration ?? true) ? 'manual' : 'auto';
|
|
6230
6973
|
if (poppingState) {
|
|
6231
|
-
window.history.
|
|
6232
|
-
window.history.replaceState(state, title);
|
|
6974
|
+
window.history.replaceState(newState, document.title);
|
|
6233
6975
|
}
|
|
6234
6976
|
else {
|
|
6235
|
-
window.history.pushState(
|
|
6977
|
+
window.history.pushState(newState, title, path);
|
|
6236
6978
|
}
|
|
6237
6979
|
if (!Utils.isNullOrEmpty(title)) {
|
|
6238
6980
|
document.title = title;
|
|
@@ -6257,6 +6999,7 @@
|
|
|
6257
6999
|
connectedCallback() {
|
|
6258
7000
|
super.connectedCallback();
|
|
6259
7001
|
window.addEventListener('popstate', this._onPopState, false);
|
|
7002
|
+
document.addEventListener('scroll', this._onScroll, false);
|
|
6260
7003
|
}
|
|
6261
7004
|
viewActivatedCallback() {
|
|
6262
7005
|
super.viewActivatedCallback();
|
|
@@ -6266,6 +7009,7 @@
|
|
|
6266
7009
|
}
|
|
6267
7010
|
}
|
|
6268
7011
|
disconnectedCallback() {
|
|
7012
|
+
document.removeEventListener('scroll', this._onScroll, false);
|
|
6269
7013
|
window.removeEventListener('popstate', this._onPopState, false);
|
|
6270
7014
|
super.disconnectedCallback();
|
|
6271
7015
|
}
|
|
@@ -6320,6 +7064,9 @@
|
|
|
6320
7064
|
__decorate$l([
|
|
6321
7065
|
Watch({ emit: false, converter: PropertyConverters.String })
|
|
6322
7066
|
], PacemRouterElement.prototype, "path", void 0);
|
|
7067
|
+
__decorate$l([
|
|
7068
|
+
Watch({ emit: false, reflectBack: true, converter: PropertyConverters.Boolean })
|
|
7069
|
+
], PacemRouterElement.prototype, "scrollRestoration", void 0);
|
|
6323
7070
|
PacemRouterElement = __decorate$l([
|
|
6324
7071
|
CustomElement({ tagName: P + '-router' })
|
|
6325
7072
|
], PacemRouterElement);
|
|
@@ -6429,6 +7176,7 @@
|
|
|
6429
7176
|
}
|
|
6430
7177
|
return output;
|
|
6431
7178
|
}
|
|
7179
|
+
/** Registers a service worker and manages its (optional) Push API subscription. */
|
|
6432
7180
|
let PacemServiceWorkerProxyElement = class PacemServiceWorkerProxyElement extends PacemEventTarget {
|
|
6433
7181
|
viewActivatedCallback() {
|
|
6434
7182
|
super.viewActivatedCallback();
|
|
@@ -6481,6 +7229,10 @@
|
|
|
6481
7229
|
}
|
|
6482
7230
|
});
|
|
6483
7231
|
}
|
|
7232
|
+
/**
|
|
7233
|
+
* Unsubscribes from push notifications.
|
|
7234
|
+
* @param subscription Subscription to cancel; defaults to the current `pushSubscription`
|
|
7235
|
+
*/
|
|
6484
7236
|
unsubscribe(subscription = this.pushSubscription) {
|
|
6485
7237
|
return new Promise((resolve, reject) => {
|
|
6486
7238
|
if (!Utils.isNull(subscription)) {
|
|
@@ -6498,6 +7250,10 @@
|
|
|
6498
7250
|
}
|
|
6499
7251
|
});
|
|
6500
7252
|
}
|
|
7253
|
+
/**
|
|
7254
|
+
* Subscribes to push notifications through the registered service worker.
|
|
7255
|
+
* @param publicKey VAPID public key (base64url or raw bytes); defaults to `publicKey`
|
|
7256
|
+
*/
|
|
6501
7257
|
subscribe(publicKey = this.publicKey) {
|
|
6502
7258
|
return new Promise((resolve, reject) => {
|
|
6503
7259
|
if (typeof publicKey === 'string') {
|
|
@@ -6585,6 +7341,7 @@
|
|
|
6585
7341
|
|| (/^arrow/.test(key) && (piece === key.substr(5)));
|
|
6586
7342
|
}
|
|
6587
7343
|
const KeyboardShortcutExecuteEventName = "execute";
|
|
7344
|
+
/** Listens for a keyboard shortcut (optionally chorded, e.g. `"ctrl+k, s"`) on a target and fires an `execute` event when matched. */
|
|
6588
7345
|
let PacemShortcutElement = class PacemShortcutElement extends PacemEventTarget {
|
|
6589
7346
|
constructor() {
|
|
6590
7347
|
super(...arguments);
|
|
@@ -6642,6 +7399,7 @@
|
|
|
6642
7399
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6643
7400
|
};
|
|
6644
7401
|
//namespace Pacem.Components {
|
|
7402
|
+
/** Sets its own `textContent` to the `text` property, escaping any markup. */
|
|
6645
7403
|
let PacemSpanElement = class PacemSpanElement extends PacemSafeContentElement {
|
|
6646
7404
|
propertyChangedCallback(name, old, val, first) {
|
|
6647
7405
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -6671,6 +7429,7 @@
|
|
|
6671
7429
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6672
7430
|
};
|
|
6673
7431
|
//namespace Pacem.Components {
|
|
7432
|
+
/** Injects a `<style>` element into the document `<head>`, populated either inline (`cssText`) or by fetching `src`. */
|
|
6674
7433
|
let PacemStyleProxyElement = class PacemStyleProxyElement extends PacemEventTarget {
|
|
6675
7434
|
propertyChangedCallback(name, old, val, first) {
|
|
6676
7435
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -6771,6 +7530,7 @@
|
|
|
6771
7530
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6772
7531
|
};
|
|
6773
7532
|
//namespace Pacem.Components {
|
|
7533
|
+
/** Renders the `text` property as its own plain-text content, replacing any existing child markup. */
|
|
6774
7534
|
let PacemTextElement = class PacemTextElement extends HTMLElement {
|
|
6775
7535
|
constructor() {
|
|
6776
7536
|
super();
|
|
@@ -6802,6 +7562,7 @@
|
|
|
6802
7562
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6803
7563
|
};
|
|
6804
7564
|
//namespace Pacem.Components {
|
|
7565
|
+
/** Fires a `tick` event on a regular interval, as long as it's enabled and `interval` is greater than zero. */
|
|
6805
7566
|
let PacemTimerElement = class PacemTimerElement extends PacemEventTarget {
|
|
6806
7567
|
constructor() {
|
|
6807
7568
|
super();
|
|
@@ -6848,6 +7609,7 @@
|
|
|
6848
7609
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6849
7610
|
};
|
|
6850
7611
|
//namespace Pacem.Components {
|
|
7612
|
+
/** Sets `document.title` to `value` whenever it changes. */
|
|
6851
7613
|
let PacemTitleProxyElement = class PacemTitleProxyElement extends PacemEventTarget {
|
|
6852
7614
|
propertyChangedCallback(name, old, val, first) {
|
|
6853
7615
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -6883,10 +7645,12 @@
|
|
|
6883
7645
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
6884
7646
|
};
|
|
6885
7647
|
//namespace Pacem.Components {
|
|
7648
|
+
/** Animates a numeric `value` from `from` to `to` over `duration`, dispatching animation lifecycle events along the way. */
|
|
6886
7649
|
let PacemTweenElement = class PacemTweenElement extends PacemEventTarget {
|
|
6887
7650
|
constructor(_tweener = new TweenService()) {
|
|
6888
7651
|
super();
|
|
6889
7652
|
this._tweener = _tweener;
|
|
7653
|
+
/** Gets or sets the animation duration, in milliseconds (default `1000`). */
|
|
6890
7654
|
this.duration = 1000;
|
|
6891
7655
|
}
|
|
6892
7656
|
propertyChangedCallback(name, old, val, first) {
|
|
@@ -6896,6 +7660,7 @@
|
|
|
6896
7660
|
this._animate();
|
|
6897
7661
|
}
|
|
6898
7662
|
}
|
|
7663
|
+
/** Starts the animation programmatically and returns a promise resolved once it completes. */
|
|
6899
7664
|
run() {
|
|
6900
7665
|
return this._animate();
|
|
6901
7666
|
}
|
|
@@ -7045,9 +7810,11 @@
|
|
|
7045
7810
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
7046
7811
|
};
|
|
7047
7812
|
//namespace Pacem.Components {
|
|
7813
|
+
/** Wraps a {@link Worker}, sending/receiving messages to/from a web worker script. */
|
|
7048
7814
|
let PacemWebWorkerProxyElement = class PacemWebWorkerProxyElement extends PacemEventTarget {
|
|
7049
7815
|
constructor() {
|
|
7050
7816
|
super(...arguments);
|
|
7817
|
+
/** Gets or sets whether to automatically send `message` as soon as it changes (default `true`). */
|
|
7051
7818
|
this.autosend = true;
|
|
7052
7819
|
this._messageHandler = (evt) => {
|
|
7053
7820
|
this.result = evt.data;
|
|
@@ -7083,6 +7850,10 @@
|
|
|
7083
7850
|
this._disposeWorker();
|
|
7084
7851
|
super.disconnectedCallback();
|
|
7085
7852
|
}
|
|
7853
|
+
/**
|
|
7854
|
+
* Posts a message to the worker.
|
|
7855
|
+
* @param message Message to send; defaults to the current `message` property
|
|
7856
|
+
*/
|
|
7086
7857
|
send(message = this.message) {
|
|
7087
7858
|
if (this.disabled) {
|
|
7088
7859
|
return;
|
|
@@ -7128,8 +7899,16 @@
|
|
|
7128
7899
|
//}
|
|
7129
7900
|
|
|
7130
7901
|
//namespace Pacem.Behaviors{
|
|
7902
|
+
/**
|
|
7903
|
+
* Base class for the "behavior" custom elements (drag & drop, swipe, rotate, rescale, ...) that attach
|
|
7904
|
+
* gesture-driven functionality to a set of registered elements without rendering anything of their own.
|
|
7905
|
+
*/
|
|
7131
7906
|
class PacemBehavior extends PacemEventTarget {
|
|
7132
7907
|
#items = new WeakSet();
|
|
7908
|
+
/**
|
|
7909
|
+
* Registers an element with this behavior, decorating it if it wasn't already registered.
|
|
7910
|
+
* @param element Element to register.
|
|
7911
|
+
*/
|
|
7133
7912
|
register(element) {
|
|
7134
7913
|
var container = this.#items;
|
|
7135
7914
|
if (!container.has(element)) {
|
|
@@ -7137,6 +7916,10 @@
|
|
|
7137
7916
|
this.decorate(element);
|
|
7138
7917
|
}
|
|
7139
7918
|
}
|
|
7919
|
+
/**
|
|
7920
|
+
* Unregisters a previously registered element, undecorating it.
|
|
7921
|
+
* @param element Element to unregister.
|
|
7922
|
+
*/
|
|
7140
7923
|
unregister(element) {
|
|
7141
7924
|
var container = this.#items;
|
|
7142
7925
|
if (container.has(element)) {
|
|
@@ -7144,6 +7927,7 @@
|
|
|
7144
7927
|
container.delete(element);
|
|
7145
7928
|
}
|
|
7146
7929
|
}
|
|
7930
|
+
/** @readonly Determines whether the given element is currently registered with this behavior. */
|
|
7147
7931
|
has(element) {
|
|
7148
7932
|
return this.#items.has(element);
|
|
7149
7933
|
}
|
|
@@ -7565,7 +8349,8 @@
|
|
|
7565
8349
|
}
|
|
7566
8350
|
}
|
|
7567
8351
|
/**
|
|
7568
|
-
* Pacem Drag & Drop element adapter
|
|
8352
|
+
* Pacem Drag & Drop element adapter: implements {@link DragDropper} on top of {@link PacemBehavior} to make
|
|
8353
|
+
* registered elements draggable and, optionally, sortable/droppable onto {@link dropTargets}.
|
|
7569
8354
|
*/
|
|
7570
8355
|
let PacemDragDropElement = class PacemDragDropElement extends PacemBehavior {
|
|
7571
8356
|
constructor() {
|
|
@@ -7629,23 +8414,28 @@
|
|
|
7629
8414
|
/** Gets or sets the drag mode ("self", "alias", "copy"). */
|
|
7630
8415
|
this.mode = DragDataMode.Self;
|
|
7631
8416
|
}
|
|
8417
|
+
/** Wires up the `mousedown`/`touchstart` listeners that arm the drag gesture on the given element. */
|
|
7632
8418
|
decorate(element) {
|
|
7633
8419
|
const options = { capture: false, passive: false };
|
|
7634
8420
|
// TODO: add special effects starting the drag process after a while (timeout) the element is pressed.
|
|
7635
8421
|
element.addEventListener('mousedown', this._startHandler, false);
|
|
7636
8422
|
element.addEventListener('touchstart', this._startHandler, options);
|
|
7637
8423
|
}
|
|
8424
|
+
/** Removes the listeners set up by {@link decorate}. */
|
|
7638
8425
|
undecorate(element) {
|
|
7639
8426
|
const options = { capture: false, passive: false };
|
|
7640
8427
|
element.removeEventListener('mousedown', this._startHandler, false);
|
|
7641
8428
|
element.removeEventListener('touchstart', this._startHandler, options);
|
|
7642
8429
|
}
|
|
8430
|
+
/** Registers a listener for a {@link DragDropEventType} lifecycle event. */
|
|
7643
8431
|
addEventListener(type, listener, useCapture) {
|
|
7644
8432
|
super.addEventListener(type, listener, useCapture);
|
|
7645
8433
|
}
|
|
8434
|
+
/** Unregisters a listener previously added for a {@link DragDropEventType} lifecycle event. */
|
|
7646
8435
|
removeEventListener(type, listener, useCapture) {
|
|
7647
8436
|
super.removeEventListener(type, listener, useCapture);
|
|
7648
8437
|
}
|
|
8438
|
+
/** Dispatches a {@link DragDropEvent}, clearing internal per-element bookkeeping once the drag ends. */
|
|
7649
8439
|
dispatchEvent(evt) {
|
|
7650
8440
|
if (evt.type === DragDropEventType.End) {
|
|
7651
8441
|
DEL_VAL$3(evt.detail.element, MOUSE_DOWN$2);
|
|
@@ -7828,6 +8618,10 @@
|
|
|
7828
8618
|
const RESCALE_FRAME = 'pacem:rescale:frame';
|
|
7829
8619
|
const MOUSE_DOWN$1 = 'pacem:rescale:origin';
|
|
7830
8620
|
const DELEGATE$1 = 'pacem:rescale:delegate';
|
|
8621
|
+
/**
|
|
8622
|
+
* Pacem Rescale element adapter: implements {@link Rescaler} on top of {@link PacemBehavior}, decorating
|
|
8623
|
+
* registered elements with draggable resize handles (see {@link RescaleHandle}).
|
|
8624
|
+
*/
|
|
7831
8625
|
let PacemRescaleElement = class PacemRescaleElement extends PacemBehavior {
|
|
7832
8626
|
constructor() {
|
|
7833
8627
|
super(...arguments);
|
|
@@ -7855,11 +8649,13 @@
|
|
|
7855
8649
|
/* logging */ (level, message, category) => this.log.apply(this, [level, message, category])));
|
|
7856
8650
|
};
|
|
7857
8651
|
}
|
|
8652
|
+
/** Adds the resize-handle frame (see {@link RescaleHandle}) around the given element. */
|
|
7858
8653
|
decorate(element) {
|
|
7859
8654
|
const el = element; getComputedStyle(el);
|
|
7860
8655
|
Utils.addClass(el, PCSS + '-rescalable');
|
|
7861
8656
|
this._setFrame(el);
|
|
7862
8657
|
}
|
|
8658
|
+
/** Removes the resize-handle frame set up by {@link decorate}. */
|
|
7863
8659
|
undecorate(element) {
|
|
7864
8660
|
const el = element;
|
|
7865
8661
|
Utils.removeClass(el, PCSS + '-rescalable');
|
|
@@ -8043,6 +8839,10 @@
|
|
|
8043
8839
|
return x;
|
|
8044
8840
|
}
|
|
8045
8841
|
}
|
|
8842
|
+
/**
|
|
8843
|
+
* Pacem Rotate element adapter: implements {@link Rotator} on top of {@link PacemBehavior} to make
|
|
8844
|
+
* registered elements rotatable around a pivot point via mouse/touch drag.
|
|
8845
|
+
*/
|
|
8046
8846
|
let PacemRotateElement = class PacemRotateElement extends PacemBehavior {
|
|
8047
8847
|
constructor() {
|
|
8048
8848
|
super(...arguments);
|
|
@@ -8080,6 +8880,7 @@
|
|
|
8080
8880
|
/* logging */ (level, message, category) => this.log.apply(this, [level, message, category])));
|
|
8081
8881
|
};
|
|
8082
8882
|
}
|
|
8883
|
+
/** Wires up the `mousedown`/`touchstart` listeners that arm the rotate gesture on the given element. */
|
|
8083
8884
|
decorate(element) {
|
|
8084
8885
|
const el = element;
|
|
8085
8886
|
Utils.addClass(el, PCSS + '-rotatable');
|
|
@@ -8087,6 +8888,7 @@
|
|
|
8087
8888
|
element.addEventListener('mousedown', this._startHandler, false);
|
|
8088
8889
|
element.addEventListener('touchstart', this._startHandler, options);
|
|
8089
8890
|
}
|
|
8891
|
+
/** Removes the listeners set up by {@link decorate}. */
|
|
8090
8892
|
undecorate(element) {
|
|
8091
8893
|
const el = element;
|
|
8092
8894
|
Utils.removeClass(el, PCSS + '-rotatable');
|
|
@@ -8318,6 +9120,10 @@
|
|
|
8318
9120
|
window.removeEventListener('pointerup', this._endHandler);
|
|
8319
9121
|
}
|
|
8320
9122
|
}
|
|
9123
|
+
/**
|
|
9124
|
+
* Pacem Swipe element adapter: implements {@link Swiper} on top of {@link PacemBehavior} to make
|
|
9125
|
+
* registered elements draggable along one axis, with snap points and fling/bounce-back detection.
|
|
9126
|
+
*/
|
|
8321
9127
|
let PacemSwipeElement = class PacemSwipeElement extends PacemBehavior {
|
|
8322
9128
|
constructor() {
|
|
8323
9129
|
super(...arguments);
|
|
@@ -8370,6 +9176,7 @@
|
|
|
8370
9176
|
this._undo(el);
|
|
8371
9177
|
};
|
|
8372
9178
|
}
|
|
9179
|
+
/** Wires up the `pointerdown` listener that arms the swipe gesture on the given element. */
|
|
8373
9180
|
decorate(element) {
|
|
8374
9181
|
// https://docs.microsoft.com/en-us/microsoft-edge/dev-guide/dom/pointer-events
|
|
8375
9182
|
if (element instanceof HTMLElement || element instanceof SVGElement) {
|
|
@@ -8377,6 +9184,7 @@
|
|
|
8377
9184
|
}
|
|
8378
9185
|
element.addEventListener('pointerdown', this._prepareHandler, false);
|
|
8379
9186
|
}
|
|
9187
|
+
/** Removes the listener set up by {@link decorate}. */
|
|
8380
9188
|
undecorate(element) {
|
|
8381
9189
|
if (element instanceof HTMLElement || element instanceof SVGElement) {
|
|
8382
9190
|
Utils.removeClass(element, PCSS + '-swipe');
|
|
@@ -8441,6 +9249,10 @@
|
|
|
8441
9249
|
const EasingConverter = {
|
|
8442
9250
|
convert: (attr) => Easings[attr] ?? Easings.linear
|
|
8443
9251
|
};
|
|
9252
|
+
/**
|
|
9253
|
+
* Abstract base class for the `@CustomElement` animation items (color/number/point animations) that can be
|
|
9254
|
+
* hosted within a {@link PacemStoryboardElement}. Implements {@link Timeline}.
|
|
9255
|
+
*/
|
|
8444
9256
|
class AnimationElement extends PacemItemElement {
|
|
8445
9257
|
}
|
|
8446
9258
|
__decorate$5([
|
|
@@ -8464,15 +9276,21 @@
|
|
|
8464
9276
|
__decorate$5([
|
|
8465
9277
|
Watch({ emit: false, converter: PropertyConverters.String })
|
|
8466
9278
|
], AnimationElement.prototype, "direction", void 0);
|
|
9279
|
+
/**
|
|
9280
|
+
* Base class for animation elements that interpolate their `from`/`to` value via {@link TweenService}.
|
|
9281
|
+
* Concrete subclasses only need to supply {@link tweenInterpolationCallback} to map normalized progress to a value of type `T`.
|
|
9282
|
+
*/
|
|
8467
9283
|
class TweenAnimationElement extends AnimationElement {
|
|
8468
9284
|
constructor(_tweener = new TweenService()) {
|
|
8469
9285
|
super();
|
|
8470
9286
|
this._tweener = _tweener;
|
|
8471
9287
|
}
|
|
9288
|
+
/** @readonly Gets the {@link TweenService} instance driving this animation. */
|
|
8472
9289
|
get tweener() {
|
|
8473
9290
|
return this._tweener;
|
|
8474
9291
|
}
|
|
8475
9292
|
#cancelToken = false;
|
|
9293
|
+
/** Starts (or restarts) the tween and returns a promise resolved once it completes. */
|
|
8476
9294
|
startAnimation() {
|
|
8477
9295
|
this.#cancelToken = false;
|
|
8478
9296
|
return new Promise((resolve, _) => {
|
|
@@ -8485,6 +9303,7 @@
|
|
|
8485
9303
|
.then(resolve);
|
|
8486
9304
|
});
|
|
8487
9305
|
}
|
|
9306
|
+
/** Cancels the running tween. */
|
|
8488
9307
|
cancelAnimation() {
|
|
8489
9308
|
this.#cancelToken = true;
|
|
8490
9309
|
}
|
|
@@ -8499,6 +9318,7 @@
|
|
|
8499
9318
|
//namespace Pacem.Components {
|
|
8500
9319
|
const DEFAULT_FROM$2 = { h: 0, l: 0, s: 0.5, a: 1 };
|
|
8501
9320
|
const DEFAULT_TO$2 = { h: 0, l: 1, s: 0.5, a: 1 };
|
|
9321
|
+
/** Animates a CSS color `target[property]` from `from` to `to` (interpolated in HSLA space) over `duration`. */
|
|
8502
9322
|
let PacemColorAnimationElement = class PacemColorAnimationElement extends TweenAnimationElement {
|
|
8503
9323
|
propertyChangedCallback(name, old, val, first) {
|
|
8504
9324
|
super.propertyChangedCallback(name, old, val, first);
|
|
@@ -8542,6 +9362,7 @@
|
|
|
8542
9362
|
_parseColor(clr) {
|
|
8543
9363
|
return pacemFoundation.Colors.hsl(pacemFoundation.Colors.parse(clr));
|
|
8544
9364
|
}
|
|
9365
|
+
/** @inheritdoc */
|
|
8545
9366
|
tweenInterpolationCallback(time, progress) {
|
|
8546
9367
|
const from = this.#from, to = this.#to;
|
|
8547
9368
|
const hsla = {
|
|
@@ -8572,7 +9393,9 @@
|
|
|
8572
9393
|
//namespace Pacem.Components {
|
|
8573
9394
|
const DEFAULT_FROM$1 = 0;
|
|
8574
9395
|
const DEFAULT_TO$1 = 1;
|
|
9396
|
+
/** Animates a numeric `target[property]` from `from` to `to` over `duration`. */
|
|
8575
9397
|
let PacemNumberAnimationElement = class PacemNumberAnimationElement extends TweenAnimationElement {
|
|
9398
|
+
/** @inheritdoc */
|
|
8576
9399
|
tweenInterpolationCallback(time, progress) {
|
|
8577
9400
|
const to = this.to ?? DEFAULT_TO$1, from = this.from ?? DEFAULT_FROM$1;
|
|
8578
9401
|
return (to - from) * progress + from;
|
|
@@ -8597,7 +9420,9 @@
|
|
|
8597
9420
|
//namespace Pacem.Components {
|
|
8598
9421
|
const DEFAULT_FROM = { x: 0, y: 0 };
|
|
8599
9422
|
const DEFAULT_TO = { x: 1, y: 1 };
|
|
9423
|
+
/** Animates a {@link Point} `target[property]` from `from` to `to` over `duration`. */
|
|
8600
9424
|
let PacemPointAnimationElement = class PacemPointAnimationElement extends TweenAnimationElement {
|
|
9425
|
+
/** @inheritdoc */
|
|
8601
9426
|
tweenInterpolationCallback(time, progress) {
|
|
8602
9427
|
const to = this.to ?? DEFAULT_TO, from = this.from ?? DEFAULT_FROM;
|
|
8603
9428
|
return {
|
|
@@ -8624,7 +9449,12 @@
|
|
|
8624
9449
|
return c > 3 && r && Object.defineProperty(target, key, r), r;
|
|
8625
9450
|
};
|
|
8626
9451
|
//namespace Pacem.Components {
|
|
9452
|
+
/**
|
|
9453
|
+
* Groups one or more {@link Timeline}s (either {@link AnimationElement} child items or an explicit
|
|
9454
|
+
* `datasource`) and starts/cancels them together, optionally on view activation via `autostart`.
|
|
9455
|
+
*/
|
|
8627
9456
|
let PacemStoryboardElement = class PacemStoryboardElement extends PacemItemsContainerElement {
|
|
9457
|
+
/** @inheritdoc */
|
|
8628
9458
|
validate(item) {
|
|
8629
9459
|
return item instanceof AnimationElement;
|
|
8630
9460
|
}
|
|
@@ -8665,12 +9495,14 @@
|
|
|
8665
9495
|
}
|
|
8666
9496
|
return Promise.all(promises);
|
|
8667
9497
|
}
|
|
9498
|
+
/** Cancels every running {@link Timeline} in this storyboard. */
|
|
8668
9499
|
cancelAnimation() {
|
|
8669
9500
|
const ds = this._adaptedDatasource || [];
|
|
8670
9501
|
for (let timeline of ds) {
|
|
8671
9502
|
timeline.cancelAnimation();
|
|
8672
9503
|
}
|
|
8673
9504
|
}
|
|
9505
|
+
/** Starts (or restarts) every {@link Timeline} in this storyboard. */
|
|
8674
9506
|
startAnimation() {
|
|
8675
9507
|
return this._animate(true);
|
|
8676
9508
|
}
|
|
@@ -8762,10 +9594,6 @@
|
|
|
8762
9594
|
RepeaterItemRemoveEventName: RepeaterItemRemoveEventName,
|
|
8763
9595
|
ResizeEvent: ResizeEvent,
|
|
8764
9596
|
ResizeEventName: ResizeEventName,
|
|
8765
|
-
RouterNavigateEvent: RouterNavigateEvent,
|
|
8766
|
-
RouterNavigateEventName: RouterNavigateEventName,
|
|
8767
|
-
RouterNavigatingEvent: RouterNavigatingEvent,
|
|
8768
|
-
RouterNavigatingEventName: RouterNavigatingEventName,
|
|
8769
9597
|
ScrollEvent: ScrollEvent,
|
|
8770
9598
|
ScrollEventName: ScrollEventName,
|
|
8771
9599
|
StateEvent: StateEvent,
|
|
@@ -8784,13 +9612,16 @@
|
|
|
8784
9612
|
});
|
|
8785
9613
|
|
|
8786
9614
|
//namespace Pacem {
|
|
9615
|
+
/** Framework-wide default settings. */
|
|
8787
9616
|
const Defaults = {
|
|
8788
9617
|
USE_SHADOW_ROOT: /*'attachShadow' in Element.prototype &&*/ false /* <- wait until html-imports */
|
|
8789
9618
|
};
|
|
8790
9619
|
//}
|
|
8791
9620
|
|
|
8792
9621
|
//namespace Pacem.RegularExpressions {
|
|
9622
|
+
/** A pattern that can never match any string (`/a^/`); useful as a safe no-op default for optional matcher parameters. */
|
|
8793
9623
|
const EMPTY_MATCHER = /a^/;
|
|
9624
|
+
/** Static regular-expression helpers. */
|
|
8794
9625
|
class Regex {
|
|
8795
9626
|
/**
|
|
8796
9627
|
* Splits an input string into segments based on a matching pattern.
|
|
@@ -8846,7 +9677,21 @@
|
|
|
8846
9677
|
cssRemove: cssRemove ?? 'fill-red-container',
|
|
8847
9678
|
};
|
|
8848
9679
|
}
|
|
9680
|
+
/**
|
|
9681
|
+
* The framework's built-in library of template-expression transform functions. Each static method here
|
|
9682
|
+
* is registered, via the {@link Transformer} decorator, under `Utils.core` (mostly by its own method
|
|
9683
|
+
* name - see each method's `@Transformer(...)` name override where present) so it becomes callable by
|
|
9684
|
+
* name from binding expressions (e.g. `{{ ^value | size }}`). Not part of the module's exported surface -
|
|
9685
|
+
* consumers use these by their registered transform name, not by importing this class.
|
|
9686
|
+
*/
|
|
8849
9687
|
class Transforms {
|
|
9688
|
+
/**
|
|
9689
|
+
* Wraps every case-insensitive occurrence of each whitespace-separated token of `query` found in `src`
|
|
9690
|
+
* (skipping matches inside HTML tags) with a `<span class="{css}">` highlight wrapper.
|
|
9691
|
+
* @param src Source HTML/text to search within.
|
|
9692
|
+
* @param query Whitespace-separated search terms.
|
|
9693
|
+
* @param css CSS class applied to the highlight `<span>`. Defaults to `{PCSS}-highlight`.
|
|
9694
|
+
*/
|
|
8850
9695
|
static highlight(src, query, css = PCSS + '-highlight') {
|
|
8851
9696
|
if (!query || !src)
|
|
8852
9697
|
return src;
|
|
@@ -8889,9 +9734,11 @@
|
|
|
8889
9734
|
return acc;
|
|
8890
9735
|
}, '');
|
|
8891
9736
|
}
|
|
9737
|
+
/** Left-pads `src` (stringified) to `length` characters using `pad`; see {@link Utils.leftPad}. */
|
|
8892
9738
|
static padleft(src, length, pad) {
|
|
8893
9739
|
return Utils.leftPad('' + src, length, pad);
|
|
8894
9740
|
}
|
|
9741
|
+
/** Formats a byte count `src` as a human-readable size string (`B`/`kB`/`MB`/`GB`), or `'-'` if not a positive number. */
|
|
8895
9742
|
static size(src) {
|
|
8896
9743
|
// TODO: use logarithms
|
|
8897
9744
|
if (!(src > 0))
|
|
@@ -8904,12 +9751,25 @@
|
|
|
8904
9751
|
return (src / 1_048_576).toFixed(2) + ' MB';
|
|
8905
9752
|
return (src / 1_073_741_824).toFixed(2) + ' GB';
|
|
8906
9753
|
}
|
|
9754
|
+
/**
|
|
9755
|
+
* Formats a decimal-degrees number `src` as degrees/minutes/seconds (e.g. `41° 53' 24"`).
|
|
9756
|
+
* @param src Decimal degrees value (sign indicates direction).
|
|
9757
|
+
* @param degSeparator Text following the degrees component.
|
|
9758
|
+
* @param minSeparator Text following the minutes component.
|
|
9759
|
+
* @param secSeparator Text following the seconds component.
|
|
9760
|
+
*/
|
|
8907
9761
|
static decToDeg(src, degSeparator = '° ', minSeparator = '\' ', secSeparator = '"') {
|
|
8908
9762
|
const sign = Math.sign(src);
|
|
8909
9763
|
src = Math.abs(src);
|
|
8910
9764
|
const deg = Math.floor(src), min0 = (src - deg) * 60, min = Math.floor(min0), sec0 = (min0 - min) * 60, sec = Math.floor(sec0);
|
|
8911
9765
|
return (sign < 0 ? '-' : '') + deg + degSeparator + min + minSeparator + sec + secSeparator;
|
|
8912
9766
|
}
|
|
9767
|
+
/**
|
|
9768
|
+
* Formats a date-like value `src` (parsed via {@link Utils.parseDate}) as text.
|
|
9769
|
+
* @param src Date, timestamp, or parseable date string.
|
|
9770
|
+
* @param format Either a named shorthand (`'iso'`, `'isodate'`, `'localdate'`, `'time'`/`'localtime'`, `'full'`, or the default locale date format) or an `Intl.DateTimeFormatOptions` object.
|
|
9771
|
+
* @param culture BCP 47 locale to format with. Defaults to the browser's language.
|
|
9772
|
+
*/
|
|
8913
9773
|
static date(src, format, culture) {
|
|
8914
9774
|
var date = Utils.parseDate(src), lang = culture || navigator.language;
|
|
8915
9775
|
if (Utils.isNull(format) || typeof format === 'string') {
|
|
@@ -8937,6 +9797,13 @@
|
|
|
8937
9797
|
return date.toLocaleString(culture, format);
|
|
8938
9798
|
}
|
|
8939
9799
|
}
|
|
9800
|
+
/**
|
|
9801
|
+
* Formats the elapsed time between `start` and `end`.
|
|
9802
|
+
* @param start Start date-like value.
|
|
9803
|
+
* @param end End date-like value.
|
|
9804
|
+
* @param format Either an `Intl.DateTimeFormatOptions` (formatted as a UTC duration, optionally sign-prefixed via `sign: true`), or omitted for the legacy `"{d}d {h}h {m}m {s}s {ms}ms"` textual format.
|
|
9805
|
+
* @param culture BCP 47 locale to format with (only used with `Intl.DateTimeFormatOptions`). Defaults to the browser's language.
|
|
9806
|
+
*/
|
|
8940
9807
|
static timespan(start, end, format, culture) {
|
|
8941
9808
|
const startDate = Utils.parseDate(start), endDate = Utils.parseDate(end), span = endDate.valueOf() - startDate.valueOf(), sign = span < 0 ? '-' : '', elapsed = Math.abs(span);
|
|
8942
9809
|
if (typeof format === 'object') {
|
|
@@ -8947,18 +9814,26 @@
|
|
|
8947
9814
|
const msecsPerDay = 86400000, msecsPerHr = 3600000, msecsPerMin = 60000, days = Math.floor(elapsed / msecsPerDay), hrs = Math.floor((elapsed % msecsPerDay) / msecsPerHr), mins = Math.floor((elapsed % msecsPerHr) / msecsPerMin), secs = Math.floor((elapsed % msecsPerMin) / 1000), msecs = Math.floor(elapsed % 1000);
|
|
8948
9815
|
return `${sign}${days}d ${hrs}h ${mins}m ${secs}s ${msecs}ms`;
|
|
8949
9816
|
}
|
|
9817
|
+
/** Formats `src` as a currency amount using `Intl.NumberFormat`. @param currency ISO 4217 currency code. @param culture BCP 47 locale. Defaults to the browser's language. */
|
|
8950
9818
|
static currency(src, currency, culture) {
|
|
8951
9819
|
return new Intl.NumberFormat(culture ?? navigator.language, { style: 'currency', currency: currency }).format(src);
|
|
8952
9820
|
}
|
|
9821
|
+
/**
|
|
9822
|
+
* Formats `src` as a number using `Intl.NumberFormat`.
|
|
9823
|
+
* @param formatOrCulture Either a BCP 47 locale string, or `Intl.NumberFormatOptions` (in which case `culture` supplies the locale).
|
|
9824
|
+
* @param culture BCP 47 locale, used only when `formatOrCulture` is a format-options object. Defaults to the browser's language.
|
|
9825
|
+
*/
|
|
8953
9826
|
static number(src, formatOrCulture, culture) {
|
|
8954
9827
|
if (typeof formatOrCulture === 'string') {
|
|
8955
9828
|
return new Intl.NumberFormat(formatOrCulture || navigator.language).format(src);
|
|
8956
9829
|
}
|
|
8957
9830
|
return new Intl.NumberFormat(culture ?? navigator.language, formatOrCulture).format(src);
|
|
8958
9831
|
}
|
|
9832
|
+
/** Filters `src` with `filter`; a thin wrapper over `Array.prototype.filter` usable from template expressions. */
|
|
8959
9833
|
static filter(src, filter) {
|
|
8960
9834
|
return src.filter(filter);
|
|
8961
9835
|
}
|
|
9836
|
+
/** Sorts `src` in place (ascending, via `>`/`<` comparison), optionally by a property path when items are objects. @param prop Property name to sort by; omit to compare items directly. */
|
|
8962
9837
|
static orderby(src, prop) {
|
|
8963
9838
|
return src.sort((a, b) => {
|
|
8964
9839
|
if (!Utils.isNullOrEmpty(prop)) {
|
|
@@ -8969,31 +9844,39 @@
|
|
|
8969
9844
|
});
|
|
8970
9845
|
}
|
|
8971
9846
|
// #region RegExp
|
|
9847
|
+
/** Tests `input` against a `RegExp` built from `pattern`/`flags`. Registered as `regex.isMatch`. */
|
|
8972
9848
|
static isMatch(input, pattern, flags) {
|
|
8973
9849
|
return new RegExp(pattern, flags).test(input);
|
|
8974
9850
|
}
|
|
8975
9851
|
// #endregion
|
|
8976
9852
|
// #region shortcuts from 'Utils'
|
|
9853
|
+
/** Template-expression shortcut for {@link Utils.isEmpty}. */
|
|
8977
9854
|
static isEmpty(obj) {
|
|
8978
9855
|
return Utils.isEmpty(obj);
|
|
8979
9856
|
}
|
|
9857
|
+
/** Template-expression shortcut for {@link Utils.isNull}. */
|
|
8980
9858
|
static isNull(obj) {
|
|
8981
9859
|
return Utils.isNull(obj);
|
|
8982
9860
|
}
|
|
9861
|
+
/** Template-expression shortcut for {@link Utils.isNullOrEmpty}. */
|
|
8983
9862
|
static isNullOrEmpty(obj) {
|
|
8984
9863
|
return Utils.isNullOrEmpty(obj);
|
|
8985
9864
|
}
|
|
9865
|
+
/** Reads a CSS custom property's value off the document; see {@link Utils.Css.getVariableValue}. Registered as `cssvar`. */
|
|
8986
9866
|
static getCssVariable(name) {
|
|
8987
9867
|
return Utils.Css.getVariableValue(name);
|
|
8988
9868
|
}
|
|
9869
|
+
/** Reads a query-string parameter's value off the current URL; see {@link Utils.URIs.parseQuery}. Registered as `querystring`. */
|
|
8989
9870
|
static getQuerystring(name) {
|
|
8990
9871
|
return Utils.URIs.parseQuery()[name];
|
|
8991
9872
|
}
|
|
8992
9873
|
// #endregion
|
|
8993
9874
|
// #region shortcuts from 'CustomElementUtils'
|
|
9875
|
+
/** Template-expression shortcut for {@link CustomElementUtils.Theme}'s `importWebFonts`. */
|
|
8994
9876
|
static importThemeWebFonts() {
|
|
8995
9877
|
return CustomElementUtils.Theme.importWebFonts();
|
|
8996
9878
|
}
|
|
9879
|
+
/** Template-expression shortcut for {@link CustomElementUtils.findHostContext}. Registered as `host`. */
|
|
8997
9880
|
static findHostContext(element) {
|
|
8998
9881
|
return CustomElementUtils.findHostContext(element);
|
|
8999
9882
|
}
|
|
@@ -9097,6 +9980,10 @@
|
|
|
9097
9980
|
Regex: Regex,
|
|
9098
9981
|
Repeater: Repeater,
|
|
9099
9982
|
RepeaterItem: RepeaterItem$1,
|
|
9983
|
+
RouterNavigateEvent: RouterNavigateEvent,
|
|
9984
|
+
RouterNavigateEventName: RouterNavigateEventName,
|
|
9985
|
+
RouterNavigatingEvent: RouterNavigatingEvent,
|
|
9986
|
+
RouterNavigatingEventName: RouterNavigatingEventName,
|
|
9100
9987
|
Storage: Storage,
|
|
9101
9988
|
TemplateElement: TemplateElement,
|
|
9102
9989
|
Throttle: Throttle,
|