@pacem/pacem-core 1.0.0-bessel → 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 +870 -9
- package/dist/browser/pacem-core.js.map +1 -1
- package/dist/browser/pacem-core.min.js +2 -2
- package/dist/bundle/pacem-core.min.mjs +6 -6
- package/dist/bundle/pacem-core.mjs +467 -1
- 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 +41 -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 +89 -1
- package/package.json +2 -1
- package/typings/index.d.ts +1089 -10
package/dist/esm/utils.js
CHANGED
|
@@ -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
|
*/
|
|
@@ -10,40 +10,56 @@ import { DeferPromise } from './promise';
|
|
|
10
10
|
// namespace Pacem {
|
|
11
11
|
const PACEM_CORE_DEFAULT = 'pacem';
|
|
12
12
|
const DEFAULT_DOWNLOAD_FILENAME = 'download';
|
|
13
|
+
/** Ready-to-use event listener that only calls `evt.stopPropagation()`; handy as an `addEventListener` callback without allocating a closure. */
|
|
13
14
|
export const stopPropagationHandler = (evt) => {
|
|
14
15
|
evt.stopPropagation();
|
|
15
16
|
};
|
|
17
|
+
/** Ready-to-use event listener that only calls `evt.preventDefault()`; handy as an `addEventListener` callback without allocating a closure. */
|
|
16
18
|
export const preventDefaultHandler = (evt) => {
|
|
17
19
|
evt.preventDefault();
|
|
18
20
|
};
|
|
21
|
+
/** Ready-to-use event listener that calls both `evt.preventDefault()` and `evt.stopPropagation()`. */
|
|
19
22
|
export const avoidHandler = (evt) => {
|
|
20
23
|
evt.preventDefault();
|
|
21
24
|
evt.stopPropagation();
|
|
22
25
|
};
|
|
23
26
|
const SEGMENT_PATTERN = /^\/[^\/]+/;
|
|
24
27
|
const ROUTE_SEGMENT_PATTERN = /\/\{([a-z\$_][\w]*)\??\}/;
|
|
28
|
+
/**
|
|
29
|
+
* The framework's static utility grab-bag: general helpers plus namespaced sub-modules (`Json`, `Dates`,
|
|
30
|
+
* `Css`, `Strings`, `Blobs`, `Images`, `Browsers`, `Cookies`, `URIs`) re-exposing (and in some cases
|
|
31
|
+
* extending) functionality from `@pacem/pacem-foundation`. Also the home of `Utils.core`, the global
|
|
32
|
+
* registry object used for cross-cutting framework state (e.g. registered {@link Transformer}s).
|
|
33
|
+
*/
|
|
25
34
|
export class Utils {
|
|
35
|
+
/** 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. */
|
|
26
36
|
static get core() {
|
|
27
37
|
const root = window['Pacem'];
|
|
28
38
|
var id = (root && root.Configuration && root.Configuration.core) || PACEM_CORE_DEFAULT;
|
|
29
39
|
return window[id] = window[id] || {};
|
|
30
40
|
}
|
|
41
|
+
/** The platform's (native or polyfilled) `customElements` registry. */
|
|
31
42
|
static get customElements() {
|
|
32
43
|
return window['customElements'];
|
|
33
44
|
}
|
|
34
45
|
// #region GENERAL
|
|
46
|
+
/** Generates a unique numeric-ish id string. */
|
|
35
47
|
static uniqueId() {
|
|
36
48
|
return Keys.uniqueId();
|
|
37
49
|
}
|
|
50
|
+
/** Generates a unique short code string. */
|
|
38
51
|
static uniqueCode() {
|
|
39
52
|
return Keys.uniqueCode();
|
|
40
53
|
}
|
|
54
|
+
/** Generates a random string of the given `length`. */
|
|
41
55
|
static randomString(length) {
|
|
42
56
|
return Keys.randomString(length);
|
|
43
57
|
}
|
|
58
|
+
/** Parses a date-like value (ISO string, `Date`, or timestamp) into a `Date`. */
|
|
44
59
|
static parseDate(input) {
|
|
45
60
|
return Dates.parse(input);
|
|
46
61
|
}
|
|
62
|
+
/** Writes `input` to the system clipboard, using the async Clipboard API when available and falling back to `document.execCommand('copy')` otherwise. */
|
|
47
63
|
static copyToClipboard(input) {
|
|
48
64
|
if (navigator.clipboard) {
|
|
49
65
|
return navigator.clipboard.writeText(input);
|
|
@@ -111,6 +127,7 @@ export class Utils {
|
|
|
111
127
|
return filter;
|
|
112
128
|
}
|
|
113
129
|
}
|
|
130
|
+
/** Escapes `input` for safe use as a CSS identifier/selector fragment. */
|
|
114
131
|
static cssEscape(input) {
|
|
115
132
|
return escape(input).replace('%', '\\');
|
|
116
133
|
}
|
|
@@ -118,9 +135,11 @@ export class Utils {
|
|
|
118
135
|
static { this.Css = {
|
|
119
136
|
colorize: Utils.colorize,
|
|
120
137
|
escape: Utils.cssEscape,
|
|
138
|
+
/** Reads a CSS custom property's current value off the document root (`:root`/`html`). */
|
|
121
139
|
getVariableValue(name) {
|
|
122
140
|
return getComputedStyle(document.documentElement).getPropertyValue(name);
|
|
123
141
|
},
|
|
142
|
+
/** Sets a CSS custom property's value on the document root (`:root`/`html`). */
|
|
124
143
|
setVariable(name, value) {
|
|
125
144
|
getComputedStyle(document.documentElement).setProperty(name, value);
|
|
126
145
|
},
|
|
@@ -150,6 +169,7 @@ export class Utils {
|
|
|
150
169
|
}
|
|
151
170
|
return false;
|
|
152
171
|
},
|
|
172
|
+
/** Parses an element's (or style declaration's) computed `transform: matrix(...)` CSS value into its 2D affine matrix components; identity if unset. */
|
|
153
173
|
deserializeTransform(element) {
|
|
154
174
|
const style = element instanceof Element ? getComputedStyle(element) : element;
|
|
155
175
|
let a = 1, b = 0, c = 0, d = 1, x = 0, y = 0, matches = /^matrix\((.*)\)$/.exec(style.transform), mij;
|
|
@@ -176,6 +196,7 @@ export class Utils {
|
|
|
176
196
|
leftPad: Strings.leftPad,
|
|
177
197
|
format: Strings.format,
|
|
178
198
|
diff: Strings.diff,
|
|
199
|
+
/** Strips HTML markup from `html`, returning its plain-text content. */
|
|
179
200
|
stripHTML: (html) => {
|
|
180
201
|
let doc = new DOMParser().parseFromString(html, 'text/html');
|
|
181
202
|
return doc.body.textContent || '';
|
|
@@ -194,6 +215,7 @@ export class Utils {
|
|
|
194
215
|
load: Imaging.loadImage,
|
|
195
216
|
resize: Imaging.resizeImage
|
|
196
217
|
}; }
|
|
218
|
+
/** Loads an image from `src` and resolves with the `HTMLImageElement` once ready (or rejects on error). */
|
|
197
219
|
static loadImage(src) {
|
|
198
220
|
return new Promise((resolve, reject) => {
|
|
199
221
|
const img = new Image();
|
|
@@ -211,15 +233,19 @@ export class Utils {
|
|
|
211
233
|
}
|
|
212
234
|
});
|
|
213
235
|
}
|
|
236
|
+
/** Converts a `Blob` to a `data:` URL string. */
|
|
214
237
|
static blobToDataURL(blob) {
|
|
215
238
|
return Utils.Blobs.toDataURL(blob);
|
|
216
239
|
}
|
|
240
|
+
/** Converts a `data:` URL string back into a `Blob`. */
|
|
217
241
|
static dataURLToBlob(dataURL) {
|
|
218
242
|
return Utils.Blobs.fromDataURL(dataURL);
|
|
219
243
|
}
|
|
244
|
+
/** Reads a `Blob`'s content as text, using the given `encoding` (default UTF-8). */
|
|
220
245
|
static blobToText(blob, encoding) {
|
|
221
246
|
return Utils.Blobs.toText(blob, encoding);
|
|
222
247
|
}
|
|
248
|
+
/** Wraps `content` in a `Blob` of the given MIME `type` (default `'text/plain'`). */
|
|
223
249
|
static textToBlob(content, type = 'text/plain') {
|
|
224
250
|
return Utils.Blobs.fromText(content, type);
|
|
225
251
|
}
|
|
@@ -305,6 +331,7 @@ export class Utils {
|
|
|
305
331
|
el.src = url;
|
|
306
332
|
return deferred.promise;
|
|
307
333
|
}
|
|
334
|
+
/** Returns the list of vendor-prefixed `getUserMedia` implementations available on `navigator`, if any. */
|
|
308
335
|
static getUserMediaFunctions() {
|
|
309
336
|
var _getUserMedia = [];
|
|
310
337
|
let methods = [navigator['getUserMedia'], navigator['webkitGetUserMedia'], navigator['msGetUserMedia'], navigator['mozGetUserMedia']];
|
|
@@ -551,10 +578,12 @@ export class Utils {
|
|
|
551
578
|
}
|
|
552
579
|
// #endregion
|
|
553
580
|
// #region DOM
|
|
581
|
+
/** Whether the document (including module scripts) has fully finished loading (`document.readyState === 'complete'`). */
|
|
554
582
|
static isDOMReady() {
|
|
555
583
|
// Must exclude 'interactive' state since module script haven't been processed yet.
|
|
556
584
|
return /* document.readyState === 'interactive' ||*/ document.readyState === 'complete';
|
|
557
585
|
}
|
|
586
|
+
/** Registers `listener` to run once the document (and its resources, e.g. images) has fully loaded (the `window` `'load'` event). */
|
|
558
587
|
static onDOMReady(listener) {
|
|
559
588
|
//window.addEventListener('DOMContentLoaded', listener, false);
|
|
560
589
|
// window 'load' event fires after 'DOMCOntentLoaded' since includes images load.
|
|
@@ -583,13 +612,16 @@ export class Utils {
|
|
|
583
612
|
}
|
|
584
613
|
return dom;
|
|
585
614
|
}
|
|
615
|
+
/** Cross-browser `Element.matches(selector)`, falling back to vendor-prefixed variants. */
|
|
586
616
|
static is(el, selector) {
|
|
587
617
|
return (el.matches || el.matchesSelector || el.msMatchesSelector || el.mozMatchesSelector || el.webkitMatchesSelector || el.oMatchesSelector)
|
|
588
618
|
.call(el, selector);
|
|
589
619
|
}
|
|
620
|
+
/** Resolves the effective language for `el`: its own `lang` attribute, else the document's, else the browser's. */
|
|
590
621
|
static lang(el) {
|
|
591
622
|
return el.lang || document.documentElement.lang || navigator.language;
|
|
592
623
|
}
|
|
624
|
+
/** JSON-stringifies `obj` (via {@link Json}), or passes through `null`/`undefined` unchanged. */
|
|
593
625
|
static jsonSortStringify(obj) {
|
|
594
626
|
if (Utils.isNull(obj)) {
|
|
595
627
|
return obj;
|
|
@@ -599,6 +631,7 @@ export class Utils {
|
|
|
599
631
|
static { this.Browsers = {
|
|
600
632
|
detect
|
|
601
633
|
}; }
|
|
634
|
+
/** Parses a `document.cookie`-style string (or `document.cookie` itself, by default) into a name/value map. */
|
|
602
635
|
static cookies(cookie) {
|
|
603
636
|
const cookies = (cookie ?? document.cookie).split(';'), retval = {};
|
|
604
637
|
for (var pair of cookies) {
|
|
@@ -614,7 +647,9 @@ export class Utils {
|
|
|
614
647
|
}
|
|
615
648
|
return retval;
|
|
616
649
|
}
|
|
650
|
+
/** Cookie helpers, preferring the native `cookieStore` API when available and falling back to `document.cookie` parsing otherwise. */
|
|
617
651
|
static { this.Cookies = {
|
|
652
|
+
/** Retrieves all cookies (or just `cookieName`, if given) as {@link CookieData} entries. */
|
|
618
653
|
getAll: async (cookieName) => {
|
|
619
654
|
const store = 'cookieStore' in window ? window.cookieStore : null;
|
|
620
655
|
const retval = [];
|
|
@@ -632,6 +667,7 @@ export class Utils {
|
|
|
632
667
|
return await store.getAll(cookieName);
|
|
633
668
|
}
|
|
634
669
|
},
|
|
670
|
+
/** Serializes a {@link CookieData} descriptor into a `document.cookie`-assignable string (including `domain`/`path`/`max-age`/`expires`/`Partitioned`/`SameSite`/`Secure` attributes, as provided). */
|
|
635
671
|
stringify: (data) => {
|
|
636
672
|
let retval = `${data.name}=${encodeURIComponent(data.value ?? '')}`;
|
|
637
673
|
if (!Utils.isNullOrEmpty(data.domain)) {
|
|
@@ -658,6 +694,7 @@ export class Utils {
|
|
|
658
694
|
return retval;
|
|
659
695
|
}
|
|
660
696
|
}; }
|
|
697
|
+
/** Tests whether `el` is currently visible (via the `checkVisibility` API when available, falling back to a CSS-visibility/size heuristic). */
|
|
661
698
|
static isVisible(el) {
|
|
662
699
|
if ('checkVisibility' in el) {
|
|
663
700
|
return el.checkVisibility({ checkOpacity: true, checkVisibilityCSS: true });
|
|
@@ -665,6 +702,7 @@ export class Utils {
|
|
|
665
702
|
return getComputedStyle(el).visibility !== 'hidden' && (el.clientWidth > 0 || el.clientHeight > 0);
|
|
666
703
|
}
|
|
667
704
|
//#region classList DOMTokenList (css)
|
|
705
|
+
/** Tests whether `el` has `className` set, using `classList` when available (with a regex-based fallback). */
|
|
668
706
|
static hasClass(el, className) {
|
|
669
707
|
if (el.classList) {
|
|
670
708
|
return el.classList.contains(className);
|
|
@@ -673,6 +711,7 @@ export class Utils {
|
|
|
673
711
|
return new RegExp('(^| )' + className + '( |$)', 'gi').test(el.className);
|
|
674
712
|
}
|
|
675
713
|
}
|
|
714
|
+
/** Adds one or more (space-separated) classes to `el`, using `classList` when available. */
|
|
676
715
|
static addClass(el, className) {
|
|
677
716
|
const css = className.trim();
|
|
678
717
|
if (el.classList) {
|
|
@@ -682,6 +721,7 @@ export class Utils {
|
|
|
682
721
|
el.setAttribute('class', el.className + ' ' + css);
|
|
683
722
|
}
|
|
684
723
|
}
|
|
724
|
+
/** Removes one or more (space-separated) classes from `el`, using `classList` when available. */
|
|
685
725
|
static removeClass(el, className) {
|
|
686
726
|
const css = className.trim();
|
|
687
727
|
if (el.classList) {
|
|
@@ -691,6 +731,7 @@ export class Utils {
|
|
|
691
731
|
el.setAttribute('class', el.className.replace(new RegExp('(^|\\b)' + css.split(' ').join('|') + '(\\b|$)', 'gi'), ' '));
|
|
692
732
|
}
|
|
693
733
|
}
|
|
734
|
+
/** Adds or removes `className` on `el` depending on `on`; see {@link addClass}/{@link removeClass}. */
|
|
694
735
|
static toggleClass(el, className, on) {
|
|
695
736
|
if (on) {
|
|
696
737
|
Utils.addClass(el, className);
|
|
@@ -701,15 +742,19 @@ export class Utils {
|
|
|
701
742
|
}
|
|
702
743
|
//#endregion
|
|
703
744
|
//#region part DOMTokenList (shadow-DOM)
|
|
745
|
+
/** Tests whether `el`'s `part` attribute (CSS Shadow Parts) contains `partName`. */
|
|
704
746
|
static hasPart(el, partName) {
|
|
705
747
|
return el.part.contains(partName);
|
|
706
748
|
}
|
|
749
|
+
/** Adds one or more (space-separated) shadow-part names to `el`'s `part` attribute. */
|
|
707
750
|
static addPart(el, partName) {
|
|
708
751
|
DOMTokenList.prototype.add.apply(el.part, partName.trim().split(' '));
|
|
709
752
|
}
|
|
753
|
+
/** Removes one or more (space-separated) shadow-part names from `el`'s `part` attribute. */
|
|
710
754
|
static removePart(el, partName) {
|
|
711
755
|
DOMTokenList.prototype.remove.apply(el.part, partName.trim().split(' '));
|
|
712
756
|
}
|
|
757
|
+
/** Adds or removes `partName` on `el`'s `part` attribute depending on `on`; see {@link addPart}/{@link removePart}. */
|
|
713
758
|
static togglePart(el, partName, on) {
|
|
714
759
|
if (on) {
|
|
715
760
|
Utils.addPart(el, partName);
|
|
@@ -745,9 +790,11 @@ export class Utils {
|
|
|
745
790
|
height: rect.height
|
|
746
791
|
};
|
|
747
792
|
}
|
|
793
|
+
/** Sums `offsetTop` up the `offsetParent` chain, giving `el`'s vertical position relative to the document rather than just its nearest positioned ancestor. */
|
|
748
794
|
static naturalOffsetTop(el) {
|
|
749
795
|
return (el?.offsetTop ?? 0) + ((el?.offsetParent && el.offsetParent instanceof HTMLElement) ? this.naturalOffsetTop(el.offsetParent) : 0);
|
|
750
796
|
}
|
|
797
|
+
/** Sums `offsetTop` up the `offsetParent` chain (note: mirrors {@link naturalOffsetTop}'s calculation, not `offsetLeft`), giving `el`'s horizontal position relative to the document. */
|
|
751
798
|
static naturalOffsetLeft(el) {
|
|
752
799
|
return (el?.offsetTop ?? 0) + ((el?.offsetParent && el.offsetParent instanceof HTMLElement) ? this.naturalOffsetLeft(el.offsetParent) : 0);
|
|
753
800
|
}
|
|
@@ -775,9 +822,11 @@ export class Utils {
|
|
|
775
822
|
static deserializeTransform(obj) {
|
|
776
823
|
return Utils.Css.deserializeTransform(obj);
|
|
777
824
|
}
|
|
825
|
+
/** Sets the window's vertical scroll position, keeping the current horizontal position. */
|
|
778
826
|
static set scrollTop(y) {
|
|
779
827
|
window.scrollTo(Utils.scrollLeft, y);
|
|
780
828
|
}
|
|
829
|
+
/** Sets the window's horizontal scroll position, keeping the current vertical position. */
|
|
781
830
|
static set scrollLeft(x) {
|
|
782
831
|
window.scrollTo(x, Utils.scrollTop);
|
|
783
832
|
}
|
|
@@ -788,12 +837,15 @@ export class Utils {
|
|
|
788
837
|
const behavior = (yFirst ? y : smooth) ? 'smooth' : 'instant';
|
|
789
838
|
window.scrollTo({ behavior, left, top });
|
|
790
839
|
}
|
|
840
|
+
/** The window's current vertical scroll position, with fallbacks for older browsers. */
|
|
791
841
|
static get scrollTop() {
|
|
792
842
|
return window.scrollY /*window.pageYOffset*/ || document.documentElement.scrollTop || document.body.scrollTop || 0;
|
|
793
843
|
}
|
|
844
|
+
/** The window's current horizontal scroll position, with fallbacks for older browsers. */
|
|
794
845
|
static get scrollLeft() {
|
|
795
846
|
return window.scrollX /*window.pageXOffset*/ || document.documentElement.scrollLeft || document.body.scrollLeft || 0;
|
|
796
847
|
}
|
|
848
|
+
/** The window's current viewport size (`{ width, height }`), with fallbacks for older browsers. */
|
|
797
849
|
static get windowSize() {
|
|
798
850
|
let win = window;
|
|
799
851
|
return {
|
|
@@ -869,16 +921,20 @@ export class Utils {
|
|
|
869
921
|
}
|
|
870
922
|
// #endregion
|
|
871
923
|
// #region other
|
|
924
|
+
/** 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). */
|
|
872
925
|
static isEmpty(obj) {
|
|
873
926
|
return NullChecker.isEmpty(obj) && !(obj instanceof Node);
|
|
874
927
|
}
|
|
928
|
+
/** Type-guarding test for `obj` being `null` or `undefined`. */
|
|
875
929
|
static isNull(obj) {
|
|
876
930
|
return NullChecker.isNull(obj);
|
|
877
931
|
}
|
|
878
932
|
// legacy
|
|
933
|
+
/** Type-guarding test for `val` being an array. */
|
|
879
934
|
static isArray(val) {
|
|
880
935
|
return Array.isArray(val);
|
|
881
936
|
}
|
|
937
|
+
/** Tests whether `val` is `null`/`undefined` or "empty"; see {@link isNull}/{@link isEmpty}. */
|
|
882
938
|
static isNullOrEmpty(val) {
|
|
883
939
|
return Utils.isNull(val) || Utils.isEmpty(val);
|
|
884
940
|
}
|
|
@@ -897,6 +953,7 @@ export class Utils {
|
|
|
897
953
|
}
|
|
898
954
|
/** Legacy alias for Object.assign(). */
|
|
899
955
|
static { this.extend = Object.assign; }
|
|
956
|
+
/** Deep-clones `obj`; see {@link DeepCloner}. */
|
|
900
957
|
static clone(obj) {
|
|
901
958
|
if (obj === undefined)
|
|
902
959
|
return undefined;
|
|
@@ -906,6 +963,7 @@ export class Utils {
|
|
|
906
963
|
static fromResult(v) {
|
|
907
964
|
return Promise.resolve(v);
|
|
908
965
|
}
|
|
966
|
+
/** Runs `task` on the next animation frame and wraps its result in a `Promise`. */
|
|
909
967
|
static fromResultAsync(task) {
|
|
910
968
|
return new Promise(resolve => requestAnimationFrame(() => resolve(task())));
|
|
911
969
|
}
|
|
@@ -929,7 +987,15 @@ export class Utils {
|
|
|
929
987
|
}
|
|
930
988
|
//#endregion
|
|
931
989
|
//#region Net
|
|
990
|
+
/** URL/route parsing and formatting helpers, chiefly backing the router (`{segment}`/`{segment?}` template resolution, query-string parsing, route metadata). */
|
|
932
991
|
static { this.URIs = {
|
|
992
|
+
/**
|
|
993
|
+
* Substitutes each `{name}`/`{name?}` template segment in `url` with the matching entry of `parameters`
|
|
994
|
+
* (URL-encoded), then strips out any remaining unmatched *optional* segments.
|
|
995
|
+
* @param url URL/route template containing `{name}`/`{name?}` segments.
|
|
996
|
+
* @param parameters Values to substitute, keyed by segment name.
|
|
997
|
+
* @param removeMatchedParameters When `true`, consumed entries are deleted from `parameters`.
|
|
998
|
+
*/
|
|
933
999
|
format: function (url, parameters, removeMatchedParameters) {
|
|
934
1000
|
// replace segments
|
|
935
1001
|
for (let name in parameters || {}) {
|
|
@@ -952,6 +1018,14 @@ export class Utils {
|
|
|
952
1018
|
}
|
|
953
1019
|
return url;
|
|
954
1020
|
},
|
|
1021
|
+
/**
|
|
1022
|
+
* Appends `parameters` to `url`'s query string (or hash fragment, when `hash` is `true`), URL-encoding
|
|
1023
|
+
* names and values, and correctly choosing `?`/`&` (or `#`/`&`) as the joining separator.
|
|
1024
|
+
* @param url Base URL.
|
|
1025
|
+
* @param parameters Values to append, keyed by parameter name.
|
|
1026
|
+
* @param removeNullEntries When `true`, entries whose value is `null`/`undefined` are omitted instead of appended as empty.
|
|
1027
|
+
* @param hash When `true`, appends to the URL's hash fragment instead of its query string.
|
|
1028
|
+
*/
|
|
955
1029
|
appendQuery: function (url, parameters, removeNullEntries = false, hash = false) {
|
|
956
1030
|
const char = hash ? '#' : '?', pattern = hash ? /#/ : /\?/;
|
|
957
1031
|
const query = (pattern.test(url) ? '&' : char) +
|
|
@@ -960,6 +1034,7 @@ export class Utils {
|
|
|
960
1034
|
.map(k => encodeURIComponent(k) + '=' + encodeURIComponent(parameters[k] ?? '')).join('&');
|
|
961
1035
|
return url + (query.length > 1 ? query : '');
|
|
962
1036
|
},
|
|
1037
|
+
/** Parses a query string (or a `Location`'s `.search`, defaulting to `window.location`) into a name/value map. */
|
|
963
1038
|
parseQuery: (query = window.location) => {
|
|
964
1039
|
if (typeof query !== 'string') {
|
|
965
1040
|
query = query.search;
|
|
@@ -970,9 +1045,11 @@ export class Utils {
|
|
|
970
1045
|
});
|
|
971
1046
|
return obj;
|
|
972
1047
|
},
|
|
1048
|
+
/** Tests whether `url` contains at least one non-optional `{name}` template segment (as opposed to only `{name?}` ones). */
|
|
973
1049
|
hasMandatoryTemplateSegments: function (url) {
|
|
974
1050
|
return /(^|\/)\{\.*\}(\/|$)/.test(url);
|
|
975
1051
|
},
|
|
1052
|
+
/** Parses a route template's `{name}`/`{name?}` segments into an ordered list of {@link RouteMetadata} descriptors, consumed by {@link parseState}. */
|
|
976
1053
|
parseRoute: function (pathTemplate) {
|
|
977
1054
|
let tmpl = pathTemplate;
|
|
978
1055
|
const trunks = [];
|
|
@@ -992,6 +1069,7 @@ export class Utils {
|
|
|
992
1069
|
}
|
|
993
1070
|
return trunks;
|
|
994
1071
|
},
|
|
1072
|
+
/** Splits `url` into its `protocol`, `domain`, `path`, `query` (including leading `?`) and `hash` (including leading `#`) parts, or `null` if it can't be parsed. */
|
|
995
1073
|
split: function (url) {
|
|
996
1074
|
const URL_PATTERN = /^((https?:)?\/\/[^\/]+)?([^\?#]+)(\?[^#]*)?(#[^#]*)?$/;
|
|
997
1075
|
const regArr = URL_PATTERN.exec(url);
|
|
@@ -1000,6 +1078,16 @@ export class Utils {
|
|
|
1000
1078
|
}
|
|
1001
1079
|
return { domain: regArr[2] ?? '', protocol: regArr[1] ?? '', path: regArr[3] ?? '', query: regArr[4] ?? '', hash: regArr[5] ?? '' };
|
|
1002
1080
|
},
|
|
1081
|
+
/**
|
|
1082
|
+
* Builds a {@link RouterState} for `url`, extracting named route segments per `metadata` (see
|
|
1083
|
+
* {@link parseRoute}) as top-level properties, alongside the standard `$path`/`$pathname`/`$querystring`/
|
|
1084
|
+
* `$query`/`$hash` keys. Throws if a non-optional route segment declared in `metadata` isn't present in `path`.
|
|
1085
|
+
* @param metadata Route segment descriptors, as produced by {@link parseRoute}.
|
|
1086
|
+
* @param url Full URL/path being navigated to.
|
|
1087
|
+
* @param path Path component override; parsed out of `url` via {@link split} when omitted.
|
|
1088
|
+
* @param query Query-string override (including leading `?`); parsed out of `url` when omitted.
|
|
1089
|
+
* @param hash Hash-fragment override (including leading `#`); parsed out of `url` when omitted.
|
|
1090
|
+
*/
|
|
1003
1091
|
parseState: function (metadata, url, path, query, hash) {
|
|
1004
1092
|
let fullPath = url;
|
|
1005
1093
|
if (Utils.isNullOrEmpty(path)) {
|
package/package.json
CHANGED
|
@@ -4,6 +4,7 @@
|
|
|
4
4
|
"dist/browser/*.*",
|
|
5
5
|
"dist/bundle/*.*",
|
|
6
6
|
"dist/esm/*.*",
|
|
7
|
+
"dist/docs/*.*",
|
|
7
8
|
"typings/*.*",
|
|
8
9
|
"README.md",
|
|
9
10
|
"LICENSE",
|
|
@@ -23,7 +24,7 @@
|
|
|
23
24
|
"types": "./typings/index.d.ts"
|
|
24
25
|
}
|
|
25
26
|
},
|
|
26
|
-
"version": "1.0.0-
|
|
27
|
+
"version": "1.0.0-binet",
|
|
27
28
|
"author": {
|
|
28
29
|
"name": "Pacem",
|
|
29
30
|
"url": "https://pacem.it"
|