@schukai/monster 4.153.1 → 4.154.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/package.json +1 -1
- package/source/components/form/message-state-button.mjs +1 -1
- package/source/components/form/select-link.mjs +12 -1
- package/source/components/state/log.mjs +3 -7
- package/source/components/state/thread.mjs +3 -7
- package/source/components/time/day.mjs +0 -19
- package/source/components/time/month-calendar.mjs +5 -1
- package/source/data/buildmap.mjs +3 -3
- package/source/data/datasource.mjs +3 -10
- package/source/data/diff.mjs +13 -12
- package/source/data/extend.mjs +23 -1
- package/source/data/pathfinder.mjs +186 -19
- package/source/dom/customelement.mjs +273 -30
- package/source/dom/updater.mjs +6 -2
- package/source/types/base.mjs +7 -6
- package/source/types/basewithoptions.mjs +1 -8
- package/source/types/internal.mjs +4 -10
- package/source/types/observerlist.mjs +9 -17
- package/source/types/proxyobserver.mjs +19 -2
- package/source/types/tokenlist.mjs +13 -8
- package/source/util/clone.mjs +44 -14
- package/test/cases/browser-bundle.mjs +75 -0
- package/test/cases/components/time/month-calendar.mjs +115 -0
- package/test/cases/data/diff.mjs +86 -0
- package/test/cases/data/extend.mjs +52 -0
- package/test/cases/data/pathfinder-resolution-baseline.mjs +322 -0
- package/test/cases/data/pathfinder.mjs +129 -0
- package/test/cases/dom/customelement-rebind-baseline.mjs +323 -0
- package/test/cases/dom/customelement-teardown.mjs +408 -0
- package/test/cases/dom/customelement.mjs +354 -0
- package/test/cases/dom/dimension.mjs +34 -10
- package/test/cases/dom/updater-remove.mjs +198 -0
- package/test/cases/packaging.mjs +213 -0
- package/test/cases/types/base.mjs +50 -0
- package/test/cases/types/observerlist.mjs +103 -1
- package/test/cases/types/proxyobserver.mjs +114 -0
- package/test/cases/types/regex.mjs +4 -0
- package/test/cases/types/tokenlist.mjs +64 -1
- package/test/cases/util/clone.mjs +115 -0
|
@@ -36,7 +36,11 @@ import {
|
|
|
36
36
|
validateObject,
|
|
37
37
|
} from "../types/validate.mjs";
|
|
38
38
|
import { clone } from "../util/clone.mjs";
|
|
39
|
-
import {
|
|
39
|
+
import {
|
|
40
|
+
getLinkedObjects,
|
|
41
|
+
hasObjectLink,
|
|
42
|
+
removeObjectLink,
|
|
43
|
+
} from "./attributes.mjs";
|
|
40
44
|
import {
|
|
41
45
|
ATTRIBUTE_DISABLED,
|
|
42
46
|
ATTRIBUTE_OPTIONS,
|
|
@@ -119,6 +123,8 @@ const updateCloneDataSymbol = Symbol("@schukai/monster/dom/@@updateCloneData");
|
|
|
119
123
|
const scriptHostElementSymbol = Symbol("scriptHostElement");
|
|
120
124
|
const optionDefaultsSymbol = Symbol("optionDefaults");
|
|
121
125
|
const managedShadowRootSymbol = Symbol("managedShadowRoot");
|
|
126
|
+
const assembledOnceSymbol = Symbol("assembledOnce");
|
|
127
|
+
const teardownCallbacksSymbol = Symbol("teardownCallbacks");
|
|
122
128
|
const visibilityStateSymbol = Symbol("visibilityState");
|
|
123
129
|
let hostVisibilityStyleSheet = null;
|
|
124
130
|
|
|
@@ -358,7 +364,7 @@ class CustomElement extends HTMLElement {
|
|
|
358
364
|
* @property {Function} templateFormatter.marker.open=null Specifies the opening marker for the templates.
|
|
359
365
|
* @property {Function} templateFormatter.marker.close=null Specifies the closing marker for the templates.
|
|
360
366
|
* @property {Boolean} templateFormatter.i18n=false Specifies whether the templates should be formatted with i18n.
|
|
361
|
-
* @property {Boolean} eventProcessing=false Specifies whether the control processes events.
|
|
367
|
+
* @property {Boolean} eventProcessing=false Specifies whether the control processes events. A bound input writes its value into the updater snapshot, not into the options. The synchronisation is one way, options to snapshot, so getOption() does not observe user input; read it with getInternalUpdateCloneData().
|
|
362
368
|
* @property {Object} updater Specifies updater options.
|
|
363
369
|
* @property {Boolean} updater.batchUpdates=false Batches updater content/attribute updates per diff.
|
|
364
370
|
* @since 1.8.0
|
|
@@ -540,16 +546,9 @@ class CustomElement extends HTMLElement {
|
|
|
540
546
|
* @since 1.10.0
|
|
541
547
|
*/
|
|
542
548
|
getOption(path, defaultValue = undefined) {
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
value = new Pathfinder(
|
|
547
|
-
this[internalSymbol].getRealSubject()["options"],
|
|
548
|
-
).getVia(path);
|
|
549
|
-
} catch (e) {}
|
|
550
|
-
|
|
551
|
-
if (value === undefined) return defaultValue;
|
|
552
|
-
return value;
|
|
549
|
+
return new Pathfinder(
|
|
550
|
+
this[internalSymbol].getRealSubject()["options"],
|
|
551
|
+
).getVia(path, defaultValue);
|
|
553
552
|
}
|
|
554
553
|
|
|
555
554
|
/**
|
|
@@ -699,6 +698,7 @@ class CustomElement extends HTMLElement {
|
|
|
699
698
|
[assembleMethodSymbol]() {
|
|
700
699
|
let elements;
|
|
701
700
|
let nodeList;
|
|
701
|
+
|
|
702
702
|
// Extract options from attributes and set them
|
|
703
703
|
const AttributeOptions = getOptionsFromAttributes.call(this);
|
|
704
704
|
if (
|
|
@@ -736,7 +736,12 @@ class CustomElement extends HTMLElement {
|
|
|
736
736
|
try {
|
|
737
737
|
nodeList = new Set([
|
|
738
738
|
...elements,
|
|
739
|
-
...getSlottedElements.call(
|
|
739
|
+
...getSlottedElements.call(
|
|
740
|
+
this,
|
|
741
|
+
undefined,
|
|
742
|
+
undefined,
|
|
743
|
+
getManagedShadowRoot.call(this),
|
|
744
|
+
),
|
|
740
745
|
]);
|
|
741
746
|
} catch (e) {
|
|
742
747
|
nodeList = elements;
|
|
@@ -764,13 +769,40 @@ class CustomElement extends HTMLElement {
|
|
|
764
769
|
);
|
|
765
770
|
// Attach a mutation observer to observe changes to the attributes of the element
|
|
766
771
|
attachAttributeChangeMutationObserver.call(this);
|
|
772
|
+
attachChildMutationObserver.call(this);
|
|
767
773
|
|
|
768
774
|
if (isFunction(this[internalSymbol]?.syncDisabledState)) {
|
|
769
|
-
this[internalSymbol].syncDisabledState();
|
|
775
|
+
this[internalSymbol].syncDisabledState(true);
|
|
770
776
|
}
|
|
771
777
|
|
|
772
778
|
syncVisibilityState.call(this);
|
|
773
779
|
|
|
780
|
+
this[assembledOnceSymbol] = true;
|
|
781
|
+
|
|
782
|
+
return this;
|
|
783
|
+
}
|
|
784
|
+
|
|
785
|
+
/**
|
|
786
|
+
* Registers a callback that runs when the element is disconnected.
|
|
787
|
+
*
|
|
788
|
+
* The registration is consumed by the first teardown, so a callback that is
|
|
789
|
+
* needed again after a reconnect has to be registered again. A callback that
|
|
790
|
+
* throws does not stop the remaining teardown; the error is reported through
|
|
791
|
+
* the error attribute.
|
|
792
|
+
*
|
|
793
|
+
* @param {function} callback
|
|
794
|
+
* @return {CustomElement}
|
|
795
|
+
* @throws {TypeError} value is not a function
|
|
796
|
+
* @since 4.154.0
|
|
797
|
+
*/
|
|
798
|
+
registerTeardown(callback) {
|
|
799
|
+
validateFunction(callback);
|
|
800
|
+
|
|
801
|
+
if (!isArray(this[teardownCallbacksSymbol])) {
|
|
802
|
+
this[teardownCallbacksSymbol] = [];
|
|
803
|
+
}
|
|
804
|
+
|
|
805
|
+
this[teardownCallbacksSymbol].push(callback);
|
|
774
806
|
return this;
|
|
775
807
|
}
|
|
776
808
|
|
|
@@ -778,7 +810,15 @@ class CustomElement extends HTMLElement {
|
|
|
778
810
|
* You know what you are doing? This function is only for advanced users.
|
|
779
811
|
* The result is a clone of the internal data.
|
|
780
812
|
*
|
|
813
|
+
* This snapshot is the object the updaters are bound to. It is the only
|
|
814
|
+
* place where values typed into a `data-monster-bind` input end up when
|
|
815
|
+
* `eventProcessing` is enabled, because the synchronisation runs one way
|
|
816
|
+
* from the options into the snapshot and never back. A reconnect rebinds to
|
|
817
|
+
* the same snapshot, so that state survives.
|
|
818
|
+
*
|
|
781
819
|
* @return {*}
|
|
820
|
+
* @see Monster.DOM.CustomElement#getOption
|
|
821
|
+
* @since 3.56.0
|
|
782
822
|
*/
|
|
783
823
|
getInternalUpdateCloneData() {
|
|
784
824
|
return clone(this[updateCloneDataSymbol]);
|
|
@@ -793,19 +833,31 @@ class CustomElement extends HTMLElement {
|
|
|
793
833
|
* @see https://developer.mozilla.org/en-US/docs/Web/API/Element/connectedCallback
|
|
794
834
|
*/
|
|
795
835
|
connectedCallback() {
|
|
796
|
-
//
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
this,
|
|
801
|
-
this[internalSymbol].getSubject()?.["options"],
|
|
802
|
-
);
|
|
803
|
-
} catch (e) {
|
|
804
|
-
addErrorAttribute(this, e);
|
|
805
|
-
}
|
|
806
|
-
// If not, call the assembleMethod to initialize the object
|
|
807
|
-
this[assembleMethodSymbol]();
|
|
836
|
+
// Already bound, nothing to do. Moving a connected element inside its own
|
|
837
|
+
// parent fires disconnect and connect, so this runs often.
|
|
838
|
+
if (hasObjectLink(this, customElementUpdaterLinkSymbol)) {
|
|
839
|
+
return;
|
|
808
840
|
}
|
|
841
|
+
|
|
842
|
+
try {
|
|
843
|
+
initOptionsFromAttributes(
|
|
844
|
+
this,
|
|
845
|
+
this[internalSymbol].getSubject()?.["options"],
|
|
846
|
+
);
|
|
847
|
+
} catch (e) {
|
|
848
|
+
addErrorAttribute(this, e);
|
|
849
|
+
}
|
|
850
|
+
|
|
851
|
+
// The assemble method is a one shot: derived classes override it and
|
|
852
|
+
// rebuild their own state in there, for example state/log.mjs resets its
|
|
853
|
+
// entries array and its entry maps. After a disconnect the rendered DOM
|
|
854
|
+
// and that state are still intact, so a reconnect only relinks.
|
|
855
|
+
if (this[assembledOnceSymbol] === true) {
|
|
856
|
+
rebindAfterReconnect.call(this);
|
|
857
|
+
return;
|
|
858
|
+
}
|
|
859
|
+
|
|
860
|
+
this[assembleMethodSymbol]();
|
|
809
861
|
}
|
|
810
862
|
|
|
811
863
|
/**
|
|
@@ -814,7 +866,16 @@ class CustomElement extends HTMLElement {
|
|
|
814
866
|
* @return {void}
|
|
815
867
|
* @since 1.7.0
|
|
816
868
|
*/
|
|
817
|
-
disconnectedCallback() {
|
|
869
|
+
disconnectedCallback() {
|
|
870
|
+
runTeardownCallbacks.call(this);
|
|
871
|
+
disposeLinkedUpdaters.call(this);
|
|
872
|
+
detachAttributeChangeMutationObserver.call(this);
|
|
873
|
+
detachChildMutationObserver.call(this);
|
|
874
|
+
|
|
875
|
+
if (isFunction(this[internalSymbol]?.disconnectDisabledObservers)) {
|
|
876
|
+
this[internalSymbol].disconnectDisabledObservers();
|
|
877
|
+
}
|
|
878
|
+
}
|
|
818
879
|
|
|
819
880
|
/**
|
|
820
881
|
* The custom element has been moved into a new document (e.g. someone called document.adoptNode(el)).
|
|
@@ -970,6 +1031,168 @@ function initFromCallbackHost() {
|
|
|
970
1031
|
callControlCallback.call(this, callBackFunctionName);
|
|
971
1032
|
}
|
|
972
1033
|
|
|
1034
|
+
/**
|
|
1035
|
+
* Relinks a previously assembled element after it was reconnected.
|
|
1036
|
+
*
|
|
1037
|
+
* Deliberately does not touch the shadow root, the rendered markup, the options
|
|
1038
|
+
* snapshot or anything a derived assemble method built. It only recreates what
|
|
1039
|
+
* the teardown released: the updaters and the observers.
|
|
1040
|
+
*
|
|
1041
|
+
* @private
|
|
1042
|
+
* @this CustomElement
|
|
1043
|
+
*/
|
|
1044
|
+
function rebindAfterReconnect() {
|
|
1045
|
+
let elements = getManagedShadowRoot.call(this)?.childNodes;
|
|
1046
|
+
if (!(elements instanceof NodeList)) {
|
|
1047
|
+
elements = this.childNodes;
|
|
1048
|
+
}
|
|
1049
|
+
|
|
1050
|
+
let nodeList;
|
|
1051
|
+
try {
|
|
1052
|
+
nodeList = new Set([
|
|
1053
|
+
...elements,
|
|
1054
|
+
...getSlottedElements.call(
|
|
1055
|
+
this,
|
|
1056
|
+
undefined,
|
|
1057
|
+
undefined,
|
|
1058
|
+
getManagedShadowRoot.call(this),
|
|
1059
|
+
),
|
|
1060
|
+
]);
|
|
1061
|
+
} catch {
|
|
1062
|
+
nodeList = elements;
|
|
1063
|
+
}
|
|
1064
|
+
|
|
1065
|
+
const cfg = {};
|
|
1066
|
+
if (this.getOption("eventProcessing") === true) {
|
|
1067
|
+
cfg.eventProcessing = true;
|
|
1068
|
+
}
|
|
1069
|
+
if (this.getOption("updater.batchUpdates") === true) {
|
|
1070
|
+
cfg.batchUpdates = true;
|
|
1071
|
+
}
|
|
1072
|
+
|
|
1073
|
+
// Binds to the same snapshot object on purpose: it carries the runtime state
|
|
1074
|
+
// the updaters produced, including values written back by event processing,
|
|
1075
|
+
// which the component options never receive.
|
|
1076
|
+
addObjectWithUpdaterToElement.call(
|
|
1077
|
+
this,
|
|
1078
|
+
nodeList,
|
|
1079
|
+
customElementUpdaterLinkSymbol,
|
|
1080
|
+
this[updateCloneDataSymbol],
|
|
1081
|
+
cfg,
|
|
1082
|
+
);
|
|
1083
|
+
|
|
1084
|
+
attachAttributeChangeMutationObserver.call(this);
|
|
1085
|
+
attachChildMutationObserver.call(this);
|
|
1086
|
+
|
|
1087
|
+
if (isFunction(this[internalSymbol]?.syncDisabledState)) {
|
|
1088
|
+
this[internalSymbol].syncDisabledState(true);
|
|
1089
|
+
}
|
|
1090
|
+
|
|
1091
|
+
syncVisibilityState.call(this);
|
|
1092
|
+
}
|
|
1093
|
+
|
|
1094
|
+
/**
|
|
1095
|
+
* @private
|
|
1096
|
+
* @this CustomElement
|
|
1097
|
+
*/
|
|
1098
|
+
function runTeardownCallbacks() {
|
|
1099
|
+
const callbacks = this[teardownCallbacksSymbol];
|
|
1100
|
+
if (!isArray(callbacks) || callbacks.length === 0) {
|
|
1101
|
+
return;
|
|
1102
|
+
}
|
|
1103
|
+
|
|
1104
|
+
// taken first, so a callback that re-registers during teardown does not run
|
|
1105
|
+
// in the same pass
|
|
1106
|
+
this[teardownCallbacksSymbol] = [];
|
|
1107
|
+
|
|
1108
|
+
for (const callback of callbacks) {
|
|
1109
|
+
try {
|
|
1110
|
+
callback.call(this);
|
|
1111
|
+
} catch (e) {
|
|
1112
|
+
addErrorAttribute(this, e);
|
|
1113
|
+
}
|
|
1114
|
+
}
|
|
1115
|
+
}
|
|
1116
|
+
|
|
1117
|
+
/**
|
|
1118
|
+
* @private
|
|
1119
|
+
* @this CustomElement
|
|
1120
|
+
*/
|
|
1121
|
+
function disposeLinkedUpdaters() {
|
|
1122
|
+
if (!hasObjectLink(this, customElementUpdaterLinkSymbol)) {
|
|
1123
|
+
return;
|
|
1124
|
+
}
|
|
1125
|
+
|
|
1126
|
+
try {
|
|
1127
|
+
for (const group of getLinkedObjects(
|
|
1128
|
+
this,
|
|
1129
|
+
customElementUpdaterLinkSymbol,
|
|
1130
|
+
)) {
|
|
1131
|
+
if (!(group instanceof Set)) {
|
|
1132
|
+
continue;
|
|
1133
|
+
}
|
|
1134
|
+
|
|
1135
|
+
for (const updater of group) {
|
|
1136
|
+
if (typeof updater?.dispose !== "function") {
|
|
1137
|
+
continue;
|
|
1138
|
+
}
|
|
1139
|
+
|
|
1140
|
+
try {
|
|
1141
|
+
updater.dispose();
|
|
1142
|
+
} catch (e) {
|
|
1143
|
+
addErrorAttribute(this, e);
|
|
1144
|
+
}
|
|
1145
|
+
}
|
|
1146
|
+
}
|
|
1147
|
+
|
|
1148
|
+
removeObjectLink(this, customElementUpdaterLinkSymbol);
|
|
1149
|
+
} catch (e) {
|
|
1150
|
+
addErrorAttribute(this, e);
|
|
1151
|
+
}
|
|
1152
|
+
}
|
|
1153
|
+
|
|
1154
|
+
/**
|
|
1155
|
+
* The symbol is deleted, not only disconnected: the attach guard treats a
|
|
1156
|
+
* present symbol as an active observer and would otherwise never replace it.
|
|
1157
|
+
*
|
|
1158
|
+
* @private
|
|
1159
|
+
* @this CustomElement
|
|
1160
|
+
*/
|
|
1161
|
+
function detachAttributeChangeMutationObserver() {
|
|
1162
|
+
const observer = this[attributeMutationObserverSymbol];
|
|
1163
|
+
if (observer) {
|
|
1164
|
+
try {
|
|
1165
|
+
observer.disconnect();
|
|
1166
|
+
} catch (e) {
|
|
1167
|
+
addErrorAttribute(this, e);
|
|
1168
|
+
}
|
|
1169
|
+
}
|
|
1170
|
+
|
|
1171
|
+
delete this[attributeMutationObserverSymbol];
|
|
1172
|
+
}
|
|
1173
|
+
|
|
1174
|
+
/**
|
|
1175
|
+
* @private
|
|
1176
|
+
* @this CustomElement
|
|
1177
|
+
*/
|
|
1178
|
+
function detachChildMutationObserver() {
|
|
1179
|
+
const observer = this[childMutationObserverSymbol];
|
|
1180
|
+
if (observer) {
|
|
1181
|
+
try {
|
|
1182
|
+
observer.disconnect();
|
|
1183
|
+
} catch (e) {
|
|
1184
|
+
addErrorAttribute(this, e);
|
|
1185
|
+
}
|
|
1186
|
+
}
|
|
1187
|
+
|
|
1188
|
+
delete this[childMutationObserverSymbol];
|
|
1189
|
+
|
|
1190
|
+
if (this[childMutationTimerSymbol]) {
|
|
1191
|
+
clearTimeout(this[childMutationTimerSymbol]);
|
|
1192
|
+
this[childMutationTimerSymbol] = null;
|
|
1193
|
+
}
|
|
1194
|
+
}
|
|
1195
|
+
|
|
973
1196
|
/**
|
|
974
1197
|
* This method is called when the element is first created.
|
|
975
1198
|
*
|
|
@@ -1098,12 +1321,21 @@ function containChildNode(node) {
|
|
|
1098
1321
|
return true;
|
|
1099
1322
|
}
|
|
1100
1323
|
|
|
1101
|
-
|
|
1102
|
-
|
|
1324
|
+
// Node.contains() already covers the whole light DOM subtree, so the only
|
|
1325
|
+
// thing left to search is the shadow root of nested elements, which no
|
|
1326
|
+
// contains() call can see.
|
|
1327
|
+
for (const child of this.childNodes) {
|
|
1328
|
+
const shadowRoot = child?.shadowRoot;
|
|
1329
|
+
if (
|
|
1330
|
+
shadowRoot instanceof ShadowRoot &&
|
|
1331
|
+
containChildNode.call(shadowRoot, node)
|
|
1332
|
+
) {
|
|
1103
1333
|
return true;
|
|
1104
1334
|
}
|
|
1105
1335
|
|
|
1106
|
-
containChildNode.call(
|
|
1336
|
+
if (child.childNodes?.length && containChildNode.call(child, node)) {
|
|
1337
|
+
return true;
|
|
1338
|
+
}
|
|
1107
1339
|
}
|
|
1108
1340
|
|
|
1109
1341
|
return false;
|
|
@@ -1228,6 +1460,8 @@ function initOptionObserver() {
|
|
|
1228
1460
|
self.attachObserver(new Observer(syncDisabledState));
|
|
1229
1461
|
|
|
1230
1462
|
self[internalSymbol].syncDisabledState = syncDisabledState;
|
|
1463
|
+
self[internalSymbol].disconnectDisabledObservers =
|
|
1464
|
+
disconnectDisabledObservers;
|
|
1231
1465
|
|
|
1232
1466
|
let updaterSyncRevision = 0;
|
|
1233
1467
|
self.attachObserver(
|
|
@@ -1698,6 +1932,15 @@ function initShadowRoot() {
|
|
|
1698
1932
|
let template;
|
|
1699
1933
|
let html;
|
|
1700
1934
|
|
|
1935
|
+
// A rebind keeps the rendered shadow DOM. Calling attachShadow() again would
|
|
1936
|
+
// throw, after which the main template used to be appended into the light
|
|
1937
|
+
// DOM and a second updater set was bound to the same host.
|
|
1938
|
+
const existing = getManagedShadowRoot.call(this);
|
|
1939
|
+
if (existing instanceof ShadowRoot) {
|
|
1940
|
+
this[managedShadowRootSymbol] = existing;
|
|
1941
|
+
return this;
|
|
1942
|
+
}
|
|
1943
|
+
|
|
1701
1944
|
try {
|
|
1702
1945
|
template = findDocumentTemplate(this.constructor.getTag());
|
|
1703
1946
|
} catch (e) {
|
package/source/dom/updater.mjs
CHANGED
|
@@ -651,7 +651,7 @@ function retrieveAndSetValue(element) {
|
|
|
651
651
|
|
|
652
652
|
let options = element?.selectedOptions;
|
|
653
653
|
if (options === undefined)
|
|
654
|
-
options = element.querySelectorAll("
|
|
654
|
+
options = element.querySelectorAll("option:checked");
|
|
655
655
|
value = Array.from(options).map(({ value }) => value);
|
|
656
656
|
|
|
657
657
|
break;
|
|
@@ -847,8 +847,12 @@ function removeElement(_change, renderContext) {
|
|
|
847
847
|
}
|
|
848
848
|
|
|
849
849
|
let changed = false;
|
|
850
|
+
// No :scope prefix: querySelectorAll() on an element already returns only its
|
|
851
|
+
// descendants, so the prefix carries no meaning here, while :scope does not
|
|
852
|
+
// resolve for shadow root content under jsdom, which made this a silent no-op
|
|
853
|
+
// for every component that renders into a shadow root.
|
|
850
854
|
for (const [, element] of this[internalSymbol].element
|
|
851
|
-
.querySelectorAll(
|
|
855
|
+
.querySelectorAll(`[${ATTRIBUTE_UPDATER_REMOVE}]`)
|
|
852
856
|
.entries()) {
|
|
853
857
|
teardownManagedSubtree(element);
|
|
854
858
|
element.parentNode.removeChild(element);
|
package/source/types/base.mjs
CHANGED
|
@@ -115,21 +115,22 @@ function checkInstanceSymbol(obj) {
|
|
|
115
115
|
}
|
|
116
116
|
|
|
117
117
|
if (proto.hasOwnProperty(instanceSymbol) !== true) {
|
|
118
|
-
return checkInstanceSymbol.apply(this, [obj
|
|
118
|
+
return checkInstanceSymbol.apply(this, [Object.getPrototypeOf(obj)]);
|
|
119
119
|
}
|
|
120
120
|
|
|
121
121
|
const symbol = proto[instanceSymbol];
|
|
122
122
|
if (symbol === undefined) {
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
return false;
|
|
123
|
+
const parent = Object.getPrototypeOf(obj);
|
|
124
|
+
if (parent) {
|
|
125
|
+
return checkInstanceSymbol.apply(this, [parent]);
|
|
127
126
|
}
|
|
127
|
+
|
|
128
|
+
return false;
|
|
128
129
|
}
|
|
129
130
|
|
|
130
131
|
if (symbol === this[instanceSymbol]) {
|
|
131
132
|
return true;
|
|
132
133
|
}
|
|
133
134
|
|
|
134
|
-
return checkInstanceSymbol.apply(this, [obj
|
|
135
|
+
return checkInstanceSymbol.apply(this, [Object.getPrototypeOf(obj)]);
|
|
135
136
|
}
|
|
@@ -80,13 +80,6 @@ class BaseWithOptions extends Base {
|
|
|
80
80
|
* @since 1.10.0
|
|
81
81
|
*/
|
|
82
82
|
getOption(path, defaultValue) {
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
try {
|
|
86
|
-
value = new Pathfinder(this[internalSymbol]).getVia(path);
|
|
87
|
-
} catch (e) {}
|
|
88
|
-
|
|
89
|
-
if (value === undefined) return defaultValue;
|
|
90
|
-
return value;
|
|
83
|
+
return new Pathfinder(this[internalSymbol]).getVia(path, defaultValue);
|
|
91
84
|
}
|
|
92
85
|
}
|
|
@@ -126,16 +126,10 @@ function equipWithInternal() {
|
|
|
126
126
|
* @return {*}
|
|
127
127
|
*/
|
|
128
128
|
this["getInternal"] = (path, defaultValue) => {
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
path,
|
|
134
|
-
);
|
|
135
|
-
} catch (e) {}
|
|
136
|
-
|
|
137
|
-
if (value === undefined) return defaultValue;
|
|
138
|
-
return value;
|
|
129
|
+
return new Pathfinder(this[internalSymbol].getRealSubject()).getVia(
|
|
130
|
+
path,
|
|
131
|
+
defaultValue,
|
|
132
|
+
);
|
|
139
133
|
};
|
|
140
134
|
}
|
|
141
135
|
|
|
@@ -55,9 +55,9 @@ class ObserverList extends Base {
|
|
|
55
55
|
detach(observer) {
|
|
56
56
|
validateInstance(observer, Observer);
|
|
57
57
|
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
for (; i
|
|
58
|
+
// Iterate backwards: splicing while moving forward skips the entry that
|
|
59
|
+
// takes the freed slot, which left adjacent duplicates attached.
|
|
60
|
+
for (let i = this.observers.length - 1; i >= 0; i--) {
|
|
61
61
|
if (this.observers[i] === observer) {
|
|
62
62
|
this.observers.splice(i, 1);
|
|
63
63
|
}
|
|
@@ -74,14 +74,8 @@ class ObserverList extends Base {
|
|
|
74
74
|
*/
|
|
75
75
|
contains(observer) {
|
|
76
76
|
validateInstance(observer, Observer);
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
for (; i < l; i++) {
|
|
80
|
-
if (this.observers[i] === observer) {
|
|
81
|
-
return true;
|
|
82
|
-
}
|
|
83
|
-
}
|
|
84
|
-
return false;
|
|
77
|
+
|
|
78
|
+
return this.observers.includes(observer);
|
|
85
79
|
}
|
|
86
80
|
|
|
87
81
|
/**
|
|
@@ -90,14 +84,12 @@ class ObserverList extends Base {
|
|
|
90
84
|
* @return {Promise}
|
|
91
85
|
*/
|
|
92
86
|
notify(subject) {
|
|
93
|
-
const
|
|
87
|
+
const promises = [];
|
|
94
88
|
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
for (; i < l; i++) {
|
|
98
|
-
pomises.push(this.observers[i].update(subject));
|
|
89
|
+
for (const observer of this.observers) {
|
|
90
|
+
promises.push(observer.update(subject));
|
|
99
91
|
}
|
|
100
92
|
|
|
101
|
-
return Promise.all(
|
|
93
|
+
return Promise.all(promises);
|
|
102
94
|
}
|
|
103
95
|
}
|
|
@@ -218,7 +218,7 @@ function getHandler() {
|
|
|
218
218
|
current = proxy.proxyMap.get(current);
|
|
219
219
|
}
|
|
220
220
|
|
|
221
|
-
if (current
|
|
221
|
+
if (isSameValue(current, value)) {
|
|
222
222
|
return true;
|
|
223
223
|
}
|
|
224
224
|
|
|
@@ -232,7 +232,7 @@ function getHandler() {
|
|
|
232
232
|
if (proxy.proxyMap.has(next)) {
|
|
233
233
|
next = proxy.proxyMap.get(next);
|
|
234
234
|
}
|
|
235
|
-
if (next
|
|
235
|
+
if (!isSameValue(next, current)) {
|
|
236
236
|
proxy.observers.notify(proxy);
|
|
237
237
|
}
|
|
238
238
|
}
|
|
@@ -274,6 +274,23 @@ function getHandler() {
|
|
|
274
274
|
return handler;
|
|
275
275
|
}
|
|
276
276
|
|
|
277
|
+
/**
|
|
278
|
+
* Two NaN values are the same value, so assigning NaN over NaN is not a change.
|
|
279
|
+
* Object.is() is not used here because it would additionally treat 0 and -0 as
|
|
280
|
+
* a change and notify for assignments that do not alter the subject.
|
|
281
|
+
* @param {*} a
|
|
282
|
+
* @param {*} b
|
|
283
|
+
* @return {boolean}
|
|
284
|
+
* @private
|
|
285
|
+
*/
|
|
286
|
+
function isSameValue(a, b) {
|
|
287
|
+
if (a === b) {
|
|
288
|
+
return true;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
return Number.isNaN(a) && Number.isNaN(b);
|
|
292
|
+
}
|
|
293
|
+
|
|
277
294
|
/**
|
|
278
295
|
* Native objects with internal slots cannot be used through a transparent Proxy
|
|
279
296
|
* because their prototype methods require the original receiver.
|
|
@@ -84,13 +84,18 @@ class TokenList extends Base {
|
|
|
84
84
|
*/
|
|
85
85
|
contains(value) {
|
|
86
86
|
if (isString(value)) {
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
87
|
+
// Repeated or surrounding whitespace produces empty segments, which are
|
|
88
|
+
// not tokens. A value without any token contains nothing.
|
|
89
|
+
const tokens = value
|
|
90
|
+
.split(" ")
|
|
91
|
+
.map((token) => token.trim())
|
|
92
|
+
.filter((token) => token !== "");
|
|
93
|
+
|
|
94
|
+
if (tokens.length === 0) {
|
|
95
|
+
return false;
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
return tokens.every((token) => this.tokens.has(token));
|
|
94
99
|
}
|
|
95
100
|
|
|
96
101
|
if (isIterable(value)) {
|
|
@@ -100,7 +105,7 @@ class TokenList extends Base {
|
|
|
100
105
|
if (this.tokens.has(token.trim()) === false) return false;
|
|
101
106
|
counter++;
|
|
102
107
|
}
|
|
103
|
-
return counter > 0
|
|
108
|
+
return counter > 0;
|
|
104
109
|
}
|
|
105
110
|
|
|
106
111
|
return false;
|