@wewear/virtual-try-on 1.4.8 → 1.4.10
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/components/index.d.ts +1 -1
- package/dist/components/loading-overlay.d.ts +0 -9
- package/dist/components/status-badge.d.ts +3 -0
- package/dist/constants.d.ts +5 -8
- package/dist/index.d.ts +0 -9
- package/dist/index.esm.js +389 -491
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +389 -491
- package/dist/index.js.map +1 -1
- package/dist/widget.d.ts +2 -41
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -4,62 +4,16 @@
|
|
|
4
4
|
(global = typeof globalThis !== 'undefined' ? globalThis : global || self, factory(global.WeWearVirtualTryOn = {}));
|
|
5
5
|
})(this, (function (exports) { 'use strict';
|
|
6
6
|
|
|
7
|
-
/** CSS class names for consistent styling */
|
|
8
7
|
const CSS_CLASSES = {
|
|
9
8
|
BUTTON_CONTAINER: "wewear-vto-button-container",
|
|
10
9
|
BUTTON: "wewear-vto-button",
|
|
11
10
|
MODAL: "wewear-vto-modal",
|
|
12
|
-
PREVIEW_BADGE: "ww-vto-preview-badge",
|
|
13
11
|
};
|
|
14
|
-
/** Z-index values for proper layering */
|
|
15
12
|
const Z_INDEX = {
|
|
16
13
|
BUTTON: 10,
|
|
17
14
|
MODAL: 99999,
|
|
18
15
|
};
|
|
19
16
|
|
|
20
|
-
function createPreviewBadge() {
|
|
21
|
-
const badge = document.createElement("div");
|
|
22
|
-
badge.className = CSS_CLASSES.PREVIEW_BADGE;
|
|
23
|
-
badge.innerText = "Enhancing result...";
|
|
24
|
-
const styleId = "ww-vto-badge-styles";
|
|
25
|
-
if (!document.getElementById(styleId)) {
|
|
26
|
-
const styles = document.createElement("style");
|
|
27
|
-
styles.id = styleId;
|
|
28
|
-
styles.innerHTML = `
|
|
29
|
-
.${CSS_CLASSES.PREVIEW_BADGE} {
|
|
30
|
-
position: absolute;
|
|
31
|
-
top: 20px;
|
|
32
|
-
right: 20px;
|
|
33
|
-
background: rgba(31, 41, 55, 0.9);
|
|
34
|
-
backdrop-filter: blur(4px);
|
|
35
|
-
-webkit-backdrop-filter: blur(4px);
|
|
36
|
-
color: white;
|
|
37
|
-
padding: 6px 14px;
|
|
38
|
-
border-radius: 6px;
|
|
39
|
-
font-size: 12px;
|
|
40
|
-
font-weight: 600;
|
|
41
|
-
letter-spacing: 0.5px;
|
|
42
|
-
z-index: 10;
|
|
43
|
-
pointer-events: none;
|
|
44
|
-
font-family: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, Helvetica, Arial, sans-serif;
|
|
45
|
-
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.15);
|
|
46
|
-
animation: ww-vto-pulse 2s ease-in-out infinite;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
@keyframes ww-vto-pulse {
|
|
50
|
-
0%, 100% {
|
|
51
|
-
opacity: 0.9;
|
|
52
|
-
}
|
|
53
|
-
50% {
|
|
54
|
-
opacity: 1;
|
|
55
|
-
}
|
|
56
|
-
}
|
|
57
|
-
`;
|
|
58
|
-
document.head.appendChild(styles);
|
|
59
|
-
}
|
|
60
|
-
return badge;
|
|
61
|
-
}
|
|
62
|
-
|
|
63
17
|
function getPositionStyles(position) {
|
|
64
18
|
switch (position) {
|
|
65
19
|
case "bottom-left":
|
|
@@ -83,117 +37,135 @@
|
|
|
83
37
|
const container = document.createElement("div");
|
|
84
38
|
container.className = `${CSS_CLASSES.BUTTON_CONTAINER} ww-button-group`;
|
|
85
39
|
const positionStyles = getPositionStyles(buttonPosition);
|
|
86
|
-
container.style.cssText = `
|
|
87
|
-
position: absolute;
|
|
88
|
-
${positionStyles}
|
|
89
|
-
display: flex;
|
|
90
|
-
gap: 4px;
|
|
91
|
-
border-radius: 100px;
|
|
92
|
-
background: white;
|
|
93
|
-
padding: 4px;
|
|
94
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
95
|
-
z-index: ${Z_INDEX.BUTTON};
|
|
96
|
-
transition: all 0.2s ease;
|
|
40
|
+
container.style.cssText = `
|
|
41
|
+
position: absolute;
|
|
42
|
+
${positionStyles}
|
|
43
|
+
display: flex;
|
|
44
|
+
gap: 4px;
|
|
45
|
+
border-radius: 100px;
|
|
46
|
+
background: white;
|
|
47
|
+
padding: 4px;
|
|
48
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
49
|
+
z-index: ${Z_INDEX.BUTTON};
|
|
50
|
+
transition: all 0.2s ease;
|
|
97
51
|
`;
|
|
98
|
-
// Camera button
|
|
99
52
|
const cameraButton = document.createElement("button");
|
|
100
53
|
cameraButton.type = "button";
|
|
101
54
|
cameraButton.className = `${CSS_CLASSES.BUTTON} ww-camera-btn`;
|
|
102
55
|
cameraButton.setAttribute("aria-label", "Virtual Try-On");
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
white-space: nowrap;
|
|
56
|
+
if (hasVirtualTryOn) {
|
|
57
|
+
cameraButton.style.cssText = `
|
|
58
|
+
display: flex;
|
|
59
|
+
width: 40px;
|
|
60
|
+
height: 40px;
|
|
61
|
+
cursor: pointer;
|
|
62
|
+
align-items: center;
|
|
63
|
+
justify-content: center;
|
|
64
|
+
border-radius: 100px;
|
|
65
|
+
padding: 0;
|
|
66
|
+
border: none;
|
|
67
|
+
background: white;
|
|
68
|
+
color: #000000;
|
|
69
|
+
transition: all 0.2s ease;
|
|
70
|
+
flex-shrink: 0;
|
|
119
71
|
`;
|
|
120
|
-
|
|
121
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;
|
|
122
|
-
<path d="
|
|
123
|
-
<
|
|
124
|
-
|
|
125
|
-
<circle cx="4" cy="20" r="2"/>
|
|
126
|
-
</svg>
|
|
127
|
-
<span style="display: block;">TRY THIS LOOK</span>
|
|
72
|
+
cameraButton.innerHTML = `
|
|
73
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;">
|
|
74
|
+
<path d="M14.5 4h-5L7 7H4a2 2 0 0 0-2 2v9a2 2 0 0 0 2 2h16a2 2 0 0 0 2-2V9a2 2 0 0 0-2-2h-3l-2.5-3z"></path>
|
|
75
|
+
<circle cx="12" cy="13" r="3"></circle>
|
|
76
|
+
</svg>
|
|
128
77
|
`;
|
|
78
|
+
}
|
|
79
|
+
else {
|
|
80
|
+
cameraButton.style.cssText = `
|
|
81
|
+
display: flex;
|
|
82
|
+
cursor: pointer;
|
|
83
|
+
align-items: center;
|
|
84
|
+
justify-content: center;
|
|
85
|
+
border-radius: 100px;
|
|
86
|
+
padding: 10px 16px;
|
|
87
|
+
border: none;
|
|
88
|
+
background: white;
|
|
89
|
+
color: #000000;
|
|
90
|
+
transition: all 0.2s ease;
|
|
91
|
+
flex-shrink: 0;
|
|
92
|
+
gap: 8px;
|
|
93
|
+
font-size: 14px;
|
|
94
|
+
font-weight: 600;
|
|
95
|
+
white-space: nowrap;
|
|
96
|
+
`;
|
|
97
|
+
cameraButton.innerHTML = `
|
|
98
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block; flex-shrink: 0;">
|
|
99
|
+
<path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/>
|
|
100
|
+
<path d="M20 2v4"/>
|
|
101
|
+
<path d="M22 4h-4"/>
|
|
102
|
+
<circle cx="4" cy="20" r="2"/>
|
|
103
|
+
</svg>
|
|
104
|
+
<span style="display: block;">TRY THIS LOOK</span>
|
|
105
|
+
`;
|
|
106
|
+
}
|
|
129
107
|
cameraButton.onclick = onCameraClick;
|
|
130
108
|
container.appendChild(cameraButton);
|
|
131
|
-
// Add refresh and toggle buttons if we have a virtual try-on
|
|
132
109
|
if (hasVirtualTryOn) {
|
|
133
|
-
// Refresh button - calls API again
|
|
134
110
|
if (onRefreshClick) {
|
|
135
111
|
const refreshButton = document.createElement("button");
|
|
136
112
|
refreshButton.type = "button";
|
|
137
113
|
refreshButton.className = "ww-refresh-btn";
|
|
138
114
|
refreshButton.setAttribute("aria-label", "Refresh virtual try-on");
|
|
139
|
-
refreshButton.style.cssText = `
|
|
140
|
-
display: flex;
|
|
141
|
-
width: 40px;
|
|
142
|
-
height: 40px;
|
|
143
|
-
cursor: pointer;
|
|
144
|
-
align-items: center;
|
|
145
|
-
justify-content: center;
|
|
146
|
-
border-radius: 100px;
|
|
147
|
-
padding: 0;
|
|
148
|
-
border: none;
|
|
149
|
-
background: white;
|
|
150
|
-
color: #333333;
|
|
151
|
-
transition: all 0.2s ease;
|
|
152
|
-
flex-shrink: 0;
|
|
115
|
+
refreshButton.style.cssText = `
|
|
116
|
+
display: flex;
|
|
117
|
+
width: 40px;
|
|
118
|
+
height: 40px;
|
|
119
|
+
cursor: pointer;
|
|
120
|
+
align-items: center;
|
|
121
|
+
justify-content: center;
|
|
122
|
+
border-radius: 100px;
|
|
123
|
+
padding: 0;
|
|
124
|
+
border: none;
|
|
125
|
+
background: white;
|
|
126
|
+
color: #333333;
|
|
127
|
+
transition: all 0.2s ease;
|
|
128
|
+
flex-shrink: 0;
|
|
153
129
|
`;
|
|
154
|
-
refreshButton.innerHTML = `
|
|
155
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;">
|
|
156
|
-
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
|
|
157
|
-
<path d="M3 3v5h5"></path>
|
|
158
|
-
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"></path>
|
|
159
|
-
<path d="M16 16h5v5"></path>
|
|
160
|
-
</svg>
|
|
130
|
+
refreshButton.innerHTML = `
|
|
131
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;">
|
|
132
|
+
<path d="M21 12a9 9 0 0 0-9-9 9.75 9.75 0 0 0-6.74 2.74L3 8"></path>
|
|
133
|
+
<path d="M3 3v5h5"></path>
|
|
134
|
+
<path d="M3 12a9 9 0 0 0 9 9 9.75 9.75 0 0 0 6.74-2.74L21 16"></path>
|
|
135
|
+
<path d="M16 16h5v5"></path>
|
|
136
|
+
</svg>
|
|
161
137
|
`;
|
|
162
138
|
refreshButton.onclick = onRefreshClick;
|
|
163
139
|
refreshButton.onclick = onRefreshClick;
|
|
164
140
|
container.appendChild(refreshButton);
|
|
165
141
|
}
|
|
166
|
-
// Toggle button (scan-face) - switches between original and virtual try-on
|
|
167
142
|
if (onToggleClick) {
|
|
168
143
|
const toggleButton = document.createElement("button");
|
|
169
144
|
toggleButton.type = "button";
|
|
170
145
|
toggleButton.className = "ww-toggle-btn";
|
|
171
146
|
toggleButton.setAttribute("aria-label", isShowingVirtualTryOn ? "Show Original Image" : "Show Virtual Try-On");
|
|
172
|
-
toggleButton.style.cssText = `
|
|
173
|
-
display: flex;
|
|
174
|
-
width: 40px;
|
|
175
|
-
height: 40px;
|
|
176
|
-
cursor: pointer;
|
|
177
|
-
align-items: center;
|
|
178
|
-
justify-content: center;
|
|
179
|
-
border-radius: 100px;
|
|
180
|
-
padding: 0;
|
|
181
|
-
border: none;
|
|
182
|
-
background: ${isShowingVirtualTryOn ? "#333333" : "#e5e7eb"};
|
|
183
|
-
color: ${isShowingVirtualTryOn ? "white" : "#333333"};
|
|
184
|
-
transition: all 0.2s ease;
|
|
185
|
-
flex-shrink: 0;
|
|
147
|
+
toggleButton.style.cssText = `
|
|
148
|
+
display: flex;
|
|
149
|
+
width: 40px;
|
|
150
|
+
height: 40px;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
align-items: center;
|
|
153
|
+
justify-content: center;
|
|
154
|
+
border-radius: 100px;
|
|
155
|
+
padding: 0;
|
|
156
|
+
border: none;
|
|
157
|
+
background: ${isShowingVirtualTryOn ? "#333333" : "#e5e7eb"};
|
|
158
|
+
color: ${isShowingVirtualTryOn ? "white" : "#333333"};
|
|
159
|
+
transition: all 0.2s ease;
|
|
160
|
+
flex-shrink: 0;
|
|
186
161
|
`;
|
|
187
|
-
toggleButton.innerHTML = `
|
|
188
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;">
|
|
189
|
-
<path d="
|
|
190
|
-
<path d="
|
|
191
|
-
<path d="
|
|
192
|
-
<
|
|
193
|
-
|
|
194
|
-
<path d="M9 9h.01"></path>
|
|
195
|
-
<path d="M15 9h.01"></path>
|
|
196
|
-
</svg>
|
|
162
|
+
toggleButton.innerHTML = `
|
|
163
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round" style="display: block;">
|
|
164
|
+
<path d="M11.017 2.814a1 1 0 0 1 1.966 0l1.051 5.558a2 2 0 0 0 1.594 1.594l5.558 1.051a1 1 0 0 1 0 1.966l-5.558 1.051a2 2 0 0 0-1.594 1.594l-1.051 5.558a1 1 0 0 1-1.966 0l-1.051-5.558a2 2 0 0 0-1.594-1.594l-5.558-1.051a1 1 0 0 1 0-1.966l5.558-1.051a2 2 0 0 0 1.594-1.594z"/>
|
|
165
|
+
<path d="M20 2v4"/>
|
|
166
|
+
<path d="M22 4h-4"/>
|
|
167
|
+
<circle cx="4" cy="20" r="2"/>
|
|
168
|
+
</svg>
|
|
197
169
|
`;
|
|
198
170
|
toggleButton.onmouseover = () => {
|
|
199
171
|
if (isShowingVirtualTryOn) {
|
|
@@ -231,114 +203,112 @@
|
|
|
231
203
|
return container;
|
|
232
204
|
}
|
|
233
205
|
|
|
234
|
-
/**
|
|
235
|
-
* Creates a loading overlay that can be shown in different containers
|
|
236
|
-
*/
|
|
237
206
|
function createLoadingOverlay(text = "Processing...") {
|
|
238
207
|
const overlay = document.createElement("div");
|
|
239
208
|
overlay.className = "ww-loading-overlay";
|
|
240
|
-
overlay.style.cssText = `
|
|
241
|
-
position: absolute;
|
|
242
|
-
top: 0;
|
|
243
|
-
left: 0;
|
|
244
|
-
width: 100%;
|
|
245
|
-
height: 100%;
|
|
246
|
-
background: linear-gradient(135deg, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.88) 100%);
|
|
247
|
-
backdrop-filter: blur(8px);
|
|
248
|
-
-webkit-backdrop-filter: blur(8px);
|
|
249
|
-
display: flex;
|
|
250
|
-
flex-direction: column;
|
|
251
|
-
align-items: center;
|
|
252
|
-
justify-content: center;
|
|
253
|
-
z-index: ${Z_INDEX.MODAL + 1};
|
|
254
|
-
border-radius: inherit;
|
|
255
|
-
animation: ww-fade-in 0.3s ease;
|
|
209
|
+
overlay.style.cssText = `
|
|
210
|
+
position: absolute;
|
|
211
|
+
top: 0;
|
|
212
|
+
left: 0;
|
|
213
|
+
width: 100%;
|
|
214
|
+
height: 100%;
|
|
215
|
+
background: linear-gradient(135deg, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.88) 100%);
|
|
216
|
+
backdrop-filter: blur(8px);
|
|
217
|
+
-webkit-backdrop-filter: blur(8px);
|
|
218
|
+
display: flex;
|
|
219
|
+
flex-direction: column;
|
|
220
|
+
align-items: center;
|
|
221
|
+
justify-content: center;
|
|
222
|
+
z-index: ${Z_INDEX.MODAL + 1};
|
|
223
|
+
border-radius: inherit;
|
|
224
|
+
animation: ww-fade-in 0.3s ease;
|
|
256
225
|
`;
|
|
257
|
-
// Add CSS animation to the document if not already added
|
|
258
226
|
if (!document.getElementById("ww-loading-animation")) {
|
|
259
227
|
const style = document.createElement("style");
|
|
260
228
|
style.id = "ww-loading-animation";
|
|
261
|
-
style.textContent = `
|
|
262
|
-
@keyframes ww-fade-in {
|
|
263
|
-
from { opacity: 0; }
|
|
264
|
-
to { opacity: 1; }
|
|
265
|
-
}
|
|
266
|
-
|
|
267
|
-
@keyframes ww-pulse-logo {
|
|
268
|
-
0%, 100% {
|
|
269
|
-
opacity: 0.8;
|
|
270
|
-
transform: scale(1);
|
|
271
|
-
}
|
|
272
|
-
50% {
|
|
273
|
-
opacity: 1;
|
|
274
|
-
transform: scale(1.05);
|
|
275
|
-
}
|
|
276
|
-
}
|
|
277
|
-
|
|
278
|
-
@keyframes ww-spinner-rotate {
|
|
279
|
-
0% { transform: rotate(0deg); }
|
|
280
|
-
100% { transform: rotate(360deg); }
|
|
281
|
-
}
|
|
282
|
-
|
|
283
|
-
@keyframes ww-dot-bounce {
|
|
284
|
-
0%, 80%, 100% {
|
|
285
|
-
transform: scale(0);
|
|
286
|
-
opacity: 0.5;
|
|
287
|
-
}
|
|
288
|
-
40% {
|
|
289
|
-
transform: scale(1);
|
|
290
|
-
opacity: 1;
|
|
291
|
-
}
|
|
292
|
-
}
|
|
293
|
-
|
|
294
|
-
.ww-loading-logo {
|
|
295
|
-
animation: ww-pulse-logo 2s ease-in-out infinite;
|
|
296
|
-
}
|
|
297
|
-
|
|
298
|
-
.ww-loading-spinner {
|
|
299
|
-
animation: ww-spinner-rotate 1s linear infinite;
|
|
300
|
-
}
|
|
229
|
+
style.textContent = `
|
|
230
|
+
@keyframes ww-fade-in {
|
|
231
|
+
from { opacity: 0; }
|
|
232
|
+
to { opacity: 1; }
|
|
233
|
+
}
|
|
234
|
+
|
|
235
|
+
@keyframes ww-pulse-logo {
|
|
236
|
+
0%, 100% {
|
|
237
|
+
opacity: 0.8;
|
|
238
|
+
transform: scale(1);
|
|
239
|
+
}
|
|
240
|
+
50% {
|
|
241
|
+
opacity: 1;
|
|
242
|
+
transform: scale(1.05);
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
|
|
246
|
+
@keyframes ww-spinner-rotate {
|
|
247
|
+
0% { transform: rotate(0deg); }
|
|
248
|
+
100% { transform: rotate(360deg); }
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
@keyframes ww-dot-bounce {
|
|
252
|
+
0%, 80%, 100% {
|
|
253
|
+
transform: scale(0);
|
|
254
|
+
opacity: 0.5;
|
|
255
|
+
}
|
|
256
|
+
40% {
|
|
257
|
+
transform: scale(1);
|
|
258
|
+
opacity: 1;
|
|
259
|
+
}
|
|
260
|
+
}
|
|
261
|
+
|
|
262
|
+
.ww-loading-logo {
|
|
263
|
+
animation: ww-pulse-logo 2s ease-in-out infinite;
|
|
264
|
+
}
|
|
265
|
+
|
|
266
|
+
.ww-loading-spinner {
|
|
267
|
+
animation: ww-spinner-rotate 1s linear infinite;
|
|
268
|
+
}
|
|
301
269
|
`;
|
|
302
270
|
document.head.appendChild(style);
|
|
303
271
|
}
|
|
304
|
-
overlay.innerHTML = `
|
|
305
|
-
<div style="display: flex; flex-direction: column; align-items: center; gap: 32px;">
|
|
306
|
-
<div style="position: relative; width: 120px; height: 120px; display: flex; align-items: center; justify-content: center;">
|
|
307
|
-
<!-- Outer spinning circle -->
|
|
308
|
-
<div class="ww-loading-spinner" style="
|
|
309
|
-
position: absolute;
|
|
310
|
-
width: 120px;
|
|
311
|
-
height: 120px;
|
|
312
|
-
border: 3px solid transparent;
|
|
313
|
-
border-top: 3px solid #333333;
|
|
314
|
-
border-right: 3px solid #333333;
|
|
315
|
-
border-radius: 50%;
|
|
316
|
-
"></div>
|
|
317
|
-
|
|
318
|
-
<!-- Logo -->
|
|
319
|
-
<svg class="ww-loading-logo" width="80" height="50" viewBox="0 0 214 135" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
320
|
-
<path d="M102.906 74.8679C102.906 77.9717 101.574 80.7453 98.9104 83.1887C96.6871 85.1918 93.9025 86.6997 90.5566 87.7123C87.695 88.5708 84.6462 89 81.4104 89C73.8821 89 68.0047 87.0189 63.7783 83.0566C59.5519 87.0189 53.6855 89 46.1792 89C42.9434 89 39.9057 88.5708 37.066 87.7123C33.7201 86.6997 30.9245 85.1918 28.6792 83.1887C26.0157 80.7453 24.684 77.9717 24.684 74.8679V41.6509H32.3774V74.8679C32.3774 76.2547 33.489 77.5645 35.7123 78.7972C37.3632 79.7217 39.0692 80.3711 40.8302 80.7453C42.5252 81.1195 44.3082 81.3066 46.1792 81.3066C48.0063 81.3066 49.7673 81.1195 51.4623 80.7453C53.2453 80.3711 54.9623 79.7217 56.6132 78.7972C58.8585 77.5645 59.9811 76.2547 59.9811 74.8679V41.6509H67.6085V74.8679C67.6085 76.2547 68.7311 77.5645 70.9764 78.7972C72.6274 79.7217 74.3443 80.3711 76.1274 80.7453C77.8223 81.1195 79.5833 81.3066 81.4104 81.3066C83.2814 81.3066 85.0755 81.1195 86.7925 80.7453C88.5314 80.3711 90.2264 79.7217 91.8774 78.7972C94.1006 77.5645 95.2123 76.2547 95.2123 74.8679V41.6509H102.906V74.8679ZM189.283 74.8679C189.283 77.9717 187.951 80.7453 185.288 83.1887C183.064 85.1918 180.28 86.6997 176.934 87.7123C174.072 88.5708 171.024 89 167.788 89C160.259 89 154.382 87.0189 150.156 83.0566C145.929 87.0189 140.063 89 132.557 89C129.321 89 126.283 88.5708 123.443 87.7123C120.097 86.6997 117.302 85.1918 115.057 83.1887C112.393 80.7453 111.061 77.9717 111.061 74.8679V41.6509H118.755V74.8679C118.755 76.2547 119.866 77.5645 122.09 78.7972C123.741 79.7217 125.447 80.3711 127.208 80.7453C128.903 81.1195 130.686 81.3066 132.557 81.3066C134.384 81.3066 136.145 81.1195 137.84 80.7453C139.623 80.3711 141.34 79.7217 142.991 78.7972C145.236 77.5645 146.358 76.2547 146.358 74.8679V41.6509H153.986V74.8679C153.986 76.2547 155.108 77.5645 157.354 78.7972C159.005 79.7217 160.722 80.3711 162.505 80.7453C164.2 81.1195 165.961 81.3066 167.788 81.3066C169.659 81.3066 171.453 81.1195 173.17 80.7453C174.909 80.3711 176.604 79.7217 178.255 78.7972C180.478 77.5645 181.59 76.2547 181.59 74.8679V41.6509H189.283V74.8679Z" fill="white"/>
|
|
321
|
-
</svg>
|
|
322
|
-
</div>
|
|
323
|
-
|
|
324
|
-
<div style="font-size: 17px; font-weight: 500; text-align: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: white; letter-spacing: 0.5px; max-width: 280px;">${text}</div>
|
|
325
|
-
|
|
326
|
-
<div style="display: flex; gap: 10px; margin-top: 4px;">
|
|
327
|
-
<div style="width: 10px; height: 10px; border-radius: 50%; background: #333333; animation: ww-dot-bounce 1.4s ease-in-out infinite;"></div>
|
|
328
|
-
<div style="width: 10px; height: 10px; border-radius: 50%; background: #333333; animation: ww-dot-bounce 1.4s ease-in-out 0.2s infinite;"></div>
|
|
329
|
-
<div style="width: 10px; height: 10px; border-radius: 50%; background: #333333; animation: ww-dot-bounce 1.4s ease-in-out 0.4s infinite;"></div>
|
|
330
|
-
</div>
|
|
331
|
-
</div>
|
|
272
|
+
overlay.innerHTML = `
|
|
273
|
+
<div style="display: flex; flex-direction: column; align-items: center; gap: 32px;">
|
|
274
|
+
<div style="position: relative; width: 120px; height: 120px; display: flex; align-items: center; justify-content: center;">
|
|
275
|
+
<!-- Outer spinning circle -->
|
|
276
|
+
<div class="ww-loading-spinner" style="
|
|
277
|
+
position: absolute;
|
|
278
|
+
width: 120px;
|
|
279
|
+
height: 120px;
|
|
280
|
+
border: 3px solid transparent;
|
|
281
|
+
border-top: 3px solid #333333;
|
|
282
|
+
border-right: 3px solid #333333;
|
|
283
|
+
border-radius: 50%;
|
|
284
|
+
"></div>
|
|
285
|
+
|
|
286
|
+
<!-- Logo -->
|
|
287
|
+
<svg class="ww-loading-logo" width="80" height="50" viewBox="0 0 214 135" fill="none" xmlns="http://www.w3.org/2000/svg">
|
|
288
|
+
<path d="M102.906 74.8679C102.906 77.9717 101.574 80.7453 98.9104 83.1887C96.6871 85.1918 93.9025 86.6997 90.5566 87.7123C87.695 88.5708 84.6462 89 81.4104 89C73.8821 89 68.0047 87.0189 63.7783 83.0566C59.5519 87.0189 53.6855 89 46.1792 89C42.9434 89 39.9057 88.5708 37.066 87.7123C33.7201 86.6997 30.9245 85.1918 28.6792 83.1887C26.0157 80.7453 24.684 77.9717 24.684 74.8679V41.6509H32.3774V74.8679C32.3774 76.2547 33.489 77.5645 35.7123 78.7972C37.3632 79.7217 39.0692 80.3711 40.8302 80.7453C42.5252 81.1195 44.3082 81.3066 46.1792 81.3066C48.0063 81.3066 49.7673 81.1195 51.4623 80.7453C53.2453 80.3711 54.9623 79.7217 56.6132 78.7972C58.8585 77.5645 59.9811 76.2547 59.9811 74.8679V41.6509H67.6085V74.8679C67.6085 76.2547 68.7311 77.5645 70.9764 78.7972C72.6274 79.7217 74.3443 80.3711 76.1274 80.7453C77.8223 81.1195 79.5833 81.3066 81.4104 81.3066C83.2814 81.3066 85.0755 81.1195 86.7925 80.7453C88.5314 80.3711 90.2264 79.7217 91.8774 78.7972C94.1006 77.5645 95.2123 76.2547 95.2123 74.8679V41.6509H102.906V74.8679ZM189.283 74.8679C189.283 77.9717 187.951 80.7453 185.288 83.1887C183.064 85.1918 180.28 86.6997 176.934 87.7123C174.072 88.5708 171.024 89 167.788 89C160.259 89 154.382 87.0189 150.156 83.0566C145.929 87.0189 140.063 89 132.557 89C129.321 89 126.283 88.5708 123.443 87.7123C120.097 86.6997 117.302 85.1918 115.057 83.1887C112.393 80.7453 111.061 77.9717 111.061 74.8679V41.6509H118.755V74.8679C118.755 76.2547 119.866 77.5645 122.09 78.7972C123.741 79.7217 125.447 80.3711 127.208 80.7453C128.903 81.1195 130.686 81.3066 132.557 81.3066C134.384 81.3066 136.145 81.1195 137.84 80.7453C139.623 80.3711 141.34 79.7217 142.991 78.7972C145.236 77.5645 146.358 76.2547 146.358 74.8679V41.6509H153.986V74.8679C153.986 76.2547 155.108 77.5645 157.354 78.7972C159.005 79.7217 160.722 80.3711 162.505 80.7453C164.2 81.1195 165.961 81.3066 167.788 81.3066C169.659 81.3066 171.453 81.1195 173.17 80.7453C174.909 80.3711 176.604 79.7217 178.255 78.7972C180.478 77.5645 181.59 76.2547 181.59 74.8679V41.6509H189.283V74.8679Z" fill="white"/>
|
|
289
|
+
</svg>
|
|
290
|
+
</div>
|
|
291
|
+
|
|
292
|
+
<div style="font-size: 17px; font-weight: 500; text-align: center; font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif; color: white; letter-spacing: 0.5px; max-width: 280px;">${text}</div>
|
|
293
|
+
|
|
294
|
+
<div style="display: flex; gap: 10px; margin-top: 4px;">
|
|
295
|
+
<div style="width: 10px; height: 10px; border-radius: 50%; background: #333333; animation: ww-dot-bounce 1.4s ease-in-out infinite;"></div>
|
|
296
|
+
<div style="width: 10px; height: 10px; border-radius: 50%; background: #333333; animation: ww-dot-bounce 1.4s ease-in-out 0.2s infinite;"></div>
|
|
297
|
+
<div style="width: 10px; height: 10px; border-radius: 50%; background: #333333; animation: ww-dot-bounce 1.4s ease-in-out 0.4s infinite;"></div>
|
|
298
|
+
</div>
|
|
299
|
+
</div>
|
|
332
300
|
`;
|
|
333
301
|
return overlay;
|
|
334
302
|
}
|
|
335
|
-
/**
|
|
336
|
-
* Shows a loading overlay in the product gallery container
|
|
337
|
-
*/
|
|
338
303
|
function showProductLoading(container, text = "Generating virtual try-on...") {
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
304
|
+
const existingOverlay = container.querySelector(".ww-loading-overlay");
|
|
305
|
+
if (existingOverlay) {
|
|
306
|
+
const textElement = existingOverlay.querySelector('div[style*="font-size: 17px"]');
|
|
307
|
+
if (textElement) {
|
|
308
|
+
textElement.textContent = text;
|
|
309
|
+
}
|
|
310
|
+
return;
|
|
311
|
+
}
|
|
342
312
|
const computedStyle = window.getComputedStyle(container);
|
|
343
313
|
if (computedStyle.position === "static") {
|
|
344
314
|
container.style.position = "relative";
|
|
@@ -346,9 +316,6 @@
|
|
|
346
316
|
const loadingOverlay = createLoadingOverlay(text);
|
|
347
317
|
container.appendChild(loadingOverlay);
|
|
348
318
|
}
|
|
349
|
-
/**
|
|
350
|
-
* Removes loading overlay from product container
|
|
351
|
-
*/
|
|
352
319
|
function removeProductLoading(container) {
|
|
353
320
|
const existingOverlay = container.querySelector(".ww-loading-overlay");
|
|
354
321
|
if (existingOverlay) {
|
|
@@ -356,99 +323,74 @@
|
|
|
356
323
|
}
|
|
357
324
|
}
|
|
358
325
|
|
|
359
|
-
function
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
;
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
padding: 0;
|
|
410
|
-
line-height: 1;
|
|
411
|
-
opacity: 0.7;
|
|
412
|
-
transition: opacity 0.2s ease;
|
|
413
|
-
width: 24px;
|
|
414
|
-
height: 24px;
|
|
415
|
-
display: flex;
|
|
416
|
-
align-items: center;
|
|
417
|
-
justify-content: center;
|
|
418
|
-
"
|
|
419
|
-
onmouseover="this.style.opacity='1'"
|
|
420
|
-
onmouseout="this.style.opacity='0.7'">
|
|
421
|
-
×
|
|
422
|
-
</button>
|
|
423
|
-
`;
|
|
424
|
-
// Close on button click
|
|
425
|
-
(_a = alertDiv.querySelector("button")) === null || _a === void 0 ? void 0 : _a.addEventListener("click", () => {
|
|
426
|
-
fadeOutAndRemove(alertDiv);
|
|
427
|
-
});
|
|
428
|
-
container.appendChild(alertDiv);
|
|
429
|
-
// Animate in
|
|
430
|
-
requestAnimationFrame(() => {
|
|
431
|
-
alertDiv.style.opacity = "1";
|
|
432
|
-
alertDiv.style.transform = "translateX(-50%) translateY(0)";
|
|
433
|
-
});
|
|
434
|
-
// Auto-dismiss after 8 seconds
|
|
435
|
-
setTimeout(() => {
|
|
436
|
-
if (alertDiv.parentElement) {
|
|
437
|
-
fadeOutAndRemove(alertDiv);
|
|
438
|
-
}
|
|
439
|
-
}, 8000);
|
|
326
|
+
function createStatusBadge(message) {
|
|
327
|
+
const badge = document.createElement("div");
|
|
328
|
+
badge.className = "ww-status-badge";
|
|
329
|
+
badge.style.cssText = `
|
|
330
|
+
position: absolute;
|
|
331
|
+
bottom: 16px;
|
|
332
|
+
left: 50%;
|
|
333
|
+
transform: translateX(-50%);
|
|
334
|
+
background: linear-gradient(135deg, rgba(0, 0, 0, 0.92) 0%, rgba(0, 0, 0, 0.88) 100%);
|
|
335
|
+
backdrop-filter: blur(12px);
|
|
336
|
+
-webkit-backdrop-filter: blur(12px);
|
|
337
|
+
color: white;
|
|
338
|
+
padding: 12px 24px;
|
|
339
|
+
border-radius: 24px;
|
|
340
|
+
font-size: 14px;
|
|
341
|
+
font-weight: 500;
|
|
342
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
343
|
+
box-shadow: 0 4px 16px rgba(0, 0, 0, 0.3), 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
344
|
+
z-index: 1000;
|
|
345
|
+
display: flex;
|
|
346
|
+
align-items: center;
|
|
347
|
+
gap: 10px;
|
|
348
|
+
animation: ww-badge-fade-in 0.3s ease;
|
|
349
|
+
letter-spacing: 0.3px;
|
|
350
|
+
`;
|
|
351
|
+
if (!document.getElementById("ww-status-badge-animation")) {
|
|
352
|
+
const style = document.createElement("style");
|
|
353
|
+
style.id = "ww-status-badge-animation";
|
|
354
|
+
style.textContent = `
|
|
355
|
+
@keyframes ww-badge-fade-in {
|
|
356
|
+
from { opacity: 0; transform: translateX(-50%) translateY(-10px); }
|
|
357
|
+
to { opacity: 1; transform: translateX(-50%) translateY(0); }
|
|
358
|
+
}
|
|
359
|
+
|
|
360
|
+
@keyframes ww-badge-pulse {
|
|
361
|
+
0%, 100% { opacity: 0.6; }
|
|
362
|
+
50% { opacity: 1; }
|
|
363
|
+
}
|
|
364
|
+
`;
|
|
365
|
+
document.head.appendChild(style);
|
|
366
|
+
}
|
|
367
|
+
badge.innerHTML = `
|
|
368
|
+
<div style="display: flex; gap: 4px;">
|
|
369
|
+
<div style="width: 6px; height: 6px; border-radius: 50%; background: white; animation: ww-badge-pulse 1.4s ease-in-out infinite;"></div>
|
|
370
|
+
<div style="width: 6px; height: 6px; border-radius: 50%; background: white; animation: ww-badge-pulse 1.4s ease-in-out 0.2s infinite;"></div>
|
|
371
|
+
<div style="width: 6px; height: 6px; border-radius: 50%; background: white; animation: ww-badge-pulse 1.4s ease-in-out 0.4s infinite;"></div>
|
|
372
|
+
</div>
|
|
373
|
+
<span>${message}</span>
|
|
374
|
+
`;
|
|
375
|
+
return badge;
|
|
440
376
|
}
|
|
441
|
-
function
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
377
|
+
function showStatusBadge(container, message) {
|
|
378
|
+
const existingBadge = container.querySelector(".ww-status-badge");
|
|
379
|
+
if (existingBadge) {
|
|
380
|
+
const textElement = existingBadge.querySelector("span");
|
|
381
|
+
if (textElement) {
|
|
382
|
+
textElement.textContent = message;
|
|
383
|
+
}
|
|
384
|
+
return;
|
|
385
|
+
}
|
|
386
|
+
const badge = createStatusBadge(message);
|
|
387
|
+
container.appendChild(badge);
|
|
447
388
|
}
|
|
448
|
-
function
|
|
449
|
-
const
|
|
450
|
-
if (
|
|
451
|
-
|
|
389
|
+
function removeStatusBadge(container) {
|
|
390
|
+
const badge = container.querySelector(".ww-status-badge");
|
|
391
|
+
if (badge) {
|
|
392
|
+
badge.remove();
|
|
393
|
+
}
|
|
452
394
|
}
|
|
453
395
|
|
|
454
396
|
class VirtualTryOnWidget {
|
|
@@ -460,7 +402,6 @@
|
|
|
460
402
|
this.lastModelImage = null;
|
|
461
403
|
this.cameraButton = null;
|
|
462
404
|
this.iframeMessageListener = null;
|
|
463
|
-
this.previewBadge = null;
|
|
464
405
|
this.config = {
|
|
465
406
|
baseUrl: config.baseUrl,
|
|
466
407
|
productPageSelector: config.productPageSelector,
|
|
@@ -469,51 +410,36 @@
|
|
|
469
410
|
buttonPosition: config.buttonPosition,
|
|
470
411
|
};
|
|
471
412
|
}
|
|
472
|
-
/**
|
|
473
|
-
* Initializes the virtual try-on widget on the current page
|
|
474
|
-
* @returns Promise that resolves when initialization is complete
|
|
475
|
-
*/
|
|
476
413
|
async init() {
|
|
477
414
|
try {
|
|
478
|
-
// Check if we're on a product page
|
|
479
415
|
if (!window.location.pathname.includes(this.config.productPageSelector)) {
|
|
480
416
|
return;
|
|
481
417
|
}
|
|
482
|
-
// Find the gallery container
|
|
483
418
|
const container = document.querySelector(this.config.gallerySelector);
|
|
484
419
|
if (!container || !(container instanceof HTMLElement)) {
|
|
485
420
|
console.warn("[WeWear VTO] Gallery container not found:", this.config.gallerySelector);
|
|
486
421
|
return;
|
|
487
422
|
}
|
|
488
|
-
// Ensure container has relative positioning for button placement
|
|
489
423
|
if (getComputedStyle(container).position === "static") {
|
|
490
424
|
container.style.position = "relative";
|
|
491
425
|
}
|
|
492
|
-
// Create and add the virtual try-on button
|
|
493
426
|
const button = createButton(this.config.buttonPosition, async () => {
|
|
494
427
|
if (this.cameraButton && !this.cameraButton.disabled) {
|
|
495
428
|
await this.handleTryOnClick();
|
|
496
429
|
}
|
|
497
430
|
});
|
|
498
|
-
// Store reference to camera button for dynamic enable/disable
|
|
499
431
|
this.cameraButton = button.querySelector(".ww-camera-btn");
|
|
500
432
|
container.appendChild(button);
|
|
501
433
|
console.log("[WeWear VTO] Widget initialized successfully");
|
|
502
|
-
// Listen for messages from the photo upload page
|
|
503
434
|
this.setupIframeListener();
|
|
504
435
|
}
|
|
505
436
|
catch (error) {
|
|
506
437
|
console.error("[WeWear VTO] Initialization failed:", error);
|
|
507
438
|
}
|
|
508
439
|
}
|
|
509
|
-
/**
|
|
510
|
-
* Handles virtual try-on button click
|
|
511
|
-
* @private
|
|
512
|
-
*/
|
|
513
440
|
async handleTryOnClick() {
|
|
514
441
|
console.log("[WeWear VTO] Button clicked, starting try-on process...");
|
|
515
442
|
try {
|
|
516
|
-
// Store original images on first click if not already stored
|
|
517
443
|
if (this.originalProductImages.length === 0) {
|
|
518
444
|
this.originalProductImages = this.getAllProductImages();
|
|
519
445
|
console.log("[WeWear VTO] Stored original product images:", this.originalProductImages);
|
|
@@ -525,13 +451,10 @@
|
|
|
525
451
|
console.warn("[WeWear VTO] Product images not found:", this.config.productImageSelector);
|
|
526
452
|
return;
|
|
527
453
|
}
|
|
528
|
-
// Store the first image as the original
|
|
529
454
|
if (!this.originalProductImageUrl) {
|
|
530
455
|
this.originalProductImageUrl = this.originalProductImages[0];
|
|
531
456
|
}
|
|
532
|
-
// Open the photo upload page in a modal
|
|
533
457
|
const photoUploadUrl = new URL(`${this.config.baseUrl}`);
|
|
534
|
-
// Pass all product images as a comma-separated string
|
|
535
458
|
photoUploadUrl.searchParams.append("ww_product_images", this.originalProductImages.join(","));
|
|
536
459
|
this.showPhotoUploadModal(photoUploadUrl.toString());
|
|
537
460
|
}
|
|
@@ -539,11 +462,6 @@
|
|
|
539
462
|
console.error("[WeWear VTO] Try-on request failed:", error);
|
|
540
463
|
}
|
|
541
464
|
}
|
|
542
|
-
/**
|
|
543
|
-
* Gets all product images from the gallery
|
|
544
|
-
* @private
|
|
545
|
-
* @returns Array of product image URLs
|
|
546
|
-
*/
|
|
547
465
|
getAllProductImages() {
|
|
548
466
|
const productImageElements = document.querySelectorAll(this.config.productImageSelector);
|
|
549
467
|
const images = [];
|
|
@@ -552,7 +470,6 @@
|
|
|
552
470
|
img.getAttribute("data-src") ||
|
|
553
471
|
img.getAttribute("data-large_image") ||
|
|
554
472
|
"";
|
|
555
|
-
// Only include http/https URLs, exclude blob URLs and data URLs
|
|
556
473
|
if (imageUrl &&
|
|
557
474
|
(imageUrl.startsWith("http://") || imageUrl.startsWith("https://")) &&
|
|
558
475
|
!images.includes(imageUrl)) {
|
|
@@ -561,92 +478,86 @@
|
|
|
561
478
|
});
|
|
562
479
|
return images;
|
|
563
480
|
}
|
|
564
|
-
/**
|
|
565
|
-
* Shows a modal with an iframe for the photo upload page
|
|
566
|
-
* @private
|
|
567
|
-
*/
|
|
568
481
|
showPhotoUploadModal(url) {
|
|
569
|
-
// Remove existing modals
|
|
570
482
|
removeElements(`.${CSS_CLASSES.MODAL}`);
|
|
571
483
|
const modal = document.createElement("div");
|
|
572
484
|
modal.className = CSS_CLASSES.MODAL;
|
|
573
|
-
modal.style.cssText = `
|
|
574
|
-
position: fixed;
|
|
575
|
-
top: 0;
|
|
576
|
-
left: 0;
|
|
577
|
-
right: 0;
|
|
578
|
-
bottom: 0;
|
|
579
|
-
width: 100%;
|
|
580
|
-
height: 100%;
|
|
581
|
-
background-color: rgba(0, 0, 0, 0.8);
|
|
582
|
-
display: flex;
|
|
583
|
-
justify-content: center;
|
|
584
|
-
align-items: center;
|
|
585
|
-
z-index: 999999999;
|
|
586
|
-
animation: ww-modal-fade-in 0.3s ease;
|
|
485
|
+
modal.style.cssText = `
|
|
486
|
+
position: fixed;
|
|
487
|
+
top: 0;
|
|
488
|
+
left: 0;
|
|
489
|
+
right: 0;
|
|
490
|
+
bottom: 0;
|
|
491
|
+
width: 100%;
|
|
492
|
+
height: 100%;
|
|
493
|
+
background-color: rgba(0, 0, 0, 0.8);
|
|
494
|
+
display: flex;
|
|
495
|
+
justify-content: center;
|
|
496
|
+
align-items: center;
|
|
497
|
+
z-index: 999999999;
|
|
498
|
+
animation: ww-modal-fade-in 0.3s ease;
|
|
587
499
|
`;
|
|
588
|
-
// Add fade-in animation
|
|
589
500
|
if (!document.getElementById("ww-modal-animation")) {
|
|
590
501
|
const style = document.createElement("style");
|
|
591
502
|
style.id = "ww-modal-animation";
|
|
592
|
-
style.textContent = `
|
|
593
|
-
@keyframes ww-modal-fade-in {
|
|
594
|
-
from { opacity: 0; }
|
|
595
|
-
to { opacity: 1; }
|
|
596
|
-
}
|
|
597
|
-
@keyframes ww-modal-scale-in {
|
|
598
|
-
from { transform: scale(0.95); opacity: 0; }
|
|
599
|
-
to { transform: scale(1); opacity: 1; }
|
|
600
|
-
}
|
|
503
|
+
style.textContent = `
|
|
504
|
+
@keyframes ww-modal-fade-in {
|
|
505
|
+
from { opacity: 0; }
|
|
506
|
+
to { opacity: 1; }
|
|
507
|
+
}
|
|
508
|
+
@keyframes ww-modal-scale-in {
|
|
509
|
+
from { transform: scale(0.95); opacity: 0; }
|
|
510
|
+
to { transform: scale(1); opacity: 1; }
|
|
511
|
+
}
|
|
601
512
|
`;
|
|
602
513
|
document.head.appendChild(style);
|
|
603
514
|
}
|
|
604
515
|
const iframeContainer = document.createElement("div");
|
|
605
|
-
iframeContainer.style.cssText = `
|
|
606
|
-
position: relative;
|
|
607
|
-
width: 90%;
|
|
608
|
-
height: 90%;
|
|
609
|
-
max-width: 480px;
|
|
610
|
-
max-height: 720px;
|
|
611
|
-
border-radius: 20px;
|
|
612
|
-
overflow: hidden;
|
|
613
|
-
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.3), 0 12px 24px rgba(0, 0, 0, 0.2);
|
|
614
|
-
animation: ww-modal-scale-in 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
615
|
-
background: white;
|
|
516
|
+
iframeContainer.style.cssText = `
|
|
517
|
+
position: relative;
|
|
518
|
+
width: 90%;
|
|
519
|
+
height: 90%;
|
|
520
|
+
max-width: 480px;
|
|
521
|
+
max-height: 720px;
|
|
522
|
+
border-radius: 20px;
|
|
523
|
+
overflow: hidden;
|
|
524
|
+
box-shadow: 0 24px 48px rgba(0, 0, 0, 0.3), 0 12px 24px rgba(0, 0, 0, 0.2);
|
|
525
|
+
animation: ww-modal-scale-in 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
526
|
+
background: white;
|
|
616
527
|
`;
|
|
617
528
|
const iframe = document.createElement("iframe");
|
|
618
529
|
iframe.src = url;
|
|
619
|
-
iframe.style.cssText = `
|
|
620
|
-
width: 100%;
|
|
621
|
-
height: 100%;
|
|
622
|
-
border: none;
|
|
530
|
+
iframe.style.cssText = `
|
|
531
|
+
width: 100%;
|
|
532
|
+
height: 100%;
|
|
533
|
+
border: none;
|
|
623
534
|
`;
|
|
624
535
|
const closeButton = document.createElement("button");
|
|
625
|
-
closeButton.innerHTML = `
|
|
626
|
-
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
627
|
-
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
628
|
-
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
629
|
-
</svg>
|
|
536
|
+
closeButton.innerHTML = `
|
|
537
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="2" stroke-linecap="round" stroke-linejoin="round">
|
|
538
|
+
<line x1="18" y1="6" x2="6" y2="18"></line>
|
|
539
|
+
<line x1="6" y1="6" x2="18" y2="18"></line>
|
|
540
|
+
</svg>
|
|
630
541
|
`;
|
|
631
542
|
closeButton.setAttribute("aria-label", "Close modal");
|
|
632
|
-
closeButton.style.cssText = `
|
|
633
|
-
position: absolute;
|
|
634
|
-
top: -48px;
|
|
635
|
-
right: 0;
|
|
636
|
-
background: rgba(255, 255, 255, 0.95);
|
|
637
|
-
backdrop-filter: blur(8px);
|
|
638
|
-
-webkit-backdrop-filter: blur(8px);
|
|
639
|
-
border: none;
|
|
640
|
-
border-radius: 50%;
|
|
641
|
-
width: 40px;
|
|
642
|
-
height: 40px;
|
|
643
|
-
cursor: pointer;
|
|
644
|
-
display: flex;
|
|
645
|
-
align-items: center;
|
|
646
|
-
justify-content: center;
|
|
647
|
-
color: #1f2937;
|
|
648
|
-
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
649
|
-
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
543
|
+
closeButton.style.cssText = `
|
|
544
|
+
position: absolute;
|
|
545
|
+
top: -48px;
|
|
546
|
+
right: 0;
|
|
547
|
+
background: rgba(255, 255, 255, 0.95);
|
|
548
|
+
backdrop-filter: blur(8px);
|
|
549
|
+
-webkit-backdrop-filter: blur(8px);
|
|
550
|
+
border: none;
|
|
551
|
+
border-radius: 50%;
|
|
552
|
+
width: 40px;
|
|
553
|
+
height: 40px;
|
|
554
|
+
cursor: pointer;
|
|
555
|
+
display: flex;
|
|
556
|
+
align-items: center;
|
|
557
|
+
justify-content: center;
|
|
558
|
+
color: #1f2937;
|
|
559
|
+
transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
|
|
560
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.2);
|
|
650
561
|
`;
|
|
651
562
|
closeButton.onmouseover = () => {
|
|
652
563
|
closeButton.style.transform = "scale(1.1) rotate(90deg)";
|
|
@@ -668,19 +579,13 @@
|
|
|
668
579
|
iframeContainer.appendChild(closeButton);
|
|
669
580
|
modal.appendChild(iframeContainer);
|
|
670
581
|
document.body.appendChild(modal);
|
|
671
|
-
// Close on backdrop click
|
|
672
582
|
modal.onclick = (e) => {
|
|
673
583
|
if (e.target === modal) {
|
|
674
584
|
closeButton.click();
|
|
675
585
|
}
|
|
676
586
|
};
|
|
677
587
|
}
|
|
678
|
-
/**
|
|
679
|
-
* Sets up listener for messages from the iframe
|
|
680
|
-
* @private
|
|
681
|
-
*/
|
|
682
588
|
setupIframeListener() {
|
|
683
|
-
// Remove existing listener if any
|
|
684
589
|
if (this.iframeMessageListener) {
|
|
685
590
|
window.removeEventListener("message", this.iframeMessageListener);
|
|
686
591
|
}
|
|
@@ -710,7 +615,6 @@
|
|
|
710
615
|
console.warn("[WeWear VTO] Missing required data to start virtual try-on.");
|
|
711
616
|
return;
|
|
712
617
|
}
|
|
713
|
-
// Use stored original images, or get them if not stored yet
|
|
714
618
|
if (this.originalProductImages.length === 0) {
|
|
715
619
|
this.originalProductImages = this.getAllProductImages();
|
|
716
620
|
}
|
|
@@ -718,37 +622,56 @@
|
|
|
718
622
|
console.warn("[WeWear VTO] No product images found.");
|
|
719
623
|
return;
|
|
720
624
|
}
|
|
721
|
-
// Hide the modal and show loading indicator
|
|
722
625
|
const modal = document.querySelector(`.${CSS_CLASSES.MODAL}`);
|
|
723
626
|
if (modal && modal instanceof HTMLElement) {
|
|
724
627
|
modal.style.display = "none";
|
|
725
628
|
}
|
|
726
629
|
const container = document.querySelector(this.config.gallerySelector);
|
|
727
630
|
if (container instanceof HTMLElement) {
|
|
728
|
-
showProductLoading(container, "
|
|
631
|
+
showProductLoading(container, "Preparing your personalized look");
|
|
729
632
|
}
|
|
633
|
+
let hasPreview = false;
|
|
730
634
|
try {
|
|
731
635
|
const submitResponse = await this.callVtoApi(this.lastModelImage, this.originalProductImages);
|
|
732
636
|
let status = await this.fetchJobStatus(submitResponse.job_id);
|
|
733
637
|
let lastPreviewCount = 0;
|
|
734
638
|
while (status.status !== "COMPLETED" && status.status !== "FAILED") {
|
|
735
|
-
|
|
639
|
+
if (!hasPreview && container instanceof HTMLElement) {
|
|
640
|
+
let statusMessage = "Preparing your personalized look";
|
|
641
|
+
if (status.current_iteration === 1) {
|
|
642
|
+
statusMessage = "Refining your look";
|
|
643
|
+
}
|
|
644
|
+
else if (status.current_iteration && status.current_iteration > 1) {
|
|
645
|
+
statusMessage = "Finalizing your look";
|
|
646
|
+
}
|
|
647
|
+
showProductLoading(container, statusMessage);
|
|
648
|
+
}
|
|
649
|
+
else if (hasPreview && container instanceof HTMLElement) {
|
|
650
|
+
let statusMessage = "Refining your look";
|
|
651
|
+
if (status.current_iteration && status.current_iteration > 1) {
|
|
652
|
+
statusMessage = "Finalizing your look";
|
|
653
|
+
}
|
|
654
|
+
showStatusBadge(container, statusMessage);
|
|
655
|
+
this.hideButtonContainer(container);
|
|
656
|
+
}
|
|
736
657
|
if (status.preview_count > lastPreviewCount) {
|
|
737
658
|
try {
|
|
738
659
|
const previewUrl = await this.fetchJobImage(submitResponse.job_id);
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
this.previewBadge = createPreviewBadge();
|
|
745
|
-
container.appendChild(this.previewBadge);
|
|
746
|
-
}
|
|
747
|
-
else if (!container.contains(this.previewBadge)) {
|
|
748
|
-
// Re-add badge if it was removed
|
|
749
|
-
container.appendChild(this.previewBadge);
|
|
660
|
+
if (!hasPreview && container instanceof HTMLElement) {
|
|
661
|
+
this.replaceProductImage(previewUrl);
|
|
662
|
+
let statusMessage = "Refining your look";
|
|
663
|
+
if (status.current_iteration && status.current_iteration > 1) {
|
|
664
|
+
statusMessage = "Finalizing your look";
|
|
750
665
|
}
|
|
666
|
+
showStatusBadge(container, statusMessage);
|
|
667
|
+
this.hideButtonContainer(container);
|
|
668
|
+
removeProductLoading(container);
|
|
669
|
+
hasPreview = true;
|
|
751
670
|
}
|
|
671
|
+
else if (hasPreview) {
|
|
672
|
+
this.replaceProductImage(previewUrl);
|
|
673
|
+
}
|
|
674
|
+
lastPreviewCount = status.preview_count;
|
|
752
675
|
}
|
|
753
676
|
catch (e) {
|
|
754
677
|
if (!(e instanceof Error && e.message === "202_PROCESSING"))
|
|
@@ -763,27 +686,41 @@
|
|
|
763
686
|
if (finalImage) {
|
|
764
687
|
this.replaceProductImage(finalImage);
|
|
765
688
|
}
|
|
766
|
-
if (
|
|
767
|
-
|
|
768
|
-
this.
|
|
689
|
+
if (container instanceof HTMLElement) {
|
|
690
|
+
removeStatusBadge(container);
|
|
691
|
+
this.showButtonContainer(container);
|
|
769
692
|
}
|
|
770
693
|
}
|
|
771
694
|
if (status.status === "FAILED") {
|
|
772
695
|
console.error("[WeWear VTO] VTO process failed:", status.message);
|
|
773
696
|
if (container instanceof HTMLElement) {
|
|
774
|
-
|
|
697
|
+
if (!hasPreview) {
|
|
698
|
+
showProductLoading(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
|
|
699
|
+
}
|
|
700
|
+
else {
|
|
701
|
+
showStatusBadge(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
|
|
702
|
+
}
|
|
775
703
|
}
|
|
704
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
776
705
|
}
|
|
777
706
|
}
|
|
778
707
|
catch (error) {
|
|
779
708
|
console.error("[WeWear VTO] Error during virtual try-on process:", error);
|
|
780
709
|
if (container instanceof HTMLElement) {
|
|
781
|
-
|
|
710
|
+
if (!hasPreview) {
|
|
711
|
+
showProductLoading(container, "We are experiencing technical issues and apologise for any inconvenience caused. Please come back later again");
|
|
712
|
+
}
|
|
713
|
+
else {
|
|
714
|
+
showStatusBadge(container, "We are experiencing technical issues. Please try again later.");
|
|
715
|
+
}
|
|
782
716
|
}
|
|
717
|
+
await new Promise((r) => setTimeout(r, 5000));
|
|
783
718
|
}
|
|
784
719
|
finally {
|
|
785
720
|
if (container instanceof HTMLElement) {
|
|
786
721
|
removeProductLoading(container);
|
|
722
|
+
removeStatusBadge(container);
|
|
723
|
+
this.showButtonContainer(container);
|
|
787
724
|
}
|
|
788
725
|
removeElements(`.${CSS_CLASSES.MODAL}`);
|
|
789
726
|
}
|
|
@@ -791,7 +728,6 @@
|
|
|
791
728
|
async callVtoApi(modelImage, productImages) {
|
|
792
729
|
const formData = new FormData();
|
|
793
730
|
formData.append("model_image", modelImage, "model_image.png");
|
|
794
|
-
// Send all product images as a JSON array
|
|
795
731
|
formData.append("product_image_urls", JSON.stringify(productImages));
|
|
796
732
|
const res = await fetch(`${this.config.baseUrl}/api/vto`, {
|
|
797
733
|
method: "POST",
|
|
@@ -823,38 +759,27 @@
|
|
|
823
759
|
async refreshVirtualTryOn() {
|
|
824
760
|
await this.startVirtualTryOn(true);
|
|
825
761
|
}
|
|
826
|
-
/**
|
|
827
|
-
* Replaces the product image in the gallery with the virtual try-on result
|
|
828
|
-
* @private
|
|
829
|
-
*/
|
|
830
762
|
replaceProductImage(imageUrl) {
|
|
831
763
|
try {
|
|
832
|
-
// Store the virtual try-on image URL
|
|
833
764
|
this.virtualTryOnImageUrl = imageUrl;
|
|
834
|
-
// Find the gallery container
|
|
835
765
|
const container = document.querySelector(this.config.gallerySelector);
|
|
836
766
|
if (!container || !(container instanceof HTMLElement)) {
|
|
837
767
|
console.warn("[WeWear VTO] Gallery container not found for image replacement:", this.config.gallerySelector);
|
|
838
768
|
return;
|
|
839
769
|
}
|
|
840
|
-
// Store the original content if not already stored
|
|
841
770
|
if (!container.hasAttribute("data-ww-original-content")) {
|
|
842
771
|
container.setAttribute("data-ww-original-content", container.innerHTML);
|
|
843
772
|
}
|
|
844
|
-
// Capture and lock container dimensions before replacement to prevent layout shift
|
|
845
773
|
if (!container.hasAttribute("data-ww-container-locked")) {
|
|
846
774
|
const containerRect = container.getBoundingClientRect();
|
|
847
|
-
// Store original dimensions
|
|
848
775
|
container.setAttribute("data-ww-container-width", containerRect.width.toString());
|
|
849
776
|
container.setAttribute("data-ww-container-height", containerRect.height.toString());
|
|
850
|
-
// Lock the container dimensions
|
|
851
777
|
container.style.width = `${containerRect.width}px`;
|
|
852
778
|
container.style.height = `${containerRect.height}px`;
|
|
853
779
|
container.style.minWidth = `${containerRect.width}px`;
|
|
854
780
|
container.style.minHeight = `${containerRect.height}px`;
|
|
855
781
|
container.setAttribute("data-ww-container-locked", "true");
|
|
856
782
|
}
|
|
857
|
-
// Capture original image dimensions before replacement to prevent layout shift
|
|
858
783
|
const originalImg = container.querySelector("img");
|
|
859
784
|
if (originalImg &&
|
|
860
785
|
!container.hasAttribute("data-ww-original-dimensions")) {
|
|
@@ -866,9 +791,7 @@
|
|
|
866
791
|
container.setAttribute("data-ww-original-rect-height", rect.height.toString());
|
|
867
792
|
container.setAttribute("data-ww-original-dimensions", "true");
|
|
868
793
|
}
|
|
869
|
-
// Show the virtual try-on image initially
|
|
870
794
|
this.showVirtualTryOnImage(container);
|
|
871
|
-
// Replace the button container with the new multi-button version
|
|
872
795
|
this.updateButtonContainer(container);
|
|
873
796
|
console.log("[WeWear VTO] Product image replaced with virtual try-on result");
|
|
874
797
|
}
|
|
@@ -876,17 +799,23 @@
|
|
|
876
799
|
console.error("[WeWear VTO] Error replacing product image:", error);
|
|
877
800
|
}
|
|
878
801
|
}
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
802
|
+
hideButtonContainer(container) {
|
|
803
|
+
const buttonContainer = container.querySelector(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
|
|
804
|
+
if (buttonContainer) {
|
|
805
|
+
buttonContainer.style.display = "none";
|
|
806
|
+
}
|
|
807
|
+
}
|
|
808
|
+
showButtonContainer(container) {
|
|
809
|
+
const buttonContainer = container.querySelector(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
|
|
810
|
+
if (buttonContainer) {
|
|
811
|
+
buttonContainer.style.display = "flex";
|
|
812
|
+
}
|
|
813
|
+
}
|
|
883
814
|
updateButtonContainer(container) {
|
|
884
|
-
// Remove existing button containers
|
|
885
815
|
const existingButtons = container.querySelectorAll(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
|
|
886
816
|
existingButtons.forEach((btn) => {
|
|
887
817
|
btn.remove();
|
|
888
818
|
});
|
|
889
|
-
// Create new button container with all buttons
|
|
890
819
|
const buttonContainer = createButtonContainer(this.config.buttonPosition, this.virtualTryOnImageUrl !== null, async () => {
|
|
891
820
|
await this.handleTryOnClick();
|
|
892
821
|
}, async () => {
|
|
@@ -903,22 +832,16 @@
|
|
|
903
832
|
}, this.isShowingVirtualTryOn);
|
|
904
833
|
container.appendChild(buttonContainer);
|
|
905
834
|
}
|
|
906
|
-
/**
|
|
907
|
-
* Shows the virtual try-on image in the container
|
|
908
|
-
* @private
|
|
909
|
-
*/
|
|
910
835
|
showVirtualTryOnImage(container) {
|
|
911
836
|
if (!this.virtualTryOnImageUrl) {
|
|
912
837
|
console.warn("[WeWear VTO] No virtual try-on image URL available");
|
|
913
838
|
return;
|
|
914
839
|
}
|
|
915
|
-
// Remove all direct children except for the button container
|
|
916
840
|
Array.from(container.children).forEach((child) => {
|
|
917
841
|
if (!child.classList.contains(CSS_CLASSES.BUTTON_CONTAINER)) {
|
|
918
842
|
child.remove();
|
|
919
843
|
}
|
|
920
844
|
});
|
|
921
|
-
// Get stored original dimensions to prevent layout shift
|
|
922
845
|
const originalWidth = container.getAttribute("data-ww-original-width") || "";
|
|
923
846
|
const originalHeight = container.getAttribute("data-ww-original-height") || "";
|
|
924
847
|
const originalRectWidth = container.getAttribute("data-ww-original-rect-width") || "";
|
|
@@ -926,10 +849,8 @@
|
|
|
926
849
|
const image = document.createElement("img");
|
|
927
850
|
image.src = this.virtualTryOnImageUrl;
|
|
928
851
|
image.alt = "Virtual Try-On Result";
|
|
929
|
-
// Use original dimensions to prevent layout shift
|
|
930
852
|
let widthStyle = "100%";
|
|
931
853
|
let heightStyle = "100%";
|
|
932
|
-
// Prefer computed style dimensions, fallback to bounding rect, then container fill
|
|
933
854
|
if (originalWidth && originalWidth !== "auto" && originalWidth !== "0px") {
|
|
934
855
|
widthStyle = originalWidth;
|
|
935
856
|
}
|
|
@@ -944,61 +865,42 @@
|
|
|
944
865
|
else if (originalRectHeight && originalRectHeight !== "0") {
|
|
945
866
|
heightStyle = `${originalRectHeight}px`;
|
|
946
867
|
}
|
|
947
|
-
image.style.cssText = `
|
|
948
|
-
width: ${widthStyle};
|
|
949
|
-
height: ${heightStyle};
|
|
950
|
-
object-fit: contain;
|
|
868
|
+
image.style.cssText = `
|
|
869
|
+
width: ${widthStyle};
|
|
870
|
+
height: ${heightStyle};
|
|
871
|
+
object-fit: contain;
|
|
951
872
|
`;
|
|
952
|
-
// Prepend the image to ensure buttons are rendered on top
|
|
953
873
|
container.prepend(image);
|
|
954
874
|
this.isShowingVirtualTryOn = true;
|
|
955
875
|
}
|
|
956
|
-
/**
|
|
957
|
-
* Shows the original product image in the container
|
|
958
|
-
* @private
|
|
959
|
-
*/
|
|
960
876
|
showOriginalImage(container) {
|
|
961
877
|
const originalContentHTML = container.getAttribute("data-ww-original-content");
|
|
962
878
|
if (originalContentHTML) {
|
|
963
|
-
// Remove all direct children except for the button container
|
|
964
879
|
Array.from(container.children).forEach((child) => {
|
|
965
880
|
if (!child.classList.contains(CSS_CLASSES.BUTTON_CONTAINER)) {
|
|
966
881
|
child.remove();
|
|
967
882
|
}
|
|
968
883
|
});
|
|
969
|
-
// Parse the original content and prepend it
|
|
970
884
|
const tempDiv = document.createElement("div");
|
|
971
885
|
tempDiv.innerHTML = originalContentHTML;
|
|
972
886
|
container.prepend(...Array.from(tempDiv.children));
|
|
973
887
|
}
|
|
974
888
|
this.isShowingVirtualTryOn = false;
|
|
975
889
|
}
|
|
976
|
-
/**
|
|
977
|
-
* Destroys the widget and cleans up resources
|
|
978
|
-
*/
|
|
979
890
|
destroy() {
|
|
980
891
|
try {
|
|
981
|
-
// Remove all buttons
|
|
982
892
|
removeElements(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
|
|
983
|
-
// Remove all modals
|
|
984
893
|
removeElements(`.${CSS_CLASSES.MODAL}`);
|
|
985
|
-
// Clear references
|
|
986
894
|
this.cameraButton = null;
|
|
987
|
-
// Remove message listener
|
|
988
895
|
if (this.iframeMessageListener) {
|
|
989
896
|
window.removeEventListener("message", this.iframeMessageListener);
|
|
990
897
|
this.iframeMessageListener = null;
|
|
991
898
|
}
|
|
992
|
-
// Reset state
|
|
993
899
|
this.virtualTryOnImageUrl = null;
|
|
994
900
|
this.isShowingVirtualTryOn = false;
|
|
995
901
|
this.lastModelImage = null;
|
|
996
902
|
this.originalProductImageUrl = null;
|
|
997
903
|
this.originalProductImages = [];
|
|
998
|
-
if (this.previewBadge) {
|
|
999
|
-
this.previewBadge.remove();
|
|
1000
|
-
this.previewBadge = null;
|
|
1001
|
-
}
|
|
1002
904
|
console.log("[WeWear VTO] Widget destroyed successfully");
|
|
1003
905
|
}
|
|
1004
906
|
catch (error) {
|
|
@@ -1010,19 +912,15 @@
|
|
|
1010
912
|
let widgetInstance = null;
|
|
1011
913
|
function initVirtualTryOn(config) {
|
|
1012
914
|
try {
|
|
1013
|
-
// Clean up any existing instance
|
|
1014
915
|
if (widgetInstance) {
|
|
1015
916
|
widgetInstance.destroy();
|
|
1016
917
|
widgetInstance = null;
|
|
1017
918
|
}
|
|
1018
|
-
// Early return if config is missing
|
|
1019
919
|
if (!config) {
|
|
1020
920
|
console.log("[WeWear VTO] Missing configuration. Widget not initialized.");
|
|
1021
921
|
return;
|
|
1022
922
|
}
|
|
1023
|
-
// Create and initialize new instance
|
|
1024
923
|
widgetInstance = new VirtualTryOnWidget(config);
|
|
1025
|
-
// Initialize immediately if DOM is ready, otherwise wait for it
|
|
1026
924
|
if (document.readyState === "loading") {
|
|
1027
925
|
document.addEventListener("DOMContentLoaded", () => {
|
|
1028
926
|
widgetInstance === null || widgetInstance === void 0 ? void 0 : widgetInstance.init().catch((error) => {
|