1 2var hidden, visibilityChange; 3if (typeof document.hidden !== "undefined") { 4 hidden = "hidden"; 5 visibilityChange = "visibilitychange"; 6} else if (typeof document.mozHidden !== "undefined") { 7 hidden = "mozHidden"; 8 visibilityChange = "mozvisibilitychange"; 9} else if (typeof document.msHidden !== "undefined") { 10 hidden = "msHidden"; 11 visibilityChange = "msvisibilitychange"; 12} else if (typeof document.webkitHidden !== "undefined") { 13 hidden = "webkitHidden"; 14 visibilityChange = "webkitvisibilitychange"; 15} else { 16 console.error("JavaScript: Page Visibility API not supported"); 17} 18 19document.addEventListener(visibilityChange, handleVisibilityChange, false); 20window.addEventListener("blur", focusLost); 21window.addEventListener("focus", focusGained); 22 23function handleVisibilityChange() { 24 handleFocusStateChange(!document[hidden]); 25} 26 27function focusLost() { 28 handleFocusStateChange(false); 29} 30 31function focusGained() { 32 handleFocusStateChange(true); 33} 34 35// If the given onkeyup event indicates a key code of 13 then the element with the given element ID is blurred 36function blurElementOnEnter(event, elementId) { 37 if (event.which == 13) { 38 var target = document.getElementById(elementId); 39 if (target) { 40 target.blur(); 41 } 42 } 43} 44 45function handleFocusStateChange(hasFocus) { 46 //console.log("JavaScript: has focus: " + hasFocus); 47 if (dotNetBrowserAPIsCallbackInstance) { 48 dotNetBrowserAPIsCallbackInstance.invokeMethodAsync("JsInteropFocusStateChanged", hasFocus); 49 } else { 50 //console.warn("JavaScript: handleFocusStateChange(): callback target not registered"); 51 } 52} 53 54//********************************************************************************* 55 56// The C# object instance reference used for interop callbacks from this code 57var dotNetBrowserAPIsCallbackInstance = null; 58 59// Called by C# to initialize this code for use by JavaScript interop calls 60function initializeBrowserAPIs(callbackInstance) { 61 dotNetBrowserAPIsCallbackInstance = callbackInstance; 62} 63 64// Stops propagation and default handling of the given event 65function stopEvent(theEvent) { 66 theEvent.stopPropagation(); 67 theEvent.preventDefault(); 68 return(false); 69} 70 71// In order to successfully support a "long-press" event accross all browsers (including Safari and various Web Views) 72// we do not use "oncontextmenu" event and, instead, role our own long-press detection based on more reliable events 73var longPressUseTouch = false; 74var longPressIsLongPress = false; 75var longPressTimerRef = null; 76var longPressScrollTimerRef = null; 77 78function isScrolling() { 79 return (longPressScrollTimerRef != null); 80} 81 82function makeLongPressCallback(callback, event) { 83 if (isScrolling()) { return; } 84 if (dotNetBrowserAPIsCallbackInstance) { 85 var loc = getLocationFromEvent(event); 86 var which = (event && event.which) ? "" + event.which : ""; 87 let elements = document.elementsFromPoint(loc.x, loc.y); 88 let ids = []; 89 for (let i = 0; i < elements.length; i++) { 90 if (elements[i].id) { 91 ids.push(elements[i].id); 92 } 93 } 94 dotNetBrowserAPIsCallbackInstance.invokeMethodAsync(callback, loc.x, loc.y, ids, which); 95 } 96} 97 98function pingTimerForScroll() { 99 if (longPressScrollTimerRef != null) { clearTimeout(longPressScrollTimerRef); } 100 longPressScrollTimerRef = setTimeout(() => { 101 longPressScrollTimerRef = null; 102 }, 550); 103} 104 105function startTimerForLongPress(event) { 106 longPressIsLongPress = false; 107 if (longPressTimerRef == null) { 108 longPressTimerRef = setTimeout(() => { 109 longPressIsLongPress = true; 110 makeLongPressCallback("JsiHandleLongPress", event); 111 longPressTimerRef = null; 112 }, 500) 113 } 114} 115 116function handleOnClickForLongPress(event) { 117 //console.error("handleOnClickForLongPress()"); 118 if (isScrolling()) { return; } 119 if (longPressUseTouch) { return; } 120 if (longPressIsLongPress) { 121 return; 122 } 123} 124 125function handleOnMouseDownForLongPress(event) { 126 //console.error("handleOnMouseDownForLongPress()"); 127 if (isScrolling()) { return; } 128 if (longPressUseTouch) { return; } 129 startTimerForLongPress(event); 130} 131 132function handleOnMouseUpForLongPress(event) { 133 //console.error("handleOnMouseUpForLongPress()"); 134 if (isScrolling()) { return; } 135 if (longPressUseTouch) { return; } 136 if (longPressTimerRef != null) { 137 clearTimeout(longPressTimerRef); 138 longPressTimerRef = null; 139 } 140 if (!longPressIsLongPress) { 141 makeLongPressCallback("JsiHandlePress", event); 142 } 143} 144 145function handleOnTouchStartForLongPress(event) { 146 //console.error("handleOnTouchStartForLongPress()"); 147 if (isScrolling()) { return; } 148 longPressUseTouch = true; 149 startTimerForLongPress(event); 150} 151 152function handleOnTouchEndForLongPress(event) { 153 //console.error("handleOnTouchEndForLongPress()"); 154 if (isScrolling()) { return; } 155 if (longPressIsLongPress) { 156 longPressIsLongPress = false; 157 return; 158 } 159 if (longPressTimerRef != null) { 160 clearTimeout(longPressTimerRef); 161 longPressTimerRef = null; 162 } 163} 164 165function handleOnScroll(event) { 166 //console.error("handleOnScroll()"); 167 pingTimerForScroll(); 168} 169 170// Registers the given DOM element ID to receive "long press" events 171function registerLongPressTrigger(elementId) { 172 addClassToElement(elementId, "long-press-trigger"); 173 var element = document.getElementById(elementId); 174 element.addEventListener("click", handleOnClickForLongPress); 175 element.addEventListener("mousedown", handleOnMouseDownForLongPress); 176 element.addEventListener("mouseup", handleOnMouseUpForLongPress); 177 element.addEventListener("touchstart", handleOnTouchStartForLongPress); 178 element.addEventListener("touchend", handleOnTouchEndForLongPress); 179 element.addEventListener("contextmenu", stopEvent); 180 element.addEventListener("scroll", handleOnScroll); 181} 182 183// Unregisters the given DOM element ID from receiving "long press" events 184function unregisterLongPressTrigger(elementId) { 185 removeClassFromElement(elementId, "long-press-trigger"); 186 var element = document.getElementById(elementId); 187 element.removeEventListener("click", handleOnClickForLongPress); 188 element.removeEventListener("mousedown", handleOnMouseDownForLongPress); 189 element.removeEventListener("mouseup", handleOnMouseUpForLongPress); 190 element.removeEventListener("touchstart", handleOnTouchStartForLongPress); 191 element.removeEventListener("touchend", handleOnTouchEndForLongPress); 192 element.removeEventListener("contextmenu", stopEvent); 193 element.removeEventListener("scroll", handleOnScroll); 194} 195 196// Returns the clientX and clientY values as "{x:_ , y:_}" from the given event, supporting click and touch 197function getLocationFromEvent(event) { 198 if (event.clientX) { 199 return ({ x: event.clientX | 0, y: event.clientY | 0 }); 200 } else if (event && event.touches[0] && event.touches[0].clientX) { 201 return ({ x: event.touches[0].clientX | 0, y: event.touches[0].clientY | 0 }); 202 } 203} 204
205// Puts focus on the given element and stops the relevant event in order to retain focus 206function setFocus(theEvent, elementId) { 207 document.getElementById(elementId).focus(); 208 return(stopEvent(theEvent)); 209} 210 211// Called by C# to toggle the visibility and space use of an element 212// The given "displayType" is the value used when making the element visible (block, inline, inline-block, etc.) 213function toggleElement(elementId, displayType) { 214 var element = document.getElementById(elementId); 215 if (element) { 216 if (element.style.display == "none") { 217 element.style.display = displayType; 218 element.style.visibility = "visible"; 219 } else { 220 element.style.display = "none"; 221 element.style.visibility = "hidden"; 222 } 223 } 224} 225 226function makeElementVisible(elementId, displayType = "block") { 227 var element = document.getElementById(elementId); 228 if (element) { 229 element.style.display = displayType; 230 element.style.visibility = "visible"; 231 } else { 232 console.error("makeElementVisible(" + elementId + "): element not found"); 233 } 234} 235 236function makeElementHidden(elementId) { 237 var element = document.getElementById(elementId); 238 if (element) { 239 element.style.display = "none"; 240 element.style.visibility = "hidden"; 241 } else { 242 console.error("makeElementHidden(" + elementId + "): element not found"); 243 } 244} 245 246// Called by C# to set the given element to absolute positioning with the given location 247function setElementLocation(elementId, top, left) { 248 var element = document.getElementById(elementId); 249 if (!setElementLocationViaElement(element, top, left)) { 250 console.error("setElementLocation(" + elementId + "): element not found"); 251 } 252} 253 254function setElementLocationViaElement(element, top, left) { 255 if (element) { 256 element.style.position = "fixed"; 257 element.style.top = top + "px"; 258 element.style.left = left + "px"; 259 return (true); 260 } 261 return (false); 262} 263 264// Called by C# to set the given element to absolute positioning with the given location and size 265function setElementRect(elementId, top, left, width, height) { 266 var element = document.getElementById(elementId); 267 if (element) { 268 element.style.position = "fixed"; 269 element.style.top = top + "px"; 270 element.style.left = left + "px"; 271 element.style.width = width + "px"; 272 element.style.height = height + "px"; 273 } 274} 275 276// Returns the given element's location as { x:_, y:_ }. 277// The function relies on element.style.left and element.style.top to understand element location. 278function getElementLocation(elementId) { 279 var element = document.getElementById(elementId); 280 var result = { 281 "x": parseInt(element.style.left), 282 "y": parseInt(element.style.top) 283 }; 284 return (result); 285} 286 287// Returns the given element's location as { x:_, y:_ }. 288// The function relies on element.offsetLeft and element.offsetTop to understand element location. 289function getElementOffsetLocation(elementId) { 290 var element = document.getElementById(elementId); 291 var result = { 292 "x": parseInt(element.offsetLeft), 293 "y": parseInt(element.offsetTop) 294 }; 295 return (result); 296} 297 298// Returns: { "x":_, "y":_, "width":_, "height":_, "top":_, "right":_, "bottom":_, "left":_ } 299function getElementRect(elementId) { 300 return(document.getElementById(elementId).getBoundingClientRect()); 301} 302 303function resizeElementUp(elementId, targetHeight) { 304 const element = document.getElementById(elementId); 305 if (element) { 306 const currentTop = parseInt(element.style.top); 307 const currentHeight = getEleementHeightViaElement(element); 308 const yDelta = currentHeight - targetHeight; 309 const targetTop = currentTop + yDelta; 310 element.style.top = targetTop + "px"; 311 element.style.height = targetHeight + "px"; 312 if (dotNetBrowserAPIsCallbackInstance) { 313 dotNetBrowserAPIsCallbackInstance.invokeMethodAsync("JsiElementUpdated", elementId); 314 } 315 } 316} 317 318function resizeElementDown(elementId, targetHeight) { 319 const element = document.getElementById(elementId); 320 if (element) { 321 element.style.height = targetHeight + "px"; 322 if (dotNetBrowserAPIsCallbackInstance) { 323 dotNetBrowserAPIsCallbackInstance.invokeMethodAsync("JsiElementUpdated", elementId); 324 } 325 } 326} 327 328// Executes the given function now and then again after any transitions on the element finish. 329function callBeforeAndAfterTransition(element, functionToCall) { 330 const transitionListener = (event) => { 331 element.removeEventListener("transitionend", transitionListener); 332 functionToCall(); 333 }; 334 element.addEventListener("transitionend", transitionListener); 335 functionToCall(); 336} 337 338// If the given element has a top, right, or bottom edge that is outside the browser window the element is moved so that the relevent edge(s) 339// aligned with the browsers viewport edge. This function accounts for any in-flight transitions and will reposition the element for 340// current state as well as state after any transition is finished. Callbacks to "JsiElementUpdated" are made whenever the element is moved. 341function fitElementOnScreen(elementId) { 342 const element = document.getElementById(elementId); 343 const functionToCall = () => { 344 if ((fitElementOnScreenViaElement(element)) && (dotNetBrowserAPIsCallbackInstance)) { 345 dotNetBrowserAPIsCallbackInstance.invokeMethodAsync("JsiElementUpdated", elementId); 346 } 347 }; 348 callBeforeAndAfterTransition(element, functionToCall); 349} 350 351// If the given element has a top, right, or bottom edge that is outside the browser window the 352// element is moved so that the relevent edge(s) aligned with the browsers viewport edge. 353function fitElementOnScreenViaElement(element) { 354 var height = getEleementHeightViaElement(element); 355 var width = parseInt(element.offsetWidth);
356 //console.error("width:" + width + " height:" + height); 357 var x = parseInt(element.style.left); 358 var y = parseInt(element.style.top); 359 //console.error("loc: " + x + " x " + y); 360 var xDelta = (window.innerWidth - (x + width)); 361 var yDelta = (window.innerHeight - (y + height)); 362 //console.error("deltas: " + xDelta + " x " + yDelta); 363 if ((xDelta < 0) || (yDelta < 0) || (y < 0) || ((y + yDelta) < 0)) { 364 if (xDelta < 0) { x = x + xDelta; } 365 if (yDelta < 0) { y = y + yDelta; } 366 if (y < 0) { y = 0; } 367 setElementLocationViaElement(element, y, x); 368 return (true); 369 } 370 return (false); 371} 372 373function getEleementHeightViaElement(element) { 374 var elementStyle = getComputedStyle(element); 375 var heightStr = elementStyle.height; 376 if (!heightStr || heightStr.length <= 0) { heightStr = element.offsetHeight; } 377 return(parseInt(heightStr.replace("px", ""))); 378} 379 380// Called by C# to toggle a sub-attribute of the CSS class indicated by the given query selector string. 381function toggleClassFromQuerySelector(querySelector, toToggle) { 382 document.querySelector(querySelector).classList.toggle(toToggle); 383} 384 385// Called by C# to set an attribute value on a DOM element 386function setElementStyle(elementId, styleName, value) { 387 var element = document.getElementById(elementId); 388 if (element) { element.style[styleName] = value; } 389} 390 391// Called by C# to set an attribute value on a DOM element 392function setElementAttribute(elementId, attributeName, value) { 393 var element = document.getElementById(elementId); 394 if (element) { element.setAttribute(attributeName, value); } 395} 396 397// Called by C# to set an attribute value on multiple DOM elements with the given class 398function setElementsAttribute(elementsClass, attributeName, value) { 399 var elements = document.getElementsByClassName(elementsClass); 400 for (var i = 0; i < elements.length; i++) { 401 var element = elements[i]; 402 if (element) { 403 element.setAttribute(attributeName, value); 404 } 405 } 406} 407 408// Called by C# to set opacity on a DOM element 409function setElementOpacity(elementId, opacity) { 410 var element = document.getElementById(elementId); 411 if (element) { element.style.opacity = opacity; } 412} 413 414// Called by C# to set background on a DOM element 415function setElementBackground(elementId, background) { 416 var elem = document.getElementById(elementId); 417 if(elem) elem.style.background = background; 418} 419 420// Called by C# to set color on a DOM element 421function setElementColor(elementId, color) { 422 var element = document.getElementById(elementId); 423 if (element) { element.style.color = color; } 424} 425 426// Called by C# to set the value on a DOM element (the element must support the ".value" field) 427function setElementValue(elementId, value) { 428 var element = document.getElementById(elementId); 429 if (element) { element.value = value; } 430} 431 432// Called by C# to set the "disabled" value on a DOM element (the element must support the ".disabled" field and the value past must be "true" or "false") 433function setElementDisabled(elementId, isDisabled) { 434 var element = document.getElementById(elementId); 435 if (element) { element.disabled = isDisabled; } 436} 437 438// Called by C# to set the placeholder text of a DOM element (the element must support the ".placeholder" field) 439function setPlaceholderText(elementId, placeholderText) { 440 var element = document.getElementById(elementId); 441 if (element) { element.placeholder = placeholderText; } 442} 443 444// Called by C# to add a CSS class to a DOM element 445function addClassToElement(elementId, className) { 446 var element = document.getElementById(elementId) 447 if (element) { 448 element.classList.add(className); 449 } else { 450 console.error("JavaScript: addClassToElement(): Element ID '" + elementId + "' not found"); 451 } 452} 453 454// Called by C# to remove a CSS class from a DOM element 455function removeClassFromElement(elementId, className) { 456 var element = document.getElementById(elementId) 457 if (element) { 458 element.classList.remove(className); 459 } else { 460 console.error("JavaScript: removeClassFromElement(): Element ID '" + elementId + "' not found"); 461 } 462} 463 464// If the browser has history to go back to then simply behaves like the browser "back" button, otherwise navigates to the site origin 465function goBackOrHome() { 466 if (history.length > 2) { 467 history.back(); 468 } else { 469 window.location.href = window.location.origin; 470 } 471} 472 473// Called by C# to set a DOM element's height to it's scrollHeight minus a given "correction delta", 474// which allows the caller to correct for things like border, margin, etc. 475function setElementHeightToScrollHeight(elementId, correctionDelta) { 476 var element = document.getElementById(elementId); 477 if (element) { 478 element.style.height = "auto"; 479 element.style.height = (element.scrollHeight - correctionDelta) + "px"; 480 } 481} 482 483// Simple accessors for properties 484function getNavigatorAppCodeName() { return (navigator.appCodeName.toString()); } 485function getNavigatorAppName() { return (navigator.appName.toString()); } 486function getNavigatorAppVersion() { return (navigator.appVersion.toString()); } 487function getNavigatorCookieEnabled() { return (navigator.cookieEnabled.toString()); } 488function getNavigatorLanguage() { return (navigator.language.toString()); } 489function getNavigatorOnLine() { return (navigator.onLine.toString()); } 490function getNavigatorPlatform() { return (navigator.platform.toString()); } 491function getNavigatorUserAgent() { return (navigator.userAgent.toString()); } 492 493//********************************************************************************* 494// Customize Firebase Auth error UI 495// NOTE: This breaks the "Dismiss" link; need to figure that out before we use this 496function customizeFirebaseAuthErrorUI() { 497 console.log("JavaScript: customizeFirebaseAuthErrorUI()"); 498 var container = document.getElementById("firebaseui-auth-container"); 499 if (container.addEventListener) { 500 container.addEventListener( 501 "DOMSubtreeModified", 502 function () { 503 var container = document.getElementById("firebaseui-auth-container"); 504 var authUiHtml = container.innerHTML; 505 console.log("Error UI updated. '" + authUiHtml + "'"); 506 var signInCancelledText = 'Please authorize the required permissions to sign in to the application'; 507 if (authUiHtml.includes(signInCancelledText)) { 508 authUiHtml = authUiHtml.replace(signInCancelledText, "Please sign in to use Rewind.<br>"); 509 container.innerHTML = authUiHtml; 510 } 511 }, 512 false); 513 console.log("JavaScript: customizeFirebaseAuthErrorUI() event listener added"); 514 } 515}
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.