PageSourceSearch

https://pasp365.com/wp-content/plugins/paspforce-live-support-ticker/assets/js/ticker.js?ver=1.0.6

js pasp365.com collected 2026-10-04 17:30:04 UTC 14,637 bytes, 456 lines download raw bytes

1(function () {
2  'use strict'
3
4  var cfg = window.paspforceLiveSupportTicker || {}
5  var previewOnly = !!cfg.previewOnly
6  var dismissKey = cfg.dismissKey || 'paspforce_ls_ticker_dismiss'
7
8  function escapeHtml(str) {
9    return String(str)
10      .replace(/&/g, '&')
11      .replace(/</g, '&lt;')
12      .replace(/>/g, '&gt;')
13      .replace(/"/g, '&quot;')
14  }
15
16  function prefersReducedMotion() {
17    try {
18      return window.matchMedia('(prefers-reduced-motion: reduce)').matches
19    } catch (e) {
20      return false
21    }
22  }
23
24  function motionMarqueeActive(bar) {
25    return (
26      bar.classList.contains('paspforce-support-ticker--motion') &&
27      !bar.classList.contains('paspforce-support-ticker--marquee-off') &&
28      !prefersReducedMotion()
29    )
30  }
31
32  function ensureMarqueeStrips(track) {
33    var strips = track.querySelectorAll('.paspforce-support-ticker__marquee-strip')
34    if (strips.length === 2) {
35      return strips
36    }
37    track.innerHTML = ''
38    var a = document.createElement('span')
39    a.className = 'paspforce-support-ticker__marquee-strip'
40    var b = document.createElement('span')
41    b.className = 'paspforce-support-ticker__marquee-strip'
42    track.appendChild(a)
43    track.appendChild(b)
44    return track.querySelectorAll('.paspforce-support-ticker__marquee-strip')
45  }
46
47  function buildStripInnerHtml(phrase, repeatCount) {
48    var html = ''
49    for (var i = 0; i < repeatCount; i++) {
50      if (i > 0) {
51        html += '<span class="paspforce-support-ticker__marquee-sep" aria-hidden="true">·</span>'
52      }
53      html += '<span class="paspforce-support-ticker__marquee-unit">' + escapeHtml(phrase) + '</span>'
54    }
55    return html
56  }
57
58  function measurePhraseWidth(viewport, phrase) {
59    var probe = document.createElement('span')
60    probe.className = 'paspforce-support-ticker__marquee-unit'
61    probe.setAttribute('aria-hidden', 'true')
62    probe.style.position = 'absolute'
63    probe.style.visibility = 'hidden'
64    probe.style.pointerEvents = 'none'
65    probe.style.whiteSpace = 'nowrap'
66    probe.textContent = phrase
67    viewport.appendChild(probe)
68    var w = probe.offsetWidth
69    viewport.removeChild(probe)
70    return w
71  }
72
73  function measureSepWidth(viewport) {
74    var probe = document.createElement('span')
75    probe.className = 'paspforce-support-ticker__marquee-sep'
76    probe.setAttribute('aria-hidden', 'true')
77    probe.style.position = 'absolute'
78    probe.style.visibility = 'hidden'
79    probe.style.pointerEvents = 'none'
80    probe.textContent = '·'
81    viewport.appendChild(probe)
82    var w = probe.offsetWidth
83    viewport.removeChild(probe)
84    return w
85  }
86
87  function measureStripWidth(viewport, phrase, repeatCount) {
88    var probe = document.createElement('span')
89    probe.className = 'paspforce-support-ticker__marquee-strip'
90    probe.setAttribute('aria-hidden', 'true')
91    probe.style.position = 'absolute'
92    probe.style.visibility = 'hidden'
93    probe.style.pointerEvents = 'none'
94    probe.style.whiteSpace = 'nowrap'
95    probe.innerHTML = buildStripInnerHtml(phrase, repeatCount)
96    viewport.appendChild(probe)
97    var w = probe.offsetWidth
98    viewport.removeChild(probe)
99    return w
100  }
101
102  function rebuildMarqueeTrack(bar) {
103    if (!motionMarqueeActive(bar)) {
104      return
105    }
106    var track = bar.querySelector('.paspforce-support-ticker__marquee-track')
107    var viewport = bar.querySelector('.paspforce-support-ticker__marquee-viewport')
108    if (!track || !viewport) {
109      return
110    }
111
112    var phrase = track.getAttribute('data-marquee-phrase') || ''
113    phrase = String(phrase).trim()
114    if (!phrase) {
115      return
116    }
117
118    var viewportW = viewport.clientWidth
119    if (viewportW <= 0) {
120      return
121    }
122
123    var unitW = measurePhraseWidth(viewport, phrase)
124    if (unitW <= 0) {
125      return
126    }
127
128    var sepW = measureSepWidth(viewport)
129    var step = unitW + sepW
130    var repeatCount = Math.max(1, Math.ceil((viewportW + unitW) / step))
131
132    while (measureStripWidth(viewport, phrase, repeatCount) < viewportW && repeatCount < 48) {
133      repeatCount += 1
134    }
135
136    var strips = ensureMarqueeStrips(track)
137    var inner = buildStripInnerHtml(phrase, repeatCount)
138    strips[0].innerHTML = inner
139    strips[1].innerHTML = inner
140
141    track.style.animation = 'none'
142    void track.offsetWidth
143    track.style.removeProperty('animation')
144  }
145
146  function renderStaticHtml(parts) {
147    var html = ''
148    for (var i = 0; i < parts.length; i++) {
149      if (i > 0) {
150        html += '<span class="paspforce-support-ticker__sep" aria-hidden="true"> · </span>'
151      }
152      html += '<span class="paspforce-support-ticker__part">' + escapeHtml(parts[i]) + '</span>'
153    }
154    return html
155  }
156
157  function applyTickerCopy(bar, parts, combined) {
158    if (!parts || !parts.length) {
159      return
160    }
161    var staticEl = bar.querySelector('.paspforce-support-ticker__static')
162    if (staticEl) {
163      staticEl.innerHTML = renderStaticHtml(parts)
164    }
165    var track = bar.querySelector('.paspforce-support-ticker__marquee-track')
166    if (track) {
167      track.setAttribute('data-marquee-phrase', combined || parts.join(' · '))
168    }
169    rebuildMarqueeTrack(bar)
170  }
171
172  function initBar(bar) {
173    if (!bar) {
174      return
175    }
176
177    var openBtn = bar.querySelector('.paspforce-support-ticker__open')
178    var dismissBtn = bar.querySelector('.paspforce-support-ticker__dismiss')
179    var isFixed = bar.classList.contains('paspforce-support-ticker--fixed')
180    var pageContainer = document.getElementById('page-container')
181    var isPreview = bar.getAttribute('data-preview') === '1' || bar.id === 'paspforce-support-ticker-preview'
182
183    var presenceState = null
184    var onlineParts = Array.isArray(cfg.onlineParts) ? cfg.onlineParts.slice() : null
185    var offlineParts = Array.isArray(cfg.offlineParts) ? cfg.offlineParts.slice() : null
186    var onlineCombined = typeof cfg.onlineCombined === 'string' ? cfg.onlineCombined : ''
187    var offlineCombined = typeof cfg.offlineCombined === 'string' ? cfg.offlineCombined : ''
188
189    function setPresenceOffline(isOffline) {
190      if (isOffline) {
191        bar.classList.add('paspforce-support-ticker--offline')
192        if (offlineParts && offlineParts.length) {
193          applyTickerCopy(bar, offlineParts, offlineCombined)
194        }
195      } else {
196        bar.classList.remove('paspforce-support-ticker--offline')
197        if (onlineParts && onlineParts.length) {
198          applyTickerCopy(bar, onlineParts, onlineCombined)
199        }
200      }
201    }
202
203    function anyDepartmentOnline(departments) {
204      if (!Array.isArray(departments)) {
205        return null
206      }
207      for (var i = 0; i < departments.length; i++) {
208        var d = departments[i]
209        if (d && d.online) {
210          return true
211        }
212        var cats = d && d.categories ? d.categories : []
213        for (var j = 0; j < cats.length; j++) {
214          if (cats[j] && cats[j].online) {
215            return true
216          }
217        }
218      }
219      return false
220    }
221
222    function pollPresence() {
223      var base = typeof cfg.apiBase === 'string' && cfg.apiBase ? cfg.apiBase : 'https://paspforce.com'
224      var url = base.replace(/\/+$/, '') + '/api/public/support-chat/departments'
225      fetch(url, { credentials: 'omit', cache: 'no-store' })
226        .then(function (res) {
227          if (!res.ok) {
228            throw new Error('presence http ' + res.status)
229          }
230          return res.json()
231        })
232        .then(function (data) {
233          var online = anyDepartmentOnline(data && data.departments)
234          if (online === null) {
235            return
236          }
237          presenceState = online
238          setPresenceOffline(!online)
239        })
240        .catch(function () {
241          /* fail open */
242        })
243    }
244
245    function isDismissed() {
246      try {
247        return sessionStorage.getItem(dismissKey) === '1'
248      } catch (e) {
249        return false
250      }
251    }
252
253    function setDismissed() {
254      try {
255        sessionStorage.setItem(dismissKey, '1')
256      } catch (e) {
257        /* ignore */
258      }
259    }
260
261    function readPageContainerBasePadding() {
262      if (!pageContainer) {
263        return
264      }
265      if (pageContainer.dataset.paspforceTickerBasePt !== undefined) {
266        return
267      }
268      var inline = pageContainer.style.paddingTop
269      var computed = parseFloat(window.getComputedStyle(pageContainer).paddingTop) || 0
270      if (inline && inline.indexOf('px') !== -1) {
271        pageContainer.dataset.paspforceTickerBasePt = String(parseFloat(inline) || computed)
272        pageContainer.dataset.paspforceTickerHadInline = '1'
273      } else {
274        pageContainer.dataset.paspforceTickerBasePt = String(computed)
275      }
276    }
277
278    function restorePageContainerPadding() {
279      if (!pageContainer || pageContainer.dataset.paspforceTickerBasePt === undefined) {
280        return
281      }
282      var base = pageContainer.dataset.paspforceTickerBasePt
283      if (pageContainer.dataset.paspforceTickerHadInline === '1') {
284        pageContainer.style.paddingTop = base.indexOf('px') !== -1 ? base : base + 'px'
285      } else {
286        pageContainer.style.removeProperty('padding-top')
287      }
288    }
289
290    function applyPageContainerPadding(tickerHeight) {
291      if (!pageContainer) {
292        return
293      }
294      readPageContainerBasePadding()
295      if (pageContainer.dataset.paspforceTickerBasePt === undefined) {
296        return
297      }
298      var base = parseFloat(pageContainer.dataset.paspforceTickerBasePt) || 0
299      pageContainer.style.paddingTop = base + tickerHeight + 'px'
300    }
301
302    function clearLayoutOffsets() {
303      var docEl = document.documentElement
304      var body = document.body
305      docEl.classList.remove('paspforce-ticker-offset-fixed')
306      docEl.style.removeProperty('--paspforce-ticker-h')
307      docEl.style.removeProperty('--paspforce-ticker-offset')
308      if (body) {
309        body.classList.remove('paspforce-ticker-offset-fixed', 'paspforce-ticker-visible')
310        body.style.removeProperty('--paspforce-ticker-h')
311        body.style.removeProperty('--paspforce-ticker-offset')
312      }
313      restorePageContainerPadding()
314    }
315
316    function applyBodyOffset() {
317      if (isPreview) {
318        return
319      }
320      var docEl = document.documentElement
321      var body = document.body
322      if (!isFixed || bar.classList.contains('paspforce-support-ticker--hidden')) {
323        clearLayoutOffsets()
324        return
325      }
326      if (body && body.classList.contains('et_divi_builder')) {
327        clearLayoutOffsets()
328        return
329      }
330
331      var h = bar.offsetHeight || 40
332      var hPx = h + 'px'
333      var adminExtra = 0
334      if (body && body.classList.contains('admin-bar')) {
335        adminExtra = window.innerWidth <= 782 ? 46 : 32
336      }
337
338      docEl.classList.add('paspforce-ticker-offset-fixed')
339      docEl.style.setProperty('--paspforce-ticker-h', hPx)
340      docEl.style.setProperty('--paspforce-ticker-offset', h + adminExtra + 'px')
341
342      if (body) {
343        body.classList.add('paspforce-ticker-offset-fixed', 'paspforce-ticker-visible')
344        body.style.setProperty('--paspforce-ticker-h', hPx)
345        body.style.setProperty('--paspforce-ticker-offset', h + adminExtra + 'px')
346      }
347
348      applyPageContainerPadding(h)
349    }
350
351    function openChat() {
352      var api = window.paspforceLiveSupport
353      if (api && typeof api.open === 'function') {
354        api.open()
355        return
356      }
357      var url = cfg.fallbackUrl || 'https://paspforce.com/support'
358      window.open(url, '_blank', 'noopener,noreferrer')
359    }
360
361    function bindMotionPause() {
362      bar.addEventListener('mouseenter', function () {
363        bar.classList.add('paspforce-support-ticker--paused')
364      })
365      bar.addEventListener('mouseleave', function () {
366        bar.classList.remove('paspforce-support-ticker--paused')
367      })
368      bar.addEventListener('focusin', function () {
369        bar.classList.add('paspforce-support-ticker--paused')
370      })
371      bar.addEventListener('focusout', function () {
372        bar.classList.remove('paspforce-support-ticker--paused')
373      })
374    }
375
376    if (!isPreview && !previewOnly) {
377      if (isDismissed()) {
378        bar.classList.add('paspforce-support-ticker--hidden')
379      } else {
380        bindMotionPause()
381        pollPresence()
382        var pollMs = typeof cfg.pollMs === 'number' && cfg.pollMs > 0 ? cfg.pollMs : 60000
383        window.setInterval(pollPresence, pollMs)
384      }
385      applyBodyOffset()
386
387      if (openBtn) {
388        openBtn.addEventListener('click', function () {
389          openChat()
390        })
391        openBtn.addEventListener('keydown', function (e) {
392          if (e.key === 'Enter' || e.key === ' ') {
393            e.preventDefault()
394            openChat()
395          }
396        })
397      }
398
399      if (dismissBtn) {
400        dismissBtn.addEventListener('click', function (e) {
401          e.stopPropagation()
402          setDismissed()
403          bar.classList.add('paspforce-support-ticker--hidden')
404          applyBodyOffset()
405        })
406      }
407
408      window.addEventListener('resize', function () {
409        applyBodyOffset()
410        rebuildMarqueeTrack(bar)
411      })
412    } else {
413      bindMotionPause()
414    }
415
416    rebuildMarqueeTrack(bar)
417
418    if (typeof window.ResizeObserver === 'function') {
419      var ro = new window.ResizeObserver(function () {
420        if (!isPreview && !previewOnly) {
421          applyBodyOffset()
422        }
423        rebuildMarqueeTrack(bar)
424      })
425      ro.observe(bar)
426      var viewport = bar.querySelector('.paspforce-support-ticker__marquee-viewport')
427      if (viewport) {
428        ro.observe(viewport)
429      }
430    }
431
432    bar.__paspforceRebuildMarquee = function () {
433      rebuildMarqueeTrack(bar)
434    }
435  }
436
437  window.paspforceLiveSupportTickerRebuildMarquee = function (barEl) {
438    if (barEl && typeof barEl.__paspforceRebuildMarquee === 'function') {
439      barEl.__paspforceRebuildMarquee()
440    }
441  }
442
443  var bars = []
444  var main = document.getElementById('paspforce-support-ticker')
445  if (main) {
446    bars.push(main)
447  }
448  var preview = document.getElementById('paspforce-support-ticker-preview')
449  if (preview) {
450    bars.push(preview)
451  }
452
453  for (var b = 0; b < bars.length; b++) {
454    initBar(bars[b])
455  }
456})()

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.