diff --git a/modules/player/popcorn.player.js b/modules/player/popcorn.player.js index bac46ba6..8d69e03a 100644 --- a/modules/player/popcorn.player.js +++ b/modules/player/popcorn.player.js @@ -1,5 +1,18 @@ (function( Popcorn ) { + // combines calls of two function calls into one + var combineFn = function( first, second ) { + + first = first || Popcorn.nop; + second = second || Popcorn.nop; + + return function() { + + first.apply( this, arguments ); + second.apply( this, arguments ); + }; + }; + Popcorn.player = function( name, player ) { // ID string matching @@ -240,6 +253,11 @@ basePlayer.paused = true; basePlayer.ended = 0; + options && options.events && Popcorn.forEach( options.events, function( val, key ) { + + basePlayer.addEventListener( key, val, false ); + }); + if ( player._setup ) { player._setup.call( basePlayer, options ); @@ -270,6 +288,14 @@ popcorn = new Popcorn.p.init( basePlayer, options ); + if ( player._teardown ) { + + popcorn.destroy = combineFn( popcorn.destroy, function() { + + player._teardown.call( basePlayer, options ); + }); + } + return popcorn; }; @@ -282,4 +308,4 @@ object.__defineSetter__( description, options.set || Popcorn.nop ); }; -})( Popcorn ); \ No newline at end of file +})( Popcorn ); diff --git a/modules/player/popcorn.player.unit.js b/modules/player/popcorn.player.unit.js index 5457efb4..a613f466 100644 --- a/modules/player/popcorn.player.unit.js +++ b/modules/player/popcorn.player.unit.js @@ -187,3 +187,25 @@ test( "Base player functionality", function() { p2.currentTime( 3 ).play(); }); + +test( "player gets a proper _teardown", function() { + + QUnit.reset(); + + var teardownCalled = false; + + expect( 1 ); + stop( 10000 ); + + Popcorn.player( "teardownTester", { + _teardown: function() { + teardownCalled = true; + } + }); + + var pop = Popcorn.teardownTester( "#video" ); + pop.destroy(); + + equal( teardownCalled, true, "teardown function was called." ); + start(); +}); diff --git a/players/youtube/popcorn.youtube.html b/players/youtube/popcorn.youtube.html index 1830e6f5..f8f19aaf 100644 --- a/players/youtube/popcorn.youtube.html +++ b/players/youtube/popcorn.youtube.html @@ -23,7 +23,7 @@ document.addEventListener( 'DOMContentLoaded', function() { var paused = true, popcorn; - + popcorn = Popcorn.youtube( '#video', 'http://www.youtube.com/watch?v=nfGV32RNkhw&autoplay=0' ); popcorn = popcorn diff --git a/players/youtube/popcorn.youtube.js b/players/youtube/popcorn.youtube.js index 959b6a3f..d49b7fd2 100755 --- a/players/youtube/popcorn.youtube.js +++ b/players/youtube/popcorn.youtube.js @@ -27,6 +27,8 @@ Popcorn.player( "youtube", { media.paused = undefined; container.id = media.id + Popcorn.guid(); + options._container = container; + media.appendChild( container ); var youtubeInit = function() { @@ -112,6 +114,8 @@ Popcorn.player( "youtube", { media.dispatchEvent( "loadeddata" ); return; + } else if ( state === 0 ) { + media.dispatchEvent( "ended" ); } }; @@ -128,7 +132,7 @@ Popcorn.player( "youtube", { var timeupdate = function() { - if ( !media.paused ) { + if ( !media.paused && youtubeObject.getCurrentTime ) { currentTime = youtubeObject.getCurrentTime(); media.dispatchEvent( "timeupdate" ); @@ -138,19 +142,22 @@ Popcorn.player( "youtube", { var volumeupdate = function() { - if ( lastMuted !== youtubeObject.isMuted() ) { + if ( youtubeObject.isMuted ) { - lastMuted = youtubeObject.isMuted(); - media.dispatchEvent( "volumechange" ); + if ( lastMuted !== youtubeObject.isMuted() ) { + + lastMuted = youtubeObject.isMuted(); + media.dispatchEvent( "volumechange" ); + } + + if ( lastVolume !== youtubeObject.getVolume() ) { + + lastVolume = youtubeObject.getVolume(); + media.dispatchEvent( "volumechange" ); + } + + setTimeout( volumeupdate, 250 ); } - - if ( lastVolume !== youtubeObject.getVolume() ) { - - lastVolume = youtubeObject.getVolume(); - media.dispatchEvent( "volumechange" ); - } - - setTimeout( volumeupdate, 250 ); }; media.play = function() { @@ -275,5 +282,9 @@ Popcorn.player( "youtube", { youtubeInit(); } + }, + _teardown: function( options ) { + + this.removeChild( document.getElementById( options._container.id ) ); } }); diff --git a/players/youtube/popcorn.youtube.unit.html b/players/youtube/popcorn.youtube.unit.html index 52194dd4..8f53f4ee 100644 --- a/players/youtube/popcorn.youtube.unit.html +++ b/players/youtube/popcorn.youtube.unit.html @@ -42,5 +42,6 @@
+
diff --git a/players/youtube/popcorn.youtube.unit.js b/players/youtube/popcorn.youtube.unit.js index 2d70948b..66fefb69 100644 --- a/players/youtube/popcorn.youtube.unit.js +++ b/players/youtube/popcorn.youtube.unit.js @@ -4,19 +4,26 @@ test( "Player play, pause, autoplay", function() { var count = 0, expects = 4, orderCheck1 = 0, - orderCheck2 = 0; + orderCheck2 = 0, + pop1, pop2, pop3, pop4; expect( expects ); function plus() { if ( ++count === expects ) { + + pop1.destroy(); + pop2.destroy(); + pop3.destroy(); + pop4.destroy(); + start(); } } stop( 20000 ); - var pop1 = Popcorn.youtube( "#video6", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); + pop1 = Popcorn.youtube( "#video6", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); pop1.listen( "load", function() { @@ -26,7 +33,7 @@ test( "Player play, pause, autoplay", function() { plus(); }); - var pop2 = Popcorn.youtube( "#video7", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); + pop2 = Popcorn.youtube( "#video7", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); pop2.listen( "load", function() { @@ -34,7 +41,7 @@ test( "Player play, pause, autoplay", function() { plus(); }); - var pop3 = Popcorn.youtube( "#video8", "http://www.youtube.com/watch?v=nfGV32RNkhw&autoplay=0" ); + pop3 = Popcorn.youtube( "#video8", "http://www.youtube.com/watch?v=nfGV32RNkhw&autoplay=0" ); pop3.listen( "load", function() { @@ -42,7 +49,7 @@ test( "Player play, pause, autoplay", function() { plus(); }); - var pop4 = Popcorn.youtube( "#video9", "http://www.youtube.com/watch?v=nfGV32RNkhw&autoplay=1" ); + pop4 = Popcorn.youtube( "#video9", "http://www.youtube.com/watch?v=nfGV32RNkhw&autoplay=1" ); pop4.listen( "load", function() { @@ -74,6 +81,7 @@ test("Update Timer", function () { Popcorn.removePlugin( "backwards" ); Popcorn.removePlugin( "wrapper" ); p2.removePlugin( "exec" ); + p2.destroy(); start(); } } @@ -235,6 +243,7 @@ test("Plugin Factory", function () { if ( ++count == expects ) { Popcorn.removePlugin("executor"); Popcorn.removePlugin("complicator"); + popped.destroy(); start(); } } @@ -350,6 +359,20 @@ test( "Popcorn YouTube Plugin Url and Duration Tests", function() { popcorn = Popcorn.youtube( '#video2', 'http://www.youtube.com/watch?v=nfGV32RNkhw' ); function plus(){ +<<<<<<< HEAD +======= + if ( ++count == expects ) { + popcorn.destroy(); + start(); + } + } + + expect( expects ); + stop( 10000 ); + + equals( popcorn.media.id, 'video2', 'Video id set' ); + plus(); +>>>>>>> 8c5e9698d980e4056323ea3f051732b55ddbffb6 if ( ++count == expects ) { start(); @@ -424,6 +447,9 @@ test( "Popcorn YouTube Plugin Url Regex Test", function() { popcorn.pause(); count++; + + popcorn.destroy(); + if ( count === expects ) { start(); @@ -436,32 +462,79 @@ test( "Controls and Annotations toggling", function() { QUnit.reset(); - expect( 6 ); + var count = 0, + expects = 6; + //popcorn = Popcorn.youtube( '#video2', 'http://www.youtube.com/watch?v=nfGV32RNkhw' ); - var popcorn = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw" ), - targetDiv = document.getElementById( "video" ); - testTarget = targetDiv.querySelector( "object" ).data; + function plus(){ + if ( ++count == expects ) { + //popcorn.destroy(); + start(); + } + } - popcorn.volume( 0 ); + expect( expects ); + //expect( 6 ); + stop( 10000 ); - ok( !/controls/.test( testTarget ), "controls are defaulted to 1 ( displayed )" ); - ok( !/iv_load_policy/.test( testTarget ), "annotations ( iv_load_policy ) are defaulted to ( enabled )" ); + var popcorn1 = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); - targetDiv.innerHTML = ""; + popcorn1.listen( "loadeddata", function() { + + var targetDiv = document.getElementById( "video" ), + testTarget = targetDiv.querySelector( "object" ).data; - popcorn = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw&controls=1&iv_load_policy=1" ); - popcorn.volume( 0 ); + popcorn1.volume( 0 ); + + ok( !/controls/.test( testTarget ), "controls are defaulted to 1 ( displayed )" ); + plus(); + ok( !/iv_load_policy/.test( testTarget ), "annotations ( iv_load_policy ) are defaulted to ( enabled )" ); + plus(); + + popcorn1.destroy(); + + var popcorn2 = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw&controls=1&iv_load_policy=1" ); + popcorn2.listen( "loadeddata", function() { + + var targetDiv = document.getElementById( "video" ), + testTarget = targetDiv.querySelector( "object" ).data; + + popcorn2.volume( 0 ); + + ok( /controls=1/.test( testTarget ), "controls is set to 1 ( displayed )" ); + plus(); + ok( /iv_load_policy=1/.test( testTarget ), "annotations ( iv_load_policy ) is set to 1 ( enabled )" ); + plus(); + + popcorn2.destroy(); + + var popcorn3 = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw&controls=0&iv_load_policy=3" ); + popcorn3.listen( "loadeddata", function() { + + var targetDiv = document.getElementById( "video" ), + testTarget = targetDiv.querySelector( "object" ).data; + + popcorn3.volume( 0 ); + + ok( /controls=0/.test( testTarget ), "controls is set to 0 ( hidden )" ); + plus(); + ok( /iv_load_policy=3/.test( testTarget ), "annotations ( iv_load_policy ) is set to 3 ( hidden )" ); + plus(); + + popcorn3.destroy(); + }); + }); + }); + + /*popcorn2.volume( 0 ); testTarget = targetDiv.querySelector( "object" ).data; - ok( /controls=1/.test( testTarget ), "controls is set to 1 ( displayed )" ); - ok( /iv_load_policy=1/.test( testTarget ), "annotations ( iv_load_policy ) is set to 1 ( enabled )" ); - targetDiv.innerHTML = ""; - - popcorn = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw&controls=0&iv_load_policy=3" ); + var popcorn3 = Popcorn.youtube( "#video", "http://www.youtube.com/watch?v=nfGV32RNkhw&controls=0&iv_load_policy=3" ); testTarget = targetDiv.querySelector( "object" ).data; ok( /controls=0/.test( testTarget ), "controls is set to 0 ( hidden )" ); ok( /iv_load_policy=3/.test( testTarget ), "annotations ( iv_load_policy ) is set to 3 ( hidden )" ); + popcorn3.destroy();*/ }); test( "Player height and width", function() { @@ -470,7 +543,8 @@ test( "Player height and width", function() { expect( 4 ); - stop(); + stop( 10000 ); + var popcorn1 = Popcorn.youtube( "#video4", "http://www.youtube.com/watch?v=nfGV32RNkhw" ), popcorn2 = Popcorn.youtube( "#video5", "http://www.youtube.com/watch?v=nfGV32RNkhw" ), readyStatePoll = function() { @@ -485,6 +559,9 @@ test( "Player height and width", function() { equal( popcorn2.media.children[ 0 ].width, 0, "Youtube player explicit width is 0" ); equal( popcorn2.media.children[ 0 ].height, 0, "Youtube player explicit height is 0" ); + + popcorn1.destroy(); + popcorn2.destroy(); start(); } }; @@ -507,9 +584,12 @@ test( "Popcorn Youtube Plugin offsetHeight && offsetWidth Test", function() { function plus() { if ( ++count === expects ) { + + popped.destroy(); start(); } } + popped = Popcorn.youtube( "#video6", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); var runner = function() { @@ -534,10 +614,55 @@ test( "Player Errors", function() { QUnit.reset(); expect( 1 ); stop( 10000 ); - var pop = Popcorn.youtube( "#video4", "http://www.youtube.com/watch?v=abcdefghijk" ); - pop.listen( "error", function() { - ok( true, "error trigger by invalid URL" ); + var pop = Popcorn.youtube( "#video4", "http://www.youtube.com/watch?v=abcdefghijk", { + events: { + error: function() { + + ok( true, "error trigger by invalid URL" ); + pop.destroy(); + start(); + } + } + }); +}); + +test( "YouTube ended event", function() { + QUnit.reset(); + expect(1); + stop( 10000 ); + + var pop = Popcorn.youtube( "#video10", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); + + pop.listen( "ended", function() { + ok( true, "YouTube is successfully firing the ended event" ); start(); }); + pop.play( 150 ); +}); + +test( "youtube player gets a proper _teardown", function() { + + QUnit.reset(); + + var count = 0, + expects = 1; + + function plus() { + if ( ++count === expects ) { + + start(); + } + } + + expect( expects ); + stop( 10000 ); + + var popcorn = Popcorn.youtube( "#video9", "http://www.youtube.com/watch?v=nfGV32RNkhw" ); + popcorn.listen( "loadeddata", function() { + + popcorn.destroy(); + equal( popcorn.media.children.length, 0, "" ); + plus(); + }); }); diff --git a/plugins/attribution/popcorn.attribution.js b/plugins/attribution/popcorn.attribution.js index 48893890..6f22ae50 100755 --- a/plugins/attribution/popcorn.attribution.js +++ b/plugins/attribution/popcorn.attribution.js @@ -160,7 +160,8 @@ nameofworkurl: { elem: "input", type: "url", - label: "Url of Work" + label: "Url of Work", + optional: true }, copyrightholder: { elem: "input", @@ -170,7 +171,8 @@ copyrightholderurl: { elem: "input", type: "url", - label: "Copyright Holder Url" + label: "Copyright Holder Url", + optional: true }, license: { elem: "input", @@ -180,7 +182,8 @@ licenseurl: { elem: "input", type: "url", - label: "License URL" + label: "License URL", + optional: true }, target: "attribution-container" } diff --git a/plugins/code/popcorn.code.js b/plugins/code/popcorn.code.js index 205fae76..c02859af 100755 --- a/plugins/code/popcorn.code.js +++ b/plugins/code/popcorn.code.js @@ -170,7 +170,8 @@ onFrame: { elem: "input", type: "function", - label: "onFrame" + label: "onFrame", + optional: true }, onEnd: { elem: "input", diff --git a/plugins/documentcloud/popcorn.documentcloud.js b/plugins/documentcloud/popcorn.documentcloud.js index aab270fc..83f5aabf 100644 --- a/plugins/documentcloud/popcorn.documentcloud.js +++ b/plugins/documentcloud/popcorn.documentcloud.js @@ -51,12 +51,14 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc width: { elem: "input", type: "text", - label: "Width" + label: "Width", + optional: true }, height: { elem: "input", type: "text", - label: "Height" + label: "Height", + optional: true }, src: { elem: "input", @@ -66,17 +68,20 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc preload: { elem: "input", type: "boolean", - label: "Preload" + label: "Preload", + optional: true }, page: { elem: "input", type: "number", - label: "Page Number" + label: "Page Number", + optional: true }, aid: { elem: "input", type: "number", - label: "Annotation Id" + label: "Annotation Id", + optional: true } } }, diff --git a/plugins/facebook/popcorn.facebook.js b/plugins/facebook/popcorn.facebook.js index fcc7ca77..6370a662 100755 --- a/plugins/facebook/popcorn.facebook.js +++ b/plugins/facebook/popcorn.facebook.js @@ -77,92 +77,110 @@ font: { elem: "input", type: "text", - label: "font" + label: "font", + optional: true }, xid: { elem: "input", type: "text", - label: "Xid" + label: "Xid", + optional: true }, href: { elem: "input", type: "url", - label: "Href" + label: "Href", + optional: true }, site: { elem: "input", type: "url", - label:"Site" + label:"Site", + optional: true }, height: { elem: "input", type: "text", - label: "Height" + label: "Height", + optional: true }, width: { elem: "input", type: "text", - label: "Width" + label: "Width", + optional: true }, action: { elem: "select", options: [ "like", "recommend" ], - label: "Action" + label: "Action", + optional: true }, stream: { elem: "select", options: [ "false", "true" ], - label: "Stream" + label: "Stream", + optional: true }, header: { elem: "select", options: [ "false", "true" ], - label: "Header" + label: "Header", + optional: true }, layout: { elem: "select", options: [ "standard", "button_count", "box_count" ], - label: "Layout" + label: "Layout", + optional: true }, max_rows: { elem: "input", type: "text", - label: "Max_rows" + label: "Max_rows", + optional: true }, border_color: { elem: "input", type: "text", - label: "Border_color" + label: "Border_color", + optional: true }, event_app_id: { elem: "input", type: "text", - label: "Event_app_id" + label: "Event_app_id", + optional: true }, colorscheme: { elem: "select", options: [ "light", "dark" ], - label: "Colorscheme" + label: "Colorscheme", + optional: true }, show_faces: { elem: "select", options: [ "false", "true" ], - label: "Showfaces" + label: "Showfaces", + optional: true }, recommendations: { elem: "select", options: [ "false", "true" ], - label: "Recommendations" + label: "Recommendations", + optional: true }, always_post_to_friends: { elem: "input", options: [ "false", "true" ], - label: "Always_post_to_friends" + label: "Always_post_to_friends", + optional: true }, num_posts: { elem: "input", type: "text", - label: "Number_of_Comments" + label: "Number_of_Comments", + optional: true } } }, @@ -210,6 +228,7 @@ options._container.id = "facebookdiv-" + Popcorn.guid(); options._facebookdiv = document.createElement( "fb:" + _type ); options._container.appendChild( options._facebookdiv ); + options._container.style.display = "none"; // All the the "types" for facebook share largely identical attributes, for loop suffices. // ** Credit to Rick Waldron, it's essentially all his code in this function. diff --git a/plugins/facebook/popcorn.facebook.unit.js b/plugins/facebook/popcorn.facebook.unit.js index 1a407ce2..30e86e3c 100755 --- a/plugins/facebook/popcorn.facebook.unit.js +++ b/plugins/facebook/popcorn.facebook.unit.js @@ -3,13 +3,14 @@ test( "Popcorn Facebook Plugin", function () { Popcorn.plugin.debug = true; var popped = Popcorn( "#video" ), - expects = 15, + expects = 16, count = 0, interval, interval2, interval3, interval4, - likediv = document.getElementById( "likediv" ); + likediv = document.getElementById( "likediv" ), + commentdiv = document.getElementById( "commentdiv" ); expect( expects ); @@ -65,7 +66,7 @@ test( "Popcorn Facebook Plugin", function () { href: "example.com", type: "COMMENTS", target: "commentdiv", - start: 1, + start: 2, end: 6, num_posts: 5 }) @@ -86,6 +87,10 @@ test( "Popcorn Facebook Plugin", function () { ok( document.getElementById( "commentdiv" ), "commentdiv exists on the page" ); plus(); + popped.exec( 1, function() { + equals( commentdiv.children[ 0 ].style.display, "none", "comment div is not visible on page with \"none\" display style" ); + plus(); + }); // I inspected the html genterated by facebook, and found that there are no uniquely identifying attributes between plug-in types // so right now, we just check ot make sure that facebook is returning a plugin and displaying it at the correct time. popped.exec( 3, function() { @@ -93,7 +98,7 @@ test( "Popcorn Facebook Plugin", function () { equals( likediv.childElementCount, 2, "likediv has 2 inner elements" ); plus(); // Checks display style is set correctly on startup - equals( likediv.children[ 0 ].style.display , "", "likediv is visible on the page with '' display style" ); + equals( likediv.children[ 0 ].style.display , "", "likediv is visible on the page with \"\" display style" ); plus(); // Checks if likediv is empty at specific time ok( document.getElementById( "likediv" ).innerHTML, "likediv is not empty at 0:03 (expected)" ); diff --git a/plugins/flickr/popcorn.flickr.js b/plugins/flickr/popcorn.flickr.js index 644b1b83..58a04b18 100755 --- a/plugins/flickr/popcorn.flickr.js +++ b/plugins/flickr/popcorn.flickr.js @@ -187,7 +187,8 @@ userid: { elem: "input", type: "text", - label: "UserID" + label: "UserID", + optional: true }, tags: { elem: "input", @@ -198,32 +199,38 @@ elem: "input", type: "text", label: "Username" + optional: true }, apikey: { elem: "input", type: "text", label: "Api_key" + optional: true }, target: "flickr-container", height: { elem: "input", type: "text", label: "Height" + optional: true }, width: { elem: "input", type: "text", label: "Width" + optional: true }, padding: { elem: "input", type: "text", label: "Padding" + optional: true }, border: { elem: "input", type: "text", label: "Border" + optional: true }, numberofimages: { elem: "input", diff --git a/plugins/gml/popcorn.gml.js b/plugins/gml/popcorn.gml.js index 1b78fae6..ccf9ae34 100755 --- a/plugins/gml/popcorn.gml.js +++ b/plugins/gml/popcorn.gml.js @@ -163,6 +163,7 @@ target && target.appendChild( options.container ); var scriptReady = function() { + Popcorn.getJSONP( "//000000book.com/data/" + options.gmltag + ".json?callback=", function( data ) { options.pjsInstance = new Processing( options.container, gmlPlayer ); @@ -173,7 +174,7 @@ if ( !window.Processing ) { - Popcorn.getScript( "//processingjs.org/content/download/processing-js-1.3.0/processing-1.3.0.min.js", scriptReady ); + Popcorn.getScript( "//cloud.github.com/downloads/processing-js/processing-js/processing-1.3.6.min.js", scriptReady ); } else { scriptReady(); diff --git a/plugins/googlefeed/popcorn.googlefeed.js b/plugins/googlefeed/popcorn.googlefeed.js index 9fb96ffe..c11c526b 100755 --- a/plugins/googlefeed/popcorn.googlefeed.js +++ b/plugins/googlefeed/popcorn.googlefeed.js @@ -173,11 +173,13 @@ elem: "input", type: "text", label: "title" + optional: true }, orientation: { elem: "select", options: [ "Vertical", "Horizontal" ], label: "orientation" + optional: true } } }); diff --git a/plugins/googlemap/popcorn.googlemap.js b/plugins/googlemap/popcorn.googlemap.js index 203dd0e7..6fcccabd 100755 --- a/plugins/googlemap/popcorn.googlemap.js +++ b/plugins/googlemap/popcorn.googlemap.js @@ -356,21 +356,25 @@ var googleCallback; elem: "select", options: [ "ROADMAP", "SATELLITE", "STREETVIEW", "HYBRID", "TERRAIN" ], label: "Type" + optional: true }, zoom: { elem: "input", type: "text", label: "Zoom" + optional: true }, lat: { elem: "input", type: "text", label: "Lat" + optional: true }, lng: { elem: "input", type: "text", label: "Lng" + optional: true }, location: { elem: "input", @@ -381,11 +385,13 @@ var googleCallback; elem: "input", type: "text", label: "Heading" + optional: true }, pitch: { elem: "input", type: "text", label: "Pitch" + optional: true } } }); diff --git a/plugins/image/popcorn.image.js b/plugins/image/popcorn.image.js index 52146236..f6082b2a 100755 --- a/plugins/image/popcorn.image.js +++ b/plugins/image/popcorn.image.js @@ -50,7 +50,8 @@ href: { elem: "input", type: "url", - label: "Link URL" + label: "Link URL", + optional: true }, target: "image-container", src: { @@ -61,7 +62,8 @@ text: { elem: "input", type: "text", - label: "TEXT" + label: "TEXT", + optional: true } } }, diff --git a/plugins/image/popcorn.image.unit.js b/plugins/image/popcorn.image.unit.js index 4dbbc435..bbbf3a7a 100755 --- a/plugins/image/popcorn.image.unit.js +++ b/plugins/image/popcorn.image.unit.js @@ -28,9 +28,7 @@ test( "Popcorn Image Plugin", function() { plus(); popped.image({ - // seconds start: 1, - // seconds end: 3, href: "http://www.drumbeat.org/", src: sources[ 0 ], @@ -38,20 +36,14 @@ test( "Popcorn Image Plugin", function() { target: "imagediv" }) .image({ - // seconds start: 4, - // seconds end: 6, - // no href src: sources[ 1 ], target: "imagediv" }) .image({ - // seconds start: 5, - // seconds end: 6, - // no href src: sources[ 2 ], target: "imagediv" }); @@ -59,14 +51,14 @@ test( "Popcorn Image Plugin", function() { setupId = popped.getLastTrackEventId(); popped.exec( 2, function() { - ok( /display: block;/.test( imagediv.innerHTML ), "Div contents are displayed" ); + equals( imagediv.children[ 0 ].style.display, "block", "Div contents are displayed" ); plus(); - ok( /img/.test( imagediv.innerHTML ), "An image exists" ); + equals( imagediv.children[ 0 ].children[ 1 ].nodeName, "IMG", "An image exists" ); plus(); }); popped.exec( 3, function() { - ok( /display: none;/.test( imagediv.innerHTML ), "Div contents are hidden again" ); + equals( imagediv.children[ 0 ].style.display, "none", "Div contents are hidden again" ); plus(); }); diff --git a/plugins/linkedin/popcorn.linkedin.js b/plugins/linkedin/popcorn.linkedin.js index e21650b7..ae032a33 100755 --- a/plugins/linkedin/popcorn.linkedin.js +++ b/plugins/linkedin/popcorn.linkedin.js @@ -70,32 +70,38 @@ memberid: { elem: "input", type: "text", - label: "Member ID" + label: "Member ID", + optional: true }, format: { elem: "input", type: "text", - label: "Format" + label: "Format", + optional: true }, companyid: { elem: "input", type: "text", - label: "Company ID" + label: "Company ID", + optional: true }, modules: { elem: "input", type: "text", - label: "Modules" + label: "Modules", + optional: true }, productid: { elem: "input", type: "text", - label: "productid" + label: "productid", + optional: true }, related: { elem: "input", type: "text", - label: "related" + label: "related", + optional: true }, start: { elem: "input", diff --git a/plugins/lowerthird/popcorn.lowerthird.js b/plugins/lowerthird/popcorn.lowerthird.js index 7792ec5d..da962c1b 100755 --- a/plugins/lowerthird/popcorn.lowerthird.js +++ b/plugins/lowerthird/popcorn.lowerthird.js @@ -53,7 +53,8 @@ salutation : { elem: "input", type: "text", - label: "Text" + label: "Text", + optional: true }, name: { elem: "input", @@ -63,7 +64,8 @@ role: { elem: "input", type: "text", - label: "Text" + label: "Text", + optional: true } } }, diff --git a/plugins/mustache/popcorn.mustache.js b/plugins/mustache/popcorn.mustache.js index ea43d280..e78e1e49 100755 --- a/plugins/mustache/popcorn.mustache.js +++ b/plugins/mustache/popcorn.mustache.js @@ -202,7 +202,8 @@ dynamic: { elem: "input", type: "text", - label: "Dynamic" + label: "Dynamic", + optional: true } } }); diff --git a/plugins/openmap/popcorn.openmap.js b/plugins/openmap/popcorn.openmap.js index caf1ddb8..a6e2b964 100755 --- a/plugins/openmap/popcorn.openmap.js +++ b/plugins/openmap/popcorn.openmap.js @@ -305,22 +305,26 @@ type: { elem: "select", options: [ "ROADMAP", "SATELLITE", "TERRAIN" ], - label: "Type" + label: "Type", + optional: true }, zoom: { elem: "input", type: "text", - label: "Zoom" + label: "Zoom", + optional: true }, lat: { elem: "input", type: "text", - label: "Lat" + label: "Lat", + optional: true }, lng: { elem: "input", type: "text", - label: "Lng" + label: "Lng", + optional: true }, location: { elem: "input", @@ -330,7 +334,8 @@ markers: { elem: "input", type: "text", - label: "List Markers" + label: "List Markers", + optional: true } } }); diff --git a/plugins/processing/popcorn.processing.js b/plugins/processing/popcorn.processing.js index 6ea4d762..448ff466 100644 --- a/plugins/processing/popcorn.processing.js +++ b/plugins/processing/popcorn.processing.js @@ -155,7 +155,8 @@ noPause: { elem: "select", options: [ "TRUE", "FALSE" ], - label: "No Loop" + label: "No Loop", + optional: true } } }); diff --git a/plugins/rdio/popcorn.rdio.html b/plugins/rdio/popcorn.rdio.html new file mode 100644 index 00000000..0832002b --- /dev/null +++ b/plugins/rdio/popcorn.rdio.html @@ -0,0 +1,77 @@ + + + + Popcorn Rdio Plug-in Demo + + + + + +

Popcorn Rdio Plug-in Demo

+

Album tracks from Erykah Badu's Baduizm will appear at 2 seconds and disappear at 10 seconds
+ Album tracks from Radiohead's The Bends will appear at 3 seconds and disappear at 10 seconds
+ Album tracks from Jamiroquai's Synkronized will appear at 10 seconds and disappear at 20 seconds
+ Tracks from Scottyhons's playlist Toronto Music will appear at 20 seconds and disappear at 40 seconds

+
+ +
+
+
+
+ + diff --git a/plugins/rdio/popcorn.rdio.js b/plugins/rdio/popcorn.rdio.js new file mode 100644 index 00000000..716cf402 --- /dev/null +++ b/plugins/rdio/popcorn.rdio.js @@ -0,0 +1,172 @@ +// Rdio Plug-in +/** + * Rdio popcorn plug-in + * Appends Rdio album track listings to an element on the page. + * Can also append a user's playlist to an element on the page. + * Option paramter can be in two forms: + * Options parameter will take a start, end, target, artist, album, and type or + * Options parameter will take a start, end, target, person, id, playlist, and type + * Start is the time that you want this plug-in to execute + * End is the time that you want this plug-in to stop executing + * Target is the id of the document element that the images are appended to + * Artist is the name of who's album image will display + * Album is the album that will display of the specified Artist + * Person is the Rdio member who's playlist will display + * ID is the playlist's unqiue Rdio playlist identifier + * Playlist is the name of the playlist + * Type specifies if the element is an album or playlist + * + + * + * @param {Object} options + * + * Example 1: + var p = Popcorn( "#video" ) + .rdio({ + start: 2, + end: 10, + target: "rdiodiv", + artist: "Jamiroquai", + album: "Synkronized", + type: "album" + }) + * + * Example 2: + var p = Popcorn( "#video" ) + .rdio({ + start: 10, + end: 20, + target: "rdiodiv", + person: "diggywiggy", + id: 413517, + playlist: "sunday", + type: "playlist" + }) +**/ + +(function( Popcorn ) { + var _album = {}, + _container = {}, + _target = {}, + _rdioURL = "http://www.rdio.com/api/oembed/?format=json&url=http://www.rdio.com/%23"; + + Popcorn.plugin( "rdio", (function( options ) { + var _loadResults = function( data, options ) { + var title = data.title, + html = data.html; + if ( data && title && html ) { + _album[ options.containerid ].htmlString = "
" + html + "
"; + } else { + if ( Popcorn.plugin.debug ) { + throw new Error( "Did not receive data from server." ); + } + } + }, + + // Handle AJAX Request + _getResults = function( options ) { + var urlBuilder = function( type ) { + var path = { + playlist: function() { + return "/people/" + ( options.person ) + "/playlists/" + options.id + "/"; + }, + album: function() { + return "/artist/" + ( options.artist ) + "/album/"; + } + }[ type ](); + + return _rdioURL + path + options[ type ] + "/&callback=_loadResults"; + }, + url = urlBuilder( options.type ); + Popcorn.getJSONP( url, function( data ) { + _loadResults( data, options ); + }, false ); + }; + + return { + _setup: function( options ) { + var key = options.containerid = Popcorn.guid(), + container = _container[ key ] = document.createElement( "div" ), + target = _target[ key ] = document.getElementById( options.target ); + if ( !target && Popcorn.plugin.debug ) { + throw new Error( "Target container could not be found." ); + } + container.style.display = "none"; + container.innerHTML = ""; + target.appendChild( container ); + _album[ key ] = { + htmlString: ( options.playlist || "Unknown Source" ) || ( options.album || "Unknown Source" ) + }; + _getResults( options ); + }, + start: function( event, options ) { + var key = options.containerid, + container = _container[ key ]; + container.innerHTML = _album[ key ].htmlString; + container.style.display = "inline"; + }, + end: function( event, options ) { + container = _container[ options.containerid ]; + container.style.display = "none"; + container.innerHTML = ""; + }, + _teardown: function( options ) { + var key = options.containerid, + target = _target[ key ]; + if ( _album[ key ] ) { + delete _album[ key ]; + } + target && target.removeChild( _container[ key ] ); + delete _target[ key ]; + delete _container[ key ]; + } + }; + })(), + { + manifest: { + about: { + name: "Popcorn Rdio Plugin", + version: "0.1", + author: "Denise Rigato" + }, + options: { + start: { + elem: "input", + type: "text", + label: "In" + }, + end: { + elem: "input", + type: "text", + label: "Out" + }, + target: "rdio", + artist: { + elem: "input", + type: "text", + label: "Artist" + }, + album: { + elem: "input", + type: "text", + label: "Album" + }, + person: { + elem: "input", + type: "text", + label: "Person" + }, + id: { + elem: "input", + type: "text", + label: "Id" + }, + playlist: { + elem: "input", + type: "text", + label: "Playlist" + } + } + } + }); +}( Popcorn )); diff --git a/plugins/rdio/popcorn.rdio.unit.html b/plugins/rdio/popcorn.rdio.unit.html new file mode 100644 index 00000000..ddb379a0 --- /dev/null +++ b/plugins/rdio/popcorn.rdio.unit.html @@ -0,0 +1,45 @@ + + + + Popcorn API + + + + + + + + + +

Popcorn Rdio Plugin

+

+
+

+
    +
    + + +
    +
    +
    + + diff --git a/plugins/rdio/popcorn.rdio.unit.js b/plugins/rdio/popcorn.rdio.unit.js new file mode 100644 index 00000000..5e45cae1 --- /dev/null +++ b/plugins/rdio/popcorn.rdio.unit.js @@ -0,0 +1,102 @@ +test( "Popcorn Rdio Plugin", function() { + var popped = Popcorn( "#video" ), + expects = 12, + count = 0, + setupId, + rdiodiv = document.getElementById( "rdiodiv" ), + rdiodiv2 = document.getElementById( "rdiodiv2" ), + rdiodiv3 = document.getElementById( "rdiodiv3" ); + + expect( expects ); + + function plus() { + if ( ++count === expects ) { + start(); + } + } + + stop(); + + ok( "rdio" in popped, "rdio is a method of the popped instance" ); + plus(); + + equals( rdiodiv2.innerHTML, "", "initially, there is nothing inside the rdiodiv" ); + plus(); + + popped.rdio({ + start: 2, + end: 4, + target: "rdiodiv", + artist: "Erykah Badu", + album: "Baduizm", + type: "album" + }) + .rdio({ + start: 2, + end: 7, + target: "rdiodiv", + person: "scottyhons", + id: 236475, + playlist: "Toronto Music", + type: "playlist" + }) + .rdio({ + start: 4, + end: 7, + target: "rdiodiv2", + artist: "Radiohead", + album: "some album", + type: "album" + }) + .rdio({ + start: 5, + end: 8, + target: "rdiodiv3", + person: "", + id: "", + playlist: "", + type: "playlist" + }); + setupId = popped.getLastTrackEventId(); + popped.exec( 2, function() { + equals( rdiodiv.childElementCount, 2, "rdiodiv now has two inner elements" ); + plus(); + equals( rdiodiv.children[ 0 ].style.display , "inline", "Erykah Badu div is visible on the page" ); + plus(); + }); + + popped.exec( 3, function() { + equals( rdiodiv.children[ 0 ].style.display , "inline", "Erykah Badu div is still visible on the page" ); + plus(); + equals( rdiodiv.children[ 1 ].style.display , "inline", "Scottyhons div is visible on the page" ); + plus(); + equals( rdiodiv2.children[ 0 ].style.display , "none", "null div is not visible on the page" ); + plus(); + equals( rdiodiv3.children[ 0 ].style.display , "none", "null div is not visible on the page" ); + plus(); + }); + + popped.exec( 5, function() { + equals( rdiodiv2.children[ 0 ].innerHTML , "Unknown Source", "Artist information could not be found" ); + plus(); + equals( rdiodiv3.children[ 0 ].innerHTML , "Unknown Source", "Playlist information could not be found" ); + plus(); + + popped.pause().removeTrackEvent( setupId ); + ok( !rdiodiv3.children[ 0 ], "removed rdio was properly destroyed" ); + plus(); + }); + + // empty track events should be safe + popped.rdio({}); + // debug should log errors on empty track events + Popcorn.plugin.debug = true; + try { + popped.rdio({}); + } catch( e ) { + ok( true, "empty event was caught by debug" ); + plus(); + } + + popped.volume( 0 ).play(); +}); diff --git a/plugins/tagthisperson/popcorn.tagthisperson.js b/plugins/tagthisperson/popcorn.tagthisperson.js index e710d47c..2472ce1f 100755 --- a/plugins/tagthisperson/popcorn.tagthisperson.js +++ b/plugins/tagthisperson/popcorn.tagthisperson.js @@ -123,12 +123,14 @@ image: { elem: "input", type: "url", - label: "Image Src" + label: "Image Src", + optional: true }, href: { elem: "input", type: "url", - label: "URL" + label: "URL", + optional: true } } }); diff --git a/plugins/timeline/popcorn.timeline.js b/plugins/timeline/popcorn.timeline.js index 7a1c1b6d..68aef7cc 100644 --- a/plugins/timeline/popcorn.timeline.js +++ b/plugins/timeline/popcorn.timeline.js @@ -131,12 +131,14 @@ innerHTML: { elem: "input", type: "text", - label: "innerHTML" + label: "innerHTML", + optional: true }, direction: { elem: "input", type: "text", - label: "direction" + label: "direction", + optional: true } } }); diff --git a/plugins/tumblr/popcorn.tumblr.js b/plugins/tumblr/popcorn.tumblr.js index b1d821fc..753c535a 100644 --- a/plugins/tumblr/popcorn.tumblr.js +++ b/plugins/tumblr/popcorn.tumblr.js @@ -269,17 +269,20 @@ api_key: { // Required for Blog Info and Blog Post retrievals elem: "input", type: "text", - label: "Application_Key" + label: "Application_Key", + optional: true }, size: { elem: "select", options: [ 16, 24, 30, 40, 48, 64, 96, 128, 512 ], - label: "avatarSize" + label: "avatarSize", + optional: true }, blogId: { // Required for BLOGPOST requests elem: "input", type: "number", - label: "Blog_ID" + label: "Blog_ID", + optional: true }, /* Optional for Photo and Video BlogPosts, defaulted to 250 pixels for photos and 400 for videos if not provided or provided width * is not found in their arrays. If multiple videos or photos are in the blogpost then it will use this same size for all of them unless @@ -288,7 +291,8 @@ width: { elem: "input", type: "number", - label: "Photo_Width" + label: "Photo_Width", + optional: true } } }, diff --git a/plugins/twitter/popcorn.twitter.js b/plugins/twitter/popcorn.twitter.js index 059a83da..a95b1d50 100755 --- a/plugins/twitter/popcorn.twitter.js +++ b/plugins/twitter/popcorn.twitter.js @@ -60,12 +60,14 @@ height: { elem: "input", type: "number", - label: "Height" + label: "Height", + optional: true }, width: { elem: "input", type: "number", - label: "Width" + label: "Width", + optional: true } } }, diff --git a/plugins/webpage/popcorn.webpage.js b/plugins/webpage/popcorn.webpage.js index 448f66cb..797a6723 100755 --- a/plugins/webpage/popcorn.webpage.js +++ b/plugins/webpage/popcorn.webpage.js @@ -38,7 +38,8 @@ id: { elem: "input", type: "text", - label: "Id" + label: "Id", + optional: true }, start: { elem: "input", diff --git a/plugins/wikipedia/popcorn.wikipedia.js b/plugins/wikipedia/popcorn.wikipedia.js index 055932ad..7bb0c9e3 100755 --- a/plugins/wikipedia/popcorn.wikipedia.js +++ b/plugins/wikipedia/popcorn.wikipedia.js @@ -54,7 +54,8 @@ var wikiCallback; lang: { elem: "input", type: "text", - label: "Language" + label: "Language", + optional: true }, src: { elem: "input", @@ -64,12 +65,14 @@ var wikiCallback; title: { elem: "input", type: "text", - label: "Title" + label: "Title", + optional: true }, numberofwords: { elem: "input", type: "text", - label: "Num Of Words" + label: "Num Of Words", + optional: true }, target: "wikipedia-container" } diff --git a/plugins/wordriver/popcorn.wordriver.js b/plugins/wordriver/popcorn.wordriver.js index dad03661..f044c31d 100755 --- a/plugins/wordriver/popcorn.wordriver.js +++ b/plugins/wordriver/popcorn.wordriver.js @@ -88,7 +88,8 @@ color: { elem: "input", type: "text", - label: "Color" + label: "Color", + optional: true } } }, diff --git a/popcorn.js b/popcorn.js index 0e72b9dd..9cb5ea1e 100644 --- a/popcorn.js +++ b/popcorn.js @@ -832,8 +832,9 @@ }; // Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances - Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) { - Popcorn.p[ key ] = Popcorn.events.fn[ key ]; + // Extend aliases (on, off, emit) + Popcorn.forEach( [ [ "trigger", "emit" ], [ "listen", "on" ], [ "unlisten", "off" ] ], function( key ) { + Popcorn.p[ key[ 0 ] ] = Popcorn.p[ key[ 1 ] ] = Popcorn.events.fn[ key[ 0 ] ]; }); // Internal Only - Adds track events to the instance object @@ -1378,7 +1379,7 @@ // Storing the plugin natives var natives = options._natives = {}, compose = "", - defaults, originalOpts, manifestOpts, mergedSetupOpts; + originalOpts, manifestOpts; Popcorn.extend( natives, setup ); @@ -1401,9 +1402,6 @@ args[ 1 ]._running && natives.end.apply( this, args ); }, natives._teardown ); - // Check for previously set default options - defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ]; - // default to an empty string if no effect exists // split string into an array of effects options.compose = options.compose && options.compose.split( " " ) || []; @@ -1435,25 +1433,40 @@ options.end = options[ "out" ] || this.duration() || Number.MAX_VALUE; } - // Merge with defaults if they exist, make sure per call is prioritized - mergedSetupOpts = defaults ? Popcorn.extend( {}, defaults, options ) : - options; + // Use hasOwn to detect non-inherited toString, since all + // objects will receive a toString - its otherwise undetectable + if ( !hasOwn.call( options, "toString" ) ) { + options.toString = function() { + var props = [ + "start: " + options.start, + "end: " + options.end, + "id: " + (options.id || options._id) + ]; + + // Matches null and undefined, allows: false, 0, "" and truthy + if ( options.target != null ) { + props.push( "target: " + options.target ); + } + + return name + " ( " + props.join(", ") + " )"; + }; + } // Resolves 239, 241, 242 - if ( !mergedSetupOpts.target ) { + if ( !options.target ) { // Sometimes the manifest may be missing entirely // or it has an options object that doesn't have a `target` property manifestOpts = "options" in manifest && manifest.options; - mergedSetupOpts.target = manifestOpts && "target" in manifestOpts && manifestOpts.target; + options.target = manifestOpts && "target" in manifestOpts && manifestOpts.target; } // Trigger _setup method if exists - options._natives._setup && options._natives._setup.call( this, mergedSetupOpts ); + options._natives._setup && options._natives._setup.call( this, options ); // Create new track event for this instance - Popcorn.addTrackEvent( this, Popcorn.extend( mergedSetupOpts, options ) ); + Popcorn.addTrackEvent( this, Popcorn.extend( options, options ) ); // Future support for plugin event definitions // for all of the native events @@ -1472,14 +1485,17 @@ return this; }; - // Assign new named definition - plugin[ name ] = function( options ) { - return pluginFn.call( this, isfn ? definition.call( this, options ) : definition, - options ); - }; - // Extend Popcorn.p with new named definition - Popcorn.extend( Popcorn.p, plugin ); + // Assign new named definition + Popcorn.p[ name ] = plugin[ name ] = function( options ) { + + // Merge with defaults if they exist, make sure per call is prioritized + var defaults = ( this.options.defaults && this.options.defaults[ name ] ) || {}, + mergedSetupOpts = Popcorn.extend( {}, defaults, options ); + + return pluginFn.call( this, isfn ? definition.call( this, mergedSetupOpts ) : definition, + mergedSetupOpts ); + }; // Push into the registry var entry = { @@ -1576,13 +1592,11 @@ // remove all trackEvents for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) { - if ( ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) && - ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) ) { + if ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) { byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] ); byStart.splice( idx, 1 ); - byEnd.splice( idx, 1 ); // update for loop if something removed, but keep checking idx--; sl--; @@ -1591,6 +1605,13 @@ obj.data.trackEvents.endIndex--; } } + + // clean any remaining references in the end index + // we do this seperate from the above check because they might not be in the same order + if ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) { + + byEnd.splice( idx, 1 ); + } } //remove all animating events diff --git a/test/data/testfunction2.js b/test/data/testfunction2.js new file mode 100644 index 00000000..8ad6c015 --- /dev/null +++ b/test/data/testfunction2.js @@ -0,0 +1,3 @@ +(function() { +window.testFunction2 = function() {}; +})(); \ No newline at end of file diff --git a/test/popcorn.unit.js b/test/popcorn.unit.js index b861c7f2..ea43ebd6 100644 --- a/test/popcorn.unit.js +++ b/test/popcorn.unit.js @@ -704,11 +704,11 @@ test( "Instance", function() { ok( a.data.trackEvents.animating, "instance a has data.trackEvents.animating property" ); ok( b.data.trackEvents.animating, "instance b has data.trackEvents.animating property" ); - ok( a.data.trackEvents.startIndex, "instance a has data.trackEvents.startIndex property" ); - ok( b.data.trackEvents.startIndex, "instance b has data.trackEvents.startIndex property" ); + equal( typeof a.data.trackEvents.startIndex, "number", "instance a has data.trackEvents.startIndex property and it is a number" ); + equal( typeof b.data.trackEvents.startIndex, "number", "instance b has data.trackEvents.startIndex property and it is a number" ); - ok( a.data.trackEvents.endIndex, "instance a has data.trackEvents.endIndex property" ); - ok( b.data.trackEvents.endIndex, "instance b has data.trackEvents.endIndex property" ); + equal( typeof a.data.trackEvents.endIndex, "number", "instance a has data.trackEvents.endIndex property and it is a number" ); + equal( typeof b.data.trackEvents.endIndex, "number", "instance b has data.trackEvents.endIndex property and it is a number" ); ok( a.data.trackEvents.previousUpdateTime >= -1, "instance a has data.trackEvents.previousUpdateTime property" ); ok( b.data.trackEvents.previousUpdateTime >= -1, "instance b has data.trackEvents.previousUpdateTime property" ); @@ -744,11 +744,17 @@ test( "roundTime", function() { QUnit.reset(); + stop( 5000 ); + var popped = Popcorn( "#video" ); - popped.play().pause().currentTime( 0.98 ); + popped.listen( "canplayall", function() { + popped.unlisten( "canplayall" ); + popped.play().pause().currentTime( 0.98 ); - equal( 1, popped.roundTime(), ".roundTime() returns 1 when currentTime is 0.98s" ); + equal( 1, popped.roundTime(), ".roundTime() returns 1 when currentTime is 0.98s" ); + start(); + }); }); @@ -861,23 +867,30 @@ test( "play(n)/pause(n) as shorthand to currentTime(n).play()/pause()", function } } - stop( 1000 ); + stop( 10000 ); function poll() { if ( $pop.media.readyState >= 2 ) { // this should trigger immediately + var firstSeekedEvent = function() { + $pop.unlisten( "seeked", firstSeekedEvent ); + equal( Math.round( $pop.currentTime() ), 10, "play(n) sets currentTime to 10" ); + plus(); + + $pop.listen( "seeked", secondSeekedEvent ); + $pop.pause( 5 ); + }, + secondSeekedEvent = function() { + + $pop.unlisten( "seeked", secondSeekedEvent ); + equal( Math.round( $pop.currentTime() ), 5, "pause(n) sets currentTime to 5" ); + plus(); + }; + + $pop.listen( "seeked", firstSeekedEvent ); $pop.play( 10 ).pause(); - - equal( Math.round( $pop.currentTime() ), 10, "play(n) sets currentTime to 10" ); - plus(); - - $pop.pause( 5 ); - - equal( Math.round( $pop.currentTime() ), 5, "pause(n) sets currentTime to 5" ); - plus(); - } else { setTimeout( poll, 10 ); } @@ -913,6 +926,8 @@ test( "play(n)/pause(n) custom stop()", function() { if ( ++count === expects ) { // Remove custom stop() method delete Popcorn.p.stop; + $pop.unlisten( "canplayall" ); + $pop.destroy(); start(); } } @@ -1030,12 +1045,16 @@ test( "Popcorn.events.hooks: canplayall", function() { //qunit-fixture var expects = 1, count = 0, - fired = 0; + fired = 0, + $pop; expect( expects ); function plus(){ if ( ++count == expects ) { + $pop.unlisten( "canplayall"); + $pop.unlisten( "canplaythrough" ); + $pop.destroy(); start(); } } @@ -1063,7 +1082,7 @@ test( "Popcorn.events.hooks: canplayall", function() { document.getElementById( "qunit-fixture" ).appendChild( video ); - var $pop = Popcorn( "#event-fixture" ); + $pop = Popcorn( "#event-fixture" ); $pop.listen( "canplayall", function( event ) { equal( ++fired, 1, "canplayall is fired only once" ); @@ -1088,6 +1107,7 @@ test( "Popcorn.events.hooks: canplayall fires immediately if ready", function() function plus(){ if ( ++count == expects ) { + $pop.destroy(); start(); } } @@ -1098,6 +1118,7 @@ test( "Popcorn.events.hooks: canplayall fires immediately if ready", function() if ( $pop.media.readyState >= 2 ) { // this should trigger immediately $pop.listen( "canplayall", function( event ) { + this.unlisten( "canplayall" ); equal( ++fired, 1, "canplayall is fired immediately if readyState permits" ); plus(); }); @@ -1242,9 +1263,10 @@ test( "position called from plugin", function() { function plus(){ if ( ++count == expects ) { - start(); Popcorn.removePlugin( "positionPlugin" ); delete Popcorn.manifest.positionPlugin; + $pop.destroy(); + start(); } } @@ -1321,6 +1343,8 @@ test( "Stored By Type", function() { ok( !p.data.events[ "play" ], "play handlers removed" ); + p.destroy(); + start(); } } @@ -1378,7 +1402,10 @@ test( "Simulated", function() { expect( expects ); function plus(){ - if ( ++count == expects ) start(); + if ( ++count == expects ) { + p.destroy(); + start(); + } } stop( 10000 ); @@ -1389,8 +1416,8 @@ test( "Simulated", function() { if ( completed.indexOf( name ) === -1 ) { ok( true, name + " fired" ); plus(); - completed.push( name ); + this.unlisten( name ); } }); }); @@ -1412,6 +1439,7 @@ test( "Real", function() { function plus(){ if ( ++count == expects ) { + p.destroy(); start(); } } @@ -1426,32 +1454,38 @@ test( "Real", function() { ok( true, name + " fired" ); plus(); completed.push( name ); + p.unlisten( name ); } }); }); - - p.pause(); - p.play(); - p.volume( 0.9 ); - p.currentTime( 49 ); + p.listen( "canplayall", function() { + this.unlisten( "canplayall" ); + this.pause(); + this.play(); + this.volume( 0.9 ); + this.currentTime( 49 ); + }); }); test( "Custom", function() { var expects = 1, - count = 0; + count = 0, + p; expect( expects ); function plus(){ if ( ++count == expects ) { + p.unlisten( "eventz0rz" ); + p.destroy() start(); } } stop( 10000 ); - var p = Popcorn( "#video" ); + p = Popcorn( "#video" ); p.listen( "eventz0rz", function( event ) { @@ -1463,6 +1497,24 @@ test( "Custom", function() { p.trigger( "eventz0rz" ); }); +test( "on/off/emit", function() { + expect( 4 ); + + var $pop = Popcorn( "#video" ); + + $pop.on( "foo", function() { + deepEqual( this, $pop, "`this` is the popcorn instance" ); + equal( typeof this.data.events.foo, "object", "events hash registered at this.data.events.foo" ); + equal( Popcorn.sizeOf( this.data.events.foo ), 1, "Only one event is registered" ); + + $pop.off( "foo" ); + + equal( this.data.events.foo, null, "events hash is null at this.data.events.foo" ); + }); + + $pop.emit( "foo" ); +}); + test( "UI/Mouse", function() { @@ -1474,6 +1526,8 @@ test( "UI/Mouse", function() { function plus(){ if ( ++count == expects ) { + p.unlisten( "click" ); + p.destroy(); start(); } } @@ -1504,6 +1558,7 @@ test( "Manifest", function() { start(); // clean up added events after tests Popcorn.removePlugin( "footnote" ); + p.destroy(); } } @@ -1590,21 +1645,24 @@ test( "Manifest removal", function() { Popcorn.removePlugin( "tester" ); equal( Popcorn.sizeOf( Popcorn.manifest ), 0, "After deleting plugin" ); + + popcorn.destroy(); }); test( "Configurable Defaults", function() { - var expects = 13, - count = 0; + var expects = 14, + count = 0, + p; function plus() { if ( ++count === expects ) { - start(); - - [ "configurable", "multiconfig", "overridden" ].forEach(function( val ) { + [ "configurable", "multiconfig", "overridden", "funtionInitDefaults" ].forEach(function( val ) { Popcorn.removePlugin( val ); delete Popcorn.manifest[ val ]; }); + p.destroy(); + start(); } } @@ -1686,7 +1744,18 @@ test( "Configurable Defaults", function() { }; }); - var p = Popcorn( "#video", { + Popcorn.plugin( "funtionInitDefaults", function( options ) { + + equal( options.defaultItem, "foo bar", "defaults work inside auto setup function" ); + plus(); + + return { + start: Popcorn.nop, + end: Popcorn.nop + }; + }); + + p = Popcorn( "#video", { defaults: { overridden: { target: "default" @@ -1694,6 +1763,12 @@ test( "Configurable Defaults", function() { } }); + p.defaults( "funtionInitDefaults", { + defaultItem: "foo bar" + }); + + p.funtionInitDefaults({}); + p.defaults( "configurable", { // set a default element target id @@ -1736,6 +1811,53 @@ test( "Configurable Defaults", function() { }).currentTime( 2 ).play(); }); +test( "Plugin toString", function() { + + expect( 2 ); + + var $pop = Popcorn( "#video" ), + trackEvent, result; + + Popcorn.plugin( "stringify" , function() { + return { + _setup: function( options ) { + }, + start: function( event, options ){ + }, + end: function( event, options ){ + }, + _teardown: function( options ) { + } + }; + }); + + $pop.stringify({ + start: 0, + end: 10, + }); + + trackEvent = $pop.getTrackEvent( $pop.getLastTrackEventId() ); + result = trackEvent.toString(); + + ok( /^stringify \( start\: 0\, end\: 10\, id\: stringify/.test(result), "Default toString value" ); + + $pop.stringify({ + start: 3, + end: 4, + toString: function() { + return "BOO YA!"; + } + }); + + trackEvent = $pop.getTrackEvent( $pop.getLastTrackEventId() ); + result = trackEvent.toString(); + + + ok( result, "BOO YA!", "Custom toString value" ); + + Popcorn.removePlugin( "stringify" ); +}); + test( "Exceptions", function() { var $pop = Popcorn( "#video" ), @@ -1800,6 +1922,7 @@ test( "Start Zero Immediately", function() { if ( ++count === expects ) { // clean up added events after tests Popcorn.removePlugin( "zero" ); + $pop.destroy(); start(); } } @@ -1834,6 +1957,7 @@ test( "Special track event listeners: trackstart, trackend", function() { if ( ++count === expects ) { // clean up added events after tests Popcorn.removePlugin( "emitter" ); + $pop.destroy(); start(); } } @@ -1938,6 +2062,7 @@ test( "Update Timer (timeupdate)", function() { Popcorn.removePlugin( "forwards" ); Popcorn.removePlugin( "backwards" ); Popcorn.removePlugin( "wrapper" ); + p2.destroy(); start(); } } @@ -2086,6 +2211,12 @@ test( "Update Timer (frameAnimation)", function() { QUnit.reset(); + if ( !document.hasFocus() ) { + + ok( false, "frame animation tests need focus" ); + return; + } + var p2 = Popcorn( "#video", { frameAnimation: true }), @@ -2265,11 +2396,12 @@ test( "timeUpdate add track event while paused", function() { function plus() { if ( ++count === expects ) { Popcorn.removePlugin( "timeUpdateTester" ); + $pop.destroy(); start(); } } - stop(); + stop( 10000 ); Popcorn.plugin( "timeUpdateTester", function() { return { @@ -2282,11 +2414,11 @@ test( "timeUpdate add track event while paused", function() { }; }); - $pop.currentTime( 1 ).pause() + $pop.currentTime( 2 ).pause(); $pop.timeUpdateTester({ start: 1, - end: 2 + end: 3 }); }); @@ -2304,6 +2436,7 @@ test( "Plugin Factory", function () { if ( ++count == expects ) { Popcorn.removePlugin( "executor" ); Popcorn.removePlugin( "complicator" ); + popped.destroy(); start(); } } @@ -2424,8 +2557,15 @@ test( "Popcorn Compose", function() { } }; + popped.listen( "seeked", function() { + this.unlisten( "seeked" ); + this.play( 0 ); + }); + + popped.pause( popped.duration() ); + function plus() { - if ( ++count == expects ) { + if ( ++count === expects ) { Popcorn.removePlugin( "testPlugin" ); Popcorn.removePlugin( "pluginOptions1" ); Popcorn.removePlugin( "pluginOptions2" ); @@ -2479,8 +2619,6 @@ test( "Popcorn Compose", function() { } }); - popped.pause( popped.duration() ); - popped.testPlugin({ start: 0, end: 1, @@ -2653,7 +2791,6 @@ test( "Popcorn Compose", function() { composeOptionsTwo = popped.getLastTrackEventId(); - popped.currentTime( 0 ).play(); }); test( "Teardown end tester", function() { @@ -2679,8 +2816,7 @@ test( "Teardown end tester", function() { options.endCalled = false; options.teardownCalled = false; }, - start: function( event, options ) { - }, + start: function( event, options ) {}, end: function( event, options ) { // passes if end is called before teardown, and only called once equal( options.endCalled, false, "ensure only teardown can call this end" ); @@ -2708,6 +2844,65 @@ test( "Teardown end tester", function() { popped.removePlugin( "teardownEndTester" ); }); +test( "Teardown end noise", function() { + + QUnit.reset(); + + var popped = Popcorn( "#video" ), + expects = 5, + count = 0; + + function plus() { + if ( ++count === expects ) { + Popcorn.removePlugin( "teardownEndTester" ); + Popcorn.removePlugin( "noise" ); + start(); + } + } + + expect( expects ); + stop( 15000 ); + + Popcorn.plugin( "noise", {}); + + Popcorn.plugin( "teardownEndTester", { + _setup: function( options ) { + options.endCalled = false; + options.teardownCalled = false; + }, + start: function( event, options ) {}, + end: function( event, options ) { + // passes if end is called before teardown, and only called once + equal( options.endCalled, false, "ensure only teardown can call this end" ); + plus(); + equal( options.teardownCalled, false, "ensure teardown is not yet called" ); + plus(); + options.endCalled = true; + }, + _teardown: function( options ) { + + // passes if teardown is called after end, and only called once + equal( options.endCalled, true, "ensure end was previously called" ); + plus(); + equal( options.teardownCalled, false, "ensure teardown is not yet called" ); + plus(); + options.teardownCalled = true; + } + }); + + // if plugin to check's end time and start time + // don't align when sorted with all other times + // teardown will not be called + popped.noise({end: 21}); + popped.teardownEndTester({end: 20}); + + popped.currentTime( 0 ).play(); + popped.removePlugin( "teardownEndTester" ); + + equal( popped.data.trackEvents.byEnd[ 1 ]._natives.type, "noise", "proper end was removed" ); + plus(); +}); + test( "Plugin Breaker", function() { QUnit.reset(); @@ -2881,116 +3076,122 @@ test( "Remove Plugin", function() { expect( expects ); stop( 10000 ); + + p.listen( "seeked", function() { + this.unlisten( "seeked" ); + + equal( rlen, 0, "Popcorn.registry.length is empty" ); + plus(); + + equal( p.data.trackEvents.byStart.length, 2, "p.data.trackEvents.byStart is initialized and has 2 entries" ); + plus(); + equal( p.data.trackEvents.byEnd.length, 2, "p.data.trackEvents.byEnd is initialized and has 2 entries" ); + plus(); + + Popcorn.plugin( "removeme", { + + start: function() { + + }, + end: function() { + + }, + _teardown: function( options ) { + ok( true, "teardown called on " + options.order + " plugin via removePlugin()" ); + plus(); + } + }); + + p.removeme({ + start: 2, + end: 3, + order: "first" + }); + + p2.removeme({ + start: 2, + end: 3, + order: "second" + }); + + equal( Popcorn.registry.length, 1, "Popcorn.registry.length is 1" ); + plus(); + equal( p.data.trackEvents.byStart.length, 3, "p.data.trackEvents.byStart is updated and has 3 entries" ); + plus(); + equal( p.data.trackEvents.byEnd.length, 3, "p.data.trackEvents.byEnd is updated and has 3 entries" ); + plus(); + + p.removePlugin( "removeme" ); + + // p.removeme still exists on the prototype, even though we said to remove it + // the tracks of that type though, are removed. + // think of it as removing all tracks of plugin type attached to an instance + ok( typeof p.removeme === "function", "removeme plugin is still defined to p instance" ); + plus(); + ok( ( "removeme" in Popcorn.prototype ), "removeme plugin is still available to Popcorn.prototype" ); + plus(); + equal( Popcorn.registry.length, 1, "Popcorn.registry.length has not changed" ); + plus(); + + ok( ( typeof p2.removeme === "function" ), "removeme plugin is defined to p2 instance" ); + plus(); + + equal( p2.data.trackEvents.byStart.length, 3, "p2.data.trackEvents.byStart is updated and has 3 entries" ); + plus(); + equal( p2.data.trackEvents.byEnd.length, 3, "p2.data.trackEvents.byEnd is updated and has 3 entries" ); + plus(); + + equal( p.data.trackEvents.byStart.length, 2, "p.data.trackEvents.byStart is updated and has 2 entries" ); + plus(); + equal( p.data.trackEvents.byEnd.length, 2, "p.data.trackEvents.byEnd is updated and has 2 entries" ); + plus(); + Popcorn.removePlugin( "removeme" ); + + ok( !( "removeme" in p2 ), "removeme plugin is no longer available to p2 instance" ); + plus(); + ok( !( "removeme" in Popcorn.prototype ), "removeme plugin is no longer available to Popcorn.prototype" ); + plus(); + equal( Popcorn.registry.length, 0, "Popcorn.registry.length is empty again" ); + plus(); + + interval = setInterval(function() { + if( p2.currentTime() > 3 ) { + + equal( p2.data.trackEvents.byStart.length, 2, "p2.data.trackEvents.byStart is updated and has 2 entries" ); + plus(); + equal( p2.data.trackEvents.byEnd.length, 2, "p2.data.trackEvents.byEnd is updated and has 2 entries" ); + plus(); + clearInterval( interval ); + } + }, 1 ); + + Popcorn.plugin( "cleanup", { + + _setup: function( options ) { + + options.exist = true; + }, + _teardown: function( options ) { + + ok( true, "cleanup function is called during removal" ); + plus(); + + ok( options.exist, "options object exists at time of cleanup" ); + plus(); + } + }); + + p2.cleanup({ + start: 2, + end: 3 + }); + p2.removeTrackEvent( p2.getLastTrackEventId() ); + + p2.currentTime( 2 ).play(); + }); + p.currentTime( 0 ).pause(); - equal( rlen, 0, "Popcorn.registry.length is empty" ); - plus(); - - equal( p.data.trackEvents.byStart.length, 2, "p.data.trackEvents.byStart is initialized and has 2 entries" ); - plus(); - equal( p.data.trackEvents.byEnd.length, 2, "p.data.trackEvents.byEnd is initialized and has 2 entries" ); - plus(); - - Popcorn.plugin( "removeme", { - - start: function() { - - }, - end: function() { - - }, - _teardown: function( options ) { - ok( true, "teardown called on " + options.order + " plugin via removePlugin()" ); - plus(); - } - }); - - p.removeme({ - start: 2, - end: 3, - order: "first" - }); - - p2.removeme({ - start: 2, - end: 3, - order: "second" - }); - - equal( Popcorn.registry.length, 1, "Popcorn.registry.length is 1" ); - plus(); - equal( p.data.trackEvents.byStart.length, 3, "p.data.trackEvents.byStart is updated and has 3 entries" ); - plus(); - equal( p.data.trackEvents.byEnd.length, 3, "p.data.trackEvents.byEnd is updated and has 3 entries" ); - plus(); - - p.removePlugin( "removeme" ); - - // p.removeme still exists on the prototype, even though we said to remove it - // the tracks of that type though, are removed. - // think of it as removing all tracks of plugin type attached to an instance - ok( typeof p.removeme === "function", "removeme plugin is still defined to p instance" ); - plus(); - ok( ( "removeme" in Popcorn.prototype ), "removeme plugin is still available to Popcorn.prototype" ); - plus(); - equal( Popcorn.registry.length, 1, "Popcorn.registry.length has not changed" ); - plus(); - - ok( ( typeof p2.removeme === "function" ), "removeme plugin is defined to p2 instance" ); - plus(); - - equal( p2.data.trackEvents.byStart.length, 3, "p2.data.trackEvents.byStart is updated and has 3 entries" ); - plus(); - equal( p2.data.trackEvents.byEnd.length, 3, "p2.data.trackEvents.byEnd is updated and has 3 entries" ); - plus(); - - equal( p.data.trackEvents.byStart.length, 2, "p.data.trackEvents.byStart is updated and has 2 entries" ); - plus(); - equal( p.data.trackEvents.byEnd.length, 2, "p.data.trackEvents.byEnd is updated and has 2 entries" ); - plus(); - Popcorn.removePlugin( "removeme" ); - - ok( !( "removeme" in p2 ), "removeme plugin is no longer available to p2 instance" ); - plus(); - ok( !( "removeme" in Popcorn.prototype ), "removeme plugin is no longer available to Popcorn.prototype" ); - plus(); - equal( Popcorn.registry.length, 0, "Popcorn.registry.length is empty again" ); - plus(); - - interval = setInterval(function() { - if( p2.currentTime() > 3 ) { - - equal( p2.data.trackEvents.byStart.length, 2, "p2.data.trackEvents.byStart is updated and has 2 entries" ); - plus(); - equal( p2.data.trackEvents.byEnd.length, 2, "p2.data.trackEvents.byEnd is updated and has 2 entries" ); - plus(); - clearInterval( interval ); - } - }, 1 ); - - Popcorn.plugin( "cleanup", { - - _setup: function( options ) { - - options.exist = true; - }, - _teardown: function( options ) { - - ok( true, "cleanup function is called during removal" ); - plus(); - - ok( options.exist, "options object exists at time of cleanup" ); - plus(); - } - }); - - p2.cleanup({ - start: 2, - end: 3 - }); - p2.removeTrackEvent( p2.getLastTrackEventId() ); - - p2.currentTime( 2 ).play(); }); test( "Protected Names", function() { @@ -3000,7 +3201,7 @@ test( "Protected Names", function() { count = 0, popped = Popcorn( "#video" ); - for ( item in Popcorn.p ) { + for ( var item in Popcorn.p ) { if ( Popcorn.p.hasOwnProperty( item ) ) { keys.push( item ); } @@ -3096,6 +3297,10 @@ test( "In/Out aliases", function() { out: 3 }); + popcorn.listen( "seeked", function() { + this.unlisten( "seeked" ).play( 0 ); + }) + popcorn.currentTime( 0 ).pause(); ok( popcorn.data.events[ "in" ], "in is a valid alias for start" ); @@ -3116,8 +3321,6 @@ test( "In/Out aliases", function() { equal( counter, 2, "Counter is at 2, out has been called" ); plus(); }); - - popcorn.play(); }); module( "Popcorn TrackEvents" ); @@ -3635,9 +3838,10 @@ test( "XML Conversion", function() { plus(); var parser = new DOMParser(), - xml = parser.parseFromString( ' ',"text/xml" ); + xml = parser.parseFromString(' ',"text/xml"), + xml2 = parser.parseFromString( data.text, "text/xml" ); - equal( data.xml.toString(), xml.toString(), "data.xml returns a document of xml for " + fileName ); + equal( xml.toString(), xml2.toString(), "data.xml returns a document of xml for " + fileName ); plus(); } }); @@ -3871,15 +4075,15 @@ test( "Popcorn.getScript()", function() { Popcorn.getScript( - "https://github.com/rwldrn/has.js/raw/master/has.js", + "http://popcornjs.org/code/test/data/testfunction2.js", function() { ok( true, "getScript C returned" ); plus(); - ok( ( "has" in window ) , "Popcorn.getScript https://github.com/rwldrn/has.js/raw/master/has.js loaded: `has` is available" ); + ok( ( "testFunction2" in window ), "Popcorn.getScript data/testfunction.js loaded: `testFunction2` is available" ); plus(); - delete window[ "has" ]; + delete window[ "testFunction2" ]; } ); @@ -3920,10 +4124,10 @@ test( "XML Response", function() { plus(); var parser = new DOMParser(), - xml = parser.parseFromString( ' ',"text/xml" ); + xml = parser.parseFromString(' ',"text/xml"), + xml2 = parser.parseFromString( data.text, "text/xml" ); - - equal( data.xml.toString(), xml.toString(), "data.xml returns a document of xml" ); + equal( xml.toString(), xml2.toString(), "data.xml returns a document of xml" ); plus(); } }); @@ -3954,8 +4158,14 @@ test( "dataType: XML Response", function() { var parser = new DOMParser(), xml = parser.parseFromString( ' ',"text/xml" ); - equal( data.toString(), xml.toString(), "dataType: 'xml', data.xml returns a document of xml" ); - plus(); + if ( data.toString ) { + equals( data.toString(), xml.toString(), "data.xml returns a document of xml"); + plus(); + } else { + var xml2 = parser.parseFromString( data.xml, "text/xml" ); + equals( xml2.toString(), xml.toString(), "data.xml returns a document of xml"); + plus(); + } } }); });