Merge remote-tracking branch 'wmm/master'

This commit is contained in:
Bobby Richter 2011-04-28 15:54:12 -04:00
Родитель 171e894889 db38a07839
Коммит 423aec6e25
52 изменённых файлов: 4820 добавлений и 2946 удалений

8
LICENSE_HEADER Normal file
Просмотреть файл

@ -0,0 +1,8 @@
/*
* popcorn.js version @VERSION
* http://popcornjs.org
*
* Copyright 2011, Mozilla Foundation
* Licensed under the MIT license
*/

Просмотреть файл

@ -55,6 +55,9 @@ POPCORN_COMPLETE_LIST := --js ${POPCORN_SRC} \
POPCORN_COMPLETE_DIST = ${DIST_DIR}/popcorn-complete.js
POPCORN_COMPLETE_MIN = ${DIST_DIR}/popcorn-complete.min.js
# Create a versioned license header for js files we ship: arg1=source arg2=dest
add_license = cat ${PREFIX}/LICENSE_HEADER | sed -e 's/@VERSION/${VERSION}/' > $(2) ; \
cat $(1) >> $(2)
all: lint lint-plugins lint-parsers lint-players popcorn plugins parsers players complete min
@@echo "Popcorn build complete."
@ -66,23 +69,27 @@ popcorn: ${POPCORN_DIST}
${POPCORN_DIST}: ${POPCORN_SRC} | ${DIST_DIR}
@@echo "Building" ${POPCORN_DIST}
@@cat ${POPCORN_SRC} | sed -e 's/@VERSION/${VERSION}/' > ${POPCORN_DIST}
@@$(call add_license, $(POPCORN_SRC), $(POPCORN_DIST))
min: ${POPCORN_MIN} ${PLUGINS_MIN} ${PARSERS_MIN} ${PLAYERS_MIN} ${POPCORN_COMPLETE_MIN}
${POPCORN_MIN}: ${POPCORN_DIST}
@@echo "Building" ${POPCORN_MIN}
$(call compile, --js ${POPCORN_DIST}, ${POPCORN_MIN})
@@$(call compile, --js ${POPCORN_DIST}, ${POPCORN_MIN}.tmp)
@@$(call add_license, ${POPCORN_MIN}.tmp, ${POPCORN_MIN})
@@rm ${POPCORN_MIN}.tmp
${POPCORN_COMPLETE_MIN}: ${POPCORN_SRC} ${PLUGINS_SRC} ${PARSERS_SRC} ${DIST_DIR}
@@echo "Building" ${POPCORN_COMPLETE_MIN}
@@$(call compile, ${POPCORN_COMPLETE_LIST}, ${POPCORN_COMPLETE_MIN})
@@$(call compile, ${POPCORN_COMPLETE_LIST}, ${POPCORN_COMPLETE_MIN}.tmp)
@@$(call add_license, ${POPCORN_COMPLETE_MIN}.tmp, ${POPCORN_COMPLETE_MIN})
@@rm ${POPCORN_COMPLETE_MIN}.tmp
plugins: ${PLUGINS_DIST}
${PLUGINS_MIN}: ${PLUGINS_DIST}
@@echo "Building" ${PLUGINS_MIN}
$(call compile, $(shell for js in ${PLUGINS_SRC} ; do echo --js $$js ; done), ${PLUGINS_MIN})
@@$(call compile, $(shell for js in ${PLUGINS_SRC} ; do echo --js $$js ; done), ${PLUGINS_MIN})
${PLUGINS_DIST}: ${PLUGINS_SRC} ${DIST_DIR}
@@echo "Building ${PLUGINS_DIST}"
@ -92,7 +99,7 @@ parsers: ${PARSERS_DIST}
${PARSERS_MIN}: ${PARSERS_DIST}
@@echo "Building" ${PARSERS_MIN}
$(call compile, $(shell for js in ${PARSERS_SRC} ; do echo --js $$js ; done), ${PARSERS_MIN})
@@$(call compile, $(shell for js in ${PARSERS_SRC} ; do echo --js $$js ; done), ${PARSERS_MIN})
${PARSERS_DIST}: ${PARSERS_SRC} ${DIST_DIR}
@@echo "Building ${PARSERS_DIST}"
@ -102,15 +109,17 @@ players: ${PLAYERS_DIST}
${PLAYERS_MIN}: ${PLAYERS_DIST}
@@echo "Building" ${PLAYERS_MIN}
$(call compile, $(shell for js in ${PLAYERS_SRC} ; do echo --js $$js ; done), ${PLAYERS_MIN})
@@$(call compile, $(shell for js in ${PLAYERS_SRC} ; do echo --js $$js ; done), ${PLAYERS_MIN})
${PLAYERS_DIST}: ${PLAYERS_SRC} ${DIST_DIR}
@@echo "Building ${PLAYERS_DIST}"
@@cat ${PLAYERS_SRC} > ${PLAYERS_DIST}
complete: ${POPCORN_SRC} ${PARSERS_SRC} ${PLUGINS_SRC} ${PLAYERS_SRC} ${DIST_DIR}
@@echo "Building popcorn + plugins + parsers + players"
@@cat ${POPCORN_SRC} ${PLUGINS_SRC} ${PARSERS_SRC} ${PLAYERS_SRC} | sed -e 's/@VERSION/${VERSION}/' > ${POPCORN_COMPLETE_DIST}
@@echo "Building popcorn + plugins + parsers + players..."
@@cat ${POPCORN_SRC} ${PLUGINS_SRC} ${PARSERS_SRC} ${PLAYERS_SRC} > ${POPCORN_COMPLETE_DIST}.tmp
@@$(call add_license, ${POPCORN_COMPLETE_DIST}.tmp, ${POPCORN_COMPLETE_DIST})
@@rm ${POPCORN_COMPLETE_DIST}.tmp
lint:
@@echo "Checking Popcorn against JSLint..."

Просмотреть файл

@ -0,0 +1,49 @@
{
"title":"Crockford Lives!",
"remote":"http://dl.dropbox.com/u/3531958/crockford.ogv",
"data": [
{
"webpage": {
"start":"0.0897589878863602",
"end":"2.001204869833337",
"target":"audio-iframe-container",
"src":"http://json.org"
}
},
{
"footnote": {
"start":"0.23530116023787565",
"end":"2.0193976413772767",
"target":"audio-footnote-container",
"text":"I invented JSON"
}
},
{
"googlemap": {
"start":"0.09096385771969716",
"end":"8.617349362660605",
"target":"audio-map-container",
"type":"ROADMAP",
"lat":37.7749295,
"lng":-122.4194155,
"location":"San Francisco, CA"
}
},
{
"webpage": {
"start":"2.073975956009095",
"end":"10.278915922325776",
"target":"audio-iframe-container",
"src":"http://jslint.org"
}
},
{
"footnote": {
"start":2.145542172351516,
"end":10.145542172351513,
"target":"audio-footnote-container",
"text":"I wrote JSLint"
}
}
]
}

Просмотреть файл

@ -0,0 +1,49 @@
{
"title":"Crockford Lives!",
"remote":"http://dl.dropbox.com/u/3531958/crockford.ogv",
"data": [
{
"webpage": {
"start":"0.0897589878863602",
"end":"2.001204869833337",
"target":"video-iframe-container",
"src":"http://json.org"
}
},
{
"footnote": {
"start":"0.23530116023787565",
"end":"2.0193976413772767",
"target":"video-footnote-container",
"text":"I invented JSON"
}
},
{
"googlemap": {
"start":"0.09096385771969716",
"end":"8.617349362660605",
"target":"video-map-container",
"type":"ROADMAP",
"lat":37.7749295,
"lng":-122.4194155,
"location":"San Francisco, CA"
}
},
{
"webpage": {
"start":"2.073975956009095",
"end":"10.278915922325776",
"target":"video-iframe-container",
"src":"http://jslint.org"
}
},
{
"footnote": {
"start":2.145542172351516,
"end":10.145542172351513,
"target":"video-footnote-container",
"text":"I wrote JSLint"
}
}
]
}

Просмотреть файл

@ -41,14 +41,26 @@
<p>Your user agent does not support the HTML5 Video element.</p>
</video>
<audio id='audio' src="../../test/italia.ogg" data-timeline-sources="data/audio.json" controls>
</audio>
<style>
.displays {
width: 300px;
height: 300px;
}
</style>
<div class="displays" id="iframe-container"></div>
<div class="displays" id="map-container"></div>
<div class="displays" id="footnote-container"></div>
<hr>
<div style="width:300px;float:left">
<div class="displays" id="video-iframe-container"></div>
<div class="displays" id="video-map-container"></div>
<div class="displays" id="video-footnote-container"></div>
</div>
<div style="width:300px;float:left">
<div class="displays" id="audio-iframe-container"></div>
<div class="displays" id="audio-map-container"></div>
<div class="displays" id="audio-footnote-container"></div>
</div>
</body>
</html>

Просмотреть файл

@ -7,18 +7,15 @@ test("Popcorn 0.3 JSON Parser Plugin", function () {
finished = false,
trackData,
trackEvents,
interval,
poppercorn = Popcorn( "#video" );
function plus() {
if ( ++count === expects ) {
start();
// clean up added events after tests
clearInterval( interval );
}
}
poppercorn.parseJSON("data/data.json");
poppercorn.parseJSON("data/video.json");
expect(expects);
@ -29,14 +26,17 @@ test("Popcorn 0.3 JSON Parser Plugin", function () {
trackEvents = trackData.trackEvents;
Popcorn.xhr({
url: 'data/data.json',
url: 'data/video.json',
success: function( data ) {
var idx = 0;
Popcorn.forEach( data.json.data, function (dataObj) {
Popcorn.forEach( dataObj, function ( obj, key ) {
//console.log( data );
Popcorn.forEach( data.json.data, function (dataObj) {
//console.log( dataObj );
Popcorn.forEach( dataObj, function ( obj, key ) {
equals( trackData.history[idx].indexOf(key), 0, "history item '" + trackData.history[idx] + "' matches data key '"+ key+ "' at correct index" );
plus();
@ -59,11 +59,11 @@ test("Popcorn 0.3 JSON Parser Plugin", function () {
plus();
equals( $("#iframe-container").children().length, 2, '$("#iframe-container").children().length' )
equals( $("#video-iframe-container").children().length, 2, '$("#video-iframe-container").children().length' )
plus();
equals( $("#map-container").children().length, 1, '$("#map-container").children().length' );
equals( $("#video-map-container").children().length, 1, '$("#video-map-container").children().length' );
plus();
equals( $("#footnote-container").children().length, 2, '$("#footnote-container").children().length' );
equals( $("#video-footnote-container").children().length, 2, '$("#video-footnote-container").children().length' );
plus();
this.pause();
@ -80,3 +80,87 @@ test("Popcorn 0.3 JSON Parser Plugin", function () {
}, 500);
});
test("Popcorn 0.3 JSON Parser Plugin - AUDIO", function () {
var expects = 9,
count = 0,
timeOut = 0,
numLoadingEvents = 5,
finished = false,
trackData,
trackEvents,
interval,
audiocorn = Popcorn.getInstanceById("audio");
function plus() {
if ( ++count === expects ) {
start();
// clean up added events after tests
clearInterval( interval );
}
}
expect(expects);
stop( 5000 );
trackData = audiocorn.data;
trackEvents = trackData.trackEvents;
Popcorn.xhr({
url: 'data/audio.json',
success: function( data ) {
var idx = 0;
Popcorn.forEach( data.json.data, function (dataObj) {
Popcorn.forEach( dataObj, function ( obj, key ) {
equals( trackData.history[idx].indexOf(key), 0, "history item '" + trackData.history[idx] + "' matches data key '"+ key+ "' at correct index" );
plus();
idx++;
});
});
}
});
audiocorn.listen("timeupdate", function ( event ) {
if ( Math.round( this.currentTime()) === 3 && !finished ) {
finished = true;
equals( trackEvents.byStart.length, numLoadingEvents + 2 , "trackEvents.byStart.length === (5 loaded, 2 padding) " );
plus();
equals( $("#audio-iframe-container").children().length, 2, '$("#audio-iframe-container").children().length' )
plus();
equals( $("#audio-map-container").children().length, 1, '$("#audio-map-container").children().length' );
plus();
equals( $("#audio-footnote-container").children().length, 2, '$("#audio-footnote-container").children().length' );
plus();
this.pause();
}
});
setTimeout(function () {
audiocorn.currentTime(0).play()
}, 500);
});

Просмотреть файл

@ -0,0 +1,90 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn Base Player Example</title>
<style>
body > div {
width: 100%;
float: left;
overflow: hidden;
}
div div {
margin-bottom:20px;
}
#player_1 {
position: absolute;
left: 250px;
top: 20px;
width: 300px;
height: 150px;
background: #FFAAAA;
}
</style>
<script src="../../popcorn.js"></script>
<script type="text/javascript" src="popcorn.baseplayer.js"></script>
<script type="text/javascript">
//On document ready
document.addEventListener( "DOMContentLoaded", function() {
var ticks = 0,
player = Popcorn.baseplayer(),
dimensions,
popcorn;
document.getElementById( "btnPlay" ).addEventListener( "click", function() { popcorn.play(); }, false );
document.getElementById( "btnPause" ).addEventListener( "click", function() { popcorn.pause(); }, false );
player._resource = document.getElementById('player_1');
player._resource.innerHTML += '<br />Width and Height: '+player.getStyle( 'width' ) + ' by ' + player.getStyle( 'height' )
+ '<br /> Positioned at: '+player.getStyle( 'left' ) + ',' + player.getStyle( 'top' );
dimensions = player.getBoundingClientRect();
document.getElementById( "playerTop" ).innerHTML = dimensions.top;
document.getElementById( "playerLeft" ).innerHTML = dimensions.left;
document.getElementById( "playerBottom" ).innerHTML = dimensions.bottom;
document.getElementById( "playerRight" ).innerHTML = dimensions.right;
document.getElementById( "playerWidth" ).innerHTML = dimensions.width;
document.getElementById( "playerHeight" ).innerHTML = dimensions.height;
popcorn = Popcorn( player )
.listen( "timeupdate", function() {
document.getElementById( "playerTicks" ).innerHTML = ++ticks;
document.getElementById( "playerMs" ).innerHTML = popcorn.currentTime().toFixed(5);
})
.exec( 2, function() {
document.getElementById( "playerOutput" ).innerHTML = "2 seconds reached";
});
// The ready state and resource selection would be handled by the player extending this base player
player.readyState = 2;
popcorn.play();
}, false);
</script>
</head>
<body>
<div>
Ticks: <span id="playerTicks" ></span><br />
Seconds: <span id="playerMs" ></span><br />
Event Output: <span id="playerOutput" ></span><br />
<button class="simple" id="btnPlay">Play</button>
<button class="simple" id="btnPause">Pause</button><br r/>
Top: <span id="playerTop" ></span><br />
Left: <span id="playerLeft" ></span><br />
Bottom: <span id="playerBottom" ></span><br />
Right: <span id="playerRight" ></span><br />
Width: <span id="playerWidth" ></span><br />
Height: <span id="playerHeight" ></span><br />
</div>
<span id="player_1">This is a container for the player, auto-sized for testing purposes. It displays CSS values.</span>
<br />
<br />
This demo showcases basic functionality of a player. It is a shell of an HTMLMediaElement with basic timing routines.<br />
At 2 seconds, the phrase "2 seconds reached" will appear beside 'Event Output'<br />
</body>
</html>

Просмотреть файл

@ -0,0 +1,167 @@
(function( global, doc ) {
Popcorn.baseplayer = function() {
return new Popcorn.baseplayer.init();
};
Popcorn.baseplayer.init = function() {
this.readyState = 0;
this.currentTime = 0;
this.duration = 0;
this.paused = 1;
this.ended = 0;
this.volume = 1;
this.muted = 0;
this.playbackRate = 1;
// These are considered to be "on" by being defined. Initialize to undefined
this.autoplay;
this.loop;
// List of events
this._events = {};
// The underlying player resource. May be <canvas>, <iframe>, <object>, array, etc
this._resource;
// The container div of the resource
this._container;
this.offsetWidth = this.width = 0;
this.offsetHeight = this.height = 0;
this.offsetLeft = 0;
this.offsetTop = 0;
this.offsetParent;
};
Popcorn.baseplayer.init.prototype = {
load: function() {},
play: function() {
this.paused = 0;
this.timeupdate();
},
pause: function() {
this.paused = 1;
},
timeupdate: function() {
// The player was paused since the last time update
if ( this.paused ) {
return;
}
// So we can refer to the instance when setTimeout is run
var self = this;
this.currentTime += 0.015;
this.dispatchEvent( "timeupdate" );
setTimeout( function() {
self.timeupdate.call( self );
}, 15 );
},
// By default, assumes this.resource is a DOM Element
// Changing the type of this.resource requires this method to be overridden
getBoundingClientRect: function() {
var b,
self = this;
if ( this._resource ) {
b = this._resource.getBoundingClientRect();
return {
bottom: Math.ceil( b.bottom ),
left: Math.ceil( b.left ),
right: Math.ceil( b.right ),
top: Math.ceil( b.top ),
// These not guaranteed to be in there
width: b.width || ( b.right - b.left ),
height: b.height || ( b.bottom - b.top )
};
} else {
b = this._container.getBoundingClientRect();
// Update bottom, right for expected values once the container loads
return {
left: b.left,
top: b.top,
width: self.offsetWidth,
height: self.offsetHeight,
bottom: b.top + this.width,
right: b.top + this.height
};
}
},
// Add an event listener to the object
addEventListener: function( evtName, fn ) {
if ( !this._events[evtName] ) {
this._events[evtName] = [];
}
this._events[evtName].push( fn );
return fn;
},
// Can take event object or simple string
dispatchEvent: function( oEvent ) {
var evt,
self = this,
eventInterface,
eventName = oEvent.type;
// A string was passed, create event object
if ( !eventName ) {
eventName = oEvent;
eventInterface = Popcorn.events.getInterface( eventName );
if ( eventInterface ) {
evt = document.createEvent( eventInterface );
evt.initEvent( eventName, true, true, window, 1 );
}
}
Popcorn.forEach( this._events[eventName], function( val ) {
val.call( self, evt, self );
});
},
// Extracts values from container onto this object
extractContainerValues: function( id ) {
this._container = document.getElementById( id );
if ( !this._container ) {
return;
}
var bounds = this._container.getBoundingClientRect();
this.offsetWidth = this.width = container.getAttribute( "width" ) || getStyle( "width" ) || 0;
this.offsetHeight = this.height = container.getAttribute( "height" ) || getStyle( "height" ) || 0;
this.offsetLeft = bounds.left;
this.offsetTop = bound.top;
this.offsetParent = this._container.offsetParent;
return this._container;
},
// By default, assumes this.resource is a DOM Element
// Changing the type of this.resource requires this method to be overridden
// Returns the computed value for CSS style 'prop' as computed by the browser
getStyle: function( prop ) {
var elem = this._resource;
if ( elem.currentStyle ) {
// IE syntax
return elem.currentStyle[prop];
} else if ( global.getComputedStyle ) {
// Firefox, Chrome et. al
return doc.defaultView.getComputedStyle( elem, null ).getPropertyValue( prop );
} else {
// Fallback, just in case
return elem.style[prop];
}
}
};
})( window, document );

Просмотреть файл

@ -0,0 +1,47 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn Base Player Plugin</title>
<link rel="stylesheet" href="../../test/qunit/qunit.css" type="text/css" media="screen">
<script src="../../test/qunit/qunit.js"></script>
<!--
do not move - this must be called immediately prior to
popcorn-api-draft.js
-->
<script src="../../popcorn.js"></script>
<script src="popcorn.baseplayer.unit.js"></script>
<script src="popcorn.baseplayer.js"></script>
<style>
body > div {
width: 100%;
float: left;
overflow: hidden;
}
div div {
margin-bottom:20px;
}
#player_1 {
position: absolute;
left: 220px;
top: 260px;
width: 300px;
height: 250px;
background: #FFAAAA;
}
</style>
</head>
<body>
<h1 id="qunit-header">Popcorn Base Player Plugin</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"> </div>
<span id="player_1">This is a container for the player, auto-sized for testing purposes.</span>
</body>
</html>

Просмотреть файл

@ -0,0 +1,176 @@
module( "Popcorn Base Player" );
test( "API", function () {
var expects = 0,
count = 0,
player = Popcorn.baseplayer(),
members = {
// HTMLMediaElement members
'readyState' : 'number',
'currentTime' : 'number',
'duration' : 'number',
'paused' : 'number',
'ended' : 'number',
'volume' : 'number',
'muted' : 'number',
'playbackRate' : 'number',
'autoplay' : 'undefined',
'loop' : 'undefined',
'load' : 'function',
'play' : 'function',
'pause' : 'function',
'timeupdate' : 'function',
// DOMElement members
'addEventListener' : 'function',
'dispatchEvent' : 'function',
'getBoundingClientRect' : 'function',
'width' : 'number',
'height' : 'number',
'offsetWidth' : 'number',
'offsetHeight' : 'number',
'offsetTop' : 'number',
'offsetLeft' : 'number',
'offsetParent' : 'undefined',
// Helper functions and members
'_events' : 'object',
'_resource' : 'undefined',
'_container' : 'undefined',
'getStyle' : 'function',
'extractContainerValues' : 'function'
};
function plus() {
if ( ++count === expects ) {
start();
}
}
Popcorn.forEach( members, function () {
expects++;
});
expect( expects );
stop( 10000 );
Popcorn.forEach( members, function ( type, prop ) {
ok( typeof player[prop] === type, "player." + prop + " is type: " + type );
plus();
});
});
test( "Default Functionality", function () {
var expects = 7,
count = 0,
player = Popcorn.baseplayer(),
dimensions,
expectedVals = {
left: 220,
top: 260,
width: 300,
height: 250
};
function plus() {
if ( ++count === expects ) {
start();
}
}
player._resource = document.getElementById('player_1');
dimensions = player.getBoundingClientRect();
Popcorn.forEach( expectedVals, function() {
expects+= 2;
});
expect( expects );
stop( 1000 );
Popcorn.forEach( expectedVals, function( val, prop ) {
equals( player.getStyle( prop ), val+'px', "Style '" + prop + "' correctly got" );
plus();
equals( dimensions[prop], val, "Bounding Client " + prop + " works" );
plus();
});
equals( dimensions.right, expectedVals.left + expectedVals.width, "Bounding Client right works" );
plus();
equals( dimensions.bottom, expectedVals.top + expectedVals.height, "Bounding Client bottom works" );
plus();
player.addEventListener( 'timeupdate', function( evt, caller ) {
ok( true, "Time update called" );
plus();
equals( player, this, "'this' is kept as player" );
plus();
equals( player, caller, "caller is kept as player" );
plus();
ok( evt instanceof window.Event, "Event object passed in" );
plus();
ok( evt.type === 'timeupdate', "Event object is type 'timeupdate'" );
plus();
player.pause();
});
player.play();
});
test( "Extension and Method Overriding", function () {
var expects = 4,
count = 0,
player = Popcorn.baseplayer(),
playerForPopcorn = Popcorn.baseplayer(),
popcorn;
function plus() {
if ( ++count === expects ) {
start();
}
}
expect(expects);
stop( 4000 );
Popcorn.extend( player, {
load: function() {
ok( true, "Load overridden" );
plus();
},
timeupdate: function() {
ok( true, "Timeupdate overridden" );
plus();
// Must dispatch event so event listeners can work!
this.dispatchEvent("timeupdate");
// We don't want to cue custom timing loop using setTimeout because we only want this to run once
}
});
player.addEventListener( "timeupdate", function() {
ok( true, "Timeupdate event dispatched!" );
plus();
});
popcorn = Popcorn( playerForPopcorn )
.exec( 2, function() {
ok( true, "Exec triggereed from popcorn after 2 seconds" );
plus();
});
player.load();
player.play();
// Each player will define its own criteria for when readyState should be changed
// No logic in base player for this, so we must do it manually so popcorn will be able to cue track events
playerForPopcorn.readyState = 2;
popcorn.play();
});

Просмотреть файл

@ -0,0 +1,140 @@
/*
* JavaScript interface for the SoundCloud Player widget
* Author: Matas Petrikas, matas@soundcloud.com
* Copyright (c) 2009 SoundCloud Ltd.
* Licensed under the MIT license:
* http://www.opensource.org/licenses/mit-license.php
*/
(function(){
var isIE = (/msie/i).test(navigator.userAgent) && !(/opera/i).test(navigator.userAgent);
var soundcloud = window.soundcloud = {
version: "0.1",
debug: false,
_listeners: [],
// re-dispatches widget events in the DOM, using JS library support, the events also should bubble up the DOM
_redispatch: function(eventType, flashId, data) {
var playerNode,
lsnrs = this._listeners[eventType] || [],
// construct the custom eventType e.g. 'soundcloud:onPlayerReady'
customEventType = 'soundcloud:' + eventType;
try{
// find the flash player, might throw an exception
playerNode = this.getPlayer(flashId);
}catch(e){
if(this.debug && window.console){
console.error('unable to dispatch widget event ' + eventType + ' for the widget id ' + flashId, data, e);
}
return;
}
// re-dispatch SoundCloud events up in the DOM
if(window.jQuery){
// if jQuery is available, trigger the custom event
jQuery(playerNode).trigger(customEventType, [data]);
}else if(window.Prototype){
// if Prototype.js is available, fire the custom event
$(playerNode).fire(customEventType, data);
}else{
// TODO add more JS libraries that support custom DOM events
}
// if there are any listeners registered to this event, trigger them all
for(var i = 0, l = lsnrs.length; i < l; i += 1) {
lsnrs[i].apply(playerNode, [playerNode, data]);
}
// log the events in debug mode
if(this.debug && window.console){
console.log(customEventType, eventType, flashId, data);
}
},
// you can add multiple listeners to a certain event
// e.g. soundcloud.addEventListener('onPlayerReady', myFunctionOne);
// soundcloud.addEventListener('onPlayerReady', myFunctionTwo);
addEventListener: function(eventType, callback) {
if(!this._listeners[eventType]){
this._listeners[eventType] = [];
}
this._listeners[eventType].push(callback);
},
// you can also remove the function listener if e.g you want to trigger it only once
// soundcloud.removeEventListener('onMediaPlay', myFunctionOne);
removeEventListener: function(eventType, callback) {
var lsnrs = this._listeners[eventType] || [];
for(var i = 0, l = lsnrs.length; i < l; i += 1) {
if(lsnrs[i] === callback){
lsnrs.splice(i, 1);
}
}
},
// get widget node based on its id (if object tag) or name (if embed tag)
// if you're using SWFObject or other dynamic Flash generators, please make sure that you set the id parameter
// only if the DOM has an id/name it's possible to call player's methods.
// Important!: because of the bug in Opera browser, the Flash can't get its own id
// so the generator should set it additionally through flashvars parameter 'object_id'
getPlayer: function(id){
var flash;
try{
if(!id){
throw "The SoundCloud Widget DOM object needs an id atribute, please refer to SoundCloud Widget API documentation.";
}
flash = isIE ? window[id] : document[id];
if(flash){
if(flash.api_getFlashId){
return flash;
}else{
throw "The SoundCloud Widget External Interface is not accessible. Check that allowscriptaccess is set to 'always' in embed code";
}
}else{
throw "The SoundCloud Widget with an id " + id + " couldn't be found";
}
}catch(e){
if (console && console.error) {
console.error(e);
}
throw e;
}
},
// fired when widget has loaded its data and is ready to accept calls from outside
// the widget will call these functions only if in it's flashvars there's a parameter enable_api=true
// @flashId: the widget id, basically the Flash node should be accessible to JS with soundcloud.getPlayer(flashId)
// @data: an object containing .mediaUri (eg. 'http://api.soundcloud.com/tracks/49931') .mediaId (e.g. '4532')
// in buffering events data contains also .percent = (e.g. '99')
onPlayerReady: function(flashId, data) {
this._redispatch('onPlayerReady', flashId, data);
},
// fired when widget starts playing current track (fired only once per track)
onMediaStart : function(flashId, data) {
this._redispatch('onMediaStart', flashId, data);
},
// fired when the track/playlist has finished playing
onMediaEnd : function(flashId, data) {
this._redispatch('onMediaEnd', flashId, data);
},
// fired when widget starts playing current track (fired on every play, seek)
onMediaPlay : function(flashId, data) {
this._redispatch('onMediaPlay', flashId, data);
},
// fired when track was paused
onMediaPause : function(flashId, data) {
this._redispatch('onMediaPause', flashId, data);
},
// fired when the widget is still buffering, means you can't seek in the track fully yet
onMediaBuffering : function(flashId, data) {
this._redispatch('onMediaBuffering', flashId, data);
},
// fired when the user seeks in the track
onMediaSeek : function(flashId, data) {
this._redispatch('onMediaSeek', flashId, data);
},
// fired when the widget is done buffering and the whole track length is seekable
onMediaDoneBuffering : function(flashId, data) {
this._redispatch('onMediaDoneBuffering', flashId, data);
},
// fired when the widget can't get the requested data from the server (the resource is removed, hidden, etc.)
onPlayerError : function(flashId, data) {
this._redispatch('onPlayerError', flashId, data);
}
};
})();

Просмотреть файл

@ -0,0 +1,241 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn Soundcloud Player Example</title>
<style>
#media_1{
width: 80%;
}
</style>
<script src="../../popcorn.js"></script>
<script type="text/javascript" src="popcorn.soundcloud.js"></script>
<!-- Plugins for demo purposes -->
<script src="../../plugins/footnote/popcorn.footnote.js"></script>
<script src="../../plugins/flickr/popcorn.flickr.js"></script>
<script src="../../plugins/attribution/popcorn.attribution.js"></script>
<script src="../../plugins/webpage/popcorn.webpage.js"></script>
<script src="../../plugins/googlefeed/popcorn.googlefeed.js"></script>
<script src="../../plugins/image/popcorn.image.js"></script>
<script src="../../plugins/subtitle/popcorn.subtitle.js"></script>
<script src="../../plugins/twitter/popcorn.twitter.js"></script>
<script type="text/javascript">
//On document ready
document.addEventListener( "DOMContentLoaded", function() {
var popcorn;
document.getElementById( "btnPlay" ).addEventListener( "click", function() {
popcorn.play();
}, false);
document.getElementById( "btnPause" ).addEventListener( "click", function() {
popcorn.pause();
}, false);
document.getElementById( "btnSeek" ).addEventListener( "click", function() {
popcorn.currentTime( 30 );
}, false);
document.getElementById( "btnVolume" ).addEventListener( "click", function() {
if ( popcorn.volume() >= 0.5 ) {
popcorn.volume( popcorn.volume()/2 );
this.innerHTML = "Double Volume";
} else {
popcorn.volume( popcorn.volume()*2 );
this.innerHTML = "Halve Volume";
}
}, false);
document.getElementById( "btnMute" ).addEventListener( "click", function() {
popcorn.mute();
}, false);
popcorn = Popcorn( Popcorn.soundcloud( "media_1", "http://soundcloud.com/forss/flickermood", {
width: "50%",
/*
This demo contains an api key used for retrieving comments from Soundcloud's API
It is to be used for demonstration purposes only, and for only this demo
*/
api: {
key: "PRaNFlda6Bhf5utPjUsptg",
commentdiv: "commentDisplay"/*,
commentformat: (function() {
var count = 0;
return function( comment ) {
return "<div>Comment # " + ( count++ ) + "<br />"
+ "<em>" + comment.user.name + "</em>: " + comment.text +"</div>";
}
})()*/
}
}) );
popcorn.listen( "load", function() {
document.getElementById( "media_duration" ).innerHTML = popcorn.duration();
document.getElementById( "media_volume" ).innerHTML = popcorn.volume();
document.getElementById( "media_currentTime" ).innerHTML = popcorn.currentTime();
document.getElementById( "media_readyState" ).innerHTML = popcorn.video.readyState;
popcorn.listen( "timeupdate", function() {
document.getElementById( "media_currentTime" ).innerHTML = popcorn.currentTime();
});
popcorn.video.addEventListener( "readystatechange", function() {
document.getElementById( "media_readyState" ).innerHTML = popcorn.video.readyState;
});
popcorn.listen( "volumechange", function() {
document.getElementById( "media_volume" ).innerHTML = popcorn.volume();
});
popcorn.trigger( "play" );
});
// Empty for the purposes of allowing popcorn.trigger("play");
popcorn.listen( "play", function() {
});
popcorn.subtitle({
start: 5, // seconds
end: 15, // seconds
text: 'This is overlaid on top of the audio visualization. You can hightlight it!',
display: 'inline',
language: "en"
});
popcorn.footnote({
start: 5, // seconds
end: 40, // seconds
text: 'The song is "Flickermood", by Forss',
target: 'footnotediv'
})
.flickr({
start: 20, // seconds
end: 40, // seconds
tags: 'georgia',
numberofimages: '8',
target: 'flickrdiv'
})
.twitter({
start: 20, // seconds
end: 45, // seconds
title: 'Oil Spill',
src: '#oilspill',
target: 'twitterdiv'
})
.attribution({
start: 5, // seconds
end: 60, // seconds
nameofwork: "Flickermood",
nameofworkurl:"http://soundcloud.com/forss/flickermood",
copyrightholder:"Eric Wahlforss",
target: 'attribdiv'
})
.webpage({
id: "webpages-a",
start: 0, // seconds
end: 15, // seconds
src: 'http://webmademovies.org/',
target: 'webpagediv'
})
.googlefeed({
start: 0, // seconds
end: 15, // seconds
target: "feeddiv",
url: "http://zenit.senecac.on.ca/~chris.tyler/planet/rss20.xml",
title: "Planet Feed",
orientation: "Vertical"
})
.image({
start: 5, // seconds
end: 15, // seconds
href: 'http://www.drumbeat.org/',
src: 'https://www.drumbeat.org/media//images/drumbeat-logo-splash.png',
target: 'imagediv'
})
.video.registerPopcornWithPlayer( popcorn );
}, false);
</script>
</head>
<body>
<div id="media_1" ></div><br />
<div>
<button class="simple" id="btnPlay">Play</button>
<button class="simple" id="btnPause">Pause</button>
<button class="seek" id="btnSeek">Seek to 30</button>
<button class="volume" id="btnVolume">Toggle Volume</button>
<button class="volume" id="btnMute">Mute</button><br />
<div style="float: left;">
Current Time (s): <span id="media_currentTime"></span>
<br />Duration (s): <span id="media_duration"></span>
<br />Volume (0-1): <span id="media_volume"></span>
<br />Ready State: <span id="media_readyState">0</span>
</div>
<div style="position: absolute; left: 350px;">
<strong>Comment Area</strong>
<div id="commentDisplay"></div>
</div>
</div>
<div style="float:right; width: 100%;">
<h3>Description</h3>
<p>
This demo will showcase how a player for Popcorn.js may be powered by Soundcloud's Flash audio player. This is done by making the flash audio player masquerade as HTML 5 media element.<br />
Due to the Flash security model, this demo must be run from a web server<br />
<hr >
Custom controls have been developed and tied into their player for demo purposes.<br />
Clicking play/pause or seeking in either the Soundcloud player or via custom controls will cause the other to update.<br />
The Soundcloud audio can be specified in the HTML source by giving the Soundcloud song url to either the Popcorn.soundcloud constructor or as the div data-src attribute.<br/>
Specifying the source in the constructor will override any source specified on the container element.<br/>
Player width/height can be specified as container attributes (data-width, data-height), or as styles (width, height). Styles can be inline or CSS.<br/>
If no width or height are given, they default to '100%' and '81px' respectively.<br/>
Width/height precendence is in this order: constructor, container attributes, natural (manually given inline/CSS styles), default<br/>
</p>
<h4>Expected Events</h4>
<ul>
<li>Throughout the track, user comments from Soundcloud will appear below 'Comment Area'.</li>
<li>From 0 to 15 seconds, the site 'http://webmademovies.org/' will appear below 'Web Page Area'.</li>
<li>From 0 to 20 seconds, blogs from 'http://zenit.senecac.on.ca/~chris.tyler/planet/rss20.xml' will appear below 'Google Feed Area'.</li>
<li>From 5 to 15 seconds, the subtitle 'This is overlaid on top of the audio visualization. You can hightlight it!' will be overlaid on the Soundcloud player.</li>
<li>From 5 to 15 seconds, the Mozilla Drumbeat logo will appear below 'Image Area'.</li>
<li>From 5 to 40 seconds, the footnote 'The song is "Flickermood", by Forss' will appear below 'Footnote Area'.</li>
<li>From 5 to 60 seconds, the 'Flickermood, Eric Wahlforss' will appear below 'Attributions Area'.</li>
<li>From 20 to 40 seconds, the a flickr stream of 8 images tagged 'georgia' will appear below 'Flickr Area'.</li>
<li>From 20 to 45 seconds, tweets tagged #oilspill will appear below 'Twitter Area'.</li>
</ul>
<br />
<br />
<br />
<div id="footnotediv" width="50%" height="50%">
<strong>Footnote Area</strong><br />
</div>
<div id="attribdiv" width="50%" height="50%">
<strong>Attributions Area</strong><br />
</div>
<div id="flickrdiv" width="50%" height="50%">
<strong>Flickr Area</strong><br />
</div>
<div id="twitterdiv" width="50%" height="50%">
<strong>Twitter Area</strong><br />
</div>
<div id="imagediv" width="50%" height="50%">
<strong>Image Area</strong><br />
</div>
<div id="feeddiv" width="50%" height="50%">
<strong>Google Feed Area</strong><br />
</div>
<div id="webpagediv" width="100px" height="50px">
<strong>Web Page Area</strong><br />
</div>
</div>
<br />
<br />
</body>
</html>

Просмотреть файл

@ -0,0 +1,693 @@
// Popcorn Soundcloud Player Wrapper
( function( Popcorn, global ) {
/**
* Soundcloud wrapper for Popcorn.
* This player adds enables Popcorn.js to handle Soundcloud audio. It does so by masking an embedded Soundcloud Flash object
* as a video and implementing the HTML5 Media Element interface.
*
* You can configure the video source and dimensions in two ways:
* 1. Use the embed code path supplied by Soundcloud the id of the desired location into a new Popcorn.soundcloud object.
* Width and height can be configured throughh CSS.
*
* <div id="player_1" style="width: 500px; height: 81px"></div>
* <script type="text/javascript">
* document.addEventListener("DOMContentLoaded", function() {
* var popcorn = Popcorn( Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" ));
* }, false);
* </script>
*
* 2. Width and height may also be configured directly with the player; this will override any CSS. This is useful for
* when different sizes are desired. for multiple players within the same parent container.
*
* <div id="player_1"></div>
* <script type="text/javascript">
* document.addEventListener("DOMContentLoaded", function() {
* var popcorn = Popcorn( Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood", {
* width: "500", // Optional, will default to CSS values
* height: "81" // Optional, will default to CSS values
* }));
* }, false);
* </script>
*
* The player can be further configured to integrate with the SoundCloud API:
*
* var popcorn = Popcorn( Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood", {
* width: "100%", // Optional, the width for the player. May also be as '##px'
* // Defaults to the maximum possible width
* height: "81px", // Optional, the height for the player. May also be as '###%'
* // Defaults to 81px
* api: { // Optional, information for Soundcloud API interaction
* key: "abcdefsdfsdf", // Required for API interaction. The Soundcloud API key
* commentdiv: "divId_for_output", // Required for comment retrieval, the Div Id for outputting comments.
* commentformat: function( comment ) {} // Optional, a function to format a comment. Returns HTML string
* }
* }));
*
* Comments are retrieved from Soundcloud when the player is registered with Popcorn by calling the registerWithPopcorn()
* function. For this to work, the api_key and commentdiv attributes must be set. Comments are output by default similar to
* how Soundcloud formats them in-player, but a custom formatting function may be supplied. It receives a comment object and
* the current date. A comment object has:
*
* var comment = {
* start: 0, // Required. Start time in ms.
* date: new Date(), // Required. Date comment wasa posted.
* text: "", // Required. Comment text
* user: { // Required. Describes the user who posted the comment
* name: "", // Required. User name
* profile: "", // Required. User profile link
* avatar: "" // Required. User avatar link
* }
* }
*
* These events are completely custom-implemented and may be subscribed to at any time:
* canplaythrough
* durationchange
* load
* loadedmetadata
* loadstart
* play
* readystatechange
* volumechange
*
* These events are related to player functionality and must be subscribed to during or after the load event:
* canplay
* ended
* error
* pause
* playing
* progress
* seeked
* timeupdate
*
* These events are not supported:
* abort
* emptied
* loadeddata
* ratechange
* seeking
* stalled
* suspend
* waiting
*
* Supported media attributes:
* autoplay ( via Popcorn )
* currentTime
* defaultPlaybackRate ( get only )
* duration ( get only )
* ended ( get only )
* initialTime ( get only, always 0 )
* loop ( get only, set by calling setLoop() )
* muted ( get only )
* paused ( get only )
* playbackRate ( get only )
* played ( get only, 0/1 only )
* readyState ( get only )
* src ( get only )
* volume
*
* load() function
* mute() function ( toggles on/off )
* play() function
* pause() function
*
* Unsupported media attributes:
* buffered
* networkState
* preload
* seekable
* seeking
* startOffsetTime
*
* canPlayType() function
*/
// Trackers
var timeupdateInterval = 33,
timeCheckInterval = 0.25,
abs = Math.abs,
floor = Math.floor,
round = Math.round,
registry = {};
function hasAllDependencies() {
return global.swfobject && global.soundcloud;
}
// Borrowed from: http://www.quirksmode.org/dom/getstyles.html
// Gets the style for the given element
function getStyle( elem, styleProp ) {
if ( elem.currentStyle ) {
// IE way
return elem.currentStyle[styleProp];
} else if ( global.getComputedStyle ) {
// Firefox, Chrome, et. al
return document.defaultView.getComputedStyle( elem, null ).getPropertyValue( styleProp );
}
}
function formatComment( comment ) {
// Calclate the difference between d and now, express as "n units ago"
function ago( d ) {
var diff = ( ( new Date() ).getTime() - d.getTime() )/1000;
function pluralize( value, unit ) {
return value + " " + unit + ( value > 1 ? "s" : "") + " ago";
}
if ( diff < 60 ) {
return pluralize( round( diff ), "second" );
}
diff /= 60;
if ( diff < 60 ) {
return pluralize( round( diff ), "minute" );
}
diff /= 60;
if ( diff < 24 ) {
return pluralize( round( diff ), "hour" );
}
diff /= 24;
// Rough approximation of months
if ( diff < 30 ) {
return pluralize( round( diff ), "day" );
}
if ( diff < 365 ) {
return pluralize( round( diff/30 ), "month" );
}
return pluralize( round( diff/365 ), "year" );
}
// Converts sec to min.sec
function timeToFraction ( totalSec ) {
var min = floor( totalSec / 60 ),
sec = round( totalSec % 60 );
return min + "." + ( sec < 10 ? "0" : "" ) + sec;
}
return '<div><a href="' + comment.user.profile + '">' +
'<img width="16px height="16px" src="' + comment.user.avatar + '"></img>' +
comment.user.name + '</a> at ' + timeToFraction( comment.start ) + ' ' +
ago( comment.date ) +
'<br />' + comment.text + '</span>';
}
function isReady( self ) {
if ( !hasAllDependencies() ) {
setTimeout( function() {
isReady( self );
}, 15 );
return;
}
var flashvars = {
enable_api: true,
object_id: self._playerId,
url: self.src,
// Hide comments in player if showing them elsewhere
show_comments: !self._options.api.key && !self._options.api.commentdiv
},
params = {
allowscriptaccess: "always",
// This is so we can overlay html ontop of Flash
wmode: 'transparent'
},
attributes = {
id: self._playerId,
name: self._playerId
},
actualTarget = document.createElement( 'div' );
actualTarget.setAttribute( "id", self._playerId );
self._container.appendChild( actualTarget );
swfobject.embedSWF( "http://player.soundcloud.com/player.swf", self._playerId, self.offsetWidth, self.height, "9.0.0", "expressInstall.swf", flashvars, params, attributes );
}
Popcorn.getScript( "http://ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js" );
// Source file originally from 'https://github.com/soundcloud/Widget-JS-API/raw/master/soundcloud.player.api.js'
Popcorn.getScript( "lib/soundcloud.player.api.js", function() {
// Play event is fired twice when player is first started. Ignore second one
var ignorePlayEvt = 1;
// Register the wrapper's load event with the player
soundcloud.addEventListener( 'onPlayerReady', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.swfObj = object;
wrapper.duration = object.api_getTrackDuration();
wrapper.currentTime = object.api_getTrackPosition();
// This eliminates volumechangee event from firing on load
wrapper.volume = wrapper.previousVolume = object.api_getVolume()/100;
// The numeric id of the track for use with Soundcloud API
wrapper._mediaId = data.mediaId;
wrapper.dispatchEvent( 'load' );
wrapper.dispatchEvent( 'canplay' );
wrapper.dispatchEvent( 'durationchange' );
wrapper.timeupdate();
});
// Register events for when the flash player plays a track for the first time
soundcloud.addEventListener( 'onMediaStart', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.played = 1;
wrapper.dispatchEvent( 'playing' );
});
// Register events for when the flash player plays a track
soundcloud.addEventListener( 'onMediaPlay', function( object, data ) {
if ( ignorePlayEvt ) {
ignorePlayEvt = 0;
return;
}
var wrapper = registry[object.api_getFlashId()];
wrapper.dispatchEvent( 'play' );
});
// Register events for when the flash player pauses a track
soundcloud.addEventListener( 'onMediaPause', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.dispatchEvent( 'pause' );
});
// Register events for when the flash player is buffering
soundcloud.addEventListener( 'onMediaBuffering', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.dispatchEvent( 'progress' );
if ( wrapper.readyState === 0 ) {
wrapper.readyState = 3;
wrapper.dispatchEvent( "readystatechange" );
}
});
// Register events for when the flash player is done buffering
soundcloud.addEventListener( 'onMediaDoneBuffering', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.dispatchEvent( 'canplaythrough' );
});
// Register events for when the flash player has finished playing
soundcloud.addEventListener( 'onMediaEnd', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.paused = 1;
//wrapper.pause();
wrapper.dispatchEvent( 'ended' );
});
// Register events for when the flash player has seeked
soundcloud.addEventListener( 'onMediaSeek', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.setCurrentTime( object.api_getTrackPosition() );
if ( wrapper.paused ) {
wrapper.dispatchEvent( "timeupdate" );
}
});
// Register events for when the flash player has errored
soundcloud.addEventListener( 'onPlayerError', function( object, data ) {
var wrapper = registry[object.api_getFlashId()];
wrapper.dispatchEvent( 'error' );
});
});
Popcorn.soundcloud = function( containerId, src, options ) {
return new Popcorn.soundcloud.init( containerId, src, options );
};
// A constructor, but we need to wrap it to allow for "static" functions
Popcorn.soundcloud.init = (function() {
function pullFromContainer( that ) {
var options = that._options,
container = that._container,
bounds = container.getBoundingClientRect(),
tmp,
undef;
that.width = options.width || getStyle( container, "width" ) || "100%";
that.height = options.height || getStyle( container, "height" ) || "81px";
that.src = options.src;
that.autoplay = options.autoplay;
if ( parseFloat( that.height, 10 ) !== 81 ) {
that.height = "81px";
}
that.offsetLeft = bounds.left;
that.offsetTop = bounds.top;
that.offsetHeight = parseFloat( that.height, 10 );
that.offsetWidth = parseFloat( that.width, 10 );
// Width and height may've been specified as a %, find the value now in case a plugin needs it (like subtitle)
if ( /[\d]+%/.test( that.width ) ) {
tmp = getStyle( container, "width" );
that._container.style.width = that.width;
that.offsetWidth = that._container.offsetWidth;
that._container.style.width = tmp;
}
if ( /[\d]+%/.test( that.height ) ) {
tmp = getStyle( container, "height" );
that._container.style.height = that.height;
that.offsetHeight = that._container.offsetHeight;
that._container.style.height = tmp;
}
}
// If container id is not supplied, assumed to be same as player id
var ctor = function ( containerId, src, options ) {
if ( !containerId ) {
throw "Must supply an id!";
} else if ( !src ) {
throw "Must supply a source!";
} else if ( /file/.test( location.protocol ) ) {
throw "Must run from a web server!";
}
var container = this._container = document.getElementById( containerId );
if ( !container ) {
throw "Could not find that container in the DOM!";
}
options = options || {};
options.api = options.api || {};
options.target = containerId;
options.src = src;
options.api.commentformat = options.api.commentformat || formatComment;
this._mediaId = 0;
this._listeners = {};
this._playerId = Popcorn.guid( options.target );
this._containerId = options.target;
this._options = options;
this._comments = [];
this._popcorn;
pullFromContainer( this );
this.duration = 0;
this.volume = 1;
this.currentTime = 0;
this.ended = 0;
this.paused = 1;
this.readyState = 0;
this.playbackRate = 1;
this.top = 0;
this.left = 0;
this.autoplay;
this.played = 0;
this.addEventListener( "load", function() {
var boundRect = this.getBoundingClientRect();
this.top = boundRect.top;
this.left = boundRect.left;
this.offsetWidth = this.swfObj.offsetWidth;
this.offsetHeight = this.swfObj.offsetHeight;
this.offsetLeft = this.swfObj.offsetLeft;
this.offsetTop = this.swfObj.offsetTop;
});
registry[ this._playerId ] = this;
isReady( this );
};
return ctor;
})();
Popcorn.soundcloud.init.prototype = Popcorn.soundcloud.prototype;
// Sequence object prototype
Popcorn.extend( Popcorn.soundcloud.prototype, {
// Set the volume as a value between 0 and 1
setVolume: function( val ) {
if ( !val && val !== 0 ) {
return;
}
// Normalize in case outside range of expected values of 0 .. 1
if ( val < 0 ) {
val = -val;
}
if ( val > 1 ) {
val %= 1;
}
// HTML video expects to be 0.0 -> 1.0, Flash object expects 0-100
this.volume = this.previousVolume = val;
this.swfObj.api_setVolume( val*100 );
this.dispatchEvent( "volumechange" );
},
// Seeks the video
setCurrentTime: function ( time ) {
if ( !time && time !== 0 ) {
return;
}
this.currentTime = this.previousCurrentTime = time;
this.ended = time >= this.duration;
// Fire events for seeking and time change
this.dispatchEvent( "seeked" );
},
// Play the video
play: function() {
// In case someone is cheeky enough to try this before loaded
if ( !this.swfObj ) {
this.addEventListener( "load", this.play );
return;
} else if ( !this.paused ) {
// No need to process if already playing
return;
}
this.paused = 0;
this.swfObj.api_play();
},
// Pause the video
pause: function() {
// In case someone is cheeky enough to try this before loaded
if ( !this.swfObj ) {
this.addEventListener( "load", this.pause );
return;
} else if ( this.paused ) {
// No need to process if already playing
return;
}
this.paused = 1;
this.swfObj.api_pause();
},
// Toggle video muting
// Unmuting will leave it at the old value
mute: function() {
// In case someone is cheeky enough to try this before loaded
if ( !this.swfObj ) {
this.addEventListener( "load", this.mute );
return;
}
if ( !this.muted() ) {
this.oldVol = this.volume;
if ( this.paused ) {
this.setVolume( 0 );
} else {
this.volume = 0;
}
} else {
if ( this.paused ) {
this.setVolume( this.oldVol );
} else {
this.volume = this.oldVol;
}
}
},
muted: function() {
return this.volume === 0;
},
// Force loading by playing the player. Pause afterwards
load: function() {
// In case someone is cheeky enough to try this before loaded
if ( !this.swfObj ) {
this.addEventListener( "load", this.load );
return;
}
this.play();
this.pause();
},
// Hook an event listener for the player event into internal event system
// Stick to HTML conventions of add event listener and keep lowercase, without prepending "on"
addEventListener: function( evt, fn ) {
if ( !this._listeners[evt] ) {
this._listeners[evt] = [];
}
this._listeners[evt].push( fn );
return fn;
},
dispatchEvent: function( evt ) {
var self = this,
evtName = evt.type || evt;
// Manually triggered a UI event, have it invoke rather than just the event handlers
if ( evtName === "play" && this.paused || evtName === "pause" && !this.paused ) {
this[evtName]();
return;
}
Popcorn.forEach( this._listeners[evtName], function( fn ) {
fn.call( self );
});
},
timeupdate: function() {
var self = this,
checkedVolume = this.swfObj.api_getVolume()/100,
seeked = 0;
// If has been changed through setting currentTime attribute
if ( abs( this.currentTime - this.previousCurrentTime ) > timeCheckInterval ) {
// Has programatically set the currentTime
this.swfObj.api_seekTo( this.currentTime );
seeked = 1;
} else {
this.previousCurrentTime = this.currentTime = this.swfObj.api_getTrackPosition();
}
// If has been changed throughh volume attribute
if ( checkedVolume !== this.previousVolume ) {
this.setVolume( checkedVolume );
} else if ( this.volume !== this.previousVolume ) {
this.setVolume( this.volume );
}
if ( !this.paused ) {
this.dispatchEvent( 'timeupdate' );
}
if( !self.ended ) {
setTimeout( function() {
self.timeupdate.call( self );
}, timeupdateInterval);
}
},
getBoundingClientRect: function() {
var b,
self = this;
if ( this.swfObj ) {
b = this.swfObj.getBoundingClientRect();
return {
bottom: b.bottom,
left: b.left,
right: b.right,
top: b.top,
// These not guaranteed to be in there
width: b.width || ( b.right - b.left ),
height: b.height || ( b.bottom - b.top )
};
} else {
//container = document.getElementById( this.playerId );
tmp = this._container.getBoundingClientRect();
// Update bottom, right for expected values once the container loads
return {
left: tmp.left,
top: tmp.top,
width: self.offsetWidth,
height: self.offsetHeight,
bottom: tmp.top + this.width,
right: tmp.top + this.height
};
}
},
registerPopcornWithPlayer: function( popcorn ) {
if ( !this.swfObj ) {
this.addEventListener( "load", function() {
this.registerPopcornWithPlayer( popcorn );
});
return;
}
this._popcorn = popcorn;
var api = this._options.api;
if ( api.key && api.commentdiv ) {
var self = this;
Popcorn.xhr({
url: "http://api.soundcloud.com/tracks/" + self._mediaId + "/comments.js?consumer_key=" + api.key,
success: function( data ) {
Popcorn.forEach( data.json, function ( obj ) {
self.addComment({
start: obj.timestamp/1000,
date: new Date( obj.created_at ),
text: obj.body,
user: {
name: obj.user.username,
profile: obj.user.permalink_url,
avatar: obj.user.avatar_url
}
});
});
}
});
}
}
});
Popcorn.extend( Popcorn.soundcloud.prototype, {
addComment: function( obj, displayFn ) {
var self = this,
comment = {
start: obj.start || 0,
date: obj.date || new Date(),
text: obj.text || "",
user: {
name: obj.user.name || "",
profile: obj.user.profile || "",
avatar: obj.user.avatar || ""
},
display: function() {
return ( displayFn || self._options.api.commentformat )( comment );
}
};
this._comments.push( comment );
if ( !this._popcorn ) {
return;
}
this._popcorn.subtitle({
start: comment.start,
target: this._options.api.commentdiv,
display: 'inline',
language: 'en',
text: comment.display()
});
}
});
})( Popcorn, window );

Просмотреть файл

@ -0,0 +1,44 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn Soundcloud Player</title>
<link rel="stylesheet" href="../../test/qunit/qunit.css" type="text/css" media="screen">
<script src="../../test/qunit/qunit.js"></script>
<!--
do not move - this must be called immediately prior to
popcorn-api-draft.js
-->
<script src="../../popcorn.js"></script>
<script src="popcorn.soundcloud.unit.js"></script>
<script src="popcorn.soundcloud.js"></script>
<style>
.soundcloudPlayer {
left: 20px;
}
#player_1 {
width: 80%;
}
#player_2 {
width: 70%;
}
</style>
</head>
<body>
<h1 id="qunit-header">Popcorn Soundcloud Player</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"> </div>
<div id="commentOutput" />
<div class="soundcloudPlayer" id="player_1" ></div>
<div class="soundcloudPlayer" id="player_2" ></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,402 @@
module( "Popcorn Soundcloud Player" );
test( "API", function () {
var expects = 0,
count = 0,
player = Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" ),
members = {
// HTMLMediaElement members
'readyState' : 'number',
'currentTime' : 'number',
'duration' : 'number',
'paused' : 'number',
'ended' : 'number',
'volume' : 'number',
'muted' : 'function',
'playbackRate' : 'number',
'autoplay' : 'undefined',
'loop' : 'undefined',
'load' : 'function',
'play' : 'function',
'pause' : 'function',
'timeupdate' : 'function',
'src' : 'string',
// DOMElement members
'addEventListener' : 'function',
'dispatchEvent' : 'function',
'getBoundingClientRect' : 'function',
'width' : 'string',
'height' : 'string',
'top' : 'number',
'left' : 'number',
'offsetLeft' : 'number',
'offsetTop' : 'number',
'offsetHeight' : 'number',
'offsetWidth' : 'number',
// Helper functions and members
'setVolume' : 'function',
'setCurrentTime' : 'function',
'timeupdate' : 'function',
'registerPopcornWithPlayer' : 'function'
};
function plus() {
if ( ++count === expects ) {
start();
}
}
Popcorn.forEach( members, function () {
expects++;
});
expect( expects );
stop( 10000 );
Popcorn.forEach( members, function ( type, prop ) {
ok( typeof player[prop] === type, "player." + prop + " is type: '" + player[prop] + "', should be '" + type + "'" );
plus();
});
});
test( "Default Attribute Functionality", function () {
var expects = 5,
count = 0,
playerDefault,
playerOverride,
members = {
// HTMLMediaElement members
'currentTime' : 0,
'readyState' : 0,
'duration' : 0,
'volume' : 1,
'paused' : 1,
'ended' : 0,
'muted' : false,
'playbackRate' : 1,
'src' : 'http://soundcloud.com/forss/flickermood',
// DOMElement members
'height' : '81px',
'top' : 0,
'left' : 0,
'offsetHeight' : 81,
};
function plus() {
if ( ++count === expects ) {
start();
cleanup();
}
}
Popcorn.forEach( members, function () {
expects++;
});
expect( expects );
stop( 10000 );
playerDefault = Popcorn.soundcloud( "player_2", "http://soundcloud.com/forss/flickermood" );
playerOverride = Popcorn.soundcloud( "player_2", "http://soundcloud.com/forss/journeyman", {
height: "100px",
width: '90%'
});
playerDefault.addEventListener( "load", function() {
equals( playerDefault.duration, 213.89, "Duration updated" );
plus();
});
Popcorn.forEach( members, function ( val, prop ) {
var actual = playerDefault[prop];
if ( typeof playerDefault[prop] === 'function' ) {
actual = playerDefault[prop]();
}
equals( actual, val, "player." + prop + " should have default value: '" + val + "'" );
plus();
});
equals( document.getElementById( "player_2" ).children.length, 2, "The container has 2 players" );
plus();
equals( playerDefault.width, playerDefault.offsetWidth+"px", "Width is stringified version of offsetWidth" );
plus();
equals( playerOverride.width, "90%", "Width has been overridden" );
plus();
equals( playerOverride.height, "81px", "Height has been overridden to 100px, but set back again to 81px" );
plus();
});
test( "Player Volume Control", function () {
var expects = 3,
count = 0,
player = Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" ),
targetVolume,
startVolume;
function plus() {
if ( ++count === expects ) {
start();
}
}
expect(expects);
stop( 50000 );
player.addEventListener( "load", function() {
// VolumeChange is fired shortly after load when the volume is retrieved from the player
// Defer volume tests until after that has run
player.addEventListener( "volumechange", function() {
if ( count >= expects ) {
return;
}
equals( player.volume, targetVolume, "Volume change set correctly" );
plus();
if ( targetVolume !== 0 ) {
targetVolume = 0;
player.mute();
} else {
targetVolume = startVolume;
// Unmute
player.mute();
}
});
player.volume = targetVolume = startVolume = ( player.volume === 1 ? 0.5 : 1 );
});
});
test( "Testing Comments", function() {
var expects = 0,
count = 0,
cmtDate = new Date(),
comment,
players = {
player1: Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" ),
player2: Popcorn.soundcloud( "player_2", "http://soundcloud.com/forss/flickermood", {
api: {
commentdiv: "commentOutput",
commentformat: function( comment ) {
return comment.text
}
}
}),
player3: Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" )
}
// Expecteed comment output
commentOutput = {
player1: function() {
return '<div><a href="Hyperlink">'
+ '<img width="16px height="16px" src="Image"></img>'
+ 'User 1</a> at 0.03 1 hour ago'
+ '<br />Hi</span>';
},
player2: function() {
return "Hi";
},
player3: function() {
return "User 1 @ 3: Hi";
}
};
function plus() {
if ( ++count === expects ) {
start();
}
}
// Set comment date to 1 hour ago
cmtDate.setTime( cmtDate.getTime() - 3600000 );
comment = {
start: 3,
date: cmtDate,
text: "Hi",
user: {
name: "User 1",
profile: "Hyperlink",
avatar: "Image"
}
};
players["player1"].addComment( comment );
players["player2"].addComment( comment );
players["player3"].addComment( comment, function( comment ) {
return comment.user.name + " @ " + comment.start + ": "+ comment.text;
});
Popcorn.forEach( players, function () {
// 1 comment per player
expects++;
});
expect( expects );
stop( 5000 );
Popcorn.forEach( players, function ( player, name ) {
equals( player._comments[0].display(), commentOutput[name](), name + " formatted as expected" );
plus();
});
});
test( "Popcorn Integration", function () {
var expects = 4,
count = 0,
player = Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" );
function plus() {
if ( ++count === expects ) {
start();
}
}
expect(expects);
stop( 20000 );
player.addEventListener( "load", function() {
ok( true, "Listen works (load event)" );
plus();
player.addEventListener( "play", function() {
ok( true, "Play triggered by popcorn.trigger" );
plus();
player.pause();
});
player.addEventListener( "pause", function() {
ok( true, "Pause explicitly called" );
plus();
player.volume = ( player.volume === 1 ? 0.5 : 1 );
});
player.addEventListener( "volumechange", function() {
ok( true, "Volume changed explicitly called" );
plus();
});
player.play();
});
});
test( "Events and Player Control", function () {
var expects = 14,
count = 0,
player = Popcorn.soundcloud( "player_1", "http://soundcloud.com/forss/flickermood" ),
targetVolume;
function plus() {
if ( ++count === expects ) {
start();
}
}
expect(expects);
stop( 300000 );
player.addEventListener( "load", function() {
ok( true, "Load was fired" );
plus();
});
player.addEventListener( "playing", function() {
ok( true, "Playing was fired" );
plus();
equals( player.paused, 0, "Paused is unset" );
plus();
});
player.addEventListener( "play", ( function() {
var hasFired = 0;
return function() {
if ( hasFired ) {
return;
}
hasFired = 1;
ok( true, "Play was fired" );
plus();
}
})());
player.addEventListener( "durationchange", function() {
ok( true, "DurationChange was fired" );
plus();
});
player.addEventListener( "readystatechange", function() {
ok( true, "ReadyStateChange was fired" );
plus();
equals( player.readyState, 3, "Ready State is now 3" );
plus();
player.pause();
});
player.addEventListener( "pause", function() {
ok( true, "Pause was fired by dispatch" );
plus();
equals( player.paused, 1, "Paused is set" );
plus();
});
player.addEventListener( "timeupdate", ( function() {
var hasFired = 0;
return function() {
if ( hasFired ) {
return;
}
hasFired = 1;
ok( true, "Timeupdate was fired by dispatch" );
plus();
}
})());
player.addEventListener( "volumechange", function() {
ok( true, "volumechange was fired by dispatch" );
plus();
});
player.addEventListener( "canplaythrough", function() {
ok( true, "Can play through" );
plus();
// Will trigger a "seeked" event to near end
player.currentTime = player.duration - 1;
});
player.addEventListener( "seeked", function() {
ok( true, "Seeked was fired" );
plus();
player.dispatchEvent( "play" );
});
player.addEventListener( "ended", function() {
ok( true, "Media is done playing" );
plus();
equals( player.paused, 1, "Paused is set on end" );
plus();
});
player.play();
});

Просмотреть файл

@ -22,7 +22,6 @@
}
</style>
<script src="../../popcorn.js"></script>
<script type="text/javascript" src="popcorn.vimeo.js"></script>
<script src="../../plugins/footnote/popcorn.footnote.js"></script>
@ -102,7 +101,10 @@
//On document ready
document.addEventListener( "DOMContentLoaded", function() {
var player = Popcorn( Popcorn.vimeo( "player_1" ) )
var player = Popcorn( Popcorn.vimeo( "player_1", "http://player.vimeo.com/video/11127501", {
width: 500,
height: 281
}) )
.footnote({
start: 5, // seconds
end: 40, // seconds
@ -178,7 +180,7 @@
</head>
<body>
<div>
<div id="player_1" width="500" height="281" src="http://player.vimeo.com/video/11127501" ></div><br />
<div id="player_1" ></div><br />
<button class="simple" id="btnPlay">Play</button>
<button class="simple" id="btnPause">Pause</button>
<button class="seek" id="btnSeek">Seek to 30</button>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -23,6 +23,6 @@
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"> </div>
<div id="player_1" width="500" height="281"></div>
<div id="player_1"></div>
</body>
</html>

Просмотреть файл

@ -22,7 +22,7 @@
var paused = true,
popcorn;
popcorn = Popcorn( Popcorn.youtube( 'video', 'http://www.youtube.com/watch?v=9oar9glUCL0' ) );
popcorn = Popcorn( Popcorn.youtube( 'video', 'http://www.youtube.com/watch?v=9oar9glUCL0', { width: 400 } ) );
popcorn = popcorn
.footnote({
@ -140,7 +140,7 @@
<body>
<div>
<div>
<div id="video" width="360" height="300" ></div><br />
<div id="video" style="width: 360px; height: 300px;" ></div><br />
<button class="simple" id="btn-play-pause">Play</button>
<button class="seek" id="btn-seek">Seek to 30</button>
<button class="volume" id="btn-volume">Toggle Volume</button>

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -28,8 +28,8 @@
Therefore, if failures occur for the first time, just run it again. Likely,
failures would disappear the second time.
</p>
<div id="video" width="360" height="300" src="http://www.youtube.com/e/ac7KhViaVqc" ></div>
<div id="video2" width="360" height="300" src="http://www.youtube.com/e/ac7KhViaVqc" ></div>
<div id="video" style="width: 360px; height: 300px" ></div>
<div id="video2" style="width: 360px; height: 300px" ></div>
</body>
</html>

Просмотреть файл

@ -1,41 +1,6 @@
var ytReady = false,
popcorn = Popcorn( Popcorn.youtube( 'video' ) );
popcorn.listen( "load", function onYouTubePlayerReady() {
ytReady = true;
});
test( "Popcorn YouTube Plugin Startup", function() {
var time = 0,
wait = 100,
timeout = 10000;
// wait for YouTube to start
stop( timeout + wait );
// run in an interval check if YouTube has started
var interval = setInterval(function() {
time += wait;
if ( ytReady ) {
start();
ok( true, "YouTube has started." );
clearInterval( interval );
return;
}
if ( time > timeout ) {
ok( false, "YouTube cannot be started." );
clearInterval( interval );
}
}, wait);
});
test( "Popcorn YouTube Plugin Event Tests", function() {
if ( !ytReady ) {
ok( false, "YouTube did not start." );
return;
}
var popcorn = Popcorn( Popcorn.youtube( 'video', "http://www.youtube.com/e/ac7KhViaVqc" ) );
function plus(){
if ( ++count == expects ) {
@ -219,4 +184,4 @@ test( "Popcorn YouTube Plugin Url and Duration Tests", function() {
});
popcorn.play();
});
});

Просмотреть файл

@ -2,13 +2,24 @@
<html>
<head>
<title>Popcorn Flickr Plug-in Demo</title>
<!--
ATTENTION
This demo uses an API key obtained for testing the Flickr Popcorn.js
plugin. Please do not use it for other purposes.
-->
<script src="../../popcorn.js"></script>
<script src="../../test/jquery.js"></script>
<script src="popcorn.flickr.js"></script>
<script>
document.addEventListener('DOMContentLoaded', function () {
var p = Popcorn('#video')
.flickr({
start: 0, // seconds
end: 5, // seconds
username: 'AniaSob',
apikey: 'd1d249260dd1673ec8810c8ce5150ae1',
target: 'flickrdiv'
} )
.flickr({
start: 5, // seconds
end: 15, // seconds
@ -37,8 +48,8 @@
</head>
<body>
<h1 id="qunit-header">Popcorn Flickr Plug-in Demo</h1>
<p>This plugin requires jquery</p>
<p>Flickr images by '35034346917@N01' will appear at 5 seconds and disappear at 15 seconds.
<p>Flickr images by 'AniaSob' will appear at 0 seconds and disappear at 5 seconds.
<br />Flickr images by '35034346917@N01' will appear at 5 seconds and disappear at 15 seconds.
<br />Flickr images tagged 'georgia' will appear at 20 seconds and disappear at 30 seconds.
<br />Flickr images by '35034346917@N01' and tagged 'georgia' will appear at 35 seconds and disappear at 45 seconds.</p>
<div>

Просмотреть файл

@ -1,20 +1,24 @@
// PLUGIN: FLICKR
// PLUGIN: Flickr
(function (Popcorn) {
/**
* Flickr popcorn plug-in
* Appends a users Flickr images to an element on the page.
* Options parameter will need a start, end, target and userid.
* Options parameter will need a start, end, target and userid or username and api_key.
* Optional parameters are numberofimages, height, width, padding, and border
* 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
* Userid is the id of who's Flickr images you wish to show
* Tags is a mutually exclusive list of image descriptor tags
* Username is the username of who's Flickr images you wish to show
* using both userid and username is redundant
* an api_key is required when using username
* Apikey is your own api key provided by Flickr
* Target is the id of the document element that the images are
* appended to, this target element must exist on the DOM
* Numberofimages specify the number of images to retreive from flickr, defaults to 8
* Height the height of the component, defaults to '50px'
* Width the width of the component, defaults to '50px'
* Numberofimages specify the number of images to retreive from flickr, defaults to 4
* Height the height of the image, defaults to '50px'
* Width the width of the image, defaults to '50px'
* Padding number of pixels between images, defaults to '5px'
* Border border size in pixels around images, defaults to '0px'
*
@ -36,96 +40,128 @@
} )
*
*/
Popcorn.plugin( "flickr" , {
Popcorn.plugin( "flickr" , function( options ) {
var containerDiv,
_userid,
_uri,
_link,
_image,
_count = options.numberofimages || 4 ,
_height = options.height || "50px",
_width = options.width || "50px",
_padding = options.padding || "5px",
_border = options.border || "0px",
i;
manifest: {
about:{
name: "Popcorn Flickr Plugin",
version: "0.1.1",
author: "Scott Downe, Steven Weerdenburg",
website: "http://scottdowne.wordpress.com/"
},
options:{
start : {elem:'input', type:'number', label:'In'},
end : {elem:'input', type:'number', label:'Out'},
userid : {elem:'input', type:'text', label:'Source'},
tags : {elem:'input', type:'text', label:'Tags'},
target : 'Flickr-container',
height : {elem:'input', type:'text', label:'Height'},
width : {elem:'input', type:'text', label:'Width'},
padding : {elem:'input', type:'text', label:'Padding'},
border : {elem:'input', type:'text', label:'Border'},
numberofimages : {elem:'input', type:'text', label:'Number of Images'}
}
},
_setup: function( options ) {
options.container = document.createElement( 'div' );
options.container.style.display = "none";
if ( document.getElementById( options.target ) ) {
document.getElementById( options.target ).appendChild( options.container );
}
var height = options.height || "50px",
width = options.width || "50px",
count = options.numberofimages || 4,
padding = options.padding || "5px",
tags = options.tags || "",
userid = options.userid || "",
border = options.border || "0px",
uri = "http://api.flickr.com/services/feeds/photos_public.gne?";
if ( userid ) {
uri += "id="+userid+"&";
}
if ( tags ) {
uri += "tags="+tags+"&";
}
uri += "lang=en-us&format=json&jsoncallback=flickr";
Popcorn.xhr.getJSONP( uri, function( data ) {
options.container.innerHTML = "<p style='padding:" + padding + ";'>" + data.title + "<p/>";
Popcorn.forEach( data.items, function ( item, i ) {
if ( i < count ) {
var link = document.createElement('a');
link.setAttribute( 'href', item.link );
link.setAttribute( "target", "_blank" );
var image = document.createElement( 'img' );
image.setAttribute( 'src', item.media.m );
image.setAttribute( 'height', height );
image.setAttribute( 'width', width );
image.setAttribute( 'style', 'border:' + border + ';padding:' + padding );
link.appendChild( image );
options.container.appendChild( link );
} else {
return false;
}
});
// create a new div this way anything in the target div is left intact
// this is later populated with Flickr images
containerDiv = document.createElement( "div" );
containerDiv.id = "flickr"+ i;
containerDiv.style.width = "100%";
containerDiv.style.height = "100%";
containerDiv.style.display = "none";
i++;
// ensure the target container the user chose exists
if ( document.getElementById( options.target ) ) {
document.getElementById( options.target ).appendChild( containerDiv );
} else {
throw ( "flickr target container doesn't exist" );
}
// get the userid from Flickr API by using the username and apikey
var isUserIDReady = function() {
if ( !_userid ) {
_uri = "http://api.flickr.com/services/rest/?method=flickr.people.findByUsername&";
_uri += "username=" + options.username + "&api_key=" + options.apikey + "&format=json&jsoncallback=flickr";
Popcorn.xhr.getJSONP( _uri, function(data) {
_userid = data.user.nsid;
getFlickrData();
});
},
/**
* @member Flickr
* The start function will be executed when the currentTime
* of the video reaches the start time provided by the
* options variable
*/
start: function( event, options ) {
options.container.style.display = "inline";
},
/**
* @member Flickr
* The end function will be executed when the currentTime
* of the video reaches the end time provided by the
* options variable
*/
end: function( event, options ) {
options.container.style.display = "none";
} else {
setTimeout(function () {
isUserIDReady();
}, 5);
}
});
};
// get the photos from Flickr API by using the user_id and/or tags
var getFlickrData = function() {
_uri = "http://api.flickr.com/services/feeds/photos_public.gne?";
_uri += "id=" + _userid + "&";
if ( options.tags ) {
_uri += "tags=" + options.tags + "&";
}
_uri += "lang=en-us&format=json&jsoncallback=flickr";
Popcorn.xhr.getJSONP( _uri, function( data ) {
containerDiv.innerHTML = "<p style='padding:" + _padding + ";'>" + data.title + "<p/>";
Popcorn.forEach( data.items, function ( item, i ) {
if ( i < _count ) {
_link = document.createElement( 'a' );
_link.setAttribute( 'href', item.link );
_link.setAttribute( "target", "_blank" );
_image = document.createElement( 'img' );
_image.setAttribute( 'src', item.media.m );
_image.setAttribute( 'height',_height );
_image.setAttribute( 'width', _width );
_image.setAttribute( 'style', 'border:' + _border + ';padding:' + _padding );
_link.appendChild( _image );
containerDiv.appendChild( _link );
} else {
return false;
}
});
});
};
if ( options.userid ) {
_userid = options.userid;
getFlickrData();
} else if ( options.username && options.apikey ) {
isUserIDReady();
}
return {
/**
* @member flickr
* The start function will be executed when the currentTime
* of the video reaches the start time provided by the
* options variable
*/
start: function( event, options ){
containerDiv.style.display = "inline";
},
/**
* @member flickr
* The end function will be executed when the currentTime
* of the video reaches the end time provided by the
* options variable
*/
end: function( event, options ){
containerDiv.style.display = "none";
}
};
},
{
about:{
name: "Popcorn Flickr Plugin",
version: "0.2",
author: "Scott Downe, Steven Weerdenburg, Annasob",
website: "http://scottdowne.wordpress.com/"
},
options:{
start : {elem:'input', type:'number', label:'In'},
end : {elem:'input', type:'number', label:'Out'},
userid : {elem:'input', type:'text', label:'UserID'},
tags : {elem:'input', type:'text', label:'Tags'},
username : {elem:'input', type:'text', label:'Username'},
apikey : {elem:'input', type:'text', label:'Api_key'},
target : 'Flickr-container',
height : {elem:'input', type:'text', label:'Height'},
width : {elem:'input', type:'text', label:'Width'},
padding : {elem:'input', type:'text', label:'Padding'},
border : {elem:'input', type:'text', label:'Border'},
numberofimages : {elem:'input', type:'text', label:'Number of Images'}
}
});
})( Popcorn );

Просмотреть файл

@ -1,7 +1,7 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn API</title>
<title>Popcorn Flickr</title>
<link rel="stylesheet" href="../../test/qunit/qunit.css" type="text/css" media="screen">
<script src="../../test/qunit/qunit.js"></script>
<!--

Просмотреть файл

@ -1,7 +1,7 @@
test("Popcorn Flickr Plugin", function () {
var popped = Popcorn("#video"),
expects = 5,
expects = 7,
count = 0,
flickrdiv = document.getElementById('flickrdiv');
@ -27,7 +27,15 @@ test("Popcorn Flickr Plugin", function () {
userid: '35034346917@N01',
numberofimages: '1',
target: 'flickrdiv'
} );
} )
.flickr({
start: 4, // seconds
end: 7, // seconds
username: 'AniaSob',
apikey: 'd1d249260dd1673ec8810c8ce5150ae1',
numberofimages: '1',
target: 'flickrdiv'
} );;
popped.exec( 2, function() {
ok( /display: inline;/.test( flickrdiv.innerHTML ), "Div contents are displayed" );
@ -35,8 +43,15 @@ test("Popcorn Flickr Plugin", function () {
ok( /img/.test( flickrdiv.innerHTML ), "An image exists" );
plus();
});
popped.exec( 5, function() {
ok( /display: inline;/.test( flickrdiv.innerHTML ), "Div contents are displayed" );
plus();
ok( /img/.test( flickrdiv.innerHTML ), "An image exists" );
plus();
});
popped.exec( 4, function() {
popped.exec( 7, function() {
ok( /display: none;/.test( flickrdiv.innerHTML ), "Div contents are hidden again" );
plus();
});

Просмотреть файл

@ -2,8 +2,9 @@
(function (Popcorn) {
var i = 1,
scriptLoaded = false,
callBack = function( data ) {
scriptLoading = false,
scriptLoaded = false,
callBack = function( data ) {
if ( typeof google !== 'undefined' && google.load ) {
@ -17,17 +18,6 @@
}
};
Popcorn.getScript( "http://www.google.com/jsapi", callBack );
Popcorn.getScript( "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.js" );
//Doing this because I cannot find something similar to getScript() for css files
var head = document.getElementsByTagName("head")[0];
var css = document.createElement('link');
css.type = "text/css";
css.rel = "stylesheet";
css.href = "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.css";
head.insertBefore( css, head.firstChild );
/**
* googlefeed popcorn plug-in
* Adds a feed from the specified blog url at the target div
@ -81,6 +71,22 @@
}
};
if ( !scriptLoading ) {
scriptLoading = true;
Popcorn.getScript( "http://www.google.com/jsapi", callBack );
Popcorn.getScript( "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.js" );
//Doing this because I cannot find something similar to getScript() for css files
var head = document.getElementsByTagName("head")[0];
var css = document.createElement('link');
css.type = "text/css";
css.rel = "stylesheet";
css.href = "http://www.google.com/uds/solutions/dynamicfeed/gfdynamicfeedcontrol.css";
head.insertBefore( css, head.firstChild );
}
initialize();
return {

Просмотреть файл

@ -2,6 +2,37 @@
var googleCallback;
(function (Popcorn) {
var i = 1,
_mapFired = false,
_mapLoaded = false,
geocoder,
loadMaps;
//google api callback
googleCallback = function( data ) {
// ensure all of the maps functions needed are loaded
// before setting _maploaded to true
if ( typeof google !== "undefined" && google.maps && google.maps.Geocoder && google.maps.LatLng ) {
geocoder = new google.maps.Geocoder();
_mapLoaded = true;
} else {
setTimeout( function() {
googleCallback( data );
}, 1);
}
};
// function that loads the google api
loadMaps = function () {
// for some reason the Google Map API adds content to the body
if ( document.body ) {
_mapFired = true;
Popcorn.getScript( "http://maps.google.com/maps/api/js?sensor=false&callback=googleCallback" );
} else {
setTimeout( function() {
loadMaps( );
}, 1);
}
};
/**
* googlemap popcorn plug-in
* Adds a map to the target div centered on the location specified by the user
@ -31,67 +62,60 @@ var googleCallback;
} )
*
*/
var newdiv,
i = 1,
_mapFired = false,
_mapLoaded = false;
// callback function fires when the script is run
googleCallback = function() {
_mapLoaded = true;
};
// insert google api script once
if (!_mapFired) {
_mapFired = true;
Popcorn.getScript("http://maps.google.com/maps/api/js?sensor=false&callback=googleCallback");
}
Popcorn.plugin( "googlemap" , function( options ) {
var newdiv,
map,
location;
// create a new div this way anything in the target div
// this is later passed on to the maps api
newdiv = document.createElement("div");
newdiv.id = "actualmap" + i;
newdiv.style.width = "100%";
newdiv.style.height = "100%";
i++;
if (document.getElementById(options.target)) {
document.getElementById(options.target).appendChild(newdiv);
// if this is the firest time running the plugins
// call the function that gets the sctipt
if ( !_mapFired ) {
loadMaps();
}
// If there is no lat/lng, and there is location, geocode the location
// you can only do this once google.maps exists
var isGeoReady = function() {
if ( !_mapLoaded ) {
setTimeout(function () {
isGeoReady();
}, 5);
} else {
if (options.location) {
var geocoder = new google.maps.Geocoder();
// calls an anonymous function called on separate thread
geocoder.geocode({ "address": options.location}, function(results, status) {
if (status === google.maps.GeocoderStatus.OK) {
// create a new div this way anything in the target div is left intact
// this is later passed on to the maps api
newdiv = document.createElement( "div" );
newdiv.id = "actualmap" + i;
newdiv.style.width = "100%";
newdiv.style.height = "100%";
i++;
// ensure the target container the user chose exists
if ( document.getElementById( options.target ) ) {
document.getElementById(options.target).appendChild(newdiv);
} else {
throw ( "map target container doesn't exist" );
}
// ensure that google maps and its functions are loaded
// before setting up the map parameters
var isMapReady = function() {
if ( _mapLoaded ) {
if ( options.location ) {
// calls an anonymous google function called on separate thread
geocoder.geocode( { "address": options.location }, function( results, status ) {
if ( status === google.maps.GeocoderStatus.OK ) {
options.lat = results[0].geometry.location.lat();
options.lng = results[0].geometry.location.lng();
location = new google.maps.LatLng(options.lat, options.lng);
map = new google.maps.Map(newdiv, {mapTypeId: google.maps.MapTypeId[options.type] || google.maps.MapTypeId.HYBRID });
location = new google.maps.LatLng( options.lat, options.lng );
map = new google.maps.Map( newdiv, { mapTypeId: google.maps.MapTypeId[ options.type ] || google.maps.MapTypeId.HYBRID } );
map.getDiv().style.display = "none";
}
});
} );
} else {
location = new google.maps.LatLng(options.lat, options.lng);
map = new google.maps.Map(newdiv, {mapTypeId: google.maps.MapTypeId[options.type] || google.maps.MapTypeId.HYBRID });
location = new google.maps.LatLng( options.lat, options.lng );
map = new google.maps.Map( newdiv, { mapTypeId: google.maps.MapTypeId[ options.type ] || google.maps.MapTypeId.HYBRID } );
map.getDiv().style.display = "none";
}
}
} else {
setTimeout(function () {
isMapReady();
}, 5);
}
};
isGeoReady();
isMapReady();
return {
/**
@ -101,13 +125,9 @@ var googleCallback;
* options variable
*/
start: function(event, options){
// dont do anything if the information didn't come back from google map
var isReady = function () {
if (!map) {
setTimeout(function () {
isReady();
}, 13);
} else {
// ensure the map has been initialized in the setup function above
var isMapSetup = function () {
if ( map ) {
map.getDiv().style.display = "block";
// reset the location and zoom just in case the user plaid with the map
map.setCenter(location);
@ -139,16 +159,20 @@ var googleCallback;
position: location,
pov: {
heading: options.heading = options.heading || 0,
pitch: options.pitch = options.pitch || 0,
pitch: options.pitch = options.pitch || 0,
zoom: options.zoom
}
})
} )
);
}
} else {
setTimeout( function () {
isMapSetup();
}, 13);
}
};
isReady();
isMapSetup();
},
/**
* @member webpage
@ -162,6 +186,11 @@ var googleCallback;
if (map) {
map.getDiv().style.display = "none";
}
},
_teardown: function( options ) {
// the map must be manually removed
document.getElementById( options.target ).removeChild( newdiv );
newdiv = map = location = null;
}
};
},

Просмотреть файл

@ -1,8 +1,9 @@
test("Popcorn Google Map Plugin", function () {
var popped = Popcorn("#video"),
expects = 10,
count = 0;
expects = 11,
count = 0,
setupId;
expect(expects);
@ -31,7 +32,7 @@ test("Popcorn Google Map Plugin", function () {
lat: 43.665429,
lng: -79.403323,
zoom: 10
} )
})
.googlemap({
start: 0, // seconds
end: 5, // seconds
@ -39,9 +40,20 @@ test("Popcorn Google Map Plugin", function () {
target: 'map2',
location:'boston',
zoom: 15
} )
})
.volume(0);
popped.googlemap({
start: 0, // seconds
end: 10, // seconds
type: 'SATELLITE',
target: 'map2',
location:'toronto',
zoom: 15
});
setupId = popped.getLastTrackEventId();
popped.exec( 4, function() {
ok(google.maps, "Google maps is available");
plus();
@ -62,7 +74,13 @@ test("Popcorn Google Map Plugin", function () {
popped.exec( 6, function() {
ok (document.getElementById('actualmap2').style.display === "none" &&
document.getElementById('actualmap1').style.display === "none", "Both maps are no lnger visible" );
document.getElementById('actualmap1').style.display === "none", "Both maps are no longer visible" );
plus();
popped.pause();
popped.removeTrackEvent( setupId );
ok( !document.getElementById('actualmap3'), "removed map was properly destroyed" );
plus();
});

Просмотреть файл

@ -3,6 +3,7 @@
(function (Popcorn) {
var scriptLoaded = false,
scriptLoading = false,
callBack = function( data ) {
if ( typeof google !== 'undefined' && google.load ) {
@ -17,8 +18,6 @@
}
};
Popcorn.getScript( "http://www.google.com/jsapi", callBack );
/**
* Google News popcorn plug-in
* Displays Google News information on a topic in a targeted div.
@ -59,6 +58,12 @@
}
},
_setup : function( options ) {
if ( !scriptLoading ) {
scriptLoading = true;
Popcorn.getScript( "http://www.google.com/jsapi", callBack );
}
options.container = document.createElement( 'div' );
if ( document.getElementById( options.target ) ) {

Просмотреть файл

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn Google News Plug-in Test</title>
<link rel="stylesheet" href="../../test/qunit/qunit.css" type="text/css" media="screen">
<script src="../../test/qunit/qunit.js"></script>
<!--
do not move - this must be called immediately prior to
popcorn-api-draft.js
-->
<script src="../../popcorn.js"></script>
<script src="popcorn.googlenews.js"></script>
<script src="popcorn.googlenews.unit.js"></script>
</head>
<body>
<h1 id="qunit-header">Popcorn Google News Plugin</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"> </div>
<video id='video'
controls preload='none'
width= '250px'
poster="../../test/poster.png">
<source id='mp4'
src="../../test/trailer.mp4"
type='video/mp4; codecs="avc1, mp4a"'>
<source id='ogv'
src="../../test/trailer.ogv"
type='video/ogg; codecs="theora, vorbis"'>
<p>Your user agent does not support the HTML5 Video element.</p>
</video>
<div id="googlenewsdiv"></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,59 @@
test( "Popcorn google news Plugin", function () {
var popped = Popcorn( "#video" ),
expects = 7,
count = 0,
googlenewsdiv = document.getElementById( "googlenewsdiv" );
expect(expects);
function plus() {
if ( ++count===expects ) {
start();
}
}
stop();
ok ( "googlenews" in popped, "googlenews is a method of the popped instance" );
plus();
equals ( googlenewsdiv.innerHTML, "", "initially, there is nothing inside the googlenewsdiv" );
plus();
popped.googlenews({
start: 0, // seconds
end: 5, // seconds
topic: "Oil Spill",
target: "googlenewsdiv"
})
.googlenews({
start: 3, // seconds
end: 10, // seconds
topic: "Village Telco",
target: "googlenewsdiv"
})
.volume( 0 );
popped.exec( 1, function() {
equals( googlenewsdiv.childElementCount, 2, "googlenewsdiv now has two inner elements" );
plus();
equals( googlenewsdiv.children[0].style.display , "inline", "first googlenews is visible on the page" );
plus();
});
popped.exec( 4, function() {
equals( googlenewsdiv.children[1].style.display , "inline", "second googlenews is visible on the page" );
plus();
});
popped.exec( 11, function() {
equals( googlenewsdiv.children[1].style.display , "none", "second googlenews is no longer visible on the page" );
plus();
equals( googlenewsdiv.children[0].style.display , "none", "first googlenews is no longer visible on the page" );
plus();
});
popped.play();
});

Просмотреть файл

@ -15,10 +15,11 @@
end: 15, // seconds
href: 'http://www.drumbeat.org/',
src: 'https://www.drumbeat.org/media//images/drumbeat-logo-splash.png',
text: 'DRUMBEAT',
target: 'imagediv'
} )
.image({
start: 20, // seconds
start: 5, // seconds
end: 45, // seconds
// no href
src: 'http://patriciabergeron.net/wp-content/uploads/web.made_.movie_marquee.gif',
@ -31,8 +32,9 @@
</head>
<body>
<h1 id="qunit-header">Popcorn Image Plug-in Demo</h1>
<p> An image will appear at 5 seconds and disappear at 15 seconds.</p>
<p> An image will appear at 20 seconds and disappear at 45 seconds.</p>
<p> Two images will appear at 5 seconds.</p>
<p> First one (with an overlayed text) will disappear at 15 seconds.</p>
<p> Second one (without an overlayed text) will disappear at 45 seconds.</p>
<div>
<video id='video'
controls
@ -51,7 +53,8 @@
</video>
</div>
<div id="imagediv">
</div>
<div style="width:400px; height:400px" id="imagediv">
</div>
</body>
</html>

Просмотреть файл

@ -8,10 +8,11 @@
* Options parameter will need a start, end, href, target and src.
* 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
* href is the url of the destination of a link
* href is the url of the destination of a link - optional
* Target is the id of the document element that the iframe needs to be attached to,
* this target element must exist on the DOM
* Src is the url of the image that you want to display
* text is the overlayed text on the image - optional
*
* @param {Object} options
*
@ -22,6 +23,7 @@
end: 15, // seconds
href: 'http://www.drumbeat.org/',
src: 'http://www.drumbeat.org/sites/default/files/domain-2/drumbeat_logo.png',
text: 'DRUMBEAT',
target: 'imagediv'
} )
*
@ -40,27 +42,52 @@
end : {elem:'input', type:'number', label:'Out'},
href : {elem:'input', type:'text', label:'Link URL'},
target : 'Image-container',
src : {elem:'input', type:'text', label:'Source URL'}
src : {elem:'input', type:'text', label:'Source URL'},
text: {elem:'input', type:'text', label:'TEXT'}
}
},
_setup: function( options ) {
options.link = document.createElement( 'a' );
options.link.style.display = "none"; // display none by default
if ( options.href ) {
options.link.href = options.href;
}
options.link.target = "_blank";
if ( document.getElementById( options.target ) ) {
document.getElementById( options.target ).appendChild( options.link ); // add the widget's div to the target div
}
var img = document.createElement( 'img' );
img.src = options.src;
img.style.borderStyle = "none"; // borders look really bad, if someone wants it they can put it on their div target
options.link.style.position = "relative";
options.link.style.textDecoration = "none";
options.link.appendChild( img );
var img = document.createElement( 'img' );
img.addEventListener( "load", function() {
img.style.borderStyle = "none"; // borders look really bad, if someone wants it they can put it on their div target
if ( options.href ) {
options.link.href = options.href;
}
options.link.target = "_blank";
if ( document.getElementById( options.target ) ) {
document.getElementById( options.target ).appendChild( options.link ); // add the widget's div to the target div
}
var fontHeight = ( img.height / 12 ) + "px";
var divText = document.createElement( 'div' );
divTextStyle = {
position: "relative",
width: img.width + "px",
textAlign: "center",
fontSize: fontHeight,
color: "black",
fontWeight : "bold",
zIndex: "10"
};
for ( var st in divTextStyle ) {
divText.style[ st ] = divTextStyle[ st ];
}
divText.innerHTML = options.text || "";
options.link.appendChild( divText );
options.link.appendChild( img );
divText.style.top = ( img.height / 2 ) - ( divText.offsetHeight / 2 ) + "px";
options.link.style.display = "none";
}, false );
img.src = options.src;
},
/**
@ -70,7 +97,7 @@
* options variable
*/
start: function( event, options ) {
options.link.style.display = "inline";
options.link.style.display = "block";
},
/**
* @member image

Просмотреть файл

@ -26,20 +26,21 @@ test("Popcorn Image Plugin", function () {
end: 3, // seconds
href: 'http://www.drumbeat.org/',
src: 'https://www.drumbeat.org/media//images/drumbeat-logo-splash.png',
text: 'DRUMBEAT',
target: 'imagediv'
} );
});
popped.exec( 2, function() {
ok( /display: inline;/.test( imagediv.innerHTML ), "Div contents are displayed" );
plus();
ok( /img/.test( imagediv.innerHTML ), "An image exists" );
plus();
ok( /display: block;/.test( imagediv.innerHTML ), "Div contents are displayed" );
plus();
ok( /img/.test( imagediv.innerHTML ), "An image exists" );
plus();
});
popped.exec( 4, function() {
ok( /display: none;/.test( imagediv.innerHTML ), "Div contents are hidden again" );
plus();
ok( /display: none;/.test( imagediv.innerHTML ), "Div contents are hidden again" );
plus();
});
popped.volume(0).play();
popped.volume(0).play();
});

Просмотреть файл

@ -14,15 +14,18 @@
<li><a onclick="clearEvents();" target="out" href="footnote/popcorn.footnote.unit.html">footnote</a></li>
<li><a onclick="clearEvents();" target="out" href="googlemap/popcorn.googlemap.unit.html">googlemap</a></li>
<li><a onclick="clearEvents();" target="out" href="googlenews/popcorn.googlenews.unit.html">googlenews</a></li>
<li><a onclick="clearEvents();" target="out" href="googlefeed/popcorn.googlefeed.unit.html">googlefeed</a></li>
<li><a onclick="clearEvents();" target="out" href="image/popcorn.image.unit.html">image</a></li>
<li><a onclick="clearEvents();" target="out" href="lastfm/popcorn.lastfm.unit.html">lastfm</a></li>
<li><a onclick="clearEvents();" target="out" href="lowerthird/popcorn.lowerthird.unit.html">lowerthird</a></li>
<li><a onclick="clearEvents();" target="out" href="mustache/popcorn.mustache.unit.html">mustache</a></li>
<li><a onclick="clearEvents();" target="out" href="openmap/popcorn.openmap.unit.html">openmap</a></li>
<li><a onclick="clearEvents();" target="out" href="subtitle/popcorn.subtitle.unit.html">subtitle</a></li>
<li><a onclick="clearEvents();" target="out" href="tagthisperson/popcorn.tagthisperson.unit.html">tagthisperson</a></li>
<li><a onclick="clearEvents();" target="out" href="twitter/popcorn.twitter.unit.html">twitter</a></li>
<li><a onclick="clearEvents();" target="out" href="webpage/popcorn.webpage.unit.html">webpage</a></li>
<li><a onclick="clearEvents();" target="out" href="wikipedia/popcorn.wikipedia.unit.html">wikipedia</a></li>
</ul>
</div>
<iframe id="out" name="out" style="position:absolute;top:0px;left:150px;height:100%;width:100%"></iframe>
@ -30,12 +33,13 @@
var eventList = [];
function runAllTests(){
var testUrl = [ "attribution", "code", "flickr", "footnote", "googlemap",
"googlenews", "image", "lastfm", "lowerthird", "mustache",
"subtitle", "tagthisperson", "twitter", "webpage", "wikipedia" ];
//googlenews and lowerthird tests do not exist, therefore their test time is set to 0
"googlenews", "googlefeed", "image", "lastfm", "lowerthird",
"mustache", "openmap", "subtitle", "tagthisperson", "twitter",
"webpage", "wikipedia"];
var testDur = [ 17000, 20000, 10000, 20000, 11000,
0, 10000, 10000, 0, 15000,
25000, 21000, 10000, 21000, 21000 ];
13000, 13000, 10000, 10000, 13000,
15000, 13000, 25000, 21000, 10000,
21000, 21000 ];
var setTest = function( index ) {
var url = function( str ) {

Просмотреть файл

@ -30,22 +30,6 @@
*
*/
// just a little tool function
// calculates the top and left position of an element
var offset = function(elem) {
if ( !elem ) { elem = this; }
var x = elem.offsetLeft;
var y = elem.offsetTop;
while ( !!( elem = elem.offsetParent ) ) {
x += elem.offsetLeft;
y += elem.offsetTop;
}
return { left: x, top: y };
};
Popcorn.plugin( "lowerthird" , {
manifest: {
@ -80,7 +64,7 @@
// the video element must have height and width defined
this.container.style.width = this.video.offsetWidth + "px";
this.container.style.left = offset( this.video ).left + "px";
this.container.style.left = this.position().left + "px";
this.video.parentNode.appendChild( this.container );
}
@ -103,7 +87,7 @@
*/
start: function(event, options){
options.container.innerHTML = ( options.salutation ? options.salutation + " " : "" ) + options.name + ( options.role ? "<br />" + options.role : "" );
this.container.style.top = offset( this.video ).top + this.video.offsetHeight - ( 40 + this.container.offsetHeight ) + "px";
this.container.style.top = this.position().top + this.video.offsetHeight - ( 40 + this.container.offsetHeight ) + "px";
},
/**
* @member lowerthird

Просмотреть файл

@ -0,0 +1,43 @@
<!DOCTYPE html>
<html>
<head>
<title>Popcorn Lower Third Plug-in Test</title>
<link rel="stylesheet" href="../../test/qunit/qunit.css" type="text/css" media="screen">
<script src="../../test/qunit/qunit.js"></script>
<!--
do not move - this must be called immediately prior to
popcorn-api-draft.js
-->
<script src="../../popcorn.js"></script>
<script src="popcorn.lowerthird.js"></script>
<script src="popcorn.lowerthird.unit.js"></script>
</head>
<body>
<h1 id="qunit-header">Popcorn Lower Third Plugin</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture"> </div>
<video id='video'
controls preload='none'
width= '250px'
poster="../../test/poster.png">
<source id='mp4'
src="../../test/trailer.mp4"
type='video/mp4; codecs="avc1, mp4a"'>
<source id='ogv'
src="../../test/trailer.ogv"
type='video/ogg; codecs="theora, vorbis"'>
<p>Your user agent does not support the HTML5 Video element.</p>
</video>
<div id="lowerthirddiv"></div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,64 @@
test( "Popcorn lower third Plugin", function () {
var popped = Popcorn( "#video" ),
expects = 7,
count = 0,
lowerthirddiv = document.getElementById( "lowerthirddiv" );
expect(expects);
function plus() {
if ( ++count===expects ) {
start();
}
}
stop();
ok ( "lowerthird" in popped, "lowerthird is a method of the popped instance" );
plus();
equals ( lowerthirddiv.innerHTML, "", "initially, there is nothing inside the lowerthirddiv" );
plus();
ok( !popped.container, "initially, there is no default div" );
plus();
popped.lowerthird({
start: 0, // seconds
end: 5, // seconds
salutation: "Mr",
name: "Hyde",
role: "Monster"
} )
.lowerthird({
start: 3, // seconds
end: 10, // seconds
target: "lowerthirddiv",
salutation: "Dr",
name: "Jekyll",
role: "Person"
} )
.volume(0);
popped.exec( 1, function() {
equals ( popped.container.innerHTML, "Mr Hyde<br>Monster", "first lowerthird is visible" );
plus();
});
popped.exec( 4, function() {
equals ( lowerthirddiv.innerHTML, "Dr Jekyll<br>Person", "second lowerthird is visible" );
plus();
});
popped.exec( 11, function() {
equals ( popped.container.innerHTML, "", "first lowerthird is empty" );
plus();
equals ( lowerthirddiv.innerHTML, "", "second lowerthird is empty" );
plus();
});
popped.play();
});

Просмотреть файл

@ -2,8 +2,6 @@
(function (Popcorn) {
Popcorn.getScript('https://github.com/janl/mustache.js/raw/master/mustache.js');
/**
* Mustache Popcorn Plug-in
*
@ -87,6 +85,9 @@
*/
Popcorn.plugin( 'mustache' , function( options ) {
Popcorn.getScript('https://github.com/janl/mustache.js/raw/master/mustache.js');
var getData, data, getTemplate, template;
var shouldReload = !!options.dynamic,

Просмотреть файл

@ -41,15 +41,6 @@ var openmapCallback;
_mapFired = false,
_mapLoaded = false;
// insert openlayers api script once
if ( !_mapFired ) {
_mapFired = true;
Popcorn.getScript('http://openlayers.org/api/OpenLayers.js',
function() {
_mapLoaded = true;
} );
}
Popcorn.plugin( "openmap" , function( options ){
var newdiv,
map,
@ -60,6 +51,15 @@ var openmapCallback;
selectControl,
popup;
// insert openlayers api script once
if ( !_mapFired ) {
_mapFired = true;
Popcorn.getScript('http://openlayers.org/api/OpenLayers.js',
function() {
_mapLoaded = true;
} );
}
// create a new div within the target div
// this is later passed on to the maps api
newdiv = document.createElement( 'div' );

Просмотреть файл

@ -68,23 +68,6 @@
*
*/
// just a little tool function
// calculates the top and left position of an element
var offset = function(obj) {
var left, top;
left = top = 0;
if (obj.offsetParent) {
do {
left += obj.offsetLeft;
top += obj.offsetTop;
} while ( !!(obj = obj.offsetParent) );
}
return {
left : left,
top : top
};
};
// translates whatever is in options.container into selected language
var translate = function( options, text ) {
@ -128,9 +111,9 @@
this.container.style.textAlign = "center";
// the video element must have height and width defined
this.container.style.width = this.video.offsetWidth + "px";
this.container.style.top = offset( this.video ).top + this.video.offsetHeight - 65 + "px";
this.container.style.left = offset( this.video ).left + "px";
this.container.style.width = this.media.offsetWidth + "px";
this.container.style.top = this.position().top + this.media.offsetHeight - 65 + "px";
this.container.style.left = this.position().left + "px";
document.body.appendChild( this.container );
}

Просмотреть файл

@ -1,6 +1,7 @@
// PLUGIN: TWITTER
(function (Popcorn) {
var scriptLoading = false;
/**
* Twitter popcorn plug-in
@ -30,10 +31,6 @@
*
*/
if ( !window.TWTR ) {
Popcorn.getScript("http://widgets.twimg.com/j/2/widget.js");
}
Popcorn.plugin( "twitter" , {
manifest: {
@ -55,6 +52,11 @@
_setup: function( options ) {
if ( !window.TWTR && !scriptLoading ) {
scriptLoading = true;
Popcorn.getScript("http://widgets.twimg.com/j/2/widget.js");
}
// setup widget div that is unique per track
options.container = document.createElement( 'div' ); // create the div to store the widget
options.container.setAttribute('id', Popcorn.guid()); // use this id to connect it to the widget

Просмотреть файл

@ -64,6 +64,7 @@
*/
start: function(event, options){
// make the iframe visible
options._iframe.src = options.src;
options._iframe.style.display = 'inline';
},
/**

Просмотреть файл

@ -1,11 +1,3 @@
/*
* popcorn.js version @VERSION
* http://popcornjs.org
*
* Copyright 2011, Mozilla Foundation
* Licensed under the MIT license
*/
(function(global, document) {
// Cache refs to speed up calls to native utils
@ -13,6 +5,7 @@
forEach = Array.prototype.forEach,
hasOwn = Object.prototype.hasOwnProperty,
slice = Array.prototype.slice,
toString = Object.prototype.toString,
// ID string matching
rIdExp = /^(#([\w\-\_\.]+))$/,
@ -25,9 +18,9 @@
// Declare constructor
// Returns an instance object.
Popcorn = function( entity ) {
Popcorn = function( entity, options ) {
// Return new Popcorn object
return new Popcorn.p.init( entity );
return new Popcorn.p.init( entity, options || null );
};
// Instance caching
@ -55,9 +48,9 @@
Popcorn.addInstance = function( instance ) {
var instanceLen = Popcorn.instances.length,
instanceId = instance.video.id && instance.video.id;
instanceId = instance.media.id && instance.media.id;
// If the video element has its own `id` use it, otherwise provide one
// If the media element has its own `id` use it, otherwise provide one
// Ensure that instances have unique ids and unique entries
// Uses `in` operator to avoid false positives on 0
instance.id = !( instanceId in Popcorn.instanceIds ) && instanceId ||
@ -87,7 +80,7 @@
// the new prototype for our Popcorn constructor
Popcorn.p = Popcorn.prototype = {
init: function( entity ) {
init: function( entity, options ) {
var matches;
@ -139,14 +132,18 @@
// Check if entity is a valid string id
matches = rIdExp.exec( entity );
// Get video element by id or object reference
this.video = matches && matches.length && matches[ 2 ] ?
// Get media element by id or object reference
this.media = matches && matches.length && matches[ 2 ] ?
document.getElementById( matches[ 2 ] ) :
entity;
// Create an audio or video element property reference
this[ ( this.media.tagName && this.media.tagName.toLowerCase() ) || "video" ] = this.media;
// Register new instance
Popcorn.addInstance( this );
this.options = options || { };
this.data = {
history: [],
events: {},
@ -168,20 +165,20 @@
// Wrap true ready check
var isReady = function( that ) {
if ( that.video.readyState >= 2 ) {
if ( that.media.readyState >= 2 ) {
// Adding padding to the front and end of the arrays
// this is so we do not fall off either end
var duration = that.video.duration;
// Check for no duration info (NaN)
var videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
var duration = that.media.duration,
// Check for no duration info (NaN)
videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
Popcorn.addTrackEvent( that, {
start: videoDurationPlus,
end: videoDurationPlus
});
that.video.addEventListener( "timeupdate", function( event ) {
that.media.addEventListener( "timeupdate", function( event ) {
var currentTime = this.currentTime,
previousTime = that.data.trackEvents.previousUpdateTime,
@ -312,7 +309,7 @@
// A Few reusable utils, memoized onto Popcorn
Popcorn.extend( Popcorn, {
error: function( msg ) {
throw msg;
throw new Error( msg );
},
guid: function( prefix ) {
Popcorn.guid.counter++;
@ -327,7 +324,39 @@
return size;
},
nop: function () {}
isArray: Array.isArray || function( array ) {
return toString.call( array ) === "[object Array]";
},
nop: function () {},
position: function( elem ) {
var clientRect = elem.getBoundingClientRect(),
bounds = {},
doc = elem.ownerDocument,
docElem = document.documentElement,
body = document.body,
clientTop, clientLeft, scrollTop, scrollLeft, top, left;
// Determine correct clientTop/Left
clientTop = docElem.clientTop || body.clientTop || 0;
clientLeft = docElem.clientLeft || body.clientLeft || 0;
// Determine correct scrollTop/Left
scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop );
scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft );
// Temp top/left
top = Math.ceil( clientRect.top + scrollTop - clientTop );
left = Math.ceil( clientRect.left + scrollLeft - clientLeft );
for ( var p in clientRect ) {
bounds[ p ] = Math.round( clientRect[ p ] );
}
return Popcorn.extend({}, bounds, { top: top, left: left });
}
});
// Memoized GUID Counter
@ -347,8 +376,8 @@
ret[ name ] = function( arg ) {
if ( typeof this.video[name] === "function" ) {
this.video[ name ]();
if ( typeof this.media[name] === "function" ) {
this.media[ name ]();
return this;
}
@ -356,12 +385,12 @@
if ( arg !== false && arg !== null && typeof arg !== "undefined" ) {
this.video[ name ] = arg;
this.media[ name ] = arg;
return this;
}
return this.video[ name ];
return this.media[ name ];
};
});
@ -374,7 +403,7 @@
// Rounded currentTime
roundTime: function () {
return -~this.video.currentTime;
return -~this.media.currentTime;
},
// Attach an event to a single point in time
@ -393,6 +422,9 @@
});
return this;
},
position: function() {
return Popcorn.position( this.media );
}
});
@ -456,7 +488,7 @@
var evt = document.createEvent( eventInterface );
evt.initEvent(type, true, true, global, 1);
this.video.dispatchEvent(evt);
this.media.dispatchEvent(evt);
return this;
}
@ -487,7 +519,7 @@
// only attach one event of any type
if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) {
this.video.addEventListener( type, function( event ) {
this.media.addEventListener( type, function( event ) {
Popcorn.forEach( self.data.events[type], function ( obj, key ) {
if ( typeof obj === "function" ) {
@ -541,6 +573,9 @@
track._natives.end = track._natives.end || Popcorn.nop;
}
track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate );
track.end = Popcorn.util.toSeconds( track.end, obj.options.framerate );
// Store this definition in an array sorted by times
obj.data.trackEvents.byStart.push( track );
obj.data.trackEvents.byEnd.push( track );
@ -569,8 +604,9 @@
// remove plugin reference from registry
for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
if ( Popcorn.registry[ registryIdx ].type === name ) {
if ( Popcorn.registry[ registryIdx ].name === name ) {
Popcorn.registry.splice( registryIdx, 1 );
delete Popcorn.registryByName[ name ];
// delete the plugin
delete obj[ name ];
@ -633,6 +669,7 @@
// Capture the position of the track being removed.
if ( o._id === trackId ) {
indexWasAt = i;
o._natives._teardown && o._natives._teardown.call( obj, o );
}
}
});
@ -707,6 +744,7 @@
Popcorn.manifest = {};
// Plugins are registered
Popcorn.registry = [];
Popcorn.registryByName = {};
// An interface for extending Popcorn
// with plugin functionality
Popcorn.plugin = function( name, definition, manifest ) {
@ -804,15 +842,109 @@
Popcorn.extend( Popcorn.p, plugin );
// Push into the registry
var entry = {
fn: plugin[ name ],
definition: definition,
base: definition,
parents: [],
name: name
};
Popcorn.registry.push(
Popcorn.extend( plugin, {
Popcorn.extend( plugin, entry, {
type: name
})
);
);
Popcorn.registryByName[ name ] = entry;
return plugin;
};
// Popcorn Plugin Inheritance Helper Methods
// Internal use only
Popcorn.plugin.getDefinition = function( name ) {
var registry = Popcorn.registryByName;
if ( registry[ name ] ) {
return registry[ name ];
}
Popcorn.error( "Cannot inherit from "+ name +"; Object does not exist" );
};
// Internal use only
Popcorn.plugin.delegate = function( instance, name, plugins ) {
return function() {
var args = arguments;
plugins.forEach( function( plugin ) {
// The new plugin simply calls the delegated methods on
// all of its parents in the order they were specified.
plugin[ name ] && plugin[ name ].apply( instance, args );
});
};
};
// Plugin inheritance
Popcorn.plugin.inherit = function( name, parents, definition, manifest ) {
// Get the names of all of the ancestor classes, in the order that
// we will be calling them. The override is for the class we're
// currently defining, since it's not in the registry yet.
var ancestors = [],
pluginFn, entry;
function getAncestors( name, override ) {
var parents = override || Popcorn.plugin.getDefinition( name ).parents;
for ( var i in parents ) {
if ( hasOwn.call( parents, i ) ) {
var p = parents[ i ];
getAncestors( p );
if ( ancestors.indexOf( p ) === -1 ) {
ancestors.push( p );
}
}
}
}
getAncestors( name, Popcorn.isArray( parents ) ? parents : [ parents ] );
ancestors.push( name );
// Now create the requested plugin under the reqested name.
pluginFn = Popcorn.plugin( name, function( options ) {
var self = this,
plugins;
function instantiate( definition ) {
return definition.call && definition.call( self, options ) || definition;
}
// When the newly-defined plugin is instantiated, it must
// explicitly instantiate all of its ancestors.
plugins = ancestors.map( function( name ) {
return instantiate( Popcorn.plugin.getDefinition( name ).base );
});
return {
_setup: Popcorn.plugin.delegate( self, "_setup", plugins ),
start: Popcorn.plugin.delegate( self, "start", plugins ),
end: Popcorn.plugin.delegate( self, "end", plugins )
};
}, manifest || definition.manifest );
entry = Popcorn.plugin.getDefinition( name );
entry.base = definition;
entry.parents = parents;
return pluginFn;
};
// Augment Popcorn;
Popcorn.inherit = Popcorn.plugin.inherit;
// stores parsers keyed on filetype
Popcorn.parsers = {};
@ -922,14 +1054,16 @@
Popcorn.xhr = function ( options ) {
options.dataType = options.dataType && options.dataType.toLowerCase() || null;
if ( options.dataType &&
( options.dataType.toLowerCase() === "jsonp" ||
options.dataType.toLowerCase() === "script" ) ) {
( options.dataType === "jsonp" ||
options.dataType === "script" ) ) {
Popcorn.xhr.getJSONP(
options.url,
options.success,
options.dataType.toLowerCase() === "script"
options.dataType === "script"
);
return;
}
@ -939,10 +1073,6 @@
// Create new XMLHttpRequest object
settings.ajax = settings.xhr();
// Normalize dataType
settings.dataType = settings.dataType.toLowerCase();
if ( settings.ajax ) {
if ( settings.type === "GET" && settings.data ) {
@ -1058,25 +1188,25 @@
script.onload = script.onreadystatechange = function() {
if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {
if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {
// Handling remote script loading callbacks
if ( isScript ) {
// Handling remote script loading callbacks
if ( isScript ) {
// getScript
success && success();
}
// getScript
success && success();
}
// Executing for JSONP requests
if ( isFired ) {
// Executing for JSONP requests
if ( isFired ) {
// Garbage collect the callback
delete window[ callback ];
// Garbage collect the callback
delete window[ callback ];
// Garbage collect the script resource
head.removeChild( script );
}
}
// Garbage collect the script resource
head.removeChild( script );
}
}
};
script.src = url;
@ -1093,37 +1223,90 @@
return Popcorn.xhr.getJSONP( url, success, true );
};
Popcorn.util = {
// Simple function to parse a timestamp into seconds
// Acceptable formats are:
// HH:MM:SS.MMM
// HH:MM:SS;FF
// Hours and minutes are optional. They default to 0
toSeconds: function( timeStr, framerate ) {
//Hours and minutes are optional
//Seconds must be specified
//Seconds can be followed by milliseconds OR by the frame information
var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
errorMessage = "Invalid time format";
if ( typeof timeStr === "number" ) {
return timeStr;
} else if ( typeof timeStr === "string" ) {
if ( ! validTimeFormat.test( timeStr ) ) {
Popcorn.error( errorMessage );
}
} else {
Popcorn.error( errorMessage );
}
var t = timeStr.split( ":" ),
lastIndex = t.length - 1,
lastElement = t[ lastIndex ];
//Fix last element:
if ( lastElement.indexOf( ";" ) > -1 ) {
var frameInfo = lastElement.split( ";" ),
frameTime = 0;
if ( framerate && ( typeof framerate === "number" ) ) {
frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
}
t[ lastIndex ] =
parseInt( frameInfo[ 0 ], 10 ) + frameTime;
}
if ( t.length === 1 ) {
return parseFloat( t[ 0 ], 10 );
} else if ( t.length === 2 ) {
return ( parseInt( t[ 0 ], 10 ) * 60 ) + parseFloat( t[ 1 ], 10 );
} else if ( t.length === 3 ) {
return ( parseInt( t[ 0 ], 10 ) * 3600 ) +
( parseInt( t[ 1 ], 10 ) * 60 ) +
parseFloat( t[ 2 ], 10 );
}
}
};
// Exposes Popcorn to global context
global.Popcorn = Popcorn;
document.addEventListener( "DOMContentLoaded", function () {
document.addEventListener( "DOMContentLoaded", function() {
var videos = document.getElementsByTagName( "video" );
// Supports non-specific elements
var dataAttr = "data-timeline-sources",
medias = document.querySelectorAll( "[" + dataAttr + "]" );
Popcorn.forEach( videos, function ( iter, key ) {
Popcorn.forEach( medias, function( idx, key ) {
var video = videos[ key ],
var media = medias[ key ],
hasDataSources = false,
dataSources, data, popcornVideo;
dataSources, data, popcornMedia;
// Ensure that the DOM has an id
if ( !video.id ) {
if ( !media.id ) {
video.id = Popcorn.guid( "__popcorn" );
media.id = Popcorn.guid( "__popcorn" );
}
// Ensure we're looking at a dom node
if ( video.nodeType && video.nodeType === 1 ) {
if ( media.nodeType && media.nodeType === 1 ) {
popcornVideo = Popcorn( "#" + video.id );
popcornMedia = Popcorn( "#" + media.id );
dataSources = ( video.getAttribute( "data-timeline-sources" ) || "" ).split(",");
dataSources = ( media.getAttribute( dataAttr ) || "" ).split(",");
if ( dataSources[ 0 ] ) {
Popcorn.forEach( dataSources, function ( source ) {
Popcorn.forEach( dataSources, function( source ) {
// split the parser and data as parser:file
data = source.split( ":" );
@ -1137,20 +1320,20 @@
}
// If the video has data sources and the correct parser is registered, continue to load
if ( dataSources[ 0 ] && popcornVideo[ data[ 0 ] ] ) {
// If the media has data sources and the correct parser is registered, continue to load
if ( dataSources[ 0 ] && popcornMedia[ data[ 0 ] ] ) {
// Set up the video and load in the datasources
popcornVideo[ data[ 0 ] ]( data[ 1 ] );
// Set up the media and load in the datasources
popcornMedia[ data[ 0 ] ]( data[ 1 ] );
}
});
}
// Only play the video if it was specified to do so
if ( !!popcornVideo.autoplay ) {
popcornVideo.play();
// Only play the media if it was specified to do so
if ( !!popcornMedia.autoplay ) {
popcornMedia.play();
}
}

242
test/butter.unit.js Normal file
Просмотреть файл

@ -0,0 +1,242 @@
$(document).ready(function(){
var video = document.getElementById('video');
var popcorn = new Popcorn('#video');
var pluginResults;
var pluginOptions;
var manifest = {
about: {name: 'test', version: '0.1', author: 'Bobby Richter', website: 'http://robothaus.org'},
options: {
start : {elem:'input', type:'text', label:'In'},
end : {elem:'input', type:'text', label:'Out'},
target : 'test-target',
}
};
var plugin = (Popcorn.plugin('test',{
manifest: manifest,
_setup:function(options){pluginOptions = options; pluginResults='_setup';},
start:function(options){pluginResults='start'},
end:function(options){pluginResults='end'}
}));
var pluginInstance = popcorn['test']({start: 1, end: 2});
module("Popcorn");
var tests = [
{
title: 'Popcorn.registry',
test: function() {
ok(Popcorn.registry, 'exists');
}
},
{
title: 'Popcorn.getTrackEvents',
test: function() {
ok(Popcorn.getTrackEvents, 'exists');
}
},
{
title: 'Popcorn.getTrackEvents(popcorn) returns correct results',
test: function() {
expect(2);
var results = Popcorn.getTrackEvents(popcorn);
equals(results[0].start, 1, 'start is 1');
equals(results[0].end, 2, 'end is 2');
}
},
{
title: 'Popcorn.getTrackEvents exists',
test: function() {
ok(Popcorn.getTrackEvents, 'exists');
}
},
{
title: 'Valid plugins are addressable as p[pluginName]',
test: function() {
ok(popcorn['test'], 'popcorn[\'test\'] exists');
}
},
{
title: 'Invalid plugin names yield undefined using p[invalidName]',
test: function() {
ok(popcorn['notValid'] === undefined, 'is undefined');
}
},
{
title: 'p[validPluginName] is expected plugin',
test: function() {
equals(popcorn['test'],plugin['test'], 'popcorn[\'test\'] == plugin[\'test\']');
}
},
{
title: 'Popcorn.registry must be a list',
test: function() {
expect(2);
ok(Popcorn.registry, 'registry exists');
equals(typeof(Popcorn.registry), 'object', 'Popcorn.registry is an object');
}
},
{
title: 'Popcorn.registry must contain plugins',
test: function() {
equals(Popcorn.registry[0], plugin, 'Popcorn.registry contains test plugin');
}
},
{
title: 'plugin.type must exist',
test: function() {
ok(plugin.type, 'exists');
}
},
{
title: 'Plugin instance options._natives must exist',
test: function() {
ok(pluginOptions._natives, 'exists');
}
},
{
title: 'Plugin instance options.target should exist',
test: function() {
ok(pluginOptions.target, 'exists');
}
},
{
title: 'Popcorn.manifest be a list',
test: function() {
expect(2);
ok(Popcorn.manifest, 'Popcorn.manifest exists');
equals(typeof(Popcorn.manifest), 'object', 'Popcorn.manifest is an object');
}
},
{
title: 'Popcorn.manifest must contain plugin manifests',
test: function() {
equals(Popcorn.manifest['test'], manifest);
}
},
{
title: 'Plugin manifest must contain options property',
test: function() {
ok(Popcorn.manifest['test'].options, 'exists');
}
},
{
title: 'popcorn.video exists',
test: function() {
ok(popcorn.video, 'exists');
}
},
{
title: 'popcorn.video.currentTime exists',
test: function() {
ok(popcorn.video.currentTime !== undefined, 'exists');
}
},
{
title: 'popcorn.video.play exists',
test: function() {
ok(popcorn.video.play, 'exists');
}
},
{
title: 'popcorn.video.pause exists',
test: function() {
ok(popcorn.video.pause, 'exists');
}
},
{
title: 'popcorn.video.volume exists',
test: function() {
ok(popcorn.video.volume, 'exists');
}
},
{
title: 'popcorn.video.duration exists',
test: function() {
ok(popcorn.video.duration !== undefined, 'exists');
}
},
{
title: 'popcorn.video.readyState exists',
test: function() {
ok(popcorn.video.readyState !== undefined, 'exists');
}
},
{
title: 'popcorn.data exists',
test: function() {
ok(popcorn.data, 'exists');
}
},
{
title: 'popcorn.data.trackEvents exists',
test: function() {
ok(popcorn.data.trackEvents, 'exists');
}
},
{
title: 'popcorn.data.trackEvents.byStart exists',
test: function() {
ok(popcorn.data.trackEvents.byStart, 'exists');
}
},
{
title: 'popcorn.data.history must be a list',
test: function() {
expect(2);
ok(popcorn.data.history, 'history exists');
equals(typeof(popcorn.data.history), 'object', 'history in an object');
}
},
{
title: 'popcorn.getLastTrackEventId exists and works',
test: function() {
expect(2);
ok(popcorn.getLastTrackEventId, 'exists');
ok(popcorn.getLastTrackEventId(), 'works');
}
},
{
title: 'popcorn.getTrackEvents exists and works',
test: function() {
expect(2);
ok(popcorn.getTrackEvents, 'exists');
ok(popcorn.getTrackEvents(), 'works');
}
},
{
title: 'popcorn.getTrackEvents === Popcorn.getTrackEvents(popcorn)',
test: function() {
expect(2);
var tracks1 = popcorn.getTrackEvents();
var tracks2 = Popcorn.getTrackEvents(popcorn);
equals(tracks1.length, tracks2.length, 'lengths are equal');
equals(tracks1[0], tracks2[0], 'objects are equal');
}
},
{
title: 'popcorn.listen exists && works',
test: function() {
expect(2);
ok(popcorn.listen, 'exists');
popcorn.listen('ended', function(){});
for (var i in popcorn.data.events['ended']) {
ok(popcorn.data.events['ended'][i], 'works');
break;
} //for
}
},
{
title: 'popcorn.removeTrackEvent exists and works',
test: function() {
expect(2);
ok(popcorn.removeTrackEvent, 'exists');
popcorn.removeTrackEvent(popcorn.getLastTrackEventId());
equals(popcorn.getTrackEvents().length, 0, 'works');
}
},
];
for (var i=0; i<tests.length; ++i) {
test(tests[i].title, tests[i].test);
} //for
});

Просмотреть файл

@ -2,386 +2,97 @@
<html>
<head>
<title>Popcorn Butter Test</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen">
<style>
body {
background-color: #a8a8a8;
font: 12pt Arial,sans-serif;
margin: 3px;
padding: 0px;
}
#title {
font-size: 180%;
text-align: center;
h3 {
margin: 3px;
padding: 0px;
}
#main-container {
width: 600px;
margin: auto;
h2 {
display: inline;
margin: 3px;
padding: 0px;
}
.pass {
color: #33FF00;
}
#test-content {
border: 3px solid #0a0a0a;
border-radius: 10px;
.expect {
color: #2d2d2d;
}
.fail {
color: #FF0000;
}
</style>
<script src="jquery.js"></script>
<script src="qunit/qunit.js"></script>
<script src="../popcorn.js"></script>
<script src="butter.unit.js"></script>
.test-item {
padding: 3px;
}
<script>
(function( Popcorn, $ ) {
.test-status {
font-weight: bold;
}
.test-info {
font-size: 70%;
margin-left: 1em;
}
.test-title:hover {
cursor: pointer;
text-decoration: underline;
}
#video-container {
display: none;
}
</style>
<script type="text/javascript" src="../popcorn.js"></script>
<script type="text/javascript" src="jquery.js"></script>
<script type="text/javascript">
$(document).ready(function() {
var video = document.getElementById('video');
var popcorn = new Popcorn('#video');
var pluginResults;
var pluginOptions;
var manifest = {
about: {name: 'test', version: '0.1', author: 'Bobby Richter', website: 'http://robothaus.org'},
options: {
start : {elem:'input', type:'text', label:'In'},
end : {elem:'input', type:'text', label:'Out'},
target : 'test-target',
}
var results = {
pass: 0,
fail: 0,
expect: 40
};
var plugin = (Popcorn.plugin('test',{
manifest: manifest,
_setup:function(options){pluginOptions = options; pluginResults='_setup';},
start:function(options){pluginResults='start'},
end:function(options){pluginResults='end'}
}));
var pluginInstance = popcorn['test']({start: 1, end: 2});
var tests = [
{
title: 'Popcorn.registry',
test: function() {
return Popcorn.registry !== undefined;
}
},
{
title: 'Popcorn.getTrackEvents',
test: function() {
return Popcorn.getTrackEvents !== undefined;
}
},
{
title: 'Popcorn.getTrackEvents(popcorn) returns correct results',
test: function() {
var results = Popcorn.getTrackEvents(popcorn);
for (var i in results) {
if (results[i].start === 1 && results[i].end === 2) {
return true;
} //if
} //for
return false;
}
},
{
title: 'Popcorn.getTrackEvents exists',
test: function() {
return Popcorn.getTrackEvents !== undefined;
}
},
{
title: 'Valid plugins are addressable as p[pluginName]',
test: function() {
return popcorn['test'] !== undefined;
}
},
{
title: 'Invalid plugin names yield undefined using p[invalidName]',
test: function() {
return popcorn['notValid'] === undefined;
}
},
{
title: 'p[validPluginName] is expected plugin',
test: function() {
return popcorn['test'] === plugin['test'];
}
},
{
title: 'Popcorn.registry must be a list',
test: function() {
return Popcorn.registry !== undefined && typeof(Popcorn.registry) === 'object';
}
},
{
title: 'Popcorn.registry must contain plugins',
test: function() {
for (var i in Popcorn.registry) {
if (Popcorn.registry[i] === plugin) {
return true;
} //if
} //for
return false;
}
},
{
title: 'plugin.type must exist',
test: function() {
return plugin.type !== undefined;
}
},
{
title: 'Plugin instance options._natives must exist',
test: function() {
return pluginOptions._natives !== undefined;
}
},
{
title: 'Plugin instance options.target should exist',
test: function() {
return pluginOptions.target !== undefined;
}
},
{
title: 'Popcorn.manifest be a list',
test: function() {
return Popcorn.manifest !== undefined && typeof(Popcorn.manifest) === 'object';
}
},
{
title: 'Valid manifests are addressable as p[pluginName]',
test: function() {
return popcorn['test'] !== undefined;
}
},
{
title: 'Invalid manifests names yield undefined using p[invalidName]',
test: function() {
return popcorn['notValid'] === undefined;
}
},
{
title: 'Popcorn.manifest must contain plugin manifests',
test: function() {
for (var i in Popcorn.manifest) {
if (Popcorn.manifest[i] === manifest) {
return true;
} //if
} //for
return false;
}
},
{
title: 'Plugin manifest must contain options property',
test: function() {
return Popcorn.manifest['test'].options !== undefined;
}
},
{
title: 'popcorn.video exists',
test: function() {
return popcorn.video !== undefined;
}
},
{
title: 'popcorn.video.currentTime exists',
test: function() {
return popcorn.video.currentTime !== undefined;
}
},
{
title: 'popcorn.video.play exists',
test: function() {
return popcorn.video.play !== undefined;
}
},
{
title: 'popcorn.video.pause exists',
test: function() {
return popcorn.video.pause !== undefined;
}
},
{
title: 'popcorn.video.volume exists',
test: function() {
return popcorn.video.volume !== undefined;
}
},
{
title: 'popcorn.video.duration exists',
test: function() {
return popcorn.video.duration !== undefined;
}
},
{
title: 'popcorn.video.readyState exists',
test: function() {
return popcorn.video.readyState !== undefined;
}
},
{
title: 'popcorn.data exists',
test: function() {
return popcorn.data !== undefined;
}
},
{
title: 'popcorn.data.trackEvents exists',
test: function() {
return popcorn.data.trackEvents !== undefined;
}
},
{
title: 'popcorn.data.trackEvents.byStart exists',
test: function() {
return popcorn.data.trackEvents.byStart !== undefined;
}
},
{
title: 'popcorn.data.history must be a list',
test: function() {
return popcorn.data.history !== undefined && typeof(popcorn.data.history) === 'object';
}
},
{
title: 'popcorn.getLastTrackEventId exists and works',
test: function() {
var exists = popcorn.getLastTrackEventId !== undefined;
var works = popcorn.getLastTrackEventId() !== undefined;
return exists && works;
}
},
{
title: 'popcorn.getTrackEvents exists and works',
test: function() {
var exists = popcorn.getTrackEvents !== undefined;
var works = popcorn.getTrackEvents();
return exists && works;
}
},
{
title: 'popcorn.getTrackEvents === Popcorn.getTrackEvents(popcorn)',
test: function() {
var exists = popcorn.getTrackEvents !== undefined;
var tracks1 = popcorn.getTrackEvents();
var tracks2 = Popcorn.getTrackEvents(popcorn);
var works = tracks1.length === tracks2.length && tracks1[0] === tracks2[0];
return exists && works;
}
},
{
title: 'popcorn.listen exists && works',
test: function() {
var exists = popcorn.listen !== undefined;
popcorn.listen('ended', function(){});
for (var i in popcorn.data.events['ended']) {
return exists && true;
} //for
return false;
}
},
{
title: 'popcorn.removeTrackEvent exists and works',
test: function() {
var exists = popcorn.removeTrackEvent !== undefined;
popcorn.removeTrackEvent(popcorn.getLastTrackEventId());
var works = popcorn.getTrackEvents().length === 0;
return exists && works;
}
},
];
$(function() {
var content = document.getElementById('test-content');
for (var i=0; i<tests.length; ++i) {
var testItem = tests[i];
var testElement = testItem.element = document.createElement('div');
var testTitle = document.createElement('span');
var testStatus = testItem.statusElement = document.createElement('span');
var testInfo = testItem.infoElement = document.createElement('div');
testElement.className = 'test-item';
testElement.style.position = 'relative';
testTitle.innerHTML = testItem.title;
testTitle.className = 'test-title';
testStatus.className = 'test-status';
testStatus.style.position = 'absolute';
testStatus.style.right = '6px';
testStatus.innerHTML = '?';
testInfo.innerHTML = testItem.test.toString();
testInfo.className = 'test-info';
testElement.appendChild(testTitle);
testElement.appendChild(testStatus);
testElement.appendChild(document.createElement('br'));
testElement.appendChild(testInfo);
content.appendChild(testElement);
function isComplete( callback ) {
(function(){
var ti = testInfo;
var tt = testTitle;
$(ti).hide();
$(tt).click(function() {
$(ti).toggle('fast');
});
})();
} //for
if ( !QUnit.config.queue.length ) {
// is complete
var scoreDiv = document.createElement('div');
scoreDiv.className = 'test-item';
scoreDiv.innerHTML = 'Score: N/A';
scoreDiv.style.fontWeight = 'bold';
content.appendChild(document.createElement('br'));
content.appendChild(scoreDiv);
callback && callback( QUnit.config.stats );
var testIndex = 0;
var score = 0;
var waitTime = 50;
function runNextTest() {
var test = tests[testIndex];
try {
var result = test.test();
test.statusElement.innerHTML = result ? '&#x2713;' : '&#x2717;';
} else {
setTimeout(function() {
isComplete( callback );
}, 10 );
}
catch (e) {
test.statusElement.innerHTML = '!';
test.infoElement.innerHTML += '<br /><b>Error:</b> ' + e.message;
} //try
++testIndex;
if (testIndex < tests.length) {
setTimeout(runNextTest, waitTime);
} //if
score += (result ? 1 : 0);
}
scoreDiv.innerHTML = 'Score: ' + score + '/' + tests.length;
} //runNextTest
isComplete(function( qResults ) {
setTimeout(runNextTest, waitTime);
// Hack together the results as used in ready.html
results.pass = qResults.all - qResults.bad;
results.fail = qResults.bad;
results.expect = qResults.all;
$.each( results, function( name, value ) {
$("#qunit-header").before(
$("<h2>", {
className: name,
html: name + ": " + value
})
);
});
});
});
})( Popcorn, jQuery );
</script>
</head>
<body>
<div id="main-container">
<div id="title">
Butter Dependency Test
</div>
<div id="test-content">
</div>
<h3>Butter Dependencies<h3>
<h1 id="qunit-header">Butter Popcorn Dependencies</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
<ol id="qunit-tests"></ol>
<div id="qunit-fixture">
</div>
<div id="video-container">
<div id="video-container" style="display:none">
<video id="video" />
</div>
<div id="test-target">
<div id="test-target" style="display:none">
</div>
</body>
</html>

Просмотреть файл

@ -0,0 +1,10 @@
{
"data": [
{
"testAudioParser": {
"start": 5,
"end": 6
}
}
]
}

Просмотреть файл

@ -7,6 +7,7 @@
<script src="qunit/qunit.js"></script>
<!--
do not move - this must be called immediately prior to
popcorn-api-draft.js
@ -17,7 +18,13 @@
<script src="popcorn.unit.js"></script>
</head>
<body>
<h1 id="qunit-header">Popcorn API</h1>
<h1 id="qunit-header">
Popcorn.js API
<iframe id="butter-tests" data-src="butter_test.html" style="width:325px;height:60px;float:right;border:0px;margin:-10px 20px 0 -10px;background:#fff" ></iframe>
<iframe id="ready-tests" data-src="ready.html" style="width:325px;height:60px;float:right;border:0px;margin:-10px 20px 0 -10px;background:#fff" ></iframe>
</h1>
<h2 id="qunit-banner"></h2>
<div id="qunit-testrunner-toolbar"></div>
<h2 id="qunit-userAgent"></h2>
@ -30,7 +37,8 @@
</div>
<video id='video'style="display:"
<video id='video'
style="display:;width:300px"
controls
poster="poster.png">
@ -44,6 +52,64 @@
<p>Your user agent does not support the HTML5 Video element.</p>
</video>
</video>
<audio id='audio' src="italia.ogg" data-timeline-sources="data/parserData.json" controls>
</audio>
<!-- Position Tests Adapted From jQuery offsets/dimensions Tests -->
<style type="text/css" media="screen">
#position-tests { display:none;margin:0; position: fixed;top:0px;left:0px; }
div.absolute { position: absolute; margin: 0px; width: 100px; height: 100px; background: #fff; }
#absolute-1 { top: 0; left: 0; }
#absolute-1-1 { top: 1px; left: 1px; }
#absolute-1-1-1 { top: 1px; left: 1px; }
#absolute-2 { top: 19px; left: 19px; }
div.relative { position: relative; top: 0; left: 0; width: 100px; height: 100px; background: #fff; overflow: hidden; }
#relative-2 { top: 20px; left: 20px; }
div.fixed { position: fixed; width: 100px; height: 100px; background: #fff; overflow: hidden; }
#fixed-1 { top: 0; left: 0; }
#fixed-2 { top: 20px; left: 20px; }
div.static { position: static; top: 0; left: 0; width: 100px; height: 100px; background: #fff; overflow: hidden; }
#static-2 { top: 20px; left: 20px; }
</style>
<div id="position-tests">
<div id="absolute-1" class="absolute"><video id="vid-absolute-1" src="trailer.ogv" style="visibility:hidden"></video>
<div id="absolute-1-1" class="absolute"><video id="vid-absolute-1-1"" src="trailer.ogv" style="visibility:hidden"></video>
<div id="absolute-1-1-1" class="absolute"><video id="vid-absolute-1-1-1" src="trailer.ogv" style="visibility:hidden"></video></div>
</div>
</div>
<div id="absolute-2" class="absolute"><video id="vid-absolute-2" src="trailer.ogv" style="visibility:hidden"></video></div>
<div id="relative-1" class="relative"><video id="vid-relative-1" src="trailer.ogv" style="visibility:hidden"></video>
<div id="relative-1-1" class="relative"><video id="vid-relative-1-1" src="trailer.ogv" style="visibility:hidden"></video>
<div id="relative-1-1-1" class="relative"><video id="vid-relative-1-1-1" src="trailer.ogv" style="visibility:hidden"></video></div>
</div>
</div>
<div id="relative-2" class="relative"><video id="vid-relative-2" src="trailer.ogv" style="visibility:hidden"></video></div>
<div id="fixed-1" class="fixed"><video id="vid-fixed-1" src="trailer.ogv" style="visibility:hidden"></video></div>
<div id="fixed-2" class="fixed"><video id="vid-fixed-2" src="trailer.ogv" style="visibility:hidden"></video></div>
<div id="static-1" class="static"><video id="vid-static-1" src="trailer.ogv" style="visibility:hidden">
<div id="static-1-1" class="static"><video id="vid-static-1-1" src="trailer.ogv" style="visibility:hidden">
<div id="static-1-1-1" class="static"><video id="vid-static-1-1-1" src="trailer.ogv" style="visibility:hidden"></div>
</div>
</div>
<div id="static-2" class="static"><video id="vid-static-2" src="trailer.ogv" style="visibility:hidden"></div>
</div>
</body>
</html>

Двоичные данные
test/italia.ogg Normal file

Двоичный файл не отображается.

1552
test/popcorn.unit.js Executable file → Normal file

Разница между файлами не показана из-за своего большого размера Загрузить разницу

Просмотреть файл

@ -1,45 +1,103 @@
<!DOCTYPE html>
<!doctype html>
<html>
<head>
<title>Popcorn API</title>
<link rel="stylesheet" href="qunit/qunit.css" type="text/css" media="screen">
<style>
body {
margin: 3px;
padding: 0px;
}
h3 {
margin: 3px;
padding: 0px;
}
h2 {
display: inline;
margin: 3px;
padding: 0px;
}
.pass {
color: #33FF00;
}
.expect {
color: #2d2d2d;
}
.fail {
color: #FF0000;
}
</style>
<script src="jquery.js"></script>
<script src="../popcorn.js"></script>
<script>
Popcorn(function (e) {
(function( Popcorn, $ ) {
console.log("Popcorn(function() { /../ }) 1");
var p = Popcorn("#video");
p.exec( 4, function () {
var readyCalled = 0,
readyOrder = [],
readyComposed = "Ready Callback 1,Ready Callback 2",
results = {
pass: 0,
fail: 0,
expect: 2
};
$(function() {
readyCalled++;
});
}).play();
});
Popcorn(function (e) {
readyCalled++;
readyOrder.push("Ready Callback 1");
Popcorn("#video").exec( 1, function () {
results[
( readyCalled === 3 ? "pass" : "fail" )
]++;
if ( readyOrder.join(",") === readyComposed ) {
results.pass++;
}
$.each( results, function( name, value ) {
$("video").before(
$("<h2>", {
className: name,
html: name + ": " + value
})
);
});
}).play().volume(0);
});
Popcorn(function () {
readyCalled++;
readyOrder.push("Ready Callback 2");
});
})( Popcorn, jQuery );
Popcorn(function () {
console.log("Popcorn(function() { /../ }) 2");
});
</script>
</head>
<body>
<video id='video'style="display:"
<h3>Popcorn(/* DOM Ready */)<h3>
<video id='video'style="display:none"
controls
poster="http://media.w3.org/2010/05/sintel/poster.png">
poster="poster.png">
<source id='mp4'
src="http://media.w3.org/2010/05/sintel/trailer.mp4"
src="trailer.mp4"
type='video/mp4; codecs="avc1, mp4a"'>
<source id='ogv'
src="http://media.w3.org/2010/05/sintel/trailer.ogv"
src="trailer.ogv"
type='video/ogg; codecs="theora, vorbis"'>
<p>Your user agent does not support the HTML5 Video element.</p>