@wewear/virtual-try-on 1.4.26 → 1.4.28

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/index.js CHANGED
@@ -26,12 +26,6 @@
26
26
  return "right: 20px; bottom: 20px;";
27
27
  }
28
28
  }
29
- function removeElements(selector) {
30
- const elements = document.querySelectorAll(selector);
31
- elements.forEach((element) => {
32
- element.remove();
33
- });
34
- }
35
29
 
36
30
  function createButtonContainer(buttonPosition, hasVirtualTryOn = false, onCameraClick, onRefreshClick, onToggleClick, isShowingVirtualTryOn = false) {
37
31
  const container = document.createElement("div");
@@ -401,7 +395,9 @@
401
395
  this.isShowingVirtualTryOn = false;
402
396
  this.lastModelImage = null;
403
397
  this.cameraButton = null;
398
+ this.modalElement = null;
404
399
  this.iframeMessageListener = null;
400
+ this.instanceId = ++VirtualTryOnWidget.instanceCounter;
405
401
  this.config = {
406
402
  baseUrl: config.baseUrl,
407
403
  productPageSelector: config.productPageSelector,
@@ -409,10 +405,19 @@
409
405
  productImageSelector: config.productImageSelector,
410
406
  buttonPosition: config.buttonPosition,
411
407
  modelTier: config.modelTier,
412
- greenSuit: config.greenSuit,
413
408
  outfitID: config.outfitID,
409
+ singleton: config.singleton,
410
+ galleryElement: config.galleryElement,
414
411
  };
415
412
  }
413
+ getContainer() {
414
+ const configContainer = this.config.galleryElement;
415
+ if (configContainer && configContainer.isConnected) {
416
+ return configContainer;
417
+ }
418
+ const queriedContainer = document.querySelector(this.config.gallerySelector);
419
+ return queriedContainer instanceof HTMLElement ? queriedContainer : null;
420
+ }
416
421
  async init() {
417
422
  try {
418
423
  const selectors = this.config.productPageSelector;
@@ -421,8 +426,8 @@
421
426
  if (!matches) {
422
427
  return;
423
428
  }
424
- const container = document.querySelector(this.config.gallerySelector);
425
- if (!container || !(container instanceof HTMLElement)) {
429
+ const container = this.getContainer();
430
+ if (!container) {
426
431
  console.warn("[WeWear VTO] Gallery container not found:", this.config.gallerySelector);
427
432
  return;
428
433
  }
@@ -469,7 +474,10 @@
469
474
  }
470
475
  }
471
476
  getAllProductImages() {
472
- const productImageElements = document.querySelectorAll(this.config.productImageSelector);
477
+ const container = this.getContainer();
478
+ const scopedElements = container === null || container === void 0 ? void 0 : container.querySelectorAll(this.config.productImageSelector);
479
+ const fallbackElements = document.querySelectorAll(this.config.productImageSelector);
480
+ const productImageElements = ((scopedElements === null || scopedElements === void 0 ? void 0 : scopedElements.length) ? scopedElements : fallbackElements);
473
481
  const images = [];
474
482
  productImageElements.forEach((img) => {
475
483
  const imageUrl = img.src ||
@@ -485,7 +493,8 @@
485
493
  return images;
486
494
  }
487
495
  showPhotoUploadModal(url) {
488
- removeElements(`.${CSS_CLASSES.MODAL}`);
496
+ this.closeModal();
497
+ VirtualTryOnWidget.activeWidgetId = this.instanceId;
489
498
  const modal = document.createElement("div");
490
499
  modal.className = CSS_CLASSES.MODAL;
491
500
  modal.style.cssText = `
@@ -579,7 +588,7 @@
579
588
  modal.style.opacity = "0";
580
589
  iframeContainer.style.transform = "scale(0.95)";
581
590
  iframeContainer.style.opacity = "0";
582
- setTimeout(() => modal.remove(), 300);
591
+ setTimeout(() => this.closeModal(), 300);
583
592
  };
584
593
  iframeContainer.appendChild(iframe);
585
594
  iframeContainer.appendChild(closeButton);
@@ -590,12 +599,25 @@
590
599
  closeButton.click();
591
600
  }
592
601
  };
602
+ this.modalElement = modal;
603
+ }
604
+ closeModal() {
605
+ if (this.modalElement) {
606
+ this.modalElement.remove();
607
+ this.modalElement = null;
608
+ }
609
+ if (VirtualTryOnWidget.activeWidgetId === this.instanceId) {
610
+ VirtualTryOnWidget.activeWidgetId = null;
611
+ }
593
612
  }
594
613
  setupIframeListener() {
595
614
  if (this.iframeMessageListener) {
596
615
  window.removeEventListener("message", this.iframeMessageListener);
597
616
  }
598
617
  this.iframeMessageListener = (event) => {
618
+ if (VirtualTryOnWidget.activeWidgetId !== this.instanceId) {
619
+ return;
620
+ }
599
621
  if (event.origin !== new URL(this.config.baseUrl).origin) {
600
622
  return;
601
623
  }
@@ -607,7 +629,7 @@
607
629
  }
608
630
  break;
609
631
  case "CLOSE_MODAL":
610
- removeElements(`.${CSS_CLASSES.MODAL}`);
632
+ this.closeModal();
611
633
  break;
612
634
  }
613
635
  };
@@ -632,58 +654,18 @@
632
654
  if (modal && modal instanceof HTMLElement) {
633
655
  modal.style.display = "none";
634
656
  }
635
- const container = document.querySelector(this.config.gallerySelector);
636
- if (container instanceof HTMLElement) {
657
+ const container = this.getContainer();
658
+ if (container) {
637
659
  showProductLoading(container, "Preparing your personalized look");
638
660
  }
639
- let hasPreview = false;
640
661
  try {
641
662
  const submitResponse = await this.callVtoApi(this.lastModelImage, this.originalProductImages);
642
663
  let status = await this.fetchJobStatus(submitResponse.job_id);
643
- let lastPreviewCount = 0;
644
664
  while (status.status !== "COMPLETED" && status.status !== "FAILED") {
645
- if (!hasPreview && container instanceof HTMLElement) {
646
- let statusMessage = "Preparing your personalized look";
647
- if (status.current_iteration === 1) {
648
- statusMessage = "Refining your look";
649
- }
650
- else if (status.current_iteration && status.current_iteration > 1) {
651
- statusMessage = "Finalizing your look";
652
- }
665
+ if (container instanceof HTMLElement) {
666
+ const statusMessage = "Preparing your personalized look";
653
667
  showProductLoading(container, statusMessage);
654
668
  }
655
- else if (hasPreview && container instanceof HTMLElement) {
656
- let statusMessage = "Refining your look";
657
- if (status.current_iteration && status.current_iteration > 1) {
658
- statusMessage = "Finalizing your look";
659
- }
660
- showStatusBadge(container, statusMessage);
661
- this.hideButtonContainer(container);
662
- }
663
- if (status.preview_count > lastPreviewCount) {
664
- try {
665
- const previewUrl = await this.fetchJobImage(submitResponse.job_id);
666
- if (!hasPreview && container instanceof HTMLElement) {
667
- this.replaceProductImage(previewUrl);
668
- let statusMessage = "Refining your look";
669
- if (status.current_iteration && status.current_iteration > 1) {
670
- statusMessage = "Finalizing your look";
671
- }
672
- showStatusBadge(container, statusMessage);
673
- this.hideButtonContainer(container);
674
- removeProductLoading(container);
675
- hasPreview = true;
676
- }
677
- else if (hasPreview) {
678
- this.replaceProductImage(previewUrl);
679
- }
680
- lastPreviewCount = status.preview_count;
681
- }
682
- catch (e) {
683
- if (!(e instanceof Error && e.message === "202_PROCESSING"))
684
- throw e;
685
- }
686
- }
687
669
  await new Promise((r) => setTimeout(r, 3000));
688
670
  status = await this.fetchJobStatus(submitResponse.job_id);
689
671
  }
@@ -700,12 +682,7 @@
700
682
  if (status.status === "FAILED") {
701
683
  console.error("[WeWear VTO] VTO process failed:", status.message);
702
684
  if (container instanceof HTMLElement) {
703
- if (!hasPreview) {
704
- showProductLoading(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
705
- }
706
- else {
707
- showStatusBadge(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
708
- }
685
+ showStatusBadge(container, "We are experiencing some technical issues and apologise for any inconvenience caused. Please come back later again");
709
686
  }
710
687
  await new Promise((r) => setTimeout(r, 5000));
711
688
  }
@@ -713,12 +690,7 @@
713
690
  catch (error) {
714
691
  console.error("[WeWear VTO] Error during virtual try-on process:", error);
715
692
  if (container instanceof HTMLElement) {
716
- if (!hasPreview) {
717
- showProductLoading(container, "We are experiencing technical issues and apologise for any inconvenience caused. Please come back later again");
718
- }
719
- else {
720
- showStatusBadge(container, "We are experiencing technical issues. Please try again later.");
721
- }
693
+ showProductLoading(container, "We are experiencing technical issues and apologise for any inconvenience caused. Please come back later again");
722
694
  }
723
695
  await new Promise((r) => setTimeout(r, 5000));
724
696
  }
@@ -728,7 +700,7 @@
728
700
  removeStatusBadge(container);
729
701
  this.showButtonContainer(container);
730
702
  }
731
- removeElements(`.${CSS_CLASSES.MODAL}`);
703
+ this.closeModal();
732
704
  }
733
705
  }
734
706
  async callVtoApi(modelImage, productImages) {
@@ -738,7 +710,6 @@
738
710
  formData.append("product_image_urls", JSON.stringify(productImages));
739
711
  formData.append("page_url", window.location.href);
740
712
  formData.append("model_tier", this.config.modelTier);
741
- formData.append("green_suit", this.config.greenSuit);
742
713
  const outfitID = (_a = this.config.outfitID) === null || _a === void 0 ? void 0 : _a.trim();
743
714
  if (outfitID)
744
715
  formData.append("outfit_id", outfitID);
@@ -775,8 +746,8 @@
775
746
  replaceProductImage(imageUrl) {
776
747
  try {
777
748
  this.virtualTryOnImageUrl = imageUrl;
778
- const container = document.querySelector(this.config.gallerySelector);
779
- if (!container || !(container instanceof HTMLElement)) {
749
+ const container = this.getContainer();
750
+ if (!container) {
780
751
  console.warn("[WeWear VTO] Gallery container not found for image replacement:", this.config.gallerySelector);
781
752
  return;
782
753
  }
@@ -813,12 +784,6 @@
813
784
  console.error("[WeWear VTO] Error replacing product image:", error);
814
785
  }
815
786
  }
816
- hideButtonContainer(container) {
817
- const buttonContainer = container.querySelector(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
818
- if (buttonContainer) {
819
- buttonContainer.style.display = "none";
820
- }
821
- }
822
787
  showButtonContainer(container) {
823
788
  const buttonContainer = container.querySelector(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
824
789
  if (buttonContainer) {
@@ -907,8 +872,12 @@
907
872
  }
908
873
  destroy() {
909
874
  try {
910
- removeElements(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
911
- removeElements(`.${CSS_CLASSES.MODAL}`);
875
+ const container = this.getContainer();
876
+ if (container) {
877
+ const existingButtons = container.querySelectorAll(`.${CSS_CLASSES.BUTTON_CONTAINER}`);
878
+ existingButtons.forEach((btn) => btn.remove());
879
+ }
880
+ this.closeModal();
912
881
  this.cameraButton = null;
913
882
  if (this.iframeMessageListener) {
914
883
  window.removeEventListener("message", this.iframeMessageListener);
@@ -926,30 +895,45 @@
926
895
  }
927
896
  }
928
897
  }
898
+ VirtualTryOnWidget.instanceCounter = 0;
899
+ VirtualTryOnWidget.activeWidgetId = null;
929
900
 
930
- let widgetInstance = null;
901
+ let widgetInstances = [];
931
902
  function initVirtualTryOn(config) {
932
903
  try {
933
- if (widgetInstance) {
934
- widgetInstance.destroy();
935
- widgetInstance = null;
936
- }
904
+ widgetInstances.forEach((instance) => instance.destroy());
905
+ widgetInstances = [];
937
906
  if (!config) {
938
907
  console.log("[WeWear VTO] Missing configuration. Widget not initialized.");
939
908
  return;
940
909
  }
941
- widgetInstance = new VirtualTryOnWidget(config);
942
- if (document.readyState === "loading") {
943
- document.addEventListener("DOMContentLoaded", () => {
944
- widgetInstance === null || widgetInstance === void 0 ? void 0 : widgetInstance.init().catch((error) => {
945
- console.error("[WeWear VTO] Failed to initialize after DOM ready:", error);
946
- });
910
+ const allContainers = Array.from(document.querySelectorAll(config.gallerySelector)).filter((container) => container instanceof HTMLElement);
911
+ const shouldUseSingleton = config.singleton !== false;
912
+ if (allContainers.length > 1 && !shouldUseSingleton) {
913
+ widgetInstances = allContainers.map((galleryElement) => {
914
+ return new VirtualTryOnWidget(Object.assign(Object.assign({}, config), { galleryElement }));
947
915
  });
948
916
  }
949
917
  else {
950
- widgetInstance.init().catch((error) => {
951
- console.error("[WeWear VTO] Failed to initialize:", error);
918
+ const firstContainer = allContainers[0];
919
+ widgetInstances = [
920
+ new VirtualTryOnWidget(Object.assign(Object.assign({}, config), { galleryElement: firstContainer })),
921
+ ];
922
+ }
923
+ const initializeWidgets = () => {
924
+ widgetInstances.forEach((widget) => {
925
+ widget.init().catch((error) => {
926
+ console.error("[WeWear VTO] Failed to initialize:", error);
927
+ });
952
928
  });
929
+ };
930
+ if (document.readyState === "loading") {
931
+ document.addEventListener("DOMContentLoaded", initializeWidgets, {
932
+ once: true,
933
+ });
934
+ }
935
+ else {
936
+ initializeWidgets();
953
937
  }
954
938
  }
955
939
  catch (error) {
@@ -957,7 +941,11 @@
957
941
  }
958
942
  }
959
943
  function getWidgetInstance() {
960
- return widgetInstance;
944
+ var _a;
945
+ return (_a = widgetInstances[0]) !== null && _a !== void 0 ? _a : null;
946
+ }
947
+ function getWidgetInstances() {
948
+ return [...widgetInstances];
961
949
  }
962
950
  if (typeof window !== "undefined") {
963
951
  initVirtualTryOn();
@@ -965,6 +953,7 @@
965
953
 
966
954
  exports.VirtualTryOnWidget = VirtualTryOnWidget;
967
955
  exports.getWidgetInstance = getWidgetInstance;
956
+ exports.getWidgetInstances = getWidgetInstances;
968
957
  exports.initVirtualTryOn = initVirtualTryOn;
969
958
 
970
959
  }));