1// IE11 polyfill 2if (!String.prototype.endsWith) { 3 String.prototype.endsWith = function (search, thisLen) { 4 if (thisLen === undefined || thisLen > this.length) { 5 thisLen = this.length; 6 } 7 return this.substring(thisLen - search.length, thisLen) === search; 8 }; 9} 10 11function safeSlice(s, startIndex, length) { 12 return typeof s === "string" ? s.slice(startIndex, length) : ""; 13} 14 15var spekResizeIFrame = function () { 16 var iframe = document.getElementById('SpektrixIFrame'); 17 var count = 0; 18 19 try { 20 var src = iframe.contentWindow.frames[0].location; 21 22 var parts = src.search.split('='); 23 var height = Number(parts[1]); 24 if (isNaN(height)) { 25 throw "NaN"; 26 } else { 27 if (typeof (spektrixMinHeight) !== 'undefined') { 28 if (spektrixMinHeight > height) { 29 iframe.style.height = spektrixMinHeight + 'px'; 30 return; 31 } 32 } 33 34 iframe.style.height = height + 'px'; 35 } 36 } catch (e) { 37 // try again if something went wrong (there are plenty of timing issues!) 38 if (count < 10) { 39 setTimeout(spekResizeIFrame, 50); 40 count++; 41 } 42 } 43}; 44 45var spekPollIFrame = function () { 46 spekResizeIFrame(); 47 48 var iframe = document.getElementById('SpektrixIFrame'); 49 50 if (iframe.addEventListener) { 51 iframe.addEventListener('load', spekResizeIFrame, false); 52 } else { 53 iframe.attachEvent('onload', spekResizeIFrame); 54 } 55}; 56 57var spekHandleResizeExactMessage = function (e) { 58 if (e && e.data && safeSlice(e.data, 0, 12) === "resizeExact:") { 59 var iframe = document.getElementById('SpektrixIFrame'); 60 var height = Number(e.data.slice(12)); 61 if (!isNaN(height)) { 62 iframe.style.height = height + 'px'; 63 iframe.contentWindow.postMessage('resizeDone', '*'); 64 spekScheduleViewportSync(); 65 } 66 } 67}; 68 69var spekHandleResizeMessage = function (e) { 70 // Handle resize messages 71 if (e && e.data && safeSlice(e.data, 0, 7) === "resize:") { 72 var iframe = document.getElementById('SpektrixIFrame'); 73 var height = Number(e.data.slice(7)); 74 if (isNaN(height)) { 75 throw "NaN"; 76 } else { 77 if (typeof (spektrixMinHeight) !== 'undefined') { 78 if (spektrixMinHeight > height) { 79 iframe.style.height = spektrixMinHeight + 'px'; 80 iframe.contentWindow.postMessage('resizeDone', '*'); 81 spekScheduleViewportSync(); 82 return; 83 } 84 } 85 iframe.style.height = height + 'px'; 86 iframe.contentWindow.postMessage('resizeDone', '*'); 87 spekScheduleViewportSync(); 88 } 89 } 90}; 91 92var spekCheckForClickOutsideCalendar = function (e) { 93 var spekIFrame = document.getElementById('SpektrixIFrame'); 94 spekIFrame.contentWindow.postMessage('hideDropdownCalendar', '*'); 95}; 96 97var spekHandleCalendarMessage = function (e) { 98 /* Handle calendar messages */ 99 if (e && e.data && safeSlice(e.data, 0, 21) === "dropdowncalendarshown") { 100 if (document.addEventListener) { 101 document.addEventListener('mousedown', spekCheckForClickOutsideCalendar, false); 102 } else { 103 document.attachEvent('onmousedown', spekCheckForClickOutsideCalendar); 104 } 105 } 106 if (e && e.data && safeSlice(e.data, 0, 22) === "dropdowncalendarhidden") { 107 if (document.removeEventListener) { 108 document.removeEventListener('mousedown', spekCheckForClickOutsideCalendar, false); 109 } else { 110 document.detachEvent('onmousedown', spekCheckForClickOutsideCalendar); 111 } 112 } 113}; 114 115function getParameterByName(name, url) { 116 if (!url) url = window.location.href; 117 name = name.replace(/[\[\]]/g, "\\$&"); 118 var regex = new RegExp("[?&]" + name + "(=([^&#]*)|&|#|$)"), 119 results = regex.exec(url); 120 if (!results) return null; 121 if (!results[2]) return ''; 122 return decodeURIComponent(results[2].replace(/\+/g, " ")); 123} 124 125function removeParameterByName(name, url) { 126 var rtn = url.split("?")[0], 127 param, 128 params_arr = [], 129 queryString = (url.indexOf("?") !== -1) ? url.split("?")[1] : ""; 130 if (queryString !== "") { 131 params_arr = queryString.split("&"); 132 for (var i = params_arr.length - 1; i >= 0; i -= 1) { 133 param = params_arr[i].split("=")[0]; 134 if (param === name) { 135 params_arr.splice(i, 1); 136 } 137 } 138 139 if (params_arr.length !== 0) { 140 rtn = rtn + "?" + params_arr.join("&"); 141 } 142 } 143 return rtn; 144} 145 146// see https://stackoverflow.com/questions/736513/how-do-i-parse-a-url-into-hostname-and-path-in-javascript 147var getLocation = function (href) { 148 var location = document.createElement("a"); 149 location.href = href; 150 return location; 151}; 152 153var handleRedirects = function () { 154 var iframe = document.getElementById('SpektrixIFrame'); 155 var parentUrl = window.location.href; 156 if (/[?&]SpektrixPasswordlessToken=/.test(parentUrl) && /[?&]SpektrixRedirectUrl=/.test(parentUrl)) { 157 158 var newSrc = getParameterByName('SpektrixRedirectUrl') + '?SpektrixPasswordlessToken='
158+ getParameterByName('SpektrixPasswordlessToken'); 159 160 var url = getLocation(newSrc); 161 if (!url.hostname.endsWith(".spektrix.com")) { 162 return; 163 } 164 165 parentUrl = removeParameterByName('SpektrixRedirectUrl', parentUrl); 166 parentUrl = removeParameterByName('SpektrixPasswordlessToken', parentUrl); 167 168 if (window.history !== undefined && window.history.pushState !== undefined) { 169 // this changes the url without reloading the page but only works with Html 5 170 window.history.pushState({}, document.title, parentUrl); 171 } 172 173 if (iframe.src !== newSrc) { // Do redirect 174 iframe.src = newSrc; 175 } 176 } 177}; 178 179var spekOriginalIframeStyles = null; 180var spekFullscreenActive = false; 181var spekFixedOverlayCandidates = []; 182var spekFixedOverlayCacheDirty = true; 183var spekFixedOverlayObserver = null; 184 185function spekRefreshFixedOverlayCandidates() { 186 if (!document.body) { 187 spekFixedOverlayCandidates = []; 188 return; 189 } 190 var candidates = []; 191 var all = document.body.getElementsByTagName('*'); 192 for (var i = 0; i < all.length; i++) { 193 var el = all[i]; 194 var cs = window.getComputedStyle ? window.getComputedStyle(el) : null; 195 if (cs && cs.position === 'fixed') { 196 candidates.push(el); 197 } 198 } 199 spekFixedOverlayCandidates = candidates; 200 spekFixedOverlayCacheDirty = false; 201} 202 203function spekStartFixedOverlayObserver() { 204 if (spekFixedOverlayObserver || typeof MutationObserver === 'undefined' || !document.body) { 205 return; 206 } 207 spekFixedOverlayObserver = new MutationObserver(function () { 208 spekFixedOverlayCacheDirty = true; 209 spekScheduleViewportSync(); 210 }); 211 spekFixedOverlayObserver.observe(document.body, { 212 childList: true, 213 subtree: true, 214 attributes: true, 215 attributeFilter: ['style', 'class'] 216 }); 217} 218 219var spekSyncViewport = function () { 220 var iframe = document.getElementById('SpektrixIFrame'); 221 if (!iframe || !iframe.contentWindow || typeof iframe.contentWindow.postMessage !== "function") { 222 return; 223 } 224 225 if (spekFixedOverlayCacheDirty && !spekFullscreenActive) { 226 spekRefreshFixedOverlayCandidates(); 227 } 228 229 var rect = iframe.getBoundingClientRect(); 230 var viewportWidth = window.innerWidth || document.documentElement.clientWidth; 231 var viewportHeight = window.innerHeight || document.documentElement.clientHeight; 232 233 var topInset = 0; 234 var bottomInset = 0; 235 var leftInset = 0; 236 var rightInset = 0; 237 238 if (!spekFullscreenActive) { 239 for (var i = 0; i < spekFixedOverlayCandidates.length; i++) { 240 var el = spekFixedOverlayCandidates[i]; 241 if (!el || el === iframe) { 242 continue; 243 } 244 var elConnected = el.isConnected; 245 if (elConnected === false || (elConnected === undefined && !document.documentElement.contains(el))) { 246 continue; 247 } 248 var cs = window.getComputedStyle ? window.getComputedStyle(el) : null; 249 if (cs && (cs.display === 'none' || cs.visibility === 'hidden' || cs.position !== 'fixed')) { 250 continue; 251 } 252 var r = el.getBoundingClientRect(); 253 if (r.width <= 0 || r.height <= 0) { 254 continue; 255 } 256 257 // Only treat an element as edge-attached chrome if its near edge sits 258 // within a small tolerance of a viewport edge. This excludes centred 259 // modals, cookie banners, full-screen overlays, etc. 260 var edgeEpsilon = 2; 261 var touchesTop = r.top <= edgeEpsilon; 262 var touchesBottom = r.bottom >= viewportHeight - edgeEpsilon; 263 var touchesLeft = r.left <= edgeEpsilon; 264 var touchesRight = r.right >= viewportWidth - edgeEpsilon; 265 if (!touchesTop && !touchesBottom && !touchesLeft && !touchesRight) { 266 continue; 267 } 268 269 // Skip full-width elements from triggering left/right insets 270 var verticalScrollbarWidth = Math.max(0, viewportWidth - document.documentElement.clientWidth);
271 var horizontalScrollbarHeight = Math.max(0, viewportHeight - document.documentElement.clientHeight); 272 var widthTolerance = edgeEpsilon + verticalScrollbarWidth; 273 var heightTolerance = edgeEpsilon + horizontalScrollbarHeight; 274 275 var fullWidth = (touchesLeft || touchesRight) && (viewportWidth - r.width) <= widthTolerance; 276 if (fullWidth) { 277 touchesLeft = false; 278 touchesRight = false; 279 } 280 281 // Skip full-height elements from triggering top/bottom insets 282 var fullHeight = (touchesTop || touchesBottom) && (viewportHeight - r.height) <= heightTolerance; 283 if (fullHeight) { 284 touchesTop = false; 285 touchesBottom = false; 286 } 287 288 if (!touchesTop && !touchesBottom && !touchesLeft && !touchesRight) { 289 continue; 290 } 291 292 var horizontallyIntersectsIframe = r.right > rect.left && r.left < rect.right; 293 var verticallyIntersectsIframe = r.bottom > rect.top && r.top < rect.bottom; 294 295 // Top/bottom insets only matter if the element actually overlaps the 296 // iframe horizontally (a fixed bar above/below the iframe column). 297 // Left/right insets only matter if it overlaps the iframe vertically. 298 var topCandidate = touchesTop && horizontallyIntersectsIframe; 299 var bottomCandidate = touchesBottom && horizontallyIntersectsIframe; 300 var leftCandidate = touchesLeft && verticallyIntersectsIframe; 301 var rightCandidate = touchesRight && verticallyIntersectsIframe; 302 if (!topCandidate && !bottomCandidate && !leftCandidate && !rightCandidate) { 303 continue; 304 } 305 306 // Among the edges the element is attached to, pick the one with the 307 // smallest gap from the corresponding far edge â i.e. the edge it most 308 // plausibly anchors to. Ties are resolved deterministically in the 309 // order top, bottom, left, right. 310 var bestEdge = null; 311 var bestDist = Infinity; 312 if (topCandidate) { 313 bestEdge = 'top'; 314 bestDist = r.top; 315 } 316 if (bottomCandidate) { 317 var dB = viewportHeight - r.bottom; 318 if (dB < bestDist) { bestEdge = 'bottom'; bestDist = dB; } 319 } 320 if (leftCandidate) { 321 var dL = r.left; 322 if (dL < bestDist) { bestEdge = 'left'; bestDist = dL; } 323 } 324 if (rightCandidate) { 325 var dR = viewportWidth - r.right; 326 if (dR < bestDist) { bestEdge = 'right'; bestDist = dR; } 327 } 328 329 if (bestEdge === 'top') { 330 if (r.bottom > topInset) topInset = r.bottom; 331 } else if (bestEdge === 'bottom') { 332 var b = viewportHeight - r.top; 333 if (b > bottomInset) bottomInset = b; 334 } else if (bestEdge === 'left') { 335 if (r.right > leftInset) leftInset = r.right; 336 } else if (bestEdge === 'right') { 337 var rt = viewportWidth - r.left; 338 if (rt > rightInset) rightInset = rt; 339 } 340 } 341 } 342 343 if (topInset < 0) topInset = 0; 344 if (bottomInset < 0) bottomInset = 0; 345 if (leftInset < 0) leftInset = 0; 346 if (rightInset < 0) rightInset = 0; 347 if (topInset > viewportHeight) topInset = viewportHeight; 348 if (bottomInset > viewportHeight) bottomInset = viewportHeight; 349 if (leftInset > viewportWidth) leftInset = viewportWidth; 350 if (rightInset > viewportWidth) rightInset = viewportWidth; 351 352 var adjustedViewportHeight = viewportHeight - topInset - bottomInset; 353 var adjustedViewportWidth = viewportWidth - leftInset - rightInset; 354 if (adjustedViewportHeight < 0) adjustedViewportHeight = 0; 355 if (adjustedViewportWidth < 0) adjustedViewportWidth = 0; 356 357 iframe.contentWindow.postMessage({ 358 type: 'sync_view_port', 359 viewport: { 360 top: rect.top - topInset, 361 left: rect.left - leftInset, 362 width: rect.width, 363 height: rect.height, 364 viewportWidth: adjustedViewportWidth, 365 viewportHeight: adjustedViewportHeight 366 } 367 }, '*'); 368}; 369 370var spekHandleFullscreenMessage = function (e) { 371 if (e && e.data && safeSlice(e.data, 0, 11) === "fullscreen:") { 372 var iframe = document.getElementById('SpektrixIFrame'); 373 if (!iframe) return; 374 var expanded = e.data.slice(11) === 'true'; 375 376 if (expanded) { 377 if (spekOriginalIframeStyles === null) { 378 spekOriginalIframeStyles = iframe.getAttribute('style') || ''; 379 } 380 iframe.style.cssText = 'position:fixed;inset:0;width:100vw;height:100vh;z-index:2147483647;border:none;'; 381 spekFullscreenActive = true; 382 spekScheduleViewportSync(); 383 } else { 384 iframe.setAttribute('style', spekOriginalIframeStyles || ''); 385 spekOriginalIframeStyles = null; 386 spekFullscreenActive = false;
387 spekScheduleViewportSync(); 388 } 389 } 390}; 391 392if (typeof (window.postMessage) !== "undefined") { 393 if (window.addEventListener) { 394 window.addEventListener('message', spekHandleResizeMessage, false); 395 window.addEventListener('message', spekHandleResizeExactMessage, false); 396 window.addEventListener('message', spekHandleCalendarMessage, false); 397 window.addEventListener('message', spekHandleFullscreenMessage, false); 398 } else { 399 window.attachEvent('onmessage', spekHandleResizeMessage); 400 window.attachEvent('onmessage', spekHandleResizeExactMessage); 401 window.attachEvent('onmessage', spekHandleCalendarMessage); 402 window.attachEvent('onmessage', spekHandleFullscreenMessage); 403 } 404} else if (window.addEventListener) { 405 window.addEventListener('load', spekPollIFrame, false); 406} else { 407 window.attachEvent('onload', spekPollIFrame); 408} 409 410var spekRequestAnimationFrame = 411 window.requestAnimationFrame || 412 window.msRequestAnimationFrame || 413 function (callback) { 414 return window.setTimeout(callback, 16); 415 }; 416var spekViewportSyncScheduled = false; 417 418function spekScheduleViewportSync() { 419 if (spekViewportSyncScheduled) { 420 return; 421 } 422 423 spekViewportSyncScheduled = true; 424 spekRequestAnimationFrame(function () { 425 spekViewportSyncScheduled = false; 426 spekSyncViewport(); 427 }); 428} 429 430function spekHandleViewportLayoutChange() { 431 spekRefreshFixedOverlayCandidates(); 432 spekStartFixedOverlayObserver(); 433 spekScheduleViewportSync(); 434} 435 436if (typeof (window.postMessage) !== "undefined") { 437 if (window.addEventListener) { 438 window.addEventListener('scroll', spekScheduleViewportSync, true); 439 window.addEventListener('resize', spekHandleViewportLayoutChange, false); 440 window.addEventListener('load', spekHandleViewportLayoutChange, false); 441 } else { 442 window.attachEvent('onscroll', spekScheduleViewportSync); 443 window.attachEvent('onresize', spekHandleViewportLayoutChange); 444 window.attachEvent('onload', spekHandleViewportLayoutChange); 445 } 446} 447 448// the code below is the equivalent to jQuery's document.ready which executes before window load/onload 449// this allows changing the iframe's src as soon as the dom elements are ready 450if (window.addEventListener) { 451 window.addEventListener("DOMContentLoaded", handleRedirects, false); 452} else { 453 document.attachEvent("onreadystatechange", function () { 454 if (document.readyState === "complete") { 455 handleRedirects(); 456 } 457 }); 458}
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.