Created
April 23, 2012 10:35
-
-
Save obeattie/2470146 to your computer and use it in GitHub Desktop.
Cross-browser HTML5 fullscreen support. What a calamity.
This file contains hidden or 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
### The window controller manages the size of the window; providing interfaces for entering/leaving full-screen modes | |
(and providing events when that happens) ### | |
class LV.WindowController | |
constructor: () -> | |
@isFullscreenCapable = not Modernizr.touch | |
@win = $(window) | |
# Create a debounced version; we only care after resizing has finished | |
@onResize = _.debounce(@_onResize, 300) | |
@bind({ | |
fullscreenEntered: @onFullscreenChanged, | |
fullscreenExited: @onFullscreenChanged | |
}) | |
@win.on('resize', @onResize) | |
$(document).on({ | |
# We want these to look immediately | |
webkitfullscreenchange: @_onResize, | |
mozfullscreenchange: @_onResize | |
}) | |
# Set the tolerances appropriately (when we fallback to detecting whether the window is full-screen or not based | |
# on window vs screen dimensions, these are used as the tolerances) | |
if navigator.userAgent.match(/Safari/) | |
@tolerance = [0, 60] | |
else | |
@tolerance = [10, 68] | |
@_onResize() | |
return | |
getCombo: (type='enter') -> | |
### Returns the appropriate key combination ### | |
platform = navigator.platform | |
if /^Win(16|32|64)$/i.test(platform) | |
# F11, easy peasy | |
return ['F11'] | |
else if /^Mac/i.test(platform) | |
# Safari has to have esc to exit, even though their menus say otherwise | |
if type is 'exit' and navigator.userAgent.match(/Safari/) | |
return ['Esc'] | |
# Firefox uses Cmd + Shift + F to exit (ffs) | |
else if navigator.userAgent.match(/Firefox/i) | |
return ['\u21e7 <small>Shift</small>', '\u2318 <small>Cmd</small>', 'F'] | |
else | |
# Cmd + Ctrl + F | |
return ['^ <small>Ctrl</small>', '\u2318 <small>Cmd</small>', 'F'] | |
enterFullscreen: () => | |
### Make the window full-screen. If the browser doesn't support the fullscreen API, then display a modal | |
instructing the user to press the correct key combination for their browser/platform ### | |
return if not @isFullscreenCapable | |
launchedNatively = false | |
# WebKit: webkitRequestFullScreen | |
if document.body.webkitRequestFullScreen | |
document.body.webkitRequestFullScreen(Element.ALLOW_KEYBOARD_INPUT) | |
# Annoying Safari bug; we can't request keyboard input and the request fails | |
launchedNatively = not (navigator.userAgent.match(/Safari/) and not navigator.userAgent.match(/Chrome/i)) | |
# Mozilla: mozRequestFullScreen | |
else if document.mozRequestFullScreen | |
document.body.mozRequestFullScreen() | |
launchedNatively = true | |
# Anything else: display the modal with the apprpriate key combination (based on OS+Browser) | |
if not launchedNatively | |
combo = @getCombo() | |
# Generate the content to go inside the modal | |
content = $("""<div class="modal-content fullscreen-modal"> | |
<p>To go full screen, press #{ _.str.pluralize('this key on your keyboard', combo.length, 'these keys on your keyboard together') }:</p> | |
<div><ul class="keys"/></div> | |
</div>""") | |
ul = content.find('ul') | |
for key in combo | |
ul.append($("<li>#{ key }</li>")) | |
# And launch the modal | |
modal = new LV.Modal(content) | |
@bindOnce('fullscreenEntered', modal.undraw) | |
@launchedNatively = launchedNatively | |
return | |
leaveFullscreen: () => | |
### Leave full-screen mode ### | |
return if not @isFullscreenCapable | |
# Try to see if there's a native implementation to get out | |
nativeImpl = Modernizr.prefixed('CancelFullScreen', document) | |
if @launchedNatively and nativeImpl | |
nativeImpl() | |
# Otherwise, another modal | |
else | |
combo = @getCombo('exit') | |
content = $("""<div class="modal-content fullscreen-modal"> | |
<p>To leave full screen, press #{ _.str.pluralize('this key on your keyboard', combo.length, 'these keys on your keyboard together') }:</p> | |
<div><ul class="keys"/></div> | |
</div>""") | |
ul = content.find('ul') | |
for key in combo | |
ul.append($("<li>#{ key }</li>")) | |
modal = new LV.Modal(content) | |
@bindOnce('fullscreenExited', modal.undraw) | |
return | |
toggleFullscreen: () => | |
### Toggle full-screen mode ### | |
if @fullscreened == true | |
@leaveFullscreen() | |
else | |
@enterFullscreen() | |
# Note that this is debounced in the constructor as @.onResize() | |
_onResize: (force) => | |
### Handle a resize event of the window; perform detection of entering/leaving full-screen modes ### | |
testDimensions = () => | |
(Math.abs(@win.width() - screen.width) <= @tolerance[0]) and | |
(Math.abs(@win.height() - screen.height) <= @tolerance[1]) | |
return if not @isFullscreenCapable | |
if @fullscreened != true and ( | |
force is true or | |
# TODO: This doesn't work... | |
#document.webkitIsFullScreen or | |
testDimensions() | |
) | |
@fullscreened = true | |
$(document.body).addClass('fullscreen') | |
@trigger('fullscreenEntered') | |
else if @fullscreened != false and ( | |
force is false or | |
# TODO: This doesn't work... | |
#(not document.webkitIsFullScreen) or | |
(not testDimensions()) | |
) | |
@fullscreened = false | |
$(document.body).removeClass('fullscreen') | |
@trigger('fullscreenExited') | |
return | |
onFullscreenChanged: () => | |
### Very annoyingly, when fullscreen is entered/exited on Chrome/Windows, a bug occurs which causes the document | |
size to be calculated as 0. To workaround this, we hide the entire body and re-show it after the initial | |
reflow (to trigger two reflows) | |
NOTE: We can't show/hide the entire body as this seems to render any Flash inside the body as defunct (and | |
we may be using the Flash audio player). Jeeezzzz. ### | |
if /^Win(16|32|64)$/i.test(navigator.platform) and navigator.userAgent.match(/Chrome/i) | |
$('#body-wrapper').hide() | |
_.defer () => | |
$('#body-wrapper').show() | |
$(window).resize() | |
return | |
return | |
_.extend(LV.WindowController::, LV.Evented) | |
LV.init.push '_initWindowController', () -> | |
# Singleton; it'll only be a function once (after that it's an object) | |
if typeof LV.WindowController is 'function' | |
LV.WindowController = new LV.WindowController() |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment