diff --git a/bigscreen.js b/bigscreen.js index b65f06e..aeaf8a5 100644 --- a/bigscreen.js +++ b/bigscreen.js @@ -1,5 +1,5 @@ /*! BigScreen - * v2.0.4 - 2014-02-06 + * v2.1.0 - 2014-09-30 * https://github.com/bdougherty/BigScreen * Copyright 2014 Brad Dougherty; Apache 2.0 License */ @@ -28,6 +28,35 @@ } return properties; }(); + var EVENT = { + ENTER: "enter", + EXIT: "exit", + CHANGE: "change", + ERROR: "error" + }; + var events = []; + var attachedEvents = {}; + Object.keys(EVENT).forEach(function(key) { + events.push(EVENT[key]); + attachedEvents[EVENT[key]] = []; + }); + function fire() { + var args = Array.prototype.slice.apply(arguments); + var event = args.shift(); + attachedEvents[event].forEach(function(callback) { + if (typeof callback === "function") { + callback.apply(callback, args); + } + }); + } + function makeListener(fn) { + return function(type, handler) { + if (events.indexOf(type) === -1) { + return; + } + fn.call(this, type, handler); + }; + } function _getVideo(element) { var videoElement = null; if (element.tagName === "VIDEO") { @@ -89,6 +118,9 @@ } var callOnEnter = function(actualElement) { var lastElement = elements[elements.length - 1]; + if (lastElement) { + return; + } if ((actualElement === lastElement.element || actualElement === lastVideoElement) && lastElement.hasEntered) { return; } @@ -100,6 +132,7 @@ } lastElement.enter.call(lastElement.element, actualElement || lastElement.element); lastElement.hasEntered = true; + fire(EVENT.ENTER, bigscreen.element); }; var callOnExit = function() { if (lastVideoElement && !hasControls && !iOS7) { @@ -111,9 +144,11 @@ var element = elements.pop(); if (element) { element.exit.call(element.element); + fire(EVENT.EXIT, element.element); if (!bigscreen.element) { elements.forEach(function(element) { element.exit.call(element.element); + fire(EVENT.EXIT, element.element); }); elements = []; bigscreen.onexit(); @@ -126,6 +161,7 @@ element = element || obj.element; obj.error.call(element, reason); bigscreen.onerror(element, reason); + fire(EVENT.ERROR, element, reason); } }; var bigscreen = { @@ -196,6 +232,15 @@ } return video.readyState < video.HAVE_METADATA ? "maybe" : video.webkitSupportsFullscreen; }, + on: makeListener(function(type, callback) { + attachedEvents[type].push(callback); + }), + off: makeListener(function(type, callback) { + var index = attachedEvents[type].indexOf(callback); + if (index > -1) { + attachedEvents[type].splice(index, 1); + } + }), onenter: emptyFunction, onexit: emptyFunction, onchange: emptyFunction, @@ -232,6 +277,7 @@ if (fn.change) { document.addEventListener(fn.change, function onFullscreenChange(event) { bigscreen.onchange(bigscreen.element); + fire(EVENT.CHANGE, bigscreen.element); if (bigscreen.element) { var previousElement = elements[elements.length - 2]; if (previousElement && previousElement.element === bigscreen.element) { @@ -246,17 +292,31 @@ }, false); } document.addEventListener("webkitbeginfullscreen", function onBeginFullscreen(event) { - elements.push({ - element: event.srcElement, - enter: emptyFunction, - exit: emptyFunction, - error: emptyFunction - }); + var shouldPushElement = true; + if (elements.length > 0) { + for (var i = 0, length = elements.length; i < length; i++) { + var video = _getVideo(elements[i].element); + if (video === event.srcElement) { + shouldPushElement = false; + break; + } + } + } + if (shouldPushElement) { + elements.push({ + element: event.srcElement, + enter: emptyFunction, + exit: emptyFunction, + error: emptyFunction + }); + } bigscreen.onchange(event.srcElement); + fire(EVENT.CHANGE, bigscreen.srcElement); callOnEnter(event.srcElement); }, true); document.addEventListener("webkitendfullscreen", function onEndFullscreen(event) { bigscreen.onchange(event.srcElement); + fire(EVENT.CHANGE, event.srcElement); callOnExit(event.srcElement); }, true); if (fn.error) { diff --git a/bigscreen.min.js b/bigscreen.min.js index 55409c6..4043103 100644 --- a/bigscreen.min.js +++ b/bigscreen.min.js @@ -1,2 +1,2 @@ -// BigScreen v2.0.4 - 2014-02-06 - Apache 2.0 License -!function(a,b,c){"use strict";function d(a){var b=null;if("VIDEO"===a.tagName)b=a;else{var c=a.getElementsByTagName("video");c[0]&&(b=c[0])}return b}function e(a){var b=d(a);if(b&&b.webkitEnterFullscreen){try{b.readyState=7,k=function(){var a=b.createElement("video"),c={request:["requestFullscreen","webkitRequestFullscreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"],exit:["exitFullscreen","webkitCancelFullScreen","webkitExitFullscreen","mozCancelFullScreen","msExitFullscreen"],enabled:["fullscreenEnabled","webkitFullscreenEnabled","mozFullScreenEnabled","msFullscreenEnabled"],element:["fullscreenElement","webkitFullscreenElement","webkitCurrentFullScreenElement","mozFullScreenElement","msFullscreenElement"],change:["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],error:["fullscreenerror","webkitfullscreenerror","mozfullscreenerror","MSFullscreenError"]},d={};for(var e in c)for(var f=0,g=c[e].length;g>f;f++)if(c[e][f]in a||c[e][f]in b||"on"+c[e][f].toLowerCase()in b){d[e]=c[e][f];break}return d}(),l=null,m=null,n=function(){},o=[],p=!1;navigator.userAgent.indexOf("Android")>-1&&navigator.userAgent.indexOf("Chrome")>-1&&(p=parseInt(navigator.userAgent.replace(/^.*Chrome\/(\d+).*$/,"$1"),10)||!0);var q=function(a){var b=o[o.length-1];(a!==b.element&&a!==l||!b.hasEntered)&&("VIDEO"===a.tagName&&(l=a),1===o.length&&t.onenter(t.element),b.enter.call(b.element,a||b.element),b.hasEntered=!0)},r=function(){!l||m||j||(l.setAttribute("controls","controls"),l.removeAttribute("controls")),l=null,m=null;var a=o.pop();a&&(a.exit.call(a.element),t.element||(o.forEach(function(a){a.exit.call(a.element)}),o=[],t.onexit()))},s=function(a,b){if(o.length>0){var c=o.pop();b=b||c.element,c.error.call(b,a),t.onerror(b,a)}},t={request:function(a,d,f,g){if(a=a||b.body,o.push({element:a,enter:d||n,exit:f||n,error:g||n}),void 0===k.request)return e(a);if(c&&b[k.enabled]===!1)return e(a);if(p!==!1&&32>p)return e(a);if(c&&void 0===k.enabled)return k.enabled="webkitFullscreenEnabled",a[k.request](),setTimeout(function(){b[k.element]?b[k.enabled]=!0:(b[k.enabled]=!1,e(a))},250),void 0;try{/5\.1[\.\d]* Safari/.test(navigator.userAgent)?a[k.request]():a[k.request](i&&Element.ALLOW_KEYBOARD_INPUT),setTimeout(function(){b[k.element]||s(c?"not_enabled":"not_allowed",a)},100)}catch(h){s("not_enabled",a)}},exit:function(){h(),b[k.exit]()},toggle:function(a,b,c,d){t.element?t.exit():t.request(a,b,c,d)},videoEnabled:function(a){if(t.enabled)return!0;a=a||b.body;var c=d(a);return c&&void 0!==c.webkitSupportsFullscreen?c.readyStatep?!1:b[k.enabled]||!1:!0}}})}catch(u){t.element=null,t.enabled=!1}k.change&&b.addEventListener(k.change,function(){if(t.onchange(t.element),t.element){var a=o[o.length-2];a&&a.element===t.element?r():(q(t.element),g())}else r()},!1),b.addEventListener("webkitbeginfullscreen",function(a){o.push({element:a.srcElement,enter:n,exit:n,error:n}),t.onchange(a.srcElement),q(a.srcElement)},!0),b.addEventListener("webkitendfullscreen",function(a){t.onchange(a.srcElement),r(a.srcElement)},!0),k.error&&b.addEventListener(k.error,function(){s("not_allowed")},!1),a.BigScreen=t}(window,document,self!==top); \ No newline at end of file +// BigScreen v2.1.0 - 2014-09-30 - Apache 2.0 License +!function(a,b,c){"use strict";function d(){var a=Array.prototype.slice.apply(arguments),b=a.shift();p[b].forEach(function(b){"function"==typeof b&&b.apply(b,a)})}function e(a){return function(b,c){-1!==o.indexOf(b)&&a.call(this,b,c)}}function f(a){var b=null;if("VIDEO"===a.tagName)b=a;else{var c=a.getElementsByTagName("video");c[0]&&(b=c[0])}return b}function g(a){var b=f(a);if(b&&b.webkitEnterFullscreen){try{b.readyState=7,m=function(){var a=b.createElement("video"),c={request:["requestFullscreen","webkitRequestFullscreen","webkitRequestFullScreen","mozRequestFullScreen","msRequestFullscreen"],exit:["exitFullscreen","webkitCancelFullScreen","webkitExitFullscreen","mozCancelFullScreen","msExitFullscreen"],enabled:["fullscreenEnabled","webkitFullscreenEnabled","mozFullScreenEnabled","msFullscreenEnabled"],element:["fullscreenElement","webkitFullscreenElement","webkitCurrentFullScreenElement","mozFullScreenElement","msFullscreenElement"],change:["fullscreenchange","webkitfullscreenchange","mozfullscreenchange","MSFullscreenChange"],error:["fullscreenerror","webkitfullscreenerror","mozfullscreenerror","MSFullscreenError"]},d={};for(var e in c)for(var f=0,g=c[e].length;g>f;f++)if(c[e][f]in a||c[e][f]in b||"on"+c[e][f].toLowerCase()in b){d[e]=c[e][f];break}return d}(),n={ENTER:"enter",EXIT:"exit",CHANGE:"change",ERROR:"error"},o=[],p={};Object.keys(n).forEach(function(a){o.push(n[a]),p[n[a]]=[]});var q=null,r=null,s=function(){},t=[],u=!1;navigator.userAgent.indexOf("Android")>-1&&navigator.userAgent.indexOf("Chrome")>-1&&(u=parseInt(navigator.userAgent.replace(/^.*Chrome\/(\d+).*$/,"$1"),10)||!0);var v=function(a){var b=t[t.length-1];b||(a!==b.element&&a!==q||!b.hasEntered)&&("VIDEO"===a.tagName&&(q=a),1===t.length&&y.onenter(y.element),b.enter.call(b.element,a||b.element),b.hasEntered=!0,d(n.ENTER,y.element))},w=function(){!q||r||l||(q.setAttribute("controls","controls"),q.removeAttribute("controls")),q=null,r=null;var a=t.pop();a&&(a.exit.call(a.element),d(n.EXIT,a.element),y.element||(t.forEach(function(a){a.exit.call(a.element),d(n.EXIT,a.element)}),t=[],y.onexit()))},x=function(a,b){if(t.length>0){var c=t.pop();b=b||c.element,c.error.call(b,a),y.onerror(b,a),d(n.ERROR,b,a)}},y={request:function(a,d,e,f){if(a=a||b.body,t.push({element:a,enter:d||s,exit:e||s,error:f||s}),void 0===m.request)return g(a);if(c&&b[m.enabled]===!1)return g(a);if(u!==!1&&32>u)return g(a);if(c&&void 0===m.enabled)return m.enabled="webkitFullscreenEnabled",a[m.request](),void setTimeout(function(){b[m.element]?b[m.enabled]=!0:(b[m.enabled]=!1,g(a))},250);try{/5\.1[\.\d]* Safari/.test(navigator.userAgent)?a[m.request]():a[m.request](k&&Element.ALLOW_KEYBOARD_INPUT),setTimeout(function(){b[m.element]||x(c?"not_enabled":"not_allowed",a)},100)}catch(h){x("not_enabled",a)}},exit:function(){j(),b[m.exit]()},toggle:function(a,b,c,d){y.element?y.exit():y.request(a,b,c,d)},videoEnabled:function(a){if(y.enabled)return!0;a=a||b.body;var c=f(a);return c&&void 0!==c.webkitSupportsFullscreen?c.readyState-1&&p[a].splice(c,1)}),onenter:s,onexit:s,onchange:s,onerror:s};try{Object.defineProperties(y,{element:{enumerable:!0,get:function(){return q&&q.webkitDisplayingFullscreen?q:b[m.element]||null}},enabled:{enumerable:!0,get:function(){return"webkitCancelFullScreen"!==m.exit||c?u!==!1&&32>u?!1:b[m.enabled]||!1:!0}}})}catch(z){y.element=null,y.enabled=!1}m.change&&b.addEventListener(m.change,function(){if(y.onchange(y.element),d(n.CHANGE,y.element),y.element){var a=t[t.length-2];a&&a.element===y.element?w():(v(y.element),i())}else w()},!1),b.addEventListener("webkitbeginfullscreen",function(a){var b=!0;if(t.length>0)for(var c=0,e=t.length;e>c;c++){var g=f(t[c].element);if(g===a.srcElement){b=!1;break}}b&&t.push({element:a.srcElement,enter:s,exit:s,error:s}),y.onchange(a.srcElement),d(n.CHANGE,y.srcElement),v(a.srcElement)},!0),b.addEventListener("webkitendfullscreen",function(a){y.onchange(a.srcElement),d(n.CHANGE,a.srcElement),w(a.srcElement)},!0),m.error&&b.addEventListener(m.error,function(){x("not_allowed")},!1),a.BigScreen=y}(window,document,self!==top); \ No newline at end of file diff --git a/package.json b/package.json index c983712..8d83346 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "BigScreen", - "version": "2.0.4", + "version": "2.1.0", "description": "A simple library for using the JavaScript Fullscreen API.", "keywords": [ "fullscreen" diff --git a/src/bigscreen.js b/src/bigscreen.js index 728a639..49fc6c9 100644 --- a/src/bigscreen.js +++ b/src/bigscreen.js @@ -32,6 +32,43 @@ return properties; }()); + // Add allowed events + var EVENT = { + ENTER: 'enter', + EXIT: 'exit', + CHANGE: 'change', + ERROR: 'error' + }; + var events = []; + var attachedEvents = {}; + + Object.keys(EVENT).forEach(function(key) { + events.push(EVENT[key]); + attachedEvents[EVENT[key]] = []; + + }); + + function fire() { + var args = Array.prototype.slice.apply(arguments); + var event = args.shift(); + + attachedEvents[event].forEach(function(callback) { + if (typeof callback === 'function') { + callback.apply(callback, args); + } + }); + } + + function makeListener(fn) { + return function(type, handler) { + if (events.indexOf(type) === -1) { + return; + } + + fn.call(this, type, handler); + }; + } + // Find a child video in the element passed. function _getVideo(element) { var videoElement = null; @@ -121,6 +158,11 @@ // browser will fire 2 `webkitfullscreenchange` events when entering full screen from inside an // iframe. This is the result of the same bug as the resizeExitHack. var lastElement = elements[elements.length - 1]; + + if (lastElement) { + return; + } + if ((actualElement === lastElement.element || actualElement === lastVideoElement) && lastElement.hasEntered) { return; } @@ -139,6 +181,8 @@ // again if there is a duplicate call (see above). lastElement.enter.call(lastElement.element, actualElement || lastElement.element); lastElement.hasEntered = true; + + fire(EVENT.ENTER, bigscreen.element); }; var callOnExit = function() { @@ -159,12 +203,14 @@ // time from the iframe resize hack. if (element) { element.exit.call(element.element); + fire(EVENT.EXIT, element.element); // When the browser has fully exited full screen, make sure to loop // through and call the rest of the callbacks and then the global exit. if (!bigscreen.element) { elements.forEach(function(element) { element.exit.call(element.element); + fire(EVENT.EXIT, element.element); }); elements = []; @@ -182,6 +228,7 @@ obj.error.call(element, reason); bigscreen.onerror(element, reason); + fire(EVENT.ERROR, element, reason); } }; @@ -301,6 +348,18 @@ return video.readyState < video.HAVE_METADATA ? 'maybe' : video.webkitSupportsFullscreen; }, + on: makeListener(function(type, callback) { + attachedEvents[type].push(callback); + }), + + off: makeListener(function(type, callback) { + var index = attachedEvents[type].indexOf(callback); + + if (index > -1) { + attachedEvents[type].splice(index, 1); + } + }), + // ### onenter, onexit, onchange, onerror // Populate the global handlers with empty functions. onenter: emptyFunction, @@ -356,6 +415,7 @@ if (fn.change) { document.addEventListener(fn.change, function onFullscreenChange(event) { bigscreen.onchange(bigscreen.element); + fire(EVENT.CHANGE, bigscreen.element); if (bigscreen.element) { // This should be treated an exit if the element that is in full screen @@ -404,11 +464,13 @@ } bigscreen.onchange(event.srcElement); + fire(EVENT.CHANGE, bigscreen.srcElement); callOnEnter(event.srcElement); }, true); document.addEventListener('webkitendfullscreen', function onEndFullscreen(event) { bigscreen.onchange(event.srcElement); + fire(EVENT.CHANGE, event.srcElement); callOnExit(event.srcElement); }, true);