Created
March 13, 2025 17:56
-
-
Save gartnera/80112cb67ddfbcc9d9122edfe53704bd to your computer and use it in GitHub Desktop.
Virtualize the Fullscreen API to expand elements without using native fullscreen. This is useful when using a window manager that doesn't like it when windows try to go fullscreen.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
// ==UserScript== | |
// @name Fullscreen API Virtualizer | |
// @namespace https://agartner.com | |
// @version 1.0 | |
// @description Virtualize the Fullscreen API to expand elements without using native fullscreen | |
// @author You | |
// @match *://*/* | |
// @grant none | |
// @run-at document-start | |
// ==/UserScript== | |
(function() { | |
'use strict'; | |
// Add CSS for virtualized fullscreen | |
document.head.appendChild(Object.assign(document.createElement('style'), { | |
textContent: ` | |
.virtualized-fullscreen { | |
position: fixed !important; | |
top: 0 !important; | |
left: 0 !important; | |
width: 100vw !important; | |
height: 100vh !important; | |
z-index: 2147483647 !important; | |
background-color: black !important; | |
margin: 0 !important; | |
padding: 0 !important; | |
border: none !important; | |
box-sizing: border-box !important; | |
} | |
` | |
})); | |
// Track fullscreen state | |
const fullscreenState = { | |
active: false, | |
element: null, | |
originalStyles: null | |
}; | |
// Style management functions | |
function saveOriginalStyles(element) { | |
const stylesToSave = ['position', 'top', 'left', 'width', 'height', 'zIndex', | |
'backgroundColor', 'margin', 'padding', 'border', 'boxSizing']; | |
const originalStyles = {}; | |
stylesToSave.forEach(prop => { | |
originalStyles[prop] = element.style[prop]; | |
}); | |
return originalStyles; | |
} | |
function restoreOriginalStyles(element, styles) { | |
Object.keys(styles).forEach(prop => { | |
element.style[prop] = styles[prop]; | |
}); | |
} | |
// Virtual fullscreen implementation | |
function virtualRequestFullscreen() { | |
if (fullscreenState.active) return Promise.resolve(); | |
const element = this; | |
fullscreenState.element = element; | |
fullscreenState.originalStyles = saveOriginalStyles(element); | |
fullscreenState.active = true; | |
element.classList.add('virtualized-fullscreen'); | |
document.dispatchEvent(new Event('fullscreenchange')); | |
return Promise.resolve(); | |
} | |
function virtualExitFullscreen() { | |
if (!fullscreenState.active) return Promise.resolve(); | |
const element = fullscreenState.element; | |
element.classList.remove('virtualized-fullscreen'); | |
restoreOriginalStyles(element, fullscreenState.originalStyles); | |
fullscreenState.active = false; | |
fullscreenState.element = null; | |
fullscreenState.originalStyles = null; | |
document.dispatchEvent(new Event('fullscreenchange')); | |
return Promise.resolve(); | |
} | |
// Helper function to override methods safely | |
function overrideMethod(object, methodName, newImplementation) { | |
if (typeof object[methodName] === 'function') { | |
object[methodName] = newImplementation; | |
} | |
} | |
// Helper function to override getters safely | |
function overrideGetter(object, propertyName, getter) { | |
if (Object.getOwnPropertyDescriptor(object, propertyName)) { | |
Object.defineProperty(object, propertyName, { get: getter }); | |
} | |
} | |
// Override request methods | |
const requestMethods = ['requestFullscreen', 'mozRequestFullScreen', | |
'webkitRequestFullscreen', 'msRequestFullscreen']; | |
requestMethods.forEach(method => { | |
overrideMethod(Element.prototype, method, virtualRequestFullscreen); | |
}); | |
// Override exit methods | |
const exitMethods = ['exitFullscreen', 'mozCancelFullScreen', | |
'webkitExitFullscreen', 'msExitFullscreen']; | |
exitMethods.forEach(method => { | |
overrideMethod(Document.prototype, method, virtualExitFullscreen); | |
}); | |
// Override element getters | |
const elementGetters = ['fullscreenElement', 'webkitFullscreenElement', | |
'mozFullScreenElement', 'msFullscreenElement']; | |
elementGetters.forEach(getter => { | |
overrideGetter(Document.prototype, getter, function() { | |
return fullscreenState.active ? fullscreenState.element : null; | |
}); | |
}); | |
// Override enabled getters | |
const enabledGetters = ['fullscreenEnabled', 'webkitFullscreenEnabled', | |
'mozFullScreenEnabled', 'msFullscreenEnabled']; | |
enabledGetters.forEach(getter => { | |
overrideGetter(Document.prototype, getter, function() { | |
return true; | |
}); | |
}); | |
// Add Escape key handler | |
document.addEventListener('keydown', function(event) { | |
if (event.key === 'Escape' && fullscreenState.active) { | |
virtualExitFullscreen(); | |
} | |
}); | |
console.log('Fullscreen API Virtualizer loaded successfully!'); | |
})(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment