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, '<') 12 .replace(/>/g, '>') 13 .replace(/"/g, '"') 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.