PageSourceSearch

https://iiiot-infotech.com/assets/site.js?v=20260823-6

js iiiot-infotech.com collected 2026-10-07 08:50:10 UTC 15,889 bytes, 369 lines download raw bytes

1if (window.lucide) window.lucide.createIcons();
2
3const menuButton = document.querySelector('.menu-toggle');
4const navigation = document.querySelector('.desktop-nav');
5const navGroups = [...document.querySelectorAll('.nav-group')];
6
7const closeMegaMenus = (exceptGroup) => {
8  navGroups.forEach((group) => {
9    if (group === exceptGroup) return;
10    group.classList.remove('is-open');
11    group.querySelector('.nav-trigger')?.setAttribute('aria-expanded', 'false');
12  });
13};
14
15navGroups.forEach((group) => {
16  const trigger = group.querySelector('.nav-trigger');
17  const setOpen = (isOpen) => {
18    closeMegaMenus(isOpen ? group : null);
19    group.classList.toggle('is-open', isOpen);
20    trigger.setAttribute('aria-expanded', String(isOpen));
21  };
22
23  trigger.addEventListener('click', () => {
24    if (window.innerWidth > 980) setOpen(true);
25    else setOpen(!group.classList.contains('is-open'));
26  });
27  group.addEventListener('mouseenter', () => {
28    if (window.innerWidth > 980) setOpen(true);
29  });
30  group.addEventListener('mouseleave', () => {
31    if (window.innerWidth > 980) setOpen(false);
32  });
33});
34
35document.addEventListener('click', (event) => {
36  if (!event.target.closest('.nav-group')) closeMegaMenus();
37});
38
39document.addEventListener('keydown', (event) => {
40  if (event.key !== 'Escape') return;
41  const openTrigger = document.querySelector('.nav-group.is-open .nav-trigger');
42  closeMegaMenus();
43  openTrigger?.focus();
44});
45
46if (menuButton && navigation) {
47  const closeMenu = () => {
48    menuButton.setAttribute('aria-expanded', 'false');
49    navigation.classList.remove('is-open');
50    document.body.classList.remove('menu-open');
51    closeMegaMenus();
52  };
53
54  menuButton.addEventListener('click', () => {
55    const isOpen = menuButton.getAttribute('aria-expanded') === 'true';
56    menuButton.setAttribute('aria-expanded', String(!isOpen));
57    navigation.classList.toggle('is-open', !isOpen);
58    document.body.classList.toggle('menu-open', !isOpen);
59  });
60
61  navigation.addEventListener('click', (event) => {
62    if (event.target.closest('a')) closeMenu();
63  });
64
65  window.addEventListener('resize', () => {
66    if (window.innerWidth > 980) closeMenu();
67  });
68}
69
70const reducedMotion = window.matchMedia('(prefers-reduced-motion: reduce)').matches;
71const hero = document.querySelector('.hero');
72const heroSlides = [...document.querySelectorAll('.hero-slide')];
73const heroBackdrops = [...document.querySelectorAll('.hero-backdrop')];
74const carouselDots = [...document.querySelectorAll('.carousel-dot')];
75
76if (hero && heroSlides.length > 1) {
77  let activeSlide = 0;
78  let autoplayTimer;
79  let pointerStart = 0;
80
81  const showSlide = (index) => {
82    activeSlide = (index + heroSlides.length) % heroSlides.length;
83    heroSlides.forEach((slide, slideIndex) => {
84      const isActive = slideIndex === activeSlide;
85      slide.classList.toggle('is-active', isActive);
86      slide.setAttribute('aria-hidden', String(!isActive));
87      heroBackdrops[slideIndex]?.classList.toggle('is-active', isActive);
88      carouselDots[slideIndex]?.classList.toggle('is-active', isActive);
89      if (isActive) carouselDots[slideIndex]?.setAttribute('aria-current', 'true');
90      else carouselDots[slideIndex]?.removeAttribute('aria-current');
91    });
92  };
93
94  const stopAutoplay = () => clearInterval(autoplayTimer);
95  const startAutoplay = () => {
96    stopAutoplay();
97    if (!reducedMotion && !document.hidden) {
98      autoplayTimer = setInterval(() => showSlide(activeSlide + 1), 6000);
99    }
100  };
101  const selectSlide = (index) => {
102    showSlide(index);
103    startAutoplay();
104  };
105
106  hero.querySelector('.carousel-prev')?.addEventListener('click', () => selectSlide(activeSlide - 1));
107  hero.querySelector('.carousel-next')?.addEventListener('click', () => selectSlide(activeSlide + 1));
108  carouselDots.forEach((dot, index) => dot.addEventListener('click', () => selectSlide(index)));
109  hero.addEventListener('keydown', (event) => {
110    if (event.key === 'ArrowLeft') selectSlide(activeSlide - 1);
111    if (event.key === 'ArrowRight') selectSlide(activeSlide + 1);
112  });
113  hero.addEventListener('mouseenter', stopAutoplay);
114  hero.addEventListener('mouseleave', startAutoplay);
115  hero.addEventListener('focusin', stopAutoplay);
116  hero.addEventListener('focusout', (event) => {
117    if (!hero.contains(event.relatedTarget)) startAutoplay();
118  });
119  hero.addEventListener('pointerdown', (event) => { pointerStart = event.clientX; });
120  hero.addEventListener('pointerup', (event) => {
121    const distance = event.clientX - pointerStart;
122    if (Math.abs(distance) > 55) selectSlide(activeSlide + (distance < 0 ? 1 : -1));
123  });
124  document.addEventListener('visibilitychange', () => document.hidden ? stopAutoplay() : startAutoplay());
125  startAutoplay();
126}
127
128const revealItems = document.querySelectorAll('.reveal');
129
130const priorityTabs = [...document.querySelectorAll('.priority-tab')];
131const priorityPanels = [...document.querySelectorAll('.priority-panel')];
132
133if (priorityTabs.length && priorityPanels.length) {
134  const selectPriority = (index, moveFocus = false) => {
135    priorityTabs.forEach((tab, tabIndex) => {
136      const isActive = tabIndex === index;
137      tab.classList.toggle('is-active', isActive);
138      tab.setAttribute('aria-selected', String(isActive));
139      tab.tabIndex = isActive ? 0 : -1;
140      if (moveFocus && isActive) tab.focus();
141    });
142    priorityPanels.forEach((panel, panelIndex) => {
143      const isActive = panelIndex === index;
144      panel.classList.toggle('is-active', isActive);
145      panel.hidden = !isActive;
146    });
147  };
148
149  priorityTabs.forEach((tab, index) => {
150    tab.addEventListener('click', () => selectPriority(index));
151    tab.addEventListener('keydown', (event) => {
152      if (!['ArrowDown', 'ArrowUp', 'ArrowLeft', 'ArrowRight', 'Home', 'End'].includes(event.key)) return;
153      event.preventDefault();
154      let nextIndex = index;
155      if (event.key === 'Home') nextIndex = 0;
156      else if (event.key === 'End') nextIndex = priorityTabs.length - 1;
157      else if (event.key === 'ArrowDown' || event.key === 'ArrowRight') nextIndex = (index + 1) % priorityTabs.length;
158      else nextIndex = (index - 1 + priorityTabs.length) % priorityTabs.length;
159      selectPriority(nextIndex, true);
160    });
161  });
162}
163
164if (reducedMotion || document.hidden || !('IntersectionObserver' in window)) {
165  revealItems.forEach((item) => item.classList.add('is-visible'));
166} else {
167  const revealObserver = new IntersectionObserver((entries, observer) => {
168    entries.forEach((entry) => {
169      if (!entry.isIntersecting) return;
170      entry.target.classList.add('is-visible');
171      observer.unobserve(entry.target);
172    });
173  }, { threshold: 0.12 });
174
175  revealItems.forEach((item) => revealObserver.observe(item));
176}
177
178const year = document.querySelector('[data-current-year]');
179if (year) year.textContent = new Date().getFullYear();
180
181const backToTop = document.querySelector('.back-to-top');
182
183if (backToTop) {
184  const updateBackToTop = () => {
185    backToTop.classList.toggle('is-visible', window.scrollY > 320);
186  };
187
188  window.addEventListener('scroll', updateBackToTop, { passive: true });
189  backToTop.addEventListener('click', () => {
190    backToTop.classList.remove('is-visible');
191    window.scrollTo({ top: 0, behavior: reducedMotion ? 'auto' : 'smooth' });
192  });
193  updateBackToTop();
194}
195
196const resourceFilters = [...document.querySelectorAll('[data-resource-filter]')];
197const resourceCards = [...document.querySelectorAll('[data-resource-type]')];
198
199const customerViewport = document.querySelector('.customer-carousel-viewport');
200const customerTrack = document.querySelector('.customer-carousel-track');
201const customerSlides = [...document.querySelectorAll('.customer-slide')];
202
203if (customerViewport && customerTrack && customerSlides.length) {
204  let customerIndex = 0;
205  let customerTimer;
206  const visibleCustomers = () => window.innerWidth <= 680 ? 1 : window.innerWidth <= 980 ? 2 : 4;
207  const showCustomer = (index) => {
208    const maxIndex = Math.max(0, customerSlides.length - visibleCustomers());
209    customerIndex = Math.min(Math.max(index, 0), maxIndex);
210    customerTrack.style.transform = `translateX(-${customerIndex * (100 / visibleCustomers())}%)`;
211  };
212  const startCustomerCarousel = () => {
213    window.clearInterval(customerTimer);
214    if (!reducedMotion) customerTimer = window.setInterval(() => {
215      const maxIndex = Math.max(0, customerSlides.length - visibleCustomers());
216      showCustomer(customerIndex >= maxIndex ? 0 : customerIndex + 1);
217    }, 4200);
218  };
219  document.querySelector('[data-customer-prev]')?.addEventListener('click', () => { showCustomer(customerIndex - 1); startCustomerCarousel(); });
220  document.querySelector('[data-customer-next]')?.addEventListener('click', () => { showCustomer(customerIndex + 1); startCustomerCarousel(); });
221  customerViewport.addEventListener('mouseenter', () => window.clearInterval(customerTimer));
222  customerViewport.addEventListener('mouseleave', startCustomerCarousel);
223  customerViewport.addEventListener('focusin', () => window.clearInterval(customerTimer));
224  customerViewport.addEventListener('focusout', startCustomerCarousel);
225  window.addEventListener('resize', () => showCustomer(customerIndex));
226  showCustomer(0);
227  startCustomerCarousel();
228}
229
230resourceFilters.forEach((filterButton) => {
231  filterButton.addEventListener('click', () => {
232    const selectedType = filterButton.dataset.resourceFilter;
233    resourceFilters.forEach((button) => button.classList.toggle('is-active', button === filterButton));
234    resourceCards.forEach((card) => {
235      card.hidden = selectedType !== 'all' && card.dataset.resourceType !== selectedType;
236    });
237  });
238});
239
240const contactEndpoint = 'https://formspree.io/f/mlezaqok';
241
242document.querySelectorAll('.contact-form').forEach((form) => {
243  const status = document.createElement('p');
244  status.className = 'form-status form-field-full';
245  status.setAttribute('role', 'status');
246  form.append(status);
247
248  form.addEventListener('submit', async (event) => {
249    event.preventDefault();
250    const submitButton = form.querySelector('button[type="submit"]');
251    submitButton?.setAttribute('disabled', '');
252    status.textContent = 'Sending your enquiry...';
253
254    try {
255      const response = await fetch(form.action || contactEndpoint, {
256        method: 'POST',
257        body: new FormData(form),
258        headers: { Accept: 'application/json' }
259      });
260      if (!response.ok) throw new Error('Contact submission failed');
261      form.reset();
262      status.textContent = 'Thank you. Your enquiry has been sent successfully.';
263    } catch {
264      status.textContent = 'We could not send your enquiry. Please try again.';
265    } finally {
266      submitButton?.removeAttribute('disabled');
267    }
268  });
269});
270
271const submitContactAndOpen = async (form, pdfUrl, statusElement) => {
272  const submitButton = form.querySelector('button[type="submit"]');
273  form.querySelector('.pdf-access-link')?.remove();
274  submitButton?.setAttribute('disabled', '');
275  if (statusElement) statusElement.textContent = 'Submitting your details...';
276
277  const formData = new FormData(form);
278  formData.set('requested_pdf', pdfUrl);
279  formData.set('_subject', 'IIIOT PDF access request');
280
281  try {
282    const response = await fetch(form.action || contactEndpoint, {
283      method: 'POST',
284      body: formData,
285      headers: { Accept: 'application/json' }
286    });
287    if (!response.ok) throw new Error('Contact submission failed');
288    const pdfLink = document.createElement('a');
289    pdfLink.className = 'button button-primary pdf-access-link';
290    pdfLink.href = pdfUrl;
291    pdfLink.target = '_blank';
292    pdfLink.rel = 'noopener noreferrer';
293    pdfLink.dataset.pdfAuthorized = 'true';
294    pdfLink.textContent = 'View PDF in new tab';
295    form.append(pdfLink);
296    pdfLink.focus();
297    if (statusElement) statusElement.textContent = 'Thank you. Your PDF is ready.';
298    return true;
299  } catch {
300    if (statusElement) statusElement.textContent = 'We could not submit your details. Please try again.';
301    return false;
302  } finally {
303    submitButton?.removeAttribute('disabled');
304  }
305};
306
307document.querySelectorAll('form[data-download-url]').forEach((form) => {
308  const status = document.createElement('p');
309  status.className = 'form-status';
310  status.setAttribute('role', 'status');
311  form.append(status);
312  form.addEventListener('submit', async (event) => {
313    event.preventDefault();
314    await submitContactAndOpen(form, form.dataset.downloadUrl, status);
315  });
316});
317
318const pdfGate = document.createElement('dialog');
319pdfGate.className = 'pdf-gate';
320pdfGate.setAttribute('aria-labelledby', 'pdf-gate-title');
321pdfGate.innerHTML = `<div class="pdf-gate-head"><div><p class="eyebrow">Resource access</p><h2 id="pdf-gate-title">Enter your details to view this PDF</h2></div><button class="pdf-gate-close" type="button" aria-label="Close PDF access form">&times;</button></div><p class="pdf-gate-copy">Submit your contact information and the requested document will open in a new tab.</p><form action="${contactEndpoint}" method="post"><div class="pdf-gate-fields"><label>Full name<input name="name" autocomplete="name" required></label><label>Work email<input name="email" type="email" autocomplete="email" required></label><label>Company<input name="company" autocomplete="organization" required></label><label>Phone<input name="phone" type="tel" autocomplete="tel" required></label></div><label class="pdf-gate-consent"><input name="contact_consent" type="checkbox" value="Yes" required><span>I agree that IIIOT may contact me about this resource and related solutions.</span></label><button class="button button-primary" type="submit">Submit and view PDF</button><p class="form-status" role="status"></p></form>`;
322document.body.append(pdfGate);
323
324let requestedPdf = '';
325document.addEventListener('click', (event) => {
326  const pdfLink = event.target.closest('a[href]');
327  if (!pdfLink || pdfLink.dataset.pdfAuthorized || !pdfLink.href.split(/[?#]/)[0].toLowerCase().endsWith('.pdf')) return;
328  event.preventDefault();
329  requestedPdf = pdfLink.href;
330  pdfGate.querySelector('.form-status').textContent = '';
331  pdfGate.showModal();
332});
333
334pdfGate.querySelector('.pdf-gate-close').addEventListener('click', () => pdfGate.close());
335pdfGate.addEventListener('click', (event) => {
336  if (event.target === pdfGate) pdfGate.close();
337});
338pdfGate.querySelector('form').addEventListener('submit', async (event) => {
339  event.preventDefault();
340  await submitContactAndOpen(event.currentTarget, requestedPdf, pdfGate.querySelector('.form-status'));
341});
342
343const externalProductPages = {
344  iiotalytics: 'https://iiotalytics.com/Products/IIoTalytics-Veda',
345  industrvalytics: 'https://iiotalytics.com/Products/IndustrValytics',
346  manufactgenai: 'https://iiotalytics.com/Products/ManufactGenAI',
347  immersivexr: 'https://iiotalytics.com/Products/ImmersiveXR',
348  xtraceability: 'https://iiotalytics.com/Products/XTraceability',
349  'video-analytics': 'https://iiotalytics.com/Products/IndustrValytics'
350};
351const productRoute = window.location.pathname.match(/\/products\/([^/]+)(?:\/index\.html)?\/?$/);
352const productActions = document.querySelector('.product-hero-actions');
353
354if (productRoute && productActions && externalProductPages[productRoute[1]]) {
355  const productDetailsLink = document.createElement('a');
356  productDetailsLink.className = 'button button-ghost';
357  productDetailsLink.href = externalProductPages[productRoute[1]];
358  productDetailsLink.target = '_blank';
359  productDetailsLink.rel = 'noopener noreferrer';
360  productDetailsLink.textContent = 'More product details';
361  productActions.append(productDetailsLink);
362}
363
364const liveChatScript = document.createElement('script');
365liveChatScript.src = 'https://mylivechat.com/chatinline.aspx?hccid=18708004';
366liveChatScript.async = true;
367liveChatScript.defer = true;
368liveChatScript.dataset.cfasync = 'false';
369document.body.append(liveChatScript);

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.