From de864df0432cdf9ddd0ae7fb043abf7385569c84 Mon Sep 17 00:00:00 2001 From: Sean Creeley Date: Mon, 30 Dec 2013 16:09:29 -0500 Subject: [PATCH] Fix a few issues with how we listen for ready --- README.rst | 110 +++++++++++++++++++++++++++++++++++++++++++++++++- SPEC.rst | 65 ++++++++++++++++------------- package.json | 2 +- src/player.js | 32 ++++++++++++--- test/test.js | 9 ++++- 5 files changed, 182 insertions(+), 36 deletions(-) diff --git a/README.rst b/README.rst index 64b1e0f..2ee521e 100644 --- a/README.rst +++ b/README.rst @@ -25,7 +25,7 @@ Install Player.js is hosted on Embedly's CDN. :: - + Ready @@ -47,6 +47,48 @@ ready is called. }); +Timing +------ +The timing between when the iframe is added and when the ready event is fired +is important. Sadly we cannot fire the ready event till the iframe is loaded, +but there is no concrete way of telling when postmessage is available to us. + +The best way is to do one of the following. + +Create the iframe via JavaScript +"""""""""""""""""""""""""""""""" +:: + + var iframe = document.createElement('iframe'); + iframe.src = 'https://example.com/iframe'; + document.body.appendChild(iframe); + + var player = new playerjs.Player(iframe); + +In this case, Player.js will listen to the onload event of the iframe and only +try to communicate when ready. + +Wait for the document to be ready. +"""""""""""""""""""""""""""""""""" +:: + + + + + +At this point we can reasonably assume that the iframe's been loaded and the +ready. Player.js will take care of listening for ready events that were fired +before the player is set up. + Methods ------- @@ -170,3 +212,69 @@ Events that can be listened to. ``error`` fires when an error occurs. + + +Receiver +-------- +If you are looking to implement the Player.js spec, we include a Receiver that +will allow you to easily listen to events and takes care of the house keeping. + +:: + + var receiver = new playerjs.Receiver(); + + receiver.on('play', function(){ + video.play(); + receiver.emit('play'); + }); + + receiver.on('pause', function(){ + video.pause(); + receiver.emit('pause'); + }); + + receiver.on('getDuration', function(callback){ + callback(video.duration); + }); + + video.addEventListener('timeupdate', function(){ + receiver.emit('timeupdate', { + seconds: video.currentTime, + duration: video.duration + }); + }); + + receiver.ready(); + + +Methods +------- + +``on`` + Requests an event from the video. The above player methods should be + implemented. If the event expects a return value a callback will be passed + into the function call:: + + receiver.on('getDuration', function(callback){ + callback(video.duration); + }); + + Otherwise you can safely ignore any inputs:: + + receiver.on('play', function(callback){ + video.play(); + }); + +``emit`` + Sends events to the parent as long as someone is listing. The above player + events should be implemented. If a value is expected, it should be passed in + as the second argument:: + + receiver.emit('timeupdate', {seconds:20, duration:40}); + +``ready`` + Once everything is in place and you are ready to start responding to events, + call this method. It performs some house keeping, along with emitting + ``ready``:: + + receiver.ready(); diff --git a/SPEC.rst b/SPEC.rst index 933180a..6e4a9a4 100644 --- a/SPEC.rst +++ b/SPEC.rst @@ -7,9 +7,9 @@ postMessage. This is based heavily off the HTML5 Video `Spec `_, Vimeo's `JavaScript -API`_, Soundclouds `Widget API +API `_, Soundclouds `Widget API `_ and YouTube's -`Player API https://developers.google.com/youtube/iframe_api_reference>`_. +`Player API `_. Why @@ -23,6 +23,15 @@ make this available via a common set of json that is passed back between the parent and the child. +License +------- +This specification is licensed under the Creative Commons Attribution License. +You can learn more about the license `here +`_. The more people that refine +this Spec, the better, so we tried to pick the least restrictive license we +could. + + Support ------- Currently the following browsers support postMessage and hence can use this: @@ -67,6 +76,7 @@ They have a common format:: If the method is a getter, i.e. ``getDuration`` the child frame will send the following message to the parent. +:: { event: 'methodName', @@ -81,7 +91,7 @@ The child frame will often fire a number of events, such as ``play``, ``finish`` and ``playProgress``. This defines what is passed back. No events should be passed back unless the parent explicitly asks for them. To -add a listener we send the following message. +add a listener we send the following message:: { method: 'addEventListener', @@ -101,37 +111,38 @@ When the event is fired, the parent will receive the following event data:: Client ------ It's helpful to have a quick example of the JavaScript before moving forward. +:: -// Play the video -document.getElementById('#iframe').contentWindow.postMessage( - JSON.stringify({ - method: 'play' - }) -); + // Play the video + document.getElementById('#iframe').contentWindow.postMessage( + JSON.stringify({ + method: 'play' + }) + ); -// Set up an event listener. + // Set up an event listener. -var iframe = document.getElementById('#iframe'), - origin = iframe.src.split('/', 3).join('/'); + var iframe = document.getElementById('#iframe'), + origin = iframe.src.split('/', 3).join('/'); -var play = function(){ - console.log('play); -}; + var play = function(){ + console.log('play); + }; -window.addEventListener('message', function(){ - if (e.origin === origin){ - if (e.event === play){ - played(); + window.addEventListener('message', function(){ + if (e.origin === origin){ + if (e.event === play){ + played(); + } } - } -}); + }); -iframe.contentWindow.postMessage( - JSON.stringify({ - method: 'addEventListener', - value: 'event' - }) -); + iframe.contentWindow.postMessage( + JSON.stringify({ + method: 'addEventListener', + value: 'event' + }) + ); Methods diff --git a/package.json b/package.json index 2d207b3..e109dec 100644 --- a/package.json +++ b/package.json @@ -1,7 +1,7 @@ { "title": "Player.js", "name": "Player.js", - "version": "0.0.4", + "version": "0.0.5", "homepage": "http://github.com/embedly/player.js", "author": { "name": "Embedly" diff --git a/src/player.js b/src/player.js index 4f7cc17..b39b1aa 100644 --- a/src/player.js +++ b/src/player.js @@ -37,6 +37,7 @@ playerjs.METHODS = [ 'addEventListener' ]; +playerjs.READIED = []; playerjs.Player.prototype.init = function(elem, options){ @@ -66,11 +67,16 @@ playerjs.Player.prototype.init = function(elem, options){ } else { playerjs.log('Post Message is not Available.'); } - - // Try the onload event, just lets us give another test. - this.elem.onload = function(){ + + // See if we caught the src event first, otherwise assume we haven't loaded + if (playerjs.READIED.indexOf(elem.src) > -1){ self.loaded = true; - }; + } else { + // Try the onload event, just lets us give another test. + this.elem.onload = function(){ + self.loaded = true; + }; + } }; playerjs.Player.prototype.send = function(data, callback, ctx){ @@ -93,7 +99,7 @@ playerjs.Player.prototype.send = function(data, callback, ctx){ } playerjs.log('Player.send', data, this.origin); - + if (this.loaded === true){ this.elem.contentWindow.postMessage(JSON.stringify(data), this.origin); } @@ -271,3 +277,19 @@ playerjs.Player.prototype.getLoop = function(callback, ctx){ }; window.playerjs = playerjs; + +// We need to catch all ready events in case the iframe is ready before the +// player is invoked. +playerjs.addEvent(window, 'message', function(e){ + var data; + try { + data = JSON.parse(e.data); + } catch (err){ + return false; + } + + // We need to determine if we are ready. + if (data.event === 'ready' && data.value.src){ + playerjs.READIED.push(data.value.src); + } +}); diff --git a/test/test.js b/test/test.js index 9410b67..1748063 100644 --- a/test/test.js +++ b/test/test.js @@ -168,6 +168,11 @@ for (var f in FRAMES){ iframe.height = 200; document.body.appendChild(iframe); - - loadPlayers(); + + // we want to load the players a couple of different ways. + if ( f % 2 === 1){ + loadPlayers(); + } else { + iframe.onload = loadPlayers; + } } \ No newline at end of file