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