@4site/engrid-scripts 0.22.10 → 0.22.12
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/a11y.d.ts +1 -0
- package/dist/a11y.js +26 -0
- package/dist/data-layer.js +9 -8
- package/dist/page-background.d.ts +58 -0
- package/dist/page-background.js +222 -35
- package/dist/version.d.ts +1 -1
- package/dist/version.js +1 -1
- package/package.json +2 -2
package/dist/a11y.d.ts
CHANGED
package/dist/a11y.js
CHANGED
|
@@ -8,6 +8,7 @@ export class A11y {
|
|
|
8
8
|
this.updateFrequencyLabel();
|
|
9
9
|
const ecardImages = document.querySelectorAll('.en__ecarditems__list img');
|
|
10
10
|
this.setAutoGeneratedAltTags(ecardImages);
|
|
11
|
+
this.manageErrorListAlertRole();
|
|
11
12
|
}
|
|
12
13
|
addGroupRole() {
|
|
13
14
|
// Add role="group" to all EN Radio fields
|
|
@@ -93,4 +94,29 @@ export class A11y {
|
|
|
93
94
|
}
|
|
94
95
|
});
|
|
95
96
|
}
|
|
97
|
+
manageErrorListAlertRole() {
|
|
98
|
+
const errorList = document.querySelector('ul.en__errorList');
|
|
99
|
+
if (!errorList)
|
|
100
|
+
return;
|
|
101
|
+
const hasErrorItems = () => Boolean(errorList.querySelector('li'));
|
|
102
|
+
const enableAlert = () => {
|
|
103
|
+
if (!errorList.hasAttribute('role')) {
|
|
104
|
+
errorList.setAttribute('role', 'alert');
|
|
105
|
+
}
|
|
106
|
+
};
|
|
107
|
+
const disableAlert = () => {
|
|
108
|
+
if (errorList.hasAttribute('role')) {
|
|
109
|
+
errorList.removeAttribute('role');
|
|
110
|
+
}
|
|
111
|
+
};
|
|
112
|
+
hasErrorItems() ? enableAlert() : disableAlert();
|
|
113
|
+
new MutationObserver(records => {
|
|
114
|
+
for (const record of records) {
|
|
115
|
+
if (record.type === 'childList') {
|
|
116
|
+
hasErrorItems() ? enableAlert() : disableAlert();
|
|
117
|
+
break;
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
}).observe(errorList, { childList: true });
|
|
121
|
+
}
|
|
96
122
|
}
|
package/dist/data-layer.js
CHANGED
|
@@ -103,12 +103,12 @@ export class DataLayer {
|
|
|
103
103
|
for (const property in pageJson) {
|
|
104
104
|
if (!Number.isNaN(pageJson[property])) {
|
|
105
105
|
dataLayerEvents.push(`EN_PAGEJSON_${property.toUpperCase()}-${pageJson[property]}`);
|
|
106
|
-
dataLayerData[`
|
|
106
|
+
dataLayerData[`EN_PAGEJSON_${property.toUpperCase()}`] =
|
|
107
107
|
pageJson[property];
|
|
108
108
|
}
|
|
109
109
|
else {
|
|
110
110
|
dataLayerEvents.push(`EN_PAGEJSON_${property.toUpperCase()}-${this.transformJSON(pageJson[property])}`);
|
|
111
|
-
dataLayerData[`
|
|
111
|
+
dataLayerData[`EN_PAGEJSON_${property.toUpperCase()}`] =
|
|
112
112
|
this.transformJSON(pageJson[property]);
|
|
113
113
|
}
|
|
114
114
|
dataLayerEvents.push("EN_PAGEJSON_" + property.toUpperCase());
|
|
@@ -116,13 +116,13 @@ export class DataLayer {
|
|
|
116
116
|
}
|
|
117
117
|
if (ENGrid.getPageCount() === ENGrid.getPageNumber()) {
|
|
118
118
|
dataLayerEvents.push("EN_SUBMISSION_SUCCESS_" + pageJson.pageType.toUpperCase());
|
|
119
|
-
dataLayerData[`
|
|
119
|
+
dataLayerData[`EN_SUBMISSION_SUCCESS_${pageJson.pageType.toUpperCase()}`] = "TRUE";
|
|
120
120
|
}
|
|
121
121
|
}
|
|
122
122
|
const urlParams = new URLSearchParams(window.location.search);
|
|
123
123
|
urlParams.forEach((value, key) => {
|
|
124
124
|
dataLayerEvents.push(`EN_URLPARAM_${key.toUpperCase()}-${this.transformJSON(value)}`);
|
|
125
|
-
dataLayerData[`
|
|
125
|
+
dataLayerData[`EN_URLPARAM_${key.toUpperCase()}`] =
|
|
126
126
|
this.transformJSON(value);
|
|
127
127
|
});
|
|
128
128
|
if (ENGrid.getPageType() === "DONATION") {
|
|
@@ -170,14 +170,15 @@ export class DataLayer {
|
|
|
170
170
|
else {
|
|
171
171
|
dataLayerEvents.push("EN_FASTFORMFILL_ALL_FAILURE");
|
|
172
172
|
}
|
|
173
|
-
// Push all collected events individually (GTM requirement)
|
|
174
|
-
dataLayerEvents.forEach((event) => {
|
|
175
|
-
this.dataLayer.push({ event });
|
|
176
|
-
});
|
|
177
173
|
// Push all collected variables at once
|
|
178
174
|
if (Object.keys(dataLayerData).length > 0) {
|
|
175
|
+
dataLayerData.event = "pageJsonVariablesReady";
|
|
179
176
|
this.dataLayer.push(dataLayerData);
|
|
180
177
|
}
|
|
178
|
+
// Push all collected events individually (GTM requirement)
|
|
179
|
+
dataLayerEvents.forEach((event) => {
|
|
180
|
+
this.dataLayer.push({ event });
|
|
181
|
+
});
|
|
181
182
|
this.attachEventListeners();
|
|
182
183
|
}
|
|
183
184
|
onSubmit() {
|
|
@@ -1,6 +1,64 @@
|
|
|
1
1
|
export declare class PageBackground {
|
|
2
2
|
private pageBackground;
|
|
3
|
+
private mutationObserver;
|
|
4
|
+
private logger;
|
|
3
5
|
constructor();
|
|
6
|
+
/**
|
|
7
|
+
* Initialize background image by finding and setting CSS custom property
|
|
8
|
+
*/
|
|
9
|
+
private initializeBackgroundImage;
|
|
10
|
+
/**
|
|
11
|
+
* Set the background image URL as a CSS custom property
|
|
12
|
+
*/
|
|
13
|
+
private setBackgroundImageUrl;
|
|
14
|
+
/**
|
|
15
|
+
* Processes attribution positioning for background images by moving positioning classes
|
|
16
|
+
* and data attributes from images to their parent column containers.
|
|
17
|
+
*
|
|
18
|
+
* This function handles two attribution patterns:
|
|
19
|
+
* 1. Class-based: <img class="attribution-bottomright" src="...">
|
|
20
|
+
* 2. Data attribute-based: <img data-background-position="bottomright" src="...">
|
|
21
|
+
*
|
|
22
|
+
* Examples:
|
|
23
|
+
*
|
|
24
|
+
* Class-based attribution:
|
|
25
|
+
* <img class="attribution-bottomright" src="background.jpg">
|
|
26
|
+
* → Moves "attribution-bottomright" class to parent .en__component--column
|
|
27
|
+
*
|
|
28
|
+
* Data attribute-based attribution:
|
|
29
|
+
* <img data-background-position="top" src="background.jpg">
|
|
30
|
+
* → Converts to "attribution-top" class and moves to parent .en__component--column
|
|
31
|
+
*
|
|
32
|
+
* Supported positioning values:
|
|
33
|
+
* - center
|
|
34
|
+
* - top, topcenter (these result in the same positioning)
|
|
35
|
+
* - right, rightcenter (these result in the same positioning)
|
|
36
|
+
* - bottom, bottomcenter (these result in the same positioning)
|
|
37
|
+
* - left, leftcenter (these result in the same positioning)
|
|
38
|
+
* - topright
|
|
39
|
+
* - bottomright
|
|
40
|
+
* - bottomleft
|
|
41
|
+
* - topleft
|
|
42
|
+
*/
|
|
43
|
+
private processAttributionPositioning;
|
|
44
|
+
/**
|
|
45
|
+
* Process attribution for a single image
|
|
46
|
+
*/
|
|
47
|
+
private processImageAttribution;
|
|
48
|
+
/**
|
|
49
|
+
* Handle class-based attribution positioning
|
|
50
|
+
*/
|
|
51
|
+
private handleClassBasedAttribution;
|
|
52
|
+
/**
|
|
53
|
+
* Handle data attribute-based attribution positioning
|
|
54
|
+
*/
|
|
55
|
+
private handleDataAttributeAttribution;
|
|
56
|
+
private setupMutationObserver;
|
|
57
|
+
reprocessAttributionPositioning(): void;
|
|
58
|
+
/**
|
|
59
|
+
* Clean up resources and observers
|
|
60
|
+
*/
|
|
61
|
+
destroy(): void;
|
|
4
62
|
private setDataAttributes;
|
|
5
63
|
private hasVideoBackground;
|
|
6
64
|
private hasImageBackground;
|
package/dist/page-background.js
CHANGED
|
@@ -1,55 +1,242 @@
|
|
|
1
|
-
import { ENGrid } from "
|
|
1
|
+
import { ENGrid, EngridLogger } from ".";
|
|
2
2
|
export class PageBackground {
|
|
3
3
|
constructor() {
|
|
4
4
|
// @TODO: Change page-backgroundImage to page-background
|
|
5
5
|
this.pageBackground = document.querySelector(".page-backgroundImage");
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
6
|
+
this.mutationObserver = null;
|
|
7
|
+
this.logger = new EngridLogger("PageBackground", "lightblue", "darkblue", "🖼️");
|
|
8
|
+
if (!this.pageBackground) {
|
|
9
|
+
this.logger.log("A background image set in the page was not found, any default image set in the theme on --engrid__page-backgroundImage_url will be used");
|
|
10
|
+
return;
|
|
11
|
+
}
|
|
12
|
+
this.initializeBackgroundImage();
|
|
13
|
+
this.setDataAttributes();
|
|
14
|
+
this.processAttributionPositioning();
|
|
15
|
+
this.setupMutationObserver();
|
|
16
|
+
}
|
|
17
|
+
/**
|
|
18
|
+
* Initialize background image by finding and setting CSS custom property
|
|
19
|
+
*/
|
|
20
|
+
initializeBackgroundImage() {
|
|
21
|
+
if (!this.pageBackground)
|
|
22
|
+
return;
|
|
23
|
+
const pageBackgroundImg = this.pageBackground.querySelector("img");
|
|
24
|
+
if (!pageBackgroundImg) {
|
|
25
|
+
this.logger.log("A background image set in the page was not found, any default image set in the theme on --engrid__page-backgroundImage_url will be used");
|
|
26
|
+
return;
|
|
27
|
+
}
|
|
28
|
+
const dataSrc = pageBackgroundImg.getAttribute("data-src");
|
|
29
|
+
const src = pageBackgroundImg.src;
|
|
30
|
+
if (dataSrc) {
|
|
31
|
+
this.setBackgroundImageUrl(dataSrc, "data-src");
|
|
32
|
+
}
|
|
33
|
+
else if (src) {
|
|
34
|
+
this.setBackgroundImageUrl(src, "src");
|
|
31
35
|
}
|
|
32
36
|
else {
|
|
33
|
-
|
|
34
|
-
|
|
37
|
+
this.logger.log("A background image set in the page was found but without a data-src or src value, no action taken", pageBackgroundImg);
|
|
38
|
+
}
|
|
39
|
+
}
|
|
40
|
+
/**
|
|
41
|
+
* Set the background image URL as a CSS custom property
|
|
42
|
+
*/
|
|
43
|
+
setBackgroundImageUrl(imageUrl, sourceType) {
|
|
44
|
+
if (!this.pageBackground || !imageUrl)
|
|
45
|
+
return;
|
|
46
|
+
try {
|
|
47
|
+
const cssUrl = `url('${imageUrl}')`;
|
|
48
|
+
this.pageBackground.style.setProperty("--engrid__page-backgroundImage_url", cssUrl);
|
|
49
|
+
this.logger.log(`A background image set in the page was found with a ${sourceType} value, setting it as --engrid__page-backgroundImage_url`, imageUrl);
|
|
50
|
+
}
|
|
51
|
+
catch (error) {
|
|
52
|
+
this.logger.error("Error setting background image URL:", error);
|
|
53
|
+
}
|
|
54
|
+
}
|
|
55
|
+
/**
|
|
56
|
+
* Processes attribution positioning for background images by moving positioning classes
|
|
57
|
+
* and data attributes from images to their parent column containers.
|
|
58
|
+
*
|
|
59
|
+
* This function handles two attribution patterns:
|
|
60
|
+
* 1. Class-based: <img class="attribution-bottomright" src="...">
|
|
61
|
+
* 2. Data attribute-based: <img data-background-position="bottomright" src="...">
|
|
62
|
+
*
|
|
63
|
+
* Examples:
|
|
64
|
+
*
|
|
65
|
+
* Class-based attribution:
|
|
66
|
+
* <img class="attribution-bottomright" src="background.jpg">
|
|
67
|
+
* → Moves "attribution-bottomright" class to parent .en__component--column
|
|
68
|
+
*
|
|
69
|
+
* Data attribute-based attribution:
|
|
70
|
+
* <img data-background-position="top" src="background.jpg">
|
|
71
|
+
* → Converts to "attribution-top" class and moves to parent .en__component--column
|
|
72
|
+
*
|
|
73
|
+
* Supported positioning values:
|
|
74
|
+
* - center
|
|
75
|
+
* - top, topcenter (these result in the same positioning)
|
|
76
|
+
* - right, rightcenter (these result in the same positioning)
|
|
77
|
+
* - bottom, bottomcenter (these result in the same positioning)
|
|
78
|
+
* - left, leftcenter (these result in the same positioning)
|
|
79
|
+
* - topright
|
|
80
|
+
* - bottomright
|
|
81
|
+
* - bottomleft
|
|
82
|
+
* - topleft
|
|
83
|
+
*/
|
|
84
|
+
processAttributionPositioning() {
|
|
85
|
+
if (!this.pageBackground) {
|
|
86
|
+
this.logger.log("No background section found for attribution positioning processing");
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
this.logger.log("Processing attribution positioning for background section:", this.pageBackground);
|
|
90
|
+
// Define all supported attribution positioning classes
|
|
91
|
+
const allowedClasses = [
|
|
92
|
+
"attribution-center",
|
|
93
|
+
"attribution-bottom",
|
|
94
|
+
"attribution-bottomcenter",
|
|
95
|
+
"attribution-bottomright",
|
|
96
|
+
"attribution-bottomleft",
|
|
97
|
+
"attribution-top",
|
|
98
|
+
"attribution-topcenter",
|
|
99
|
+
"attribution-topright",
|
|
100
|
+
"attribution-topleft",
|
|
101
|
+
"attribution-left",
|
|
102
|
+
"attribution-leftcenter",
|
|
103
|
+
"attribution-right",
|
|
104
|
+
"attribution-rightcenter",
|
|
105
|
+
];
|
|
106
|
+
try {
|
|
107
|
+
// Find all images in the background section (after any DOM transformations)
|
|
108
|
+
const images = this.pageBackground.querySelectorAll("img");
|
|
109
|
+
this.logger.log("Found images in background section:", images.length);
|
|
110
|
+
images.forEach((img) => {
|
|
111
|
+
this.processImageAttribution(img, allowedClasses);
|
|
112
|
+
});
|
|
113
|
+
}
|
|
114
|
+
catch (error) {
|
|
115
|
+
this.logger.error("Error processing attribution positioning:", error);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
/**
|
|
119
|
+
* Process attribution for a single image
|
|
120
|
+
*/
|
|
121
|
+
processImageAttribution(img, allowedClasses) {
|
|
122
|
+
// Pattern 1: Check for class-based attribution positioning
|
|
123
|
+
// Example: <img class="attribution-bottomright" src="...">
|
|
124
|
+
const matchedClass = allowedClasses.find((cls) => img.classList.contains(cls));
|
|
125
|
+
// Pattern 2: Check for data attribute-based attribution positioning
|
|
126
|
+
// Example: <img data-background-position="bottomright" src="...">
|
|
127
|
+
const dataPosition = img.getAttribute("data-background-position");
|
|
128
|
+
if (matchedClass) {
|
|
129
|
+
this.handleClassBasedAttribution(img, matchedClass);
|
|
130
|
+
}
|
|
131
|
+
else if (dataPosition) {
|
|
132
|
+
this.handleDataAttributeAttribution(img, dataPosition);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
/**
|
|
136
|
+
* Handle class-based attribution positioning
|
|
137
|
+
*/
|
|
138
|
+
handleClassBasedAttribution(img, matchedClass) {
|
|
139
|
+
this.logger.log("Found attribution class on image:", matchedClass, img);
|
|
140
|
+
const parentDiv = img.closest(".en__component--column");
|
|
141
|
+
if (parentDiv) {
|
|
142
|
+
// Move the class from image to parent column
|
|
143
|
+
img.classList.remove(matchedClass);
|
|
144
|
+
parentDiv.classList.add(matchedClass);
|
|
145
|
+
this.logger.log("Moved attribution class from image to parent column:", matchedClass, parentDiv);
|
|
146
|
+
}
|
|
147
|
+
else {
|
|
148
|
+
this.logger.log("No parent .en__component--column found for image:", img);
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
/**
|
|
152
|
+
* Handle data attribute-based attribution positioning
|
|
153
|
+
*/
|
|
154
|
+
handleDataAttributeAttribution(img, dataPosition) {
|
|
155
|
+
// Convert data attribute value to attribution class format
|
|
156
|
+
const attributionClass = `attribution-${dataPosition}`;
|
|
157
|
+
this.logger.log("Found data-background-position on image:", dataPosition, "->", attributionClass, img);
|
|
158
|
+
const parentDiv = img.closest(".en__component--column");
|
|
159
|
+
if (parentDiv) {
|
|
160
|
+
// Remove data attribute from image and add class to parent column
|
|
161
|
+
img.removeAttribute("data-background-position");
|
|
162
|
+
parentDiv.classList.add(attributionClass);
|
|
163
|
+
this.logger.log("Moved data-background-position from image to parent column as class:", attributionClass, parentDiv);
|
|
164
|
+
}
|
|
165
|
+
else {
|
|
166
|
+
this.logger.log("No parent .en__component--column found for image:", img);
|
|
167
|
+
}
|
|
168
|
+
}
|
|
169
|
+
setupMutationObserver() {
|
|
170
|
+
if (!this.pageBackground || !window.MutationObserver) {
|
|
171
|
+
if (!window.MutationObserver) {
|
|
172
|
+
this.logger.log("MutationObserver not supported in this browser");
|
|
173
|
+
}
|
|
174
|
+
return;
|
|
175
|
+
}
|
|
176
|
+
try {
|
|
177
|
+
this.mutationObserver = new MutationObserver((mutations) => {
|
|
178
|
+
let shouldReprocess = false;
|
|
179
|
+
mutations.forEach((mutation) => {
|
|
180
|
+
// Check if nodes were added or attributes changed
|
|
181
|
+
if (mutation.type === "childList" || mutation.type === "attributes") {
|
|
182
|
+
shouldReprocess = true;
|
|
183
|
+
}
|
|
184
|
+
});
|
|
185
|
+
if (shouldReprocess) {
|
|
186
|
+
this.logger.log("DOM changes detected in background section, reprocessing attribution classes");
|
|
187
|
+
// Use a small delay to ensure all changes are complete
|
|
188
|
+
setTimeout(() => {
|
|
189
|
+
this.processAttributionPositioning();
|
|
190
|
+
}, 100);
|
|
191
|
+
}
|
|
192
|
+
});
|
|
193
|
+
// Start observing the background section
|
|
194
|
+
this.mutationObserver.observe(this.pageBackground, {
|
|
195
|
+
childList: true,
|
|
196
|
+
subtree: true,
|
|
197
|
+
attributes: true,
|
|
198
|
+
attributeFilter: ["class"],
|
|
199
|
+
});
|
|
200
|
+
this.logger.log("MutationObserver set up for background section");
|
|
201
|
+
}
|
|
202
|
+
catch (error) {
|
|
203
|
+
this.logger.error("Error setting up MutationObserver:", error);
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
// Public method to manually trigger reprocessing
|
|
207
|
+
reprocessAttributionPositioning() {
|
|
208
|
+
this.logger.log("Manually reprocessing attribution positioning");
|
|
209
|
+
this.processAttributionPositioning();
|
|
210
|
+
}
|
|
211
|
+
/**
|
|
212
|
+
* Clean up resources and observers
|
|
213
|
+
*/
|
|
214
|
+
destroy() {
|
|
215
|
+
if (this.mutationObserver) {
|
|
216
|
+
this.mutationObserver.disconnect();
|
|
217
|
+
this.mutationObserver = null;
|
|
218
|
+
this.logger.log("MutationObserver disconnected");
|
|
35
219
|
}
|
|
36
|
-
this.setDataAttributes();
|
|
37
220
|
}
|
|
38
221
|
setDataAttributes() {
|
|
39
|
-
if (this.hasVideoBackground())
|
|
222
|
+
if (this.hasVideoBackground()) {
|
|
40
223
|
return ENGrid.setBodyData("page-background", "video");
|
|
41
|
-
|
|
224
|
+
}
|
|
225
|
+
if (this.hasImageBackground()) {
|
|
42
226
|
return ENGrid.setBodyData("page-background", "image");
|
|
227
|
+
}
|
|
43
228
|
return ENGrid.setBodyData("page-background", "empty");
|
|
44
229
|
}
|
|
45
230
|
hasVideoBackground() {
|
|
46
|
-
if (this.pageBackground) {
|
|
47
|
-
return
|
|
231
|
+
if (!this.pageBackground) {
|
|
232
|
+
return false;
|
|
48
233
|
}
|
|
234
|
+
return !!this.pageBackground.querySelector("video");
|
|
49
235
|
}
|
|
50
236
|
hasImageBackground() {
|
|
51
|
-
if (this.pageBackground) {
|
|
52
|
-
return
|
|
237
|
+
if (!this.pageBackground) {
|
|
238
|
+
return false;
|
|
53
239
|
}
|
|
240
|
+
return (!this.hasVideoBackground() && !!this.pageBackground.querySelector("img"));
|
|
54
241
|
}
|
|
55
242
|
}
|
package/dist/version.d.ts
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export declare const AppVersion = "0.22.
|
|
1
|
+
export declare const AppVersion = "0.22.12";
|
package/dist/version.js
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
export const AppVersion = "0.22.
|
|
1
|
+
export const AppVersion = "0.22.12";
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@4site/engrid-scripts",
|
|
3
|
-
"version": "0.22.
|
|
3
|
+
"version": "0.22.12",
|
|
4
4
|
"description": "Scripts for the ENGRID Project",
|
|
5
5
|
"main": "./dist/index.js",
|
|
6
6
|
"types": "./dist/index.d.ts",
|
|
@@ -25,7 +25,7 @@
|
|
|
25
25
|
"strongly-typed-events": "^2.0.9"
|
|
26
26
|
},
|
|
27
27
|
"homepage": "https://github.com/4site-interactive-studios/engrid#readme",
|
|
28
|
-
"gitHead": "
|
|
28
|
+
"gitHead": "eba6f194c3b3b70b2551c6842dc2af0eb2a9b3a0",
|
|
29
29
|
"devDependencies": {
|
|
30
30
|
"nodemon": "^2.0.20"
|
|
31
31
|
}
|