PageSourceSearch

https://www.audemarspiguet.cc/dist/front/static/new_default_theme/featured-collection.js?v=641

js audemarspiguet.cc collected 2026-10-09 18:06:08 UTC 11,577 bytes, 312 lines download raw bytes

1if (!customElements.get("f-featured-collection")) {
2  class MFeaturedCollection extends HTMLElement {
3    constructor() {
4      super();
5    }
6
7    connectedCallback() {
8      this.selectors = {
9        loadMoreBtn: "[data-load-more]",
10        loadMoreBtnWrapper: ".f-featured-collection__button",
11        productsContainer: "[data-products-container]",
12        products: [".f-product-card"],
13        soldNumber: ["[data-sold-number]"],
14        availableNumber: ["[data-available-number]"],
15        countDown: "[data-flashsale-countdown]",
16        slideControls: ".f-slider-controls",
17        slideContainer: ".f-mixed-layout__wrapper", // slideContainer
18      };
19      this.domNodes = queryDomNodes(this.selectors, this);
20
21      this.enableSlider = this.dataset.enableSlider === "1";
22      this.showPagination = this.dataset.showPagination === "1";
23      this.showNavigation = this.dataset.showNavigation === "1";
24      this.enableCountdown = this.dataset.enableCountdown === "1";
25      const countdownEndDate = this.dataset.countdownEndDate;
26
27      const countdownEndDateSec = this.adjustDateWithRandomSeconds(countdownEndDate);
28      this.expires_date = this.formatDateToUTC(countdownEndDateSec);
29      // alert(this.expires_date );
30      this.progressSettings = {
31        total_quantity: 100,
32        sold_to: 80,
33        sold_from: 20,
34        expires_date: this.expires_date
35      };
36
37      this.id = this.dataset.id;
38      this.items = this.dataset.items;
39      this.mobileDisableSlider = this.dataset.mobileDisableSlider === "true";
40
41      this.initByScreenSize();
42      document.addEventListener("matchMobile", () => {
43        this.initByScreenSize();
44      });
45      document.addEventListener("unmatchMobile", () => {
46        this.initByScreenSize();
47      });
48      if (this.enableCountdown) this.initFlashSale();
49
50      this.canLoad = true;
51      this.currentPage = 1;
52      this.spinner = spinner();
53
54    }
55
56    adjustDateWithRandomSeconds(dd) {
57      // 解析输入日期字符串(支持两种格式)
58      if (!dd) {
59        dd = '2025-01-01';
60      }
61      let inputDate;
62      if (dd.length === 10) {
63        // YYYY-MM-DD 格式
64        inputDate = new Date(dd + 'T00:00:00');
65      } else {
66        // YYYY-MM-DD HH:mm:ss 格式
67        const [datePart, timePart] = dd.split(' ');
68        inputDate = new Date(datePart + 'T' + timePart);
69      }
70
71      // 获取当前时间
72      const now = new Date();
73
74      // 比较输入日期和当前时间
75      if (inputDate < now) {
76        // 生成 13200 秒(3小时40分钟)到 86400 秒(24小时)之间的随机秒数
77        const minSeconds = 1000;
78        const maxSeconds = 37600;
79        const randomSeconds = Math.floor(
80          Math.random() * (maxSeconds - minSeconds + 1) + minSeconds
81        );
82
83        // 计算新时间:当前时间 + 随机秒数
84        const newDate = new Date(now.getTime() + randomSeconds * 1000);
85
86        // 格式化为 YYYY-MM-DD HH:mm:ss
87        const year = newDate.getFullYear();
88        const month = String(newDate.getMonth() + 1).padStart(2, '0');
89        const day = String(newDate.getDate()).padStart(2, '0');
90        const hours = String(newDate.getHours()).padStart(2, '0');
91        const minutes = String(newDate.getMinutes()).padStart(2, '0');
92        const seconds = String(newDate.getSeconds()).padStart(2, '0');
93
94        return `${year}-${month}-${day} ${hours}:${minutes}:${seconds}`;
95      }
96
97      // 如果不需要调整,保持原格式
98      if (dd.length === 10) {
99        return dd + ' 00:00:00';
100      }
101      return dd;
102    }
103
104    formatDateToUTC(dateString) {
105      // 处理只有日期的情况(YYYY-MM-DD)
106      if (/^\d{4}-\d{2}-\d{2}$/.test(dateString)) {
107        return dateString + 'T00:00:00Z';
108      }
109
110      // 处理日期+时间的情况(YYYY-MM-DD HH:mm:ss)
111      if (/^\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}$/.test(dateString)) {
112        return dateString.replace(' ', 'T') + 'Z';
113      }
114
115      // 处理带毫秒/其他分隔符的情况
116      const [datePart, timePart] = dateString.split(' ');
117      if (!datePart || !timePart) {
118        throw new Error('Invalid date format. Expected "YYYY-MM-DD" or "YYYY-MM-DD HH:mm:ss"');
119      }
120
121      // 标准化时间部分(补零和格式处理)
122      const [hours = '00', minutes = '00', seconds = '00'] = timePart.split(':');
123      const formattedTime = [
124        hours.padStart(2, '0'),
125        minutes.padStart(2, '0'),
126        seconds.padStart(2, '0')
127      ].join(':');
128
129      return `${datePart}T${formattedTime}Z`;
130    }
131
132
133    initByScreenSize() {
134      if (!this.enableSlider) return;
135      const { slideContainer, slideControls } = queryDomNodes(this.selectors, this);
136
137      if (FecTheme.config.mqlMobile && this.mobileDisableSlider) {
138        slideControls && slideControls.classList.add("m:hidden");
139        slideContainer && slideContainer.classList.remove("swiper-container");
140        if (this.swiper) this.swiper.destroy(false, true);
141      } else {
142        slideControls && slideControls.classList.remove("m:hidden");
143        this.initSlider();
144      }
145    }
146
147    initSlider() {
148      const { slideContainer } = queryDomNodes(this.selectors, this);
149      const controlsContainer = this.querySelector(".f-slider-controls");
150      const prevButton = controlsContainer && controlsContainer.querySelector(".f-slider-controls__button-prev");
151      const nextButton = controlsContainer && controlsContainer.querySelector(".f-slider-controls__button-next");
152      const slideItemsLength = slideContainer.querySelector(".swiper-wrapper").childElementCount;
153
154      slideContainer && slideContainer.classList.add("swiper-container");
155
156      this.slider = new Swiper(slideContainer, {
157        slidesPerView: 2,
158        showPagination: this.showPagination,
159        showNavigation: this.showNavigation,
160        loop: this.enableCountdown ? false : true,
161        pagination: this.showPagination
162          ? {
163              el: this.querySelector(".swiper-pagination"),
164              clickable: true,
165            }
166          : false,
167        breakpoints: {
168          768: {
169            slidesPerView: parseInt(this.items) >= 3 ? 3 : parseInt(this.items),
170          },
171          1024: {
172            slidesPerView: parseInt(this.items) >= 4 ? 4 : parseInt(this.items),
173          },
174          1280: {
175            slidesPerView: parseInt(this.items),
176          },
177        },
178        threshold: 2,
179        on: {
180          init: () => {
181            setTimeout(() => {
182              // Calculate controls position
183              const firstItem = this.querySelector(".f-image") || this.querySelector(".f-placeholder-svg");
184              if (firstItem && controlsContainer) {
185                const itemHeight = firstItem.clientHeight;
186                controlsContainer.style.setProperty("--offset-top", parseInt(itemHeight) / 2 + "px");
187
188                prevButton && prevButton.classList.remove("m:hidden");
189                nextButton && nextButton.classList.remove("m:hidden");
190              }
191            }, 200);
192          },
193          breakpoint: (swiper, breakpointParams) => {
194            if (controlsContainer) {
195              const { slidesPerView } = breakpointParams;
196              if (slideItemsLength > slidesPerView) {
197                controlsContainer.classList.remove("m:hidden");
198                swiper.allowTouchMove = true;
199              } else {
200                controlsContainer.classList.add("m:hidden");
201                swiper.allowTouchMove = false;
202              }
203            }
204          },
205        },
206      });
207
208
209      if (this.slider && this.showNavigation) {
210        prevButton && prevButton.addEventListener("click", () => this.slider.slidePrev());
211        nextButton && nextButton.addEventListener("click", () => this.slider.slideNext());
212      }
213
214      this.swiper = slideContainer && slideContainer.swiper;
215    }
216
217
218    async handleLoadMore() {
219      this.currentPage++;
220      this.canLoad = this.currentPage < this.totalPages;
221      this.toggleLoading(true);
222      const collectionID = this.dataset.collectionId;
223
224      const url = this.dataset.url;
225      const dataUrl = `${url}?page=${this.currentPage}&section_id=${this.id}`;
226      fetchCache(dataUrl).then((html) => {
227        this.toggleLoading(false);
228        const dom = generateDomFromString(html);
229        const products = dom.querySelector(this.selectors.productsContainer);
230        const oldCards = this.domNodes.productsContainer.childElementCount;
231        let isAppended = false;
232
233        if (products) {
234          Array.from(products.childNodes).forEach((product) => {
235            this.domNodes.productsContainer.appendChild(product);
236            const check = setInterval(() => {
237              if (this.domNodes.productsContainer.childElementCount > oldCards) {
238                clearInterval(check);
239                isAppended = true;
240              }
241            }, 50);
242          });
243        }
244
245        if (this.enableCountdown ) {
246          const check = setInterval(() => {
247            if (isAppended) {
248              clearInterval(check);
249              const cards = this.domNodes.productsContainer.querySelectorAll(".f-product-card");
250              cards.forEach((card) => {
251                const content = card.querySelector(".f-product-card__content");
252                const progress = card.querySelector(".f-product-sale-progress");
253                if (!progress) {
254                  let soldNumber;
255                  if (card.dataset.soldNumber) {
256                    soldNumber = card.dataset.soldNumber;
257                  }
258                  const component = saleProgress(this.progressSettings, card.dataset.productId, soldNumber);
259                  const newComponent = generateDomFromString(component);
260                  content.appendChild(newComponent);
261                }
262              });
263            }
264          }, 50);
265        }
266
267        this.triggerLoad = false;
268
269        if (!this.canLoad) {
270          this.domNodes.loadMoreBtn && this.domNodes.loadMoreBtn.classList.add("m:hidden");
271        }
272      });
273    }
274
275    toggleLoading(status) {
276      if (!this.domNodes.loadMoreBtn) return;
277      if (status) {
278        this.domNodes.loadMoreBtn.classList.add("f-spinner-loading");
279      } else {
280        this.domNodes.loadMoreBtn.classList.remove("f-spinner-loading");
281      }
282    }
283
284    async initFlashSale() {
285
286        this.domNodes.products.forEach((card) => {
287          const content = card.querySelector(".f-product-card__content");
288          const pcardSale = card.querySelector(".f-product-sale-progress");
289          let soldNumber;
290          if (card.dataset.soldNumber) {
291            soldNumber = card.dataset.soldNumber;
292          }
293          const component = saleProgress(this.progressSettings, card.dataset.productId, soldNumber);
294          const newComponent = generateDomFromString(component);
295          if (!pcardSale) {
296            content.appendChild(newComponent);
297          }
298        });
299        this.initCountDown();
300
301    }
302
303    initCountDown() {
304      const endTime = new Date(this.expires_date).getTime();
305      this.countDownTimer = new CountdownTimer(this.domNodes.countDown, Date.now(), endTime, {
306        loop: true,
307      });
308    }
309  }
310
311  customElements.define("f-featured-collection", MFeaturedCollection);
312}

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.