Skip to content

Instantly share code, notes, and snippets.

@obeattie
Created April 23, 2012 10:35
Show Gist options
  • Save obeattie/2470146 to your computer and use it in GitHub Desktop.
Save obeattie/2470146 to your computer and use it in GitHub Desktop.
Cross-browser HTML5 fullscreen support. What a calamity.
### 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