PageSourceSearch

https://xagent.id/assets/business-card.js?v=company-coverflow-20260902-1

js xagent.id collected 2026-10-05 06:32:43 UTC 14,871 bytes, 382 lines download raw bytes

1/* Coverflow navigation around the shared company-card renderer.
2 * The carousel owns only selection and motion. The card component remains the
3 * sole source of front/back markup on both the homepage and company pages. */
4(function () {
5  "use strict";
6
7  var AUTO_FLIP_MS = 2000;
8  var MAX_CARDS = 7;
9  var reduceMotion = window.matchMedia && window.matchMedia("(prefers-reduced-motion: reduce)").matches;
10
11  function t(source) {
12    return window.XAgentI18n && window.XAgentI18n.t ? window.XAgentI18n.t(source) : source;
13  }
14
15  function isInteractive(target) {
16    return Boolean(target && target.closest && target.closest("a, button, input, select, textarea"));
17  }
18
19  function installCard(card, isActive) {
20    if (!card || card.__businessCardController) return card && card.__businessCardController;
21    var front = card.querySelector(".namecard-front");
22    var back = card.querySelector(".namecard-back");
23    var autoFlipTimer = 0;
24
25    function active() {
26      return typeof isActive === "function" ? isActive() : true;
27    }
28
29    function cancelAutoFlip() {
30      if (!autoFlipTimer) return;
31      window.clearTimeout(autoFlipTimer);
32      autoFlipTimer = 0;
33    }
34
35    function render(flipped) {
36      var enabled = active();
37      card.classList.toggle("is-flipped", Boolean(flipped));
38      card.dataset.cardSide = flipped ? "agent" : "human";
39      card.tabIndex = enabled ? 0 : -1;
40      card.setAttribute("aria-label", flipped ? t("View human side") : t("View Agent side"));
41      if (front) front.setAttribute("aria-hidden", enabled && !flipped ? "false" : "true");
42      if (back) back.setAttribute("aria-hidden", enabled && flipped ? "false" : "true");
43      [front, back].forEach(function (face, index) {
44        if (!face) return;
45        var faceActive = enabled && (flipped ? index === 1 : index === 0);
46        face.querySelectorAll("a, button").forEach(function (control) {
47          control.tabIndex = faceActive ? 0 : -1;
48        });
49      });
50      card.querySelectorAll("[data-copy-agent-id]").forEach(function (button) {
51        button.setAttribute("aria-label", t("Copy Agent ID"));
52      });
53    }
54
55    function scheduleAutoFlip() {
56      cancelAutoFlip();
57      if (reduceMotion || !active()) return;
58      autoFlipTimer = window.setTimeout(function () {
59        autoFlipTimer = 0;
60        if (active()) render(true);
61      }, AUTO_FLIP_MS);
62    }
63
64    function toggleCard() {
65      if (!active()) return;
66      cancelAutoFlip();
67      render(!card.classList.contains("is-flipped"));
68    }
69
70    function onClick(event) {
71      if (isInteractive(event.target) || !active()) return;
72      toggleCard();
73    }
74
75    function onKeydown(event) {
76      if (event.target !== card || !active() || (event.key !== "Enter" && event.key !== " ")) return;
77      event.preventDefault();
78      toggleCard();
79    }
80
81    card.addEventListener("click", onClick);
82    card.addEventListener("keydown", onKeydown);
83    card.addEventListener("pointerdown", cancelAutoFlip);
84    card.addEventListener("focusin", cancelAutoFlip);
85
86    var controller = {
87      activate: function () {
88        render(false);
89        scheduleAutoFlip();
90      },
91      deactivate: function () {
92        cancelAutoFlip();
93        render(false);
94      },
95      refresh: function () {
96        render(card.classList.contains("is-flipped"));
97      },
98      destroy: function () {
99        cancelAutoFlip();
100        card.removeEventListener("click", onClick);
101        card.removeEventListener("keydown", onKeydown);
102        delete card.__businessCardController;
103      }
104    };
105    card.__businessCardController = controller;
106    render(false);
107    if (active()) scheduleAutoFlip();
108    return controller;
109  }
110
111  function safeLocalPath(value) {
112    try {
113      var parsed = new URL(String(value || ""), window.location.href);
114      if (parsed.origin !== window.location.origin) return "";
115      return parsed.pathname + parsed.search + parsed.hash;
116    } catch (_error) {
117      return "";
118    }
119  }
120
121  function normalizeEntry(entry) {
122    var host = String((entry || {}).host || "").trim().toLowerCase();
123    var sourceUrl = safeLocalPath((entry || {}).source_url);
124    var detailUrl = safeLocalPath((entry || {}).detail_url);
125    if (!host || !sourceUrl || !detailUrl) return null;
126    return {
127      host: host,
128      sourceUrl: sourceUrl,
129      detailUrl: detailUrl,
130      preload: (entry || {}).preload || null
131    };
132  }
133
134  function createPreparedSlide(entry) {
135    var slide = document.createElement("article");
136    slide.className = "business-card-coverflow-slide";
137    slide.setAttribute("data-coverflow-slide", "");
138
139    var card = document.createElement("div");
140    card.className = "company-card-pair business-card";
141    card.setAttribute("data-company-card", "");
142    card.setAttribute("data-company-card-pair", "");
143    card.setAttribute("data-company-card-flip", "");
144    card.setAttribute("data-business-card", "");
145    card.setAttribute("tabindex", "-1");
146    card.setAttribute("role", "group");
147    card.setAttribute("aria-label", t("View Agent side"));
148
149    var detail = document.createElement("a");
150    detail.className = "business-card-detail-link";
151    detail.setAttribute("data-coverflow-detail", "");
152    detail.href = entry.detailUrl;
153    detail.innerHTML = t("View full company card") + ' <span aria-hidden="true">↗</span>';
154
155    slide.append(card, detail);
156    window.XAgentCompanyCard.mount(card);
157    if (entry.preload && window.XAgentCompanyCard.renderBootstrap) {
158      window.XAgentCompanyCard.renderBootstrap(card, entry.preload);
159    }
160    card.setAttribute("data-company-card-source", entry.sourceUrl);
161    return { entry: entry, slide: slide, card: card, detail: detail };
162  }
163
164  function readPreloadEntries(stage) {
165    var source = stage.querySelector("[data-featured-card-preload]");
166    if (!source) return [];
167    try {
168      var entries = JSON.parse(source.textContent || "[]");
169      return Array.isArray(entries)
170        ? entries.map(normalizeEntry).filter(Boolean).slice(0, MAX_CARDS)
171        : [];
172    } catch (_error) {
173      return [];
174    }
175  }
176
177  function initCarousel(stage) {
178    var viewport = stage.querySelector("[data-coverflow-viewport]");
179    var track = stage.querySelector("[data-coverflow-track]");
180    var previous = stage.querySelector("[data-coverflow-previous]");
181    var next = stage.querySelector("[data-coverflow-next]");
182    var dots = stage.querySelector("[data-coverflow-dots]");
183    var status = stage.querySelector("[data-coverflow-status]");
184    var message = stage.querySelector("[data-coverflow-message]");
185    if (!viewport || !track || !dots || !window.XAgentCompanyCard) return;
186
187    var slides = [];
188    var controllers = [];
189    var activeIndex = 0;
190    var pointerStart = null;
191    var suppressClick = false;
192
193    function cardName(index) {
194      return slides[index] ? slides[index].entry.host : "";
195    }
196
197    function render(announce) {
198      slides.forEach(function (item, index) {
199        var offset = index - activeIndex;
200        var distance = Math.min(Math.abs(offset), 3);
201        var translate = offset * 64;
202        var rotate = offset * -30;
203        var scale = 1 - distance * 0.12;
204        var visible = distance <= 2;
205        item.slide.style.transform = "translate3d(calc(-50% + " + translate + "%), 0, 0) rotateY(" + rotate + "deg) scale(" + scale + ")";
206        item.slide.style.zIndex = String(20 - distance);
207        item.slide.classList.toggle("is-active", index === activeIndex);
208        item.slide.classList.toggle("is-visible", visible);
209        item.slide.setAttribute("aria-current", index === activeIndex ? "true" : "false");
210        item.slide.setAttribute("aria-hidden", index === activeIndex ? "false" : "true");
211        item.detail.tabIndex = index === activeIndex ? 0 : -1;
212        if (index === activeIndex) controllers[index].activate();
213        else controllers[index].deactivate();
214      });
215      dots.querySelectorAll("button").forEach(function (dot, index) {
216        dot.classList.toggle("is-active", index === activeIndex);
217        if (index === activeIndex) dot.setAttribute("aria-current", "true");
218        else dot.removeAttribute("aria-current");
219      });
220      var disabled = slides.length < 2;
221      if (previous) previous.disabled = disabled;
222      if (next) next.disabled = disabled;
223      if (status && announce !== false && slides.length) {
224        status.textContent = (activeIndex + 1) + " / " + slides.length + ": " + cardName(activeIndex);
225      }
226    }
227
228    function setActive(index, announce) {
229      if (!slides.length) return;
230      activeIndex = (index + slides.length) % slides.length;
231      render(announce);
232    }
233
234    function register(items) {
235      controllers.forEach(function (controller) { controller.destroy(); });
236      controllers = [];
237      slides = items;
238      activeIndex = 0;
239      track.replaceChildren();
240      dots.replaceChildren();
241      slides.forEach(function (item, index) {
242        item.slide.dataset.coverflowIndex = String(index);
243        track.appendChild(item.slide);
244        controllers.push(installCard(item.card, function () { return index === activeIndex; }));
245        item.slide.addEventListener("click", function (event) {
246          if (isInteractive(event.target) || index === activeIndex) return;
247          setActive(index, true);
248        });
249        var dot = document.createElement("button");
250        dot.type = "button";
251        dot.setAttribute("aria-label", t("Show company card") + ": " + item.entry.host);
252        dot.addEventListener("click", function () { setActive(index, true); });
253        dots.appendChild(dot);
254      });
255      render(true);
256      stage.dataset.carouselState = "ready";
257      if (message) message.hidden = true;
258    }
259
260    function findSlide(host) {
261      return slides.find(function (item) { return item.entry.host === host; });
262    }
263
264    function hydrate(item) {
265      return window.XAgentCompanyCard.loadSource(item.card, item.entry.sourceUrl)
266        .then(function () { return item; })
267        .catch(function () { return null; });
268    }
269
270    if (previous) previous.addEventListener("click", function () { setActive(activeIndex - 1, true); });
271    if (next) next.addEventListener("click", function () { setActive(activeIndex + 1, true); });
272    viewport.addEventListener("keydown", function (event) {
273      if (event.key !== "ArrowLeft" && event.key !== "ArrowRight") return;
274      event.preventDefault();
275      setActive(activeIndex + (event.key === "ArrowRight" ? 1 : -1), true);
276    });
277    viewport.addEventListener("pointerdown", function (event) {
278      if (isInteractive(event.target)) return;
279      pointerStart = { x: event.clientX, y: event.clientY };
280    });
281    viewport.addEventListener("pointerup", function (event) {
282      if (!pointerStart) return;
283      var deltaX = event.clientX - pointerStart.x;
284      var deltaY = event.clientY - pointerStart.y;
285      pointerStart = null;
286      if (Math.abs(deltaX) < 48 || Math.abs(deltaX) < Math.abs(deltaY)) return;
287      suppressClick = true;
288      setActive(activeIndex + (deltaX < 0 ? 1 : -1), true);
289      window.setTimeout(function () { suppressClick = false; }, 0);
290    });
291    viewport.addEventListener("pointercancel", function () { pointerStart = null; });
292    viewport.addEventListener("click", function (event) {
293      if (!suppressClick) return;
294      event.preventDefault();
295      event.stopImmediatePropagation();
296    }, true);
297
298    var collectionSource = safeLocalPath(stage.getAttribute("data-company-card-carousel-source"));
299    if (!collectionSource) return;
300    var preloadedEntries = readPreloadEntries(stage);
301    if (preloadedEntries.length) {
302      register(preloadedEntries.map(createPreparedSlide));
303    } else {
304      stage.dataset.carouselState = "loading";
305      if (message) {
306        message.hidden = false;
307        message.textContent = t("Loading featured company cards");
308      }
309    }
310    fetch(collectionSource, { headers: { Accept: "application/json" }, credentials: "omit" })
311      .then(function (response) {
312        return response.json().then(function (payload) {
313          if (!response.ok || !payload || !payload.ok || !Array.isArray(payload.entries)) {
314            throw new Error("featured_cards_unavailable");
315          }
316          return payload.entries.map(normalizeEntry).filter(Boolean).slice(0, MAX_CARDS);
317        });
318      })
319      .then(function (entries) {
320        var sameRoster = entries.length === slides.length && entries.every(function (entry) {
321          return Boolean(findSlide(entry.host));
322        });
323        if (!sameRoster) {
324          register(entries.map(createPreparedSlide));
325        } else {
326          entries.forEach(function (entry) {
327            var item = findSlide(entry.host);
328            item.entry.sourceUrl = entry.sourceUrl;
329            item.entry.detailUrl = entry.detailUrl;
330            item.detail.href = entry.detailUrl;
331          });
332        }
333        return Promise.all(slides.map(hydrate));
334      })
335      .then(function (items) {
336        var loaded = items.filter(Boolean);
337        if (!slides.length || !loaded.length) throw new Error("featured_cards_unavailable");
338        stage.dataset.carouselState = "ready";
339        if (message) message.hidden = true;
340      })
341      .catch(function () {
342        if (slides.length) {
343          stage.dataset.carouselState = "ready";
344          if (message) message.hidden = true;
345          return;
346        }
347        controllers.forEach(function (controller) { controller.destroy(); });
348        controllers = [];
349        slides = [];
350        track.replaceChildren();
351        dots.replaceChildren();
352        if (previous) previous.disabled = true;
353        if (next) next.disabled = true;
354        stage.dataset.carouselState = "fallback";
355        var unavailable = t("Featured company cards are temporarily unavailable");
356        if (message) {
357          message.hidden = false;
358          message.textContent = unavailable;
359        }
360        if (status) status.textContent = unavailable;
361      });
362
363    window.addEventListener("xagent:languagechange", function () {
364      if (message && stage.dataset.carouselState !== "ready") {
365        message.textContent = t(stage.dataset.carouselState === "loading"
366          ? "Loading featured company cards"
367          : "Featured company cards are temporarily unavailable");
368      }
369      controllers.forEach(function (controller) { controller.refresh(); });
370      slides.forEach(function (item) {
371        item.detail.innerHTML = t("View full company card") + ' <span aria-hidden="true">↗</span>';
372      });
373      render(false);
374    });
375  }
376
377  document.querySelectorAll("[data-company-card-carousel]").forEach(initCarousel);
378  document.querySelectorAll("[data-business-card]").forEach(function (card) {
379    if (!card.closest("[data-company-card-carousel]")) installCard(card);
380  });
381  window.XAgentBusinessCard = { install: installCard };
382})();

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.