oxedyne/daimond/www/js/fullscreen.js
4.3 KiB, 1 run
created by r2519314175:1365, which is this file's identity for as long as the history lasts, whatever it is later renamed to
download · who wrote it · its history
| 1 | /* ============================================================ |
| 2 | Daimond — the app-wide full-screen toggle (phone only) |
| 3 | ------------------------------------------------------------ |
| 4 | Firefox on Android does not install the PWA standalone, so an |
| 5 | old device stuck on it has no immersive view at all. The |
| 6 | browser Fullscreen API is the one way there: it hides the |
| 7 | address bar, the tabs and the status bar together. Desktop |
| 8 | already has F11 and a real standalone install, so this control |
| 9 | never shows there. |
| 10 | |
| 11 | Nothing is persisted -- full screen is transient, and the |
| 12 | browser drops it on navigation and on the system back gesture. |
| 13 | The button's face is kept true by listening for |
| 14 | `fullscreenchange`, so leaving by the back gesture (no click) |
| 15 | still flips the icon and label back. |
| 16 | |
| 17 | Gated on `DaimondShell.isPhone()`, the app's own layout signal |
| 18 | (the single 760 cliff), so the control appears and vanishes |
| 19 | with the phone layout and stays off the landscape breakpoint |
| 20 | gap a naive innerWidth check would fall into. |
| 21 | ============================================================ */ |
| 22 | (function () { |
| 23 | 'use strict'; |
| 24 | |
| 25 | var docEl = document.documentElement; |
| 26 | |
| 27 | /// The element currently filling the screen, across vendor prefixes. |
| 28 | function fsElement() { |
| 29 | return document.fullscreenElement |
| 30 | || document.webkitFullscreenElement |
| 31 | || document.mozFullScreenElement |
| 32 | || document.msFullscreenElement |
| 33 | || null; |
| 34 | } |
| 35 | |
| 36 | /// Does this browser offer the Fullscreen API at all? |
| 37 | function canFull() { |
| 38 | return !!(docEl.requestFullscreen || docEl.webkitRequestFullscreen |
| 39 | || docEl.mozRequestFullScreen || docEl.msRequestFullscreen); |
| 40 | } |
| 41 | |
| 42 | function enter() { |
| 43 | var fn = docEl.requestFullscreen || docEl.webkitRequestFullscreen |
| 44 | || docEl.mozRequestFullScreen || docEl.msRequestFullscreen; |
| 45 | if (fn) try { fn.call(docEl); } catch (e) { /* denied, or no user gesture */ } |
| 46 | } |
| 47 | |
| 48 | function leave() { |
| 49 | var fn = document.exitFullscreen || document.webkitExitFullscreen |
| 50 | || document.mozCancelFullScreen || document.msExitFullscreen; |
| 51 | if (fn) try { fn.call(document); } catch (e) { /* already out */ } |
| 52 | } |
| 53 | |
| 54 | /// A translated string, or the English fallback if the key is absent. |
| 55 | function t(key, fallback) { |
| 56 | return (window.DaimondI18n && DaimondI18n.has && DaimondI18n.has(key)) |
| 57 | ? DaimondI18n.t(key) : fallback; |
| 58 | } |
| 59 | |
| 60 | /// Phone only, decided by the app's own signal rather than a width read of |
| 61 | /// our own -- one 760 cliff, shared with the rail and the sheet. |
| 62 | function isPhone() { |
| 63 | return !!(window.DaimondShell && DaimondShell.isPhone && DaimondShell.isPhone()); |
| 64 | } |
| 65 | |
| 66 | var btn = null; |
| 67 | |
| 68 | /// Show or hide the control for the width, and say which state it is in. |
| 69 | function sync() { |
| 70 | if (!btn) return; |
| 71 | // No API, or not the phone layout: the control has no business on screen. |
| 72 | if (!canFull() || !isPhone()) { btn.style.display = 'none'; return; } |
| 73 | btn.style.display = ''; |
| 74 | var on = !!fsElement(); |
| 75 | // The label names the destination while the mode is on: "Exit full |
| 76 | // screen", not "Full screen (on)". |
| 77 | var label = on ? t('fullscreen.exit', 'Exit full screen') |
| 78 | : t('fullscreen.enter', 'Full screen'); |
| 79 | btn.classList.toggle('on', on); |
| 80 | btn.setAttribute('aria-pressed', on ? 'true' : 'false'); |
| 81 | btn.setAttribute('aria-label', label); |
| 82 | btn.title = label; |
| 83 | } |
| 84 | |
| 85 | function init() { |
| 86 | btn = document.getElementById('fullscreen-btn'); |
| 87 | if (!btn) return; |
| 88 | // A browser without the API never sees the button. |
| 89 | if (!canFull()) { btn.style.display = 'none'; return; } |
| 90 | |
| 91 | btn.addEventListener('click', function () { |
| 92 | if (fsElement()) leave(); else enter(); |
| 93 | }); |
| 94 | |
| 95 | // Keep the face true even when the user leaves by the system back |
| 96 | // gesture, which fires a change event but never a click. |
| 97 | ['fullscreenchange', 'webkitfullscreenchange', |
| 98 | 'mozfullscreenchange', 'MSFullscreenChange'].forEach(function (ev) { |
| 99 | document.addEventListener(ev, sync); |
| 100 | }); |
| 101 | |
| 102 | // The 760 cliff, watched the same way the shell watches it. |
| 103 | var mq = window.matchMedia('(max-width: 760px)'); |
| 104 | if (mq.addEventListener) mq.addEventListener('change', sync); |
| 105 | else if (mq.addListener) mq.addListener(sync); |
| 106 | |
| 107 | // Re-label on a language change. |
| 108 | if (window.DaimondI18n && DaimondI18n.onChange) DaimondI18n.onChange(sync); |
| 109 | |
| 110 | sync(); |
| 111 | } |
| 112 | |
| 113 | if (document.readyState === 'loading') { |
| 114 | document.addEventListener('DOMContentLoaded', init); |
| 115 | } else { |
| 116 | init(); |
| 117 | } |
| 118 | })(); |