PageSourceSearch

https://evertz.com/js/web.js?039

js evertz.com collected 2026-10-02 03:45:34 UTC 39,725 bytes, 1,185 lines download raw bytes

1/**
2 * Evertz.com global javascript environment file
3 *
4 * This file should be included on every page that uses the default
5 * Evertz.com website template. It must be included at the end of the
6 * page source.
7 *
8 * Requires: jquery
9 *
10 */
11
12var PAGE_SUBMENU;
13var PAGE_DOWNROOT = '/';
14
15const MIRROR = (window.location.protocol == 'file:');
16if (MIRROR) {
17  let favicon = document.querySelector('link[rel="shortcut icon"]');
18  if (favicon) PAGE_DOWNROOT = favicon.getAttribute('href').replace(/favicon\.ico/, '');
19}
20
21
22/**
23 * Handy element positioning function from Quirksmode.org
24 *
25 */
26function findPos(obj) {
27  var curleft = curtop = 0;
28  do {
29    if (!obj) break;
30    curleft += obj.offsetLeft;
31    curtop += obj.offsetTop;
32  } while (obj = obj.offsetParent);
33  return [curleft, curtop];
34}
35
36
37/**
38 * Return the dimensions of the viewport, also from Quirksmode.org
39 *
40 */
41function innerDimensions() {
42  if (self.innerHeight) {
43    return [self.innerWidth, self.innerHeight];
44  } else if (document.documentElement && document.documentElement.clientHeight) {
45    return [document.documentElement.clientWidth, document.documentElement.clientHeight];
46  } else if (document.body)
47    return [document.body.clientWidth, document.body.clientHeight];
48  return [0, 0];
49}
50
51
52/**
53 * Calculate brightness value by RGB or HEX color.
54 * @param color (String) The color value in RGB or HEX (for example: #000000 || #000 || rgb(0,0,0) || rgba(0,0,0,0))
55 * @returns (Number) The brightness value (dark) 0 ... 255 (light)
56 */
57function brightnessByColor(color) {
58  color = '' + color;
59  let r = false, m, isHEX = color.indexOf('#') == 0, isRGB = color.indexOf('rgb') == 0;
60  if (isHEX) {
61    m = color.substr(1).match(color.length == 7 ? /(\S{2})/g : /(\S{1})/g);
62    if (m) r = parseInt(m[0], 16), g = parseInt(m[1], 16), b = parseInt(m[2], 16);
63  }
64  if (isRGB) {
65    m = color.match(/(\d+)/g);
66    if (m) r = m[0], g = m[1], b = m[2];
67  }
68  if (r) return ((r*299)+(g*587)+(b*114))/1000;
69}
70
71
72/**
73 * Natural sort function
74 *
75 */
76function alphanum(a, b) {
77  function chunkify(t) {
78    let tz = [], x = 0, y = -1, n = 0, i, j;
79    while (i = (j = t.charAt(x++)).charCodeAt(0)) {
80      let m = (i == 46 || (i >=48 && i <= 57));
81      if (m !== n) {
82        tz[++y] = '';
83        n = m;
84      }
85      tz[y] += j;
86    }
87    return tz;
88  }
89  let aa = chunkify(a);
90  let bb = chunkify(b);
91  for (let x = 0; aa[x] && bb[x]; x++) {
92    if (aa[x] !== bb[x]) {
93      let c = Number(aa[x]), d = Number(bb[x]);
94      if (c == aa[x] && d == bb[x]) {
95        return c - d;
96      } else return (aa[x] > bb[x]) ? 1 : -1;
97    }
98  }
99  return aa.length - bb.length;
100}
101
102
103/**
104 * Scroll to an element, accounting for main and sub menus
105 *
106 */
107function scrollToElem(target) {
108  if (typeof target == 'string') {
109    let href = target;
110    if (href.indexOf('#') === 0) href = href.substring(1);
111    target = document.getElementById(href);
112    if (!target) target = document.querySelector('a[name="' + href + '"]');
113  }
114
115  if (target) {
116    let pos = findPos(target);
117    let nav1 = document.querySelector('nav.navbar');
118    nav1 = (nav1) ? nav1.offsetHeight : 0;
119    let nav2 = document.querySelector('.sub-menu');
120    nav2 = (nav2) ? nav2.offsetHeight : 0;
121    pos = pos[1] - nav1 - nav2 - 10;
122    let oldpos = window.scrollY;
123
124    setTimeout(function() {
125      window.scrollTo({ top: oldpos, left: 0, behavior: 'auto' });
126      $('html, body').animate({ scrollTop: pos }, 300);
127    }, 0);
128    return true;
129  } else return false;
130}
131
132// Scroll to Top button action
133document.getElementById('scrollToTop').addEventListener('click', function() {
134  $('html, body').animate({ scrollTop: 0 }, 300);
135  window.history.replaceState({}, '', window.location.href.replace(/#.*$/, ''));
136}, false);
137
138
139// Hide vertical scroll bar when main navbar dropdowns are shown on mobile
140let navNavbar = document.querySelector('nav.navbar');
141if (navNavbar) {
142  navNavbar.addEventListener('show.bs.dropdown', function() {
143    setTimeout(function() {
144      navNavbar.classList.add('dropdown-open');
145    }, 5);
146  }, false);
147  navNavbar.addEventListener('hidden.bs.dropdown', function() {
148    navNavbar.classList.remove('dropdown-open');
149    // document.documentElement.style.overflow = '';
150  }, false);
151
152  navNavbar.addEventListener('show.bs.collapse', function() {
153    setTimeout(function() {
154      navNavbar.classList.add('dropdown-open');
155    }, 5);
156    // if (innerDimensions()[0] < 768)
157    //   document.documentElement.style.overflow = 'hidden';
158  });
159  navNavbar.addEventListener('hidden.bs.collapse', function() {
160    navNavbar.classList.remove('dropdown-open');
161    // document.documentElement.style.overflow = '';
162  });
163
164  // Expand and contract the search field on focus and blur
165  let navbarTypeahead = document.querySelectorAll('nav.navbar form .os_typeahead');
166  let navbarSupportedContent = document.getElementById('navbarSupportedContent');
167  for (let x = 0; x < navbarTypeahead.length; x++) {
168    navbarTypeahead[x].addEventListener('focus', function() {
169      clearTimeout(navNavbar.timeout);
170      navNavbar.classList.add('searching');
171      navbarSupportedContent.classList.add('squished');
172    }, false);
173    navbarTypeahead[x].addEventListener('blur', function() {
174      navNavbar.timeout = setTimeout(function() {
175        navNavbar.classList.remove('searching');
176        setTimeout(function() {
177          navbarSupportedContent.classList.remove('squished');
178        }, 300);
179      }, 200);
180    }, false);
181  }
182
183  let searchSubmit = document.getElementById('searchSubmit');
184  if (searchSubmit) {
185    searchSubmit.addEventListener('click', function(e) {
186      if (!navNavbar.classList.contains('searching')) {
187        e.preventDefault();
188        clearTimeout(navNavbar.timeout);
189        navNavbar.classList.add('searching');
190        for (let x = 0; x < navbarTypeahead.length; x++)
191          navbarTypeahead[x].focus();
192        return false;
193      } return true;
194    }, false);
195  }
196}
197
198
199// Clear and exit an HTTP Authenticated session
200function httpAuthLogout() {
201  let xmlhttp;
202  if (window.XMLHttpRequest) {
203    xmlhttp = new XMLHttpRequest();
204
205  // code for IE
206  } else if (window.ActiveXObject)
207    xmlhttp = new ActiveXObject('Microsoft.XMLHTTP');
208
209  // IE clear HTTP Authentication
210  if (window.ActiveXObject) {
211    document.execCommand('ClearAuthenticationCache');
212    window.location.href = '/';
213
214  } else {
215    xmlhttp.open('GET', window.location.pathname, true, 'logout', 'logout');
216    xmlhttp.send('');
217    xmlhttp.onreadystatechange = function() {
218      if (xmlhttp.readyState == 4) {
219        window.location.href = '/';
220      }
221    }
222  }
223  return false;
224}
225
226
227
228// Standard Evertz image slideshow
229for (let x = 0, slideShows = document.querySelectorAll('ul.slideshow'); x < slideShows.length; x++) {
230  slideShows[x].lis = slideShows[x].getElementsByTagName('li');
231
232  for (let y = 0; y < slideShows[x].lis.length; y++)
233    if (y) slideShows[x].lis[y].classList.add('d-none');
234
235  let prev = document.createElement('div');
236      prev.classList.add('prev', 'h-100');
237      prev.setAttribute('role', 'button');
238      prev.parent = slideShows[x];
239      prev.addEventListener('click', function() {
240        let idx = 0;
241        for (let y = 0; y < this.parent.lis.length; y++)
242          if (!this.parent.lis[y].classList.contains('d-none')) idx = y;
243        if (--idx < 0) idx = this.parent.lis.length - 1;
244        for (let y = 0; y < this.parent.lis.length; y++) {
245          if (y == idx) {
246            this.parent.lis[y].classList.remove('d-none');
247          } else this.parent.lis[y].classList.add('d-none');
248        }
249      }, false);
250  slideShows[x].appendChild(prev);
251
252  let next = document.createElement('div');
253      next.classList.add('next', 'h-100');
254      next.setAttribute('role', 'button');
255      next.parent = slideShows[x];
256      next.addEventListener('click', function() {
257        let idx = 0;
258        for (let y = 0; y < this.parent.lis.length; y++)
259          if (!this.parent.lis[y].classList.contains('d-none')) idx = y;
260        if (++idx > this.parent.lis.length - 1) idx = 0;
261        for (let y = 0; y < this.parent.lis.length; y++) {
262          if (y == idx) {
263            this.parent.lis[y].classList.remove('d-none');
264          } else this.parent.lis[y].classList.add('d-none');
265        }
266      }, false);
267  slideShows[x].appendChild(next);
268}
269
270
271
272// Link Applications/Solutions card blocks
273let cardBlocks = document.querySelectorAll('.card-link');
274for (let x = 0; x < cardBlocks.length; x++) {
275  let a = cardBlocks[x].getElementsByTagName('a')[0];
276  if (a) {
277    cardBlocks[x].title = a.href;
278    cardBlocks[x].addEventListener('click', function() {
279      window.location.href = a.href;
280    }, false);
281  }
282}
283
284
285
286// Ensure card-flip blocks are tall enough to fit their backside content
287function cardFlipHeight() {
288  let cardFlips = document.querySelectorAll('.card-flip');
289  for (let x = 0; x < cardFlips.length; x++) {
290    let cf = cardFlips[x].querySelector('.card-front');
291    cf.style.minHeight = '0';
292    cf.classStorage = cf.className;
293    cf.className = 'card-front';
294
295    if (cf.offsetHeight) {
296      let cb = cardFlips[x].querySelector('.card-back');
297      cb.classStorage = cb.className;
298      cb.className = 'card-back';
299
300      if (cb.offsetHeight > cf.offsetHeight)
301        cf.style.minHeight = cb.offsetHeight + 'px';
302
303      cb.className = cb.classStorage;
304    }
305    cf.className = cf.classStorage;
306  }
307}
308
309
310// Execute on a node which may contain a replaced inline-text-svg logo
311// to ensure it's the proper colour to be read.
312function svgLogoColorSet(node) {
313  let img = node.querySelectorAll(':scope img.inline-text-svg');
314  for (let x = 0, bright; x < img.length; x++) {
315    bright = brightnessByColor(window.getComputedStyle(img[x].parentNode).color);
316    img[x].src = img[x].src.replace(/(\.white)?\.svg$/, (bright < 128) ? '.svg' : '.white.svg');
317  }
318}
319
320
321// Parallax section blocks
322let parallaxSections = document.querySelectorAll('#main-content section.parallax');
323for (let x = 0; x < parallaxSections.length; x++) {
324  let img = parallaxSections[x].getElementsByTagName('img')[0];
325  if (img) {
326    parallaxSections[x].style.backgroundImage = 'url(' + img.src + ')';
327    parallaxSections[x].removeChild(img);
328  }
329}
330
331
332
333// Stop videos in modals from playing when modals are closed
334let allModals = document.querySelectorAll('.modal');
335for (let x = 0; x < allModals.length; x++) {
336  allModals[x].vids = allModals[x].querySelectorAll('video');
337  if (allModals[x].vids.length) {
338    allModals[x].addEventListener('hide.bs.modal', function() {
339      for (let y = 0; y < this.vids.length; y++)
340        this.vids[y].pause();
341    }, false);
342  }
343  allModals[x].ytvids = allModals[x].querySelectorAll('iframe.ytvideo');
344  if (allModals[x].ytvids.length) {
345    allModals[x].addEventListener('hide.bs.modal', function() {
346      for (let y = 0; y < this.ytvids.length; y++) {
347        let dummySrc = this.ytvids[y].src;
348        this.ytvids[y].src = dummySrc;
349      }
350    }, false);
351  }
352}
353
354
355
356// Release Notes modal on Firmware and Software downloads pages
357let modalReleaseNotes = document.getElementById('modalReleaseNotes');
358if (modalReleaseNotes) {
359  modalReleaseNotes.addEventListener('show.bs.modal', function (event) {
360    let button = event.relatedTarget;
361    let modalText = modalReleaseNotes.querySelector('textarea');
362
363    let http = getHTTPObject();
364    http.open('GET', button.getAttribute('href'), true);
365    http.onreadystatechange = function() {
366      if (http.readyState == 4)
367        modalText.value = http.responseText;
368    };
369    http.send(null);
370  });
371  modalReleaseNotes.addEventListener('hide.bs.modal', function (event) {
372    let modalText = modalReleaseNotes.querySelector('textarea');
373    modalText.scrollTop = 0;
374  });
375}
376
377
378
379/**
380 * Match text in text nodes and replace with callback function
381 *   - From: https://stackoverflow.com/a/29301739
382 *
383 * @param node: Element - document node to search through for text nodes
384 * @param regex: Regex - text to match for replacement
385 * @param callback: Function - what to do with the matched text
386 * @param excludeElements: Array - list of elements to exclude
387 *
388 */
389function matchText(node, regex, callback, excludeElements) { 
390  excludeElements || (excludeElements = ['script', 'style', 'iframe', 'canvas', 'textarea', 'select', 'input']);
391  if (!node || !node.firstChild) return node;
392  let child = node.firstChild;
393
394  while (child) {
395    switch (child.nodeType) {
396      case 1:
397        if (excludeElements.indexOf(child.tagName.toLowerCase()) > -1) break;
398        matchText(child, regex, callback, excludeElements);
399        break;
400      case 3:
401        let bk = 0;
402        child.data.replace(regex, function(all) {
403          let args = [].slice.call(arguments);
404          let newTextNode = child.splitText(args[args.length - 2] + bk);
405          bk -= child.data.length + all.length;
406          newTextNode.data = newTextNode.data.substr(all.length);
407          child.parentNode.insertBefore(
408            callback.apply(window, [child].concat(args)),
409            newTextNode
410          );
411          child = newTextNode;
412        });
413        regex.lastIndex = 0;
414    }
415    child = child.nextSibling;
416  }
417  return node;
418}
419
420
421
422// Apply -dark class to the entire carousel depending on current slide
423let frontPageCarousel = document.getElementById('frontPageCarousel');
424if (frontPageCarousel) {
425  frontPageCarousel.addEventListener('slide.bs.carousel', function(e) {
426    let items = this.querySelectorAll('.carousel-inner .carousel-item');
427    if (items[e.to]) {
428      if (items[e.to].classList.contains('carousel-dark')) {
429        this.classList.add('carousel-dark');
430      } else this.classList.remove('carousel-dark');
431    }
432  }, false);
433}
434
435
436// Hide <time> events in .event-list elements that are in the past
437let eventlist = document.querySelectorAll('.event-list');
438for (let x = 0, items, today = Math.floor(Date.now() / 1000); x < eventlist.length; x++) {
439  items = 0;
440  let dates = eventlist[x].querySelectorAll('.event-list * time:first-of-type');
441  for (let y = 0, elemDate; y < dates.length; y++) {
442    elemDate = new Date(dates[y].getAttribute('datetime'));
443    if (Math.floor(elemDate.getTime() / 1000) < today - 86400) {
444      items++;
445      dates[y].parentNode.style.display = 'none';
446    }
447  }
448  if (items == dates.length) { // If everything was hidden
449    let item = dates[0].parentNode.cloneNode(false);
450        item.style.display = '';
451      let em = document.createElement('em');
452          em.appendChild(document.createTextNode('No events scheduled.'));
453        item.appendChild(em);
454      eventlist[x].appendChild(item);
455  }
456}
457
458
459// Attach scrollto functions to accordion open calls
460let accordionElements = document.querySelectorAll('.accordion-collapse');
461for (let x = 0; x < accordionElements.length; x++) {
462  accordionElements[x].addEventListener('shown.bs.collapse', function() {
463    let elemheaderid = this.getAttribute('aria-labelledby');
464    scrollToElem(elemheaderid);
465  }, false);
466}
467
468
469
470// Add container <div> elements to all headers in table.display <th>
471let tableDisplayTH = document.querySelectorAll('.product-table table.tip-up thead th');
472for (let x = 0; x < tableDisplayTH.length; x++) {
473  let div = document.createElement('div');
474  while (tableDisplayTH[x].firstChild)
475    div.appendChild(tableDisplayTH[x].firstChild);
476  tableDisplayTH[x].appendChild(div);
477}
478function tableDisplayHeaderHeight() {
479  for (let x = 0; x < tableDisplayTH.length; x++) {
480    if (!tableDisplayTH[x].colspan) {
481      let div = tableDisplayTH[x].getElementsByTagName('div')[0];
482      if (div && window.getComputedStyle(div).position == 'absolute') {
483        tableDisplayTH[x].style.height = (div.offsetWidth + 15) + 'px';
484        tableDisplayTH[x].style.minWidth = (div.offsetHeight + 10) + 'px';
485      } else {
486        tableDisplayTH[x].style.height = 'auto';
487        tableDisplayTH[x].style.minWidth = 0;
488      }
489    }
490  }
491}
492
493
494
495// ********** GLOBAL EVENT LISTENERS ********** */
496window.addEventListener('DOMContentLoaded', function() {
497
498  if (MIRROR) {
499    let search = document.querySelector('nav.navbar ul.navbar-nav + form');
500    if (search) {
501      search.method = 'get';
502      search.action = PAGE_DOWNROOT + 'search/index.html';
503    }
504  }
505
506
507  // If requested, build a submenu automatically
508  let autoSubmenu = document.querySelector('ul[data-build="auto"]');
509  if (autoSubmenu) {
510    let mainContentIds = document.querySelectorAll('#main-content *[id]');
511    for (let x = 0; x < mainContentIds.length; x++) {
512      let li = document.createElement('li');
513          li.classList.add('nav-item', 'lh-lg');
514        let a = document.createElement('a');
515            a.classList.add('nav-link', 'text-light');
516            a.href = '#' + mainContentIds[x].id;
517          let span = document.createElement('span');
518              span.innerHTML = mainContentIds[x].innerHTML;
519            a.appendChild(span);
520          li.appendChild(a);
521        autoSubmenu.appendChild(li);
522    }
523  }
524
525
526  // Attach the scrollToElem function to all internal page links
527  let links = document.querySelectorAll('body a[href^="#"]:not(a[href="#"])');
528  for (let x = 0; x < links.length; x++) {
529    links[x].addEventListener('click', function(e) {
530      scrollToElem(this.getAttribute('href'));
531    }, false);
532  }
533
534  // Create links out of non-<a> elements having data-href attributes
535  // Be sure to add role="button" to these elements
536  // let dataHref = document.querySelectorAll('body [data-href]');
537  // for (let x = 0; x < dataHref.length; x++) {
538  //   dataHref[x].addEventListener('click', function(e) {
539  //     let href = this.getAttribute('data-href');
540  //     if (href && href.indexOf('#') === 0) {
541  //       if (href.length > 1) {
542  //         scrollToElem(href);
543  //       }
544  //     } else {
545  //       let target = this.getAttribute('target');
546  //       if (target && target == '_blank') {
547  //         window.open(href, target);
548  //       } else window.location.href = href;
549  //     }
550  //   }, false);
551  // }
552
553  // If the page submenu exists, attach the highlighting function
554  PAGE_SUBMENU = document.querySelector('.sub-menu');
555  if (PAGE_SUBMENU) {
556
557    // If this page has a sub-menu link that matches the current page
558    // URL, then highlight it
559    PAGE_SUBMENU.links = PAGE_SUBMENU.querySelectorAll('a.nav-link[href^="/"]');
560    for (let x = 0, u; x < PAGE_SUBMENU.links.length; x++) {
561      PAGE_SUBMENU.links[x].classList.remove('show');
562      u = new URL(PAGE_SUBMENU.links[x].href);
563      if (u.pathname == window.location.pathname)
564        PAGE_SUBMENU.links[x].classList.add('show');
565    }
566
567    // Highlight the appropriate link in the submenu, it if exists
568    PAGE_SUBMENU.anchors = PAGE_SUBMENU.querySelectorAll('a.nav-link[href^="#"]');
569    PAGE_SUBMENU.highlight = function() {
570
571      // Detect if the submenu is currently sticky
572      let nav1 = document.querySelector('nav.navbar').offsetHeight;
573      let npos = findPos(PAGE_SUBMENU);
574      if (Math.ceil(npos[1] - window.scrollY) <= nav1) {
575        this.classList.add('sticky');
576      } else if (Math.floor(npos[1] - window.scrollY) > nav1)
577        this.classList.remove('sticky');
578
579      // Remove all previous link highlights
580      for (let y = 0; y < this.anchors.length; y++)
581        this.anchors[y].classList.remove('show');
582
583      // Highlight appropriate submenu links if we've scrolled to them
584      for (let x = this.anchors.length - 1, href, target, pos; x >= 0; x--) {
585        href = this.anchors[x].getAttribute('href');
586
587        if (href.indexOf('#') === 0) {
588          href = href.substring(1);
589          target = document.getElementById(href);
590          if (!target) target = document.querySelector('a[name="' + href + '"]');
591
592          if (target) {
593            pos = findPos(target);
594
595            if (pos[1] < window.scrollY + innerDimensions()[1] / 2) {
596              if (!this.anchors[x].classList.contains('show')) {
597                this.anchors[x].classList.add('show');
598                break;
599              }
600            }
601          }
602        }
603      }
604    }
605  }
606
607
608  // Set up text-hyphen-nowrap elements
609  [...document.querySelectorAll('.text-hyphen-nowrap')].map(
610    elem => textHyphenNowrap(elem)
611  );
612
613
614  inlineLogoReplace(document.querySelector('body'));
615
616  [...document.querySelectorAll('img.inline-text-svg')].map(img => {
617    if (img.nextSibling && img.nextSibling.nodeType == 3) {
618      ['.', ',', '\'s'].map(tail => {
619        if (img.nextSibling.nodeValue.substring(0, tail.length) == tail) {
620          img.nextSibling.nodeValue = img.nextSibling.nodeValue.substring(tail.length);
621          let nobr = document.createElement('span');
622              nobr.classList.add('text-nowrap');
623          img.parentNode.insertBefore(nobr, img);
624              nobr.appendChild(img);
625              nobr.appendChild(document.createTextNode(tail));
626        }
627      });
628    }
629  });
630
631
632  // Now that the nav-item's in the submenu highlight fully yellow,
633  // make sure any svg .white logos they contain, are swapped to
634  // black on highlight.
635  // let subMenuSVGs = document.querySelectorAll('.sub-menu img.inline-text-svg');
636  // for (let x = 0; x < subMenuSVGs.length; x++) {
637  //   if (subMenuSVGs[x].src.indexOf('.white') > 0) {
638  //     let darkSVG = subMenuSVGs[x].cloneNode();
639  //         darkSVG.src = subMenuSVGs[x].src.replace(/\.white/, '');
640  //       subMenuSVGs[x].parentNode.insertBefore(darkSVG, subMenuSVGs[x].nextSibling);
641  //   }
642  // }
643
644  // The above is now done by adding a dark drop shadow to any images
645  // in the submenu.
646
647}, false);
648
649
650// Replace words with hyphens with <span class="text-nowrap">word</span>
651function textHyphenNowrap(node) {
652  for (let x = node.childNodes.length - 1; x >= 0; x--) {
653    switch (node.childNodes[x].nodeType) {
654      case 1: textHyphenNowrap(node.childNodes[x]); break;
655      case 3:
656        node.childNodes[x].parentNode.replaceChild(
657          (function(nodeText) {
658            let frag = document.createDocumentFragment();
659            nodeText = nodeText.split(/<\/?nobr>/);
660            for (let x = 0, nowr; x < nodeText.length; x++) {
661              if (x % 2) {
662                  nowr = document.createElement('span');
663                  nowr.classList.add('text-nowrap');
664                  nowr.appendChild(document.createTextNode(nodeText[x]));
665                frag.appendChild(nowr);
666              } else frag.appendChild(document.createTextNode(nodeText[x]));
667            }
668            return frag;
669          })(node.childNodes[x].nodeValue.replace(/\b([a-z0-9]+(-[a-z0-9]+)+)\b/ig, '<nobr>$1</nobr>')),
670          node.childNodes[x]
671        );
672    }
673  }
674}
675
676
677
678// ***** TEXT NODE LOGO INSERTION ***********************************
679function inlineLogoReplace(element) {
680  function insertInlineLogo(node, match, offset, imagefile, alt) {
681    let parent = node;
682    while (parent = parent.parentNode) {
683      if (parent.nodeName == 'Q' || parent.nodeName == 'MARK')
684        return document.createTextNode(match);
685      if (parent.classList && parent.classList.contains('no-replace'))
686        return document.createTextNode(match);
687    }
688    let bright = brightnessByColor(window.getComputedStyle(node.parentNode).color);
689    let img = document.createElement('img');
690        img.src = PAGE_DOWNROOT + 'img/evertz/' + imagefile + ((bright < 128) ? '.svg' : '.white.svg');
691        img.alt = alt;
692        img.title = alt;
693        img.classList.add('inline-text-svg');
694        img.classList.add(imagefile);
695
696      img.addEventListener('load', function() {
697        this.style.setProperty('--aspect-ratio', this.naturalWidth / this.naturalHeight);
698        this.style.aspectRatio = this.naturalWidth / this.naturalHeight;
699      });
700    return img;
701  }
702
703
704  // Exponent logo
705  matchText(
706    element, new RegExp('\\b' + 'Exponent' + '\\b', ''),
707    function(node, match, offset) {
708      return insertInlineLogo(node, match, offset, 'exponent', 'Exponent');
709    }
710  );
711
712  // Overture logos
713  matchText(
714    element, new RegExp('\\b' + 'Overture™?(\\s+|-)?RT(\\s+|-)L(IVE|ive)' + '\\b', ''),
715    function(node, match, offset) {
716      return insertInlineLogo(node, match, offset, 'overture-rt-live', 'Overture™ RT LIVE');
717    }
718  );
719  matchText(
720    element, new RegExp('\\b' + 'Overture™?(\\s+|-)?RT' + '\\b', ''),
721    function(node, match, offset) {
722      return insertInlineLogo(node, match, offset, 'overture-rt', 'Overture™ RT');
723    }
724  );
725  matchText(
726    element, new RegExp('\\b' + 'Overture(?!-)(™|\\b)', ''),
727    function(node, match, offset) {
728      return insertInlineLogo(node, match, offset, 'overture', 'Overture™');
729    }
730  );
731
732  // VistaLINK PRO logo
733  matchText(
734    element, new RegExp('\\b' + 'VistaLINK(?=®\\s*IRM|®\\s*CSM|\\s*IRM|\\s*CSM)®?', ''),
735    function(node, match, offset) {
736      return insertInlineLogo(node, match, offset, 'vistalink', 'VistaLINK®');
737    }
738  );
739  matchText(
740    element, new RegExp('\\b' + 'VistaLINK®?\\s*PRO', ''),
741    function(node, match, offset) {
742      return insertInlineLogo(node, match, offset, 'vistalink-pro', 'VistaLINK® PRO');
743    }
744  );
745  matchText(
746    element, new RegExp('\\b' + 'VistaLINK®?', ''),
747    function(node, match, offset) {
748      return insertInlineLogo(node, match, offset, 'vistalink', 'VistaLINK®');
749    }
750  );
751
752  // IntelliGain logo
753  matchText(
754    element, new RegExp('\\b' + 'IntelliGain[™®]?', ''),
755    function(node, match, offset) {
756      return insertInlineLogo(node, match, offset, 'intelligain', 'IntelliGain®');
757    }
758  );
759
760  // SCORPION logo
761  matchText(
762    element, new RegExp('\\b' + '(?<!-)SCORPION(?!-)' + '\\b', ''),
763    function(node, match, offset) {
764      return insertInlineLogo(node, match, offset, 'scorpion', 'SCORPION');
765    }
766  );
767
768  // DreamCatcher logo
769  matchText(
770    element, new RegExp('\\b' + 'DreamCatcher™?', ''),
771    function(node, match, offset) {
772      return insertInlineLogo(node, match, offset, 'dreamcatcher', 'DreamCatcher™');
773    }
774  );
775
776  // Mediator-X logo
777  matchText(
778    element, new RegExp('\\b' + 'Mediator(\\s+|-)?X' + '\\b', ''),
779    function(node, match, offset) {
780      return insertInlineLogo(node, match, offset, 'mediator-x', 'Mediator-X');
781    }
782  );
783
784  // RenderX logo
785  matchText(
786    element, new RegExp('\\b' + 'Render(\\s+|-)?X' + '\\b', ''),
787    function(node, match, offset) {
788      return insertInlineLogo(node, match, offset, 'renderx', 'RenderX');
789    }
790  );
791
792  // Vista-in-VUE logo
793  matchText(
794    element, new RegExp('V(ista|ISTA)(\\s+|-)(in|IN)(\\s+|-)VUE', ''),
795    function(node, match, offset) {
796      return insertInlineLogo(node, match, offset, 'vista-in-vue', 'Vista-in-VUE');
797    }
798  );
799
800  // VUEAPPS logo
801  matchText(
802    element, new RegExp('\\b' + 'VUEAPPS' + '\\b', ''),
803    function(node, match, offset) {
804      return insertInlineLogo(node, match, offset, 'vueapps', 'VUEAPPS');
805    }
806  );
807
808  // REVUE logo
809  matchText(
810    element, new RegExp('\\b' + 'REVUE' + '\\b', ''),
811    function(node, match, offset) {
812      return insertInlineLogo(node, match, offset, 'revue', 'REVUE');
813    }
814  );
815
816  // VUE logo
817  matchText(
818    element, new RegExp('\\b' + '(?<!-)VUE(?!-)' + '\\b', ''),
819    function(node, match, offset) {
820      return insertInlineLogo(node, match, offset, 'vue', 'VUE');
821    }
822  );
823
824  // MAGNUM logos
825  matchText(
826    element, new RegExp('\\b' + 'MAGNUM(\\s+|-)A(nalytics|NALYTICS)' + '\\b', ''),
827    function(node, match, offset) {
828      return insertInlineLogo(node, match, offset, 'magnum-analytics', 'MAGNUM-ANALYTICS');
829    }
830  );
831  matchText(
832    element, new RegExp('\\b' + 'MAGNUM(\\s+|-)NMS' + '\\b', ''),
833    function(node, match, offset) {
834      return insertInlineLogo(node, match, offset, 'magnum-nms', 'MAGNUM-NMS');
835    }
836  );
837  matchText(
838    element, new RegExp('\\b' + 'MAGNUM(\\s+|-)OS(?!-)' + '\\b', ''),
839    function(node, match, offset) {
840      return insertInlineLogo(node, match, offset, 'magnum-os', 'MAGNUM OS');
841    }
842  );
843  matchText(
844    element, new RegExp('\\b' + 'MAGNUM(?!-)(?! OS)' + '\\b', ''),
845    function(node, match, offset) {
846      return insertInlineLogo(node, match, offset, 'magnum', 'MAGNUM');
847    }
848  );
849
850  // Studer logo
851  matchText(
852    element, new RegExp('\\b' + 'Studer®?', ''),
853    function(node, match, offset) {
854      return insertInlineLogo(node, match, offset, 'studer', 'Studer®');
855    }
856  );
857
858  // ATCi logo
859  matchText(
860    element, new RegExp('\\b' + 'ATCi' + '\\b', ''),
861    function(node, match, offset) {
862      return insertInlineLogo(node, match, offset, 'atci', 'ATCi');
863    }
864  );
865
866  // inSITE logo
867  matchText(
868    element, new RegExp('\\b' + 'inSITE™?', ''),
869    function(node, match, offset) {
870      return insertInlineLogo(node, match, offset, 'insite', 'inSITE™');
871    }
872  );
873
874  // evEDGE logo
875  matchText(
876    element, new RegExp('\\b' + 'evEDGE™?(?!-)' + '\\b', ''),
877    function(node, match, offset) {
878      return insertInlineLogo(node, match, offset, 'evedge', 'evEDGE™');
879    }
880  );
881
882  // EQX logo
883  let htmlRem = parseFloat(window.getComputedStyle(document.documentElement).fontSize);
884  matchText(
885    element, new RegExp('\\b' + 'EQX(?!-)' + '\\b', ''),
886    function(node, match, offset) {
887      // Only convert title-sized EQX's
888      if (parseFloat(window.getComputedStyle(node.parentNode).fontSize) > htmlRem) {
889        return insertInlineLogo(node, match, offset, 'eqx', 'EQX');
890      } else return document.createTextNode(match);
891    }
892  );
893
894  // BRAVO Studio logo
895  matchText(
896    // new RegExp('\\b' + '(?<!-)BRAVO(?!-)' + '\\b', ''),
897    element, new RegExp('\\b' + 'BRAVO\\s+Studio(?!\')' + '\\b', ''),
898    function(node, match, offset) {
899      return insertInlineLogo(node, match, offset, 'bravo-studio', 'BRAVO Studio');
900    }
901  );
902
903  // BRAVO logo
904  matchText(
905    // new RegExp('\\b' + '(?<!-)BRAVO(?!-|\\s+Studio)' + '\\b', ''),
906    element, new RegExp('\\b' + 'BRAVO(?!-|\\s+Studio)' + '\\b', ''),
907    function(node, match, offset) {
908      return insertInlineLogo(node, match, offset, 'bravo', 'BRAVO');
909    }
910  );
911
912  // evertz.io logo
913  matchText(
914    element, new RegExp('\\b' + 'evertz\\.io' + '\\b', ''),
915    function(node, match, offset) {
916      return insertInlineLogo(node, match, offset, 'evertz-io', 'evertz.io');
917    }
918  );
919
920  // Ease Live logo
921  matchText(
922    element, new RegExp('\\b' + 'Ease Live' + '\\b', ''),
923    function(node, match, offset) {
924      return insertInlineLogo(node, match, offset, 'ease-live', 'Ease Live');
925    }
926  );
927
928  // Quintech logo
929  matchText(
930    element, new RegExp('\\b' + 'Quintech' + '\\b', ''),
931    function(node, match, offset) {
932      return insertInlineLogo(node, match, offset, 'quintech', 'Quintech');
933    }
934  );
935
936  // REFLEKTOR logo
937  matchText(
938    element, new RegExp('\\b' + 'REFLEKTOR' + '\\b', 'i'),
939    function(node, match, offset) {
940      return insertInlineLogo(node, match, offset, 'reflektor', 'REFLEKTOR');
941    }
942  );
943
944  // StreamPro logo
945  matchText(
946    element, new RegExp('\\b' + 'StreamPro' + '\\b', ''),
947    function(node, match, offset) {
948      return insertInlineLogo(node, match, offset, 'streampro', 'StreamPro');
949    }
950  );
951
952  // NEXX logo
953  matchText(
954    element, new RegExp('\\b' + '(?<!-)NEXX(?!-)' + '\\b', ''),
955    function(node, match, offset) {
956      return insertInlineLogo(node, match, offset, 'nexx', 'NEXX');
957    }
958  );
959
960  // NATX logotype
961  matchText(
962    element, new RegExp('\\b' + '(?<!-)NATX(?!-)' + '\\b', ''),
963    function(node, match, offset) {
964      return insertInlineLogo(node, match, offset, 'natx', 'NATX');
965    }
966  );
967
968  // PREFEX logotype
969  matchText(
970    element, new RegExp('\\b' + 'PREFEX(?!-)' + '\\b', ''),
971    function(node, match, offset) {
972      return insertInlineLogo(node, match, offset, 'prefex', 'PREFEX');
973    }
974  );
975
976  // evTOC logotype
977  matchText(
978    element, new RegExp('\\b' + 'evTOC' + '\\b', ''),
979    function(node, match, offset) {
980      return insertInlineLogo(node, match, offset, 'evtoc', 'evTOC');
981    }
982  );
983
984  // SDN-RF logotype
985  matchText(
986    element, new RegExp('\\b' + 'SDN-RF' + '\\b', ''),
987    function(node, match, offset) {
988      return insertInlineLogo(node, match, offset, 'sdn-rf', 'SDN-RF');
989    }
990  );
991
992  // ENX logotype
993  matchText(
994    element, new RegExp('\\b' + '(?<!-)ENX(?!-)' + '\\b', ''),
995    function(node, match, offset) {
996      return insertInlineLogo(node, match, offset, 'enx', 'ENX');
997    }
998  );
999
1000  // XChange logo
1001  matchText(
1002    element, new RegExp('\\b' + 'XChange' + '\\b', ''),
1003    function(node, match, offset) {
1004      return insertInlineLogo(node, match, offset, 'xchange', 'XChange');
1005    }
1006  );
1007
1008  // EXE logotype
1009  // matchText(
1010  //   element, new RegExp('\\b' + '(?<!-)EXE(?!-)' + '\\b', ''),
1011  //   function(node, match, offset) {
1012  //     return insertInlineLogo(node, match, offset, 'exe', 'EXE');
1013  //   }
1014  // );
1015
1016  // Check for slashes without proper spacing
1017  // matchText(
1018  //   element, new RegExp('\\b' + '/' + '\\b', 'g'),
1019  //   function(node, match, offset) {
1020  //     return document.createTextNode(':::::/:::::');
1021  //   }
1022  // );
1023}
1024
1025
1026window.addEventListener('load', function() {
1027
1028  // Background blocks min-height
1029  let sectBkgImg = document.querySelectorAll('#main-content section > img.background.full-height');
1030  for (let x = 0; x < sectBkgImg.length; x++) {
1031    let ratio = sectBkgImg[x].parentNode.offsetWidth / sectBkgImg[x].naturalWidth;
1032    sectBkgImg[x].parentNode.style.minHeight = Math.floor(sectBkgImg[x].naturalHeight * ratio) + 'px';
1033  }
1034
1035
1036  // Fake position:fixed; for parallaxed video backgrounds
1037  var parallaxVideos = document.querySelectorAll('.parallax > video:first-child');
1038  for (var x = 0; x < parallaxVideos.length; x++)
1039    parallaxVideos[x].style.top = -parallaxVideos[x].parentNode.getBoundingClientRect().top + 'px';
1040  window.addEventListener('scroll', function() {
1041    for (var x = 0; x < parallaxVideos.length; x++)
1042      parallaxVideos[x].style.top = -parallaxVideos[x].parentNode.getBoundingClientRect().top + 'px';
1043  }, false);
1044
1045
1046  // If a hash value exists in the address
1047  if (location.hash != null && location.hash != '' && location.hash.match(/^#\w[\w\d_-]+$/)) {
1048
1049    // If an accordion id with the hash exists, expand it
1050    let collapse = document.querySelector('.accordion ' + location.hash + '.collapse');
1051    if (collapse) {
1052      collapse = new bootstrap.Collapse(collapse);
1053      collapse.show();
1054    }
1055
1056    // If a modal exists with this ID, show it
1057    let hashModal = document.querySelector(location.hash + '.modal');
1058    if (hashModal) {
1059      hashModal = new bootstrap.Modal(location.hash);
1060      hashModal.show(true);
1061    }
1062
1063    // Scroll to the hash element
1064    scrollToElem(window.location.hash);
1065
1066  // Else if there is any element with id "results", scroll to it
1067  } else {
1068    let results = document.getElementById('results');
1069    if (results) scrollToElem(results);
1070  }
1071
1072  tableDisplayHeaderHeight();
1073  cardFlipHeight();
1074
1075  // Since W3C has removed support for the media attribute on <source>
1076  // elements, we cannot serve videos responsively by screen size.
1077  // Remove all videos with the media attribute so only the largest
1078  // video remains. There is a campaign on to restore the media
1079  // attribute so this code might be removed someday.
1080  let htmlVideo = document.getElementsByTagName('video');
1081  for (let x = 0, s; x < htmlVideo.length; x++) {
1082    s = htmlVideo[x].getElementsByTagName('source');
1083    for (let y = 0; y < s.length; y++)
1084      if (s[y].getAttribute('media'))
1085        htmlVideo[x].removeChild(s[y]);
1086  }
1087
1088  // Set the data-scroll value on the <html> element to the document
1089  // scroll value, for CSS selectors.
1090  document.documentElement.setAttribute('data-scroll', window.scrollY);
1091
1092  // Enable Popper.js tooltips
1093  let toolTipElems = document.querySelectorAll('[data-bs-toggle="tooltip"]');
1094  let toolTipList = [...toolTipElems].map(elem => new bootstrap.Tooltip(elem));
1095
1096
1097  // Change replaced SVG logo colour on tab events
1098  let tabs = document.querySelectorAll('.nav-tabs .nav-item *[role="tab"]');
1099  for (let x = 0; x < tabs.length; x++) {
1100    tabs[x].addEventListener('shown.bs.tab', function(e) {
1101      setTimeout(function() {
1102        svgLogoColorSet(e.target);
1103        svgLogoColorSet(e.relatedTarget);
1104      }, 100);
1105    }, false);
1106  }
1107
1108
1109  // Enable lightbox video links
1110  let lightboxVideo = document.querySelectorAll('*[data-lightbox-video]');
1111  for (let x = 0; x < lightboxVideo.length; x++) {
1112    lightboxVideo[x].addEventListener('click', function() {
1113      PAGE_LIGHTBOX.activate(JSON.parse(this.getAttribute('data-lightbox-video')), true);
1114    });
1115  }
1116
1117}, false);
1118
1119
1120document.addEventListener('scroll', function() {
1121
1122  // Record the scroll distance of the page in the <html> element
1123  document.documentElement.setAttribute('data-scroll', window.scrollY);
1124
1125  if (PAGE_SUBMENU) PAGE_SUBMENU.highlight();
1126}, false);
1127
1128
1129// Replace characters and images on user copy
1130// document.body.addEventListener('copy', function(e) {
1131//   let contents = window.getSelection().getRangeAt(0).cloneContents();
1132
1133//   // Remove nodes that shouldn't be visible
1134//   let invisiNodes = [
1135//     'head',
1136//     'script',
1137//     '.d-none',
1138//     '.invisible',
1139//     '.skip-nav',
1140//     'noscript',
1141//     '[aria-hidden="true"]',
1142//     '[aria-expanded="false"] + .dropdown-menu'
1143//   ];
1144//   for (let x = 0, n; x < invisiNodes.length; x++) {
1145//     n = contents.querySelectorAll(invisiNodes[x]);
1146//     for (let y = 0; y < n.length; y++)
1147//       n[y].parentNode.removeChild(n[y]);
1148//   }
1149
1150//   let doc = document.createElement('html');
1151//   doc.appendChild(contents);
1152//   e.clipboardData.setData('text/html', doc.innerHTML);
1153
1154//   // Replace images with alt-text
1155//   let imgs = contents.querySelectorAll('img');
1156//   for (let x = 0; x < imgs.length; x++)
1157//     imgs[x].parentNode.replaceChild(document.createTextNode(imgs[x].alt), imgs[x]);
1158
1159//   // Get text content of remaining node tree
1160//   let text = contents.textContent;
1161
1162//   // Replace non-breaking hyphens with regular hyphens
1163//   text = text.replace(/\u2011/g, '-');
1164
1165//   // Remove zero-width no-break space
1166//   text = text.replace(/\ufeff/g, '');
1167
1168//   e.clipboardData.setData('text/plain', text);
1169//   e.preventDefault();
1170// }, false);
1171
1172
1173window.addEventListener('resize', function() {
1174
1175  // Background blocks min-height
1176  let sectBkgImg = document.querySelectorAll('#main-content section > img.background.full-height');
1177  for (let x = 0; x < sectBkgImg.length; x++) {
1178    let ratio = sectBkgImg[x].parentNode.offsetWidth / sectBkgImg[x].naturalWidth;
1179    sectBkgImg[x].parentNode.style.minHeight = Math.floor(sectBkgImg[x].naturalHeight * ratio) + 'px';
1180  }
1181
1182  tableDisplayHeaderHeight();
1183  cardFlipHeight();
1184
1185}, false);

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.