@jinntec/fore 2.9.0 → 3.0.1
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/fore-dev.js +4461 -1852
- package/dist/fore.js +4460 -1840
- package/package.json +3 -1
- package/src/DependentXPathQueries.js +37 -2
- package/src/ForeElementMixin.js +64 -38
- package/src/actions/fx-delete.js +424 -73
- package/src/actions/fx-insert.js +471 -73
- package/src/actions/fx-setattribute.js +5 -5
- package/src/actions/fx-setvalue.js +11 -9
- package/src/fore.js +28 -71
- package/src/functions/registerFunction.js +65 -20
- package/src/fx-bind.js +128 -73
- package/src/fx-fore.js +190 -97
- package/src/fx-instance.js +138 -142
- package/src/fx-model.js +292 -102
- package/src/fx-submission.js +246 -135
- package/src/fx-var.js +28 -4
- package/src/json/JSONDomFacade.js +84 -0
- package/src/json/JSONLens.js +67 -0
- package/src/json/JSONNode.js +248 -0
- package/src/json/lensFromNode.js +5 -0
- package/src/modelitem.js +16 -2
- package/src/tools/fx-action-log.js +1 -1
- package/src/ui/UIElement.js +16 -2
- package/src/ui/fx-items.js +26 -32
- package/src/ui/fx-repeat.js +682 -246
- package/src/ui/fx-repeatitem.js +16 -1
- package/src/ui/repeat-base.js +8 -4
- package/src/withDraggability.js +0 -1
- package/src/xpath-evaluation.js +1763 -740
- package/src/xpath-path.js +274 -24
- package/src/xpath-util.js +92 -46
package/src/fx-fore.js
CHANGED
|
@@ -288,6 +288,7 @@ export class FxFore extends HTMLElement {
|
|
|
288
288
|
this.createNodes = this.hasAttribute('create-nodes') ? true : false;
|
|
289
289
|
this._localNamesWithChanges = new Set();
|
|
290
290
|
this.setAttribute('role', 'form'); // set aria role
|
|
291
|
+
this._pendingRefresh = false;
|
|
291
292
|
}
|
|
292
293
|
|
|
293
294
|
/**
|
|
@@ -561,6 +562,7 @@ export class FxFore extends HTMLElement {
|
|
|
561
562
|
await Promise.all(libs.map(l => l.readyPromise || Promise.resolve()));
|
|
562
563
|
|
|
563
564
|
await modelElement.modelConstruct();
|
|
565
|
+
console.log("varbindings ",this._instanceVarBindings);
|
|
564
566
|
this._handleModelConstructDone();
|
|
565
567
|
}
|
|
566
568
|
|
|
@@ -674,6 +676,79 @@ export class FxFore extends HTMLElement {
|
|
|
674
676
|
}
|
|
675
677
|
}
|
|
676
678
|
|
|
679
|
+
/**
|
|
680
|
+
* Ensure there is an fx-var for each fx-instance in this fx-fore's fx-model scope.
|
|
681
|
+
*
|
|
682
|
+
* - For instances with an @id, create `$id` with value `instance('id')`.
|
|
683
|
+
* - For the first instance WITHOUT an @id, create `$default` with value `instance()`.
|
|
684
|
+
* - IMPORTANT: if an instance has id="default", we STILL bind `$default` to `instance()`
|
|
685
|
+
* (avoids recursion / stack overflow during fx-var refresh in some cycles).
|
|
686
|
+
*
|
|
687
|
+
* Vars are inserted as direct children of `<fx-fore>` immediately before `<fx-model>`.
|
|
688
|
+
* The method is idempotent.
|
|
689
|
+
*/
|
|
690
|
+
_ensureInstanceVars() {
|
|
691
|
+
if (this.__instanceVarsEnsured) return;
|
|
692
|
+
this.__instanceVarsEnsured = true;
|
|
693
|
+
|
|
694
|
+
// Resolve this fx-fore's own fx-model (not nested ones)
|
|
695
|
+
const model = this.querySelector(':scope > fx-model');
|
|
696
|
+
if (!model) return;
|
|
697
|
+
|
|
698
|
+
// Collect instances that are direct children of this model (doc order)
|
|
699
|
+
const instances = Array.from(model.querySelectorAll(':scope > fx-instance'));
|
|
700
|
+
|
|
701
|
+
// Collect existing fx-var names at fx-fore scope (author-defined and previously generated)
|
|
702
|
+
const existingVars = new Set(
|
|
703
|
+
Array.from(this.querySelectorAll(':scope > fx-var'))
|
|
704
|
+
.map(v => (v.getAttribute('name') || '').trim())
|
|
705
|
+
.filter(Boolean),
|
|
706
|
+
);
|
|
707
|
+
|
|
708
|
+
let defaultAssigned = false;
|
|
709
|
+
|
|
710
|
+
for (const inst of instances) {
|
|
711
|
+
const rawId = (inst.getAttribute('id') || '').trim();
|
|
712
|
+
|
|
713
|
+
// First id-less instance => $default = instance()
|
|
714
|
+
if (!rawId) {
|
|
715
|
+
if (defaultAssigned) continue;
|
|
716
|
+
defaultAssigned = true;
|
|
717
|
+
|
|
718
|
+
const name = 'default';
|
|
719
|
+
if (existingVars.has(name)) continue;
|
|
720
|
+
|
|
721
|
+
const fxVar = document.createElement('fx-var');
|
|
722
|
+
fxVar.setAttribute('name', name);
|
|
723
|
+
fxVar.setAttribute('value', 'instance()');
|
|
724
|
+
fxVar.setAttribute('data-generated', 'instance-var');
|
|
725
|
+
|
|
726
|
+
this.insertBefore(fxVar, model);
|
|
727
|
+
existingVars.add(name);
|
|
728
|
+
continue;
|
|
729
|
+
}
|
|
730
|
+
|
|
731
|
+
// Normal id-based instance var
|
|
732
|
+
const name = rawId;
|
|
733
|
+
if (existingVars.has(name)) continue;
|
|
734
|
+
|
|
735
|
+
const fxVar = document.createElement('fx-var');
|
|
736
|
+
fxVar.setAttribute('name', name);
|
|
737
|
+
|
|
738
|
+
// IMPORTANT: avoid `instance('default')` recursion in fx-var refresh
|
|
739
|
+
if (name === 'default') {
|
|
740
|
+
fxVar.setAttribute('value', 'instance()');
|
|
741
|
+
} else {
|
|
742
|
+
fxVar.setAttribute('value', `instance('${name}')`);
|
|
743
|
+
}
|
|
744
|
+
|
|
745
|
+
fxVar.setAttribute('data-generated', 'instance-var');
|
|
746
|
+
|
|
747
|
+
this.insertBefore(fxVar, model);
|
|
748
|
+
existingVars.add(name);
|
|
749
|
+
}
|
|
750
|
+
}
|
|
751
|
+
|
|
677
752
|
_injectDevtools() {
|
|
678
753
|
if (this.ownerDocument.querySelector('fx-devtools')) {
|
|
679
754
|
// There's already a devtools, so we can ignore this one.
|
|
@@ -713,9 +788,11 @@ export class FxFore extends HTMLElement {
|
|
|
713
788
|
}
|
|
714
789
|
|
|
715
790
|
markAsClean() {
|
|
791
|
+
console.log('marking as clean', this);
|
|
716
792
|
this.addEventListener(
|
|
717
793
|
'value-changed',
|
|
718
794
|
() => {
|
|
795
|
+
console.log('MARK as modified', this)
|
|
719
796
|
this.dirtyState = dirtyStates.DIRTY;
|
|
720
797
|
this.classList.toggle('fx-modified')
|
|
721
798
|
},
|
|
@@ -723,6 +800,7 @@ export class FxFore extends HTMLElement {
|
|
|
723
800
|
);
|
|
724
801
|
this.dirtyState = dirtyStates.CLEAN;
|
|
725
802
|
this.classList.remove('fx-modified');
|
|
803
|
+
this.querySelectorAll('.visited').forEach(el => el.classList.remove('visited'));
|
|
726
804
|
}
|
|
727
805
|
|
|
728
806
|
/**
|
|
@@ -793,89 +871,73 @@ export class FxFore extends HTMLElement {
|
|
|
793
871
|
/**
|
|
794
872
|
* @param {(boolean|{reason:'index-function'})} [force]fx-fore
|
|
795
873
|
*/
|
|
874
|
+
/**
|
|
875
|
+
* @param {(boolean|{reason:'index-function'})} [force]
|
|
876
|
+
*/
|
|
877
|
+
/**
|
|
878
|
+
* @param {(boolean|{reason:'index-function'})} [force]
|
|
879
|
+
*/
|
|
796
880
|
async refresh(force) {
|
|
881
|
+
// If we're already refreshing, do NOT drop the request.
|
|
882
|
+
// Queue a hard refresh and return a promise that resolves when the next refresh finishes.
|
|
797
883
|
if (this.isRefreshing) {
|
|
798
|
-
|
|
799
|
-
|
|
884
|
+
// keep "strongest" request: any true means hard refresh
|
|
885
|
+
this._pendingRefresh = this._pendingRefresh || force === true;
|
|
800
886
|
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
...(force || { reason: undefined }),
|
|
805
|
-
elementLocalnamesWithChanges: Array.from(this._localNamesWithChanges),
|
|
806
|
-
};
|
|
807
|
-
this._localNamesWithChanges.clear();
|
|
887
|
+
return new Promise(resolve => {
|
|
888
|
+
this.addEventListener('refresh-done', () => resolve(), { once: true });
|
|
889
|
+
});
|
|
808
890
|
}
|
|
809
|
-
*/
|
|
810
891
|
|
|
811
892
|
this.isRefreshing = true;
|
|
812
893
|
this.isRefreshPhase = true;
|
|
813
894
|
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
Fore.refreshChildren(this, force);
|
|
822
|
-
} else {
|
|
823
|
-
// Process all batched no tifications at the end of the refresh phase
|
|
824
|
-
console.log('🔄 🎯 ### processing batched notifications');
|
|
825
|
-
await this._processBatchedNotifications();
|
|
826
|
-
}
|
|
827
|
-
|
|
828
|
-
// ### refresh template expressions
|
|
829
|
-
if (force === true || this.initialRun || this._scanForNewTemplateExpressionsNextRefresh) {
|
|
830
|
-
this._updateTemplateExpressions();
|
|
831
|
-
this._scanForNewTemplateExpressionsNextRefresh = false; // reset
|
|
832
|
-
}
|
|
833
|
-
|
|
834
|
-
this._processTemplateExpressions();
|
|
835
|
-
|
|
836
|
-
this.isRefreshPhase = false;
|
|
837
|
-
|
|
838
|
-
// console.log('### <<<<< dispatching refresh-done - end of UI update cycle >>>>>');
|
|
839
|
-
// this.dispatchEvent(new CustomEvent('refresh-done'));
|
|
840
|
-
this.initialRun = false;
|
|
841
|
-
this.style.visibility = 'visible';
|
|
842
|
-
console.info(
|
|
843
|
-
`%c ✅ refresh-done on #${this.id}`,
|
|
844
|
-
'background:darkorange; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
845
|
-
this.getModel().modelItems,
|
|
846
|
-
);
|
|
895
|
+
try {
|
|
896
|
+
if (force === true || this.initialRun) {
|
|
897
|
+
console.log('🔄 🔴🔴🔴 ### full refresh() on ', this);
|
|
898
|
+
await Fore.refreshChildren(this, force);
|
|
899
|
+
} else {
|
|
900
|
+
await this._processBatchedNotifications();
|
|
901
|
+
}
|
|
847
902
|
|
|
848
|
-
|
|
903
|
+
if (force === true || this.initialRun || this._scanForNewTemplateExpressionsNextRefresh) {
|
|
904
|
+
this._updateTemplateExpressions();
|
|
905
|
+
this._scanForNewTemplateExpressionsNextRefresh = false;
|
|
906
|
+
}
|
|
849
907
|
|
|
850
|
-
|
|
851
|
-
/*
|
|
852
|
-
calling the parent to refresh causes errors and inconsistent state. Also it is questionable
|
|
853
|
-
if a child should actually interact with its parent in this way.
|
|
908
|
+
this._processTemplateExpressions();
|
|
854
909
|
|
|
855
|
-
|
|
910
|
+
this.isRefreshPhase = false;
|
|
911
|
+
this.initialRun = false;
|
|
912
|
+
this.style.visibility = 'visible';
|
|
856
913
|
|
|
857
|
-
|
|
858
|
-
|
|
914
|
+
console.info(
|
|
915
|
+
`%c ✅ refresh-done on #${this.id}`,
|
|
916
|
+
'background:darkorange; color:black; padding:.5rem; display:inline-block; white-space: nowrap; border-radius:0.3rem;width:100%;',
|
|
917
|
+
this.getModel().modelItems,
|
|
918
|
+
);
|
|
859
919
|
|
|
860
|
-
|
|
920
|
+
Fore.dispatch(this, 'refresh-done', {});
|
|
861
921
|
|
|
862
|
-
|
|
863
|
-
|
|
922
|
+
const subFores = Array.from(this.querySelectorAll('fx-fore'));
|
|
923
|
+
for (const subFore of subFores) {
|
|
924
|
+
if (subFore.ready) {
|
|
925
|
+
await subFore.refresh(true);
|
|
864
926
|
}
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
927
|
+
}
|
|
928
|
+
} finally {
|
|
929
|
+
this.isRefreshing = false;
|
|
930
|
+
|
|
931
|
+
// If anything requested a refresh while we were refreshing, run exactly one more.
|
|
932
|
+
// This prevents "dropped" refresh requests (your timeout).
|
|
933
|
+
if (this._pendingRefresh) {
|
|
934
|
+
const pendingHard = this._pendingRefresh === true;
|
|
935
|
+
this._pendingRefresh = false;
|
|
936
|
+
// Important: do NOT await in finally without clearing flags first.
|
|
937
|
+
await this.refresh(pendingHard);
|
|
872
938
|
}
|
|
873
939
|
}
|
|
874
|
-
this.isRefreshing = false;
|
|
875
|
-
// Clear the batch
|
|
876
|
-
// this.batchedNotifications.clear();
|
|
877
940
|
}
|
|
878
|
-
|
|
879
941
|
/**
|
|
880
942
|
* Add a ModelItem to the batch of notifications to be processed at the end of the refresh phase
|
|
881
943
|
* @param {ModelItem | import('./ui/UIElement.js').UIElement} item - The ModelItem or UI Element to add to the batch
|
|
@@ -892,6 +954,9 @@ export class FxFore extends HTMLElement {
|
|
|
892
954
|
*/
|
|
893
955
|
_processBatchedNotifications() {
|
|
894
956
|
if (this.batchedNotifications.size > 0) {
|
|
957
|
+
console.log(`🔄 🎯 ### processing ${ this.batchedNotifications.size} batched notifications`);
|
|
958
|
+
console.log('🔄 🎯 ### processing ', Array.from(this.batchedNotifications));
|
|
959
|
+
|
|
895
960
|
// console.log(`🔍 Processing ${this.batchedNotifications.size} batched notifications`);
|
|
896
961
|
|
|
897
962
|
// Process all batched notifications
|
|
@@ -932,6 +997,10 @@ export class FxFore extends HTMLElement {
|
|
|
932
997
|
}
|
|
933
998
|
});
|
|
934
999
|
|
|
1000
|
+
// Update template expressions after processing batched notifications
|
|
1001
|
+
// This ensures template expressions are re-evaluated when data changes
|
|
1002
|
+
this._processTemplateExpressions();
|
|
1003
|
+
|
|
935
1004
|
// Clear the batch
|
|
936
1005
|
this.batchedNotifications.clear();
|
|
937
1006
|
}
|
|
@@ -958,6 +1027,7 @@ export class FxFore extends HTMLElement {
|
|
|
958
1027
|
|
|
959
1028
|
// console.log('######### storedTemplateExpressions', this.storedTemplateExpressions.length);
|
|
960
1029
|
|
|
1030
|
+
if(!tmplExpressions) return;
|
|
961
1031
|
/*
|
|
962
1032
|
storing expressions and their nodes for re-evaluation
|
|
963
1033
|
*/
|
|
@@ -1021,45 +1091,64 @@ export class FxFore extends HTMLElement {
|
|
|
1021
1091
|
* @param {Node} node the node which will get updated with evaluation result
|
|
1022
1092
|
*/
|
|
1023
1093
|
evaluateTemplateExpression(expr, node) {
|
|
1024
|
-
// ### do not evaluate template expressions
|
|
1094
|
+
// ### do not evaluate template expressions within nonrelevant sections
|
|
1025
1095
|
if (node.nodeType === Node.ATTRIBUTE_NODE && node.ownerElement.closest('[nonrelevant]')) return;
|
|
1026
1096
|
if (node.nodeType === Node.TEXT_NODE && node.parentNode.closest('[nonrelevant]')) return;
|
|
1027
1097
|
if (node.nodeType === Node.ELEMENT_NODE && node.closest('[nonrelevant]')) return;
|
|
1028
1098
|
|
|
1029
|
-
//
|
|
1030
|
-
|
|
1099
|
+
// ---- IMPORTANT GUARD ----
|
|
1100
|
+
// Prevent JSON object/array literals in fx-insert@origin from being treated as
|
|
1101
|
+
// template expressions (they contain {...} but are not XPath templates).
|
|
1102
|
+
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
1103
|
+
const el = node.ownerElement;
|
|
1104
|
+
if (el && el.localName === 'fx-insert' && node.name === 'origin') {
|
|
1105
|
+
const v = String(node.value ?? '').trim();
|
|
1106
|
+
const isJsonLiteral =
|
|
1107
|
+
(v.startsWith('{') && v.endsWith('}')) || (v.startsWith('[') && v.endsWith(']'));
|
|
1108
|
+
if (isJsonLiteral) return;
|
|
1109
|
+
}
|
|
1110
|
+
}
|
|
1111
|
+
// -------------------------
|
|
1112
|
+
|
|
1113
|
+
// The element that "defines" the template expression is the correct basis for:
|
|
1114
|
+
// - namespace resolution (xmlns lookup)
|
|
1115
|
+
// - fx-var scoping (in-scope variables)
|
|
1116
|
+
// - context() in repeats (repeat item detection)
|
|
1117
|
+
const definitionElement =
|
|
1118
|
+
node.nodeType === Node.ATTRIBUTE_NODE
|
|
1119
|
+
? node.ownerElement
|
|
1120
|
+
: node.nodeType === Node.TEXT_NODE
|
|
1121
|
+
? (node.parentElement || node.parentNode)
|
|
1122
|
+
: node;
|
|
1123
|
+
|
|
1124
|
+
const formElement =
|
|
1125
|
+
definitionElement && definitionElement.nodeType === Node.ELEMENT_NODE
|
|
1126
|
+
? definitionElement
|
|
1127
|
+
: this;
|
|
1128
|
+
|
|
1129
|
+
const replaced = String(expr ?? '').replace(/{[^}]*}/g, match => {
|
|
1031
1130
|
if (match === '{}') return match;
|
|
1131
|
+
|
|
1032
1132
|
const naked = match.substring(1, match.length - 1);
|
|
1033
1133
|
const inscope = getInScopeContext(node, naked);
|
|
1134
|
+
|
|
1034
1135
|
if (!inscope) {
|
|
1035
|
-
console.warn('no inscope context for expr', naked);
|
|
1036
|
-
const errNode =
|
|
1037
|
-
node.nodeType === Node.TEXT_NODE || node.nodeType === Node.ATTRIBUTE_NODE
|
|
1038
|
-
? node.parentNode
|
|
1039
|
-
: node;
|
|
1040
1136
|
return match;
|
|
1041
1137
|
}
|
|
1042
|
-
// Templates are special: they use the namespace configuration from the place where they are
|
|
1043
|
-
// being defined
|
|
1044
|
-
const instanceId = XPathUtil.getInstanceId(naked,node);
|
|
1045
|
-
|
|
1046
|
-
// If there is an instance referred
|
|
1047
|
-
const inst = instanceId
|
|
1048
|
-
? this.getModel().getInstance(instanceId)
|
|
1049
|
-
: this.getModel().getDefaultInstance();
|
|
1050
1138
|
|
|
1051
1139
|
try {
|
|
1052
|
-
|
|
1053
|
-
//
|
|
1054
|
-
|
|
1140
|
+
// IMPORTANT:
|
|
1141
|
+
// Do NOT pass `null` as the 4th argument here.
|
|
1142
|
+
// Passing `null` suppresses variable collection, which hides implicit vars
|
|
1143
|
+
// like `$default`.
|
|
1144
|
+
return evaluateXPathToString(naked, inscope, formElement);
|
|
1055
1145
|
} catch (error) {
|
|
1056
1146
|
console.warn('ignoring unparseable expr', error);
|
|
1057
1147
|
return match;
|
|
1058
1148
|
}
|
|
1059
1149
|
});
|
|
1060
1150
|
|
|
1061
|
-
// Update to the new value
|
|
1062
|
-
// images from reloading for example
|
|
1151
|
+
// Update to the new value only if it changed (avoid iframe/image reload etc.)
|
|
1063
1152
|
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
1064
1153
|
const parent = node.ownerElement;
|
|
1065
1154
|
if (parent.getAttribute(node.nodeName) !== replaced) {
|
|
@@ -1070,9 +1159,7 @@ export class FxFore extends HTMLElement {
|
|
|
1070
1159
|
node.textContent = replaced;
|
|
1071
1160
|
}
|
|
1072
1161
|
}
|
|
1073
|
-
}
|
|
1074
|
-
|
|
1075
|
-
// eslint-disable-next-line class-methods-use-this
|
|
1162
|
+
} // eslint-disable-next-line class-methods-use-this
|
|
1076
1163
|
_getTemplateExpression(node) {
|
|
1077
1164
|
if (this.ignoredNodes) {
|
|
1078
1165
|
if (node.nodeType === Node.ATTRIBUTE_NODE) {
|
|
@@ -1351,7 +1438,7 @@ export class FxFore extends HTMLElement {
|
|
|
1351
1438
|
const lastMatchingSibling = nodeset.reverse().find(node => parentElement.contains(node));
|
|
1352
1439
|
if (lastMatchingSibling) {
|
|
1353
1440
|
return lastMatchingSibling;
|
|
1354
|
-
|
|
1441
|
+
}
|
|
1355
1442
|
// Otherwise, just default to appending... If this runs multiple times for multiple nodes
|
|
1356
1443
|
// it's unexpected to always prepend and get the order of children reversed from the UI.
|
|
1357
1444
|
|
|
@@ -1376,7 +1463,6 @@ export class FxFore extends HTMLElement {
|
|
|
1376
1463
|
// Insert after the previous control
|
|
1377
1464
|
return referenceNode;
|
|
1378
1465
|
}
|
|
1379
|
-
|
|
1380
1466
|
/**
|
|
1381
1467
|
* @param {HTMLElement} root The root of the data initialization. fx-repeat overrides this when it makes new repeat items
|
|
1382
1468
|
*
|
|
@@ -1649,16 +1735,23 @@ export class FxFore extends HTMLElement {
|
|
|
1649
1735
|
}
|
|
1650
1736
|
|
|
1651
1737
|
_logError(e) {
|
|
1738
|
+
// Prevent the error event from bubbling up and potentially triggering
|
|
1739
|
+
// parent error handlers that might call refresh() again
|
|
1652
1740
|
e.stopPropagation();
|
|
1741
|
+
e.stopImmediatePropagation(); // Added to stop other listeners on this element
|
|
1653
1742
|
e.preventDefault();
|
|
1654
1743
|
|
|
1655
1744
|
console.error('ERROR', e.detail.message);
|
|
1656
|
-
|
|
1657
|
-
if
|
|
1658
|
-
|
|
1659
|
-
|
|
1660
|
-
|
|
1661
|
-
|
|
1745
|
+
|
|
1746
|
+
// Guard the display logic: if showing the error causes another error,
|
|
1747
|
+
// we must break the cycle.
|
|
1748
|
+
if (this.strict && !this._isLogging) {
|
|
1749
|
+
this._isLogging = true;
|
|
1750
|
+
try {
|
|
1751
|
+
this._displayError(e);
|
|
1752
|
+
} finally {
|
|
1753
|
+
this._isLogging = false;
|
|
1754
|
+
}
|
|
1662
1755
|
}
|
|
1663
1756
|
}
|
|
1664
1757
|
|