, create a container and routinely
+ // update its size/position to be that of the media
+ if ( target ) {
+ target.appendChild( options.figure );
+ }
+
+
+ img.src = options.src;
+
+ // options.toString = function() {
+ // var string = options.src || options._natives.manifest.options.src[ "default" ],
+ // match = string // .replace( /.*\//g, "" );
+ // return match.length ? match : string;
+ // };
+ },
+
+ /**
+ * @member figure
+ * 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.anchor.style.display = "inline";
+ options.figure.style.display = "block";
+ },
+ /**
+ * @member figure
+ * 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.figure.style.display = "none";
+ },
+ _teardown: function( options ) {
+ if ( options.trackedContainer ) {
+ options.trackedContainer.destroy();
+ }
+ else if ( options.anchor.parentNode ) {
+ options.figure.parentNode.removeChild( options.figure );
+ }
+ }
+ };
+ },
+
+ {
+ about: {
+ name: "Popcorn Figure Plugin",
+ version: "0.1",
+ author: "Scott Downe, Matt Price",
+ website: "http://scottdowne.wordpress.com/"
+ },
+ options: {
+ start: {
+ elem: "input",
+ type: "number",
+ label: "Start"
+ },
+ end: {
+ elem: "input",
+ type: "number",
+ label: "End"
+ },
+ src: {
+ elem: "input",
+ type: "url",
+ label: "Figure URL",
+ "default": "http://mozillapopcorn.org/wp-content/themes/popcorn/images/for_developers.png"
+ },
+ href: {
+ elem: "input",
+ type: "url",
+ label: "Link",
+ "default": "http://mozillapopcorn.org/wp-content/themes/popcorn/images/for_developers.png",
+ optional: true
+ },
+ id: {
+ elem: "input",
+ type: "text",
+ label: "Figure ID",
+ optional: true
+ },
+ target: "figure-container",
+ text: {
+ elem: "input",
+ type: "text",
+ label: "Caption",
+ "default": "popcorn-container",
+ optional: true
+ }
+ }
+ });
+})( Popcorn );
diff --git a/plugins/figure/popcorn.figure.unit.html b/plugins/figure/popcorn.figure.unit.html
new file mode 100755
index 000000000..2bd24e0c1
--- /dev/null
+++ b/plugins/figure/popcorn.figure.unit.html
@@ -0,0 +1,52 @@
+
+
+
+ Popcorn Imageredux Plug-in Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your user agent does not support the HTML5 Video element.
+
+
+
+
+
+
+
+
diff --git a/plugins/figure/popcorn.figure.unit.js b/plugins/figure/popcorn.figure.unit.js
new file mode 100755
index 000000000..f7cba0fdc
--- /dev/null
+++ b/plugins/figure/popcorn.figure.unit.js
@@ -0,0 +1,203 @@
+asyncTest( "Popcorn Image Plugin", function() {
+
+ var popped = Popcorn( "#video" ),
+ expects = 9,
+ count = 0,
+ setupId,
+ imagediv = document.getElementById( "imagediv" ),
+ sources = [
+ "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ "https://images.acenda-static.com/petmountain/product/retina/1500x1500/7/75bce62c59222e.jpg",
+ "http://www.botskool.com/sites/default/files/images/javascript.png"
+ ];
+
+ expect( expects );
+
+ function plus() {
+ if ( ++count === expects ) {
+ popped.destroy();
+ start();
+ }
+ }
+
+ ok( "image" in popped, "image is a method of the popped instance" );
+ plus();
+
+ equal( imagediv.innerHTML, "", "initially, there is nothing inside the imagediv" );
+ plus();
+
+ popped.image({
+ start: 1,
+ end: 3,
+ href: "http://www.drumbeat.org/",
+ src: sources[ 0 ],
+ text: "DRUMBEAT",
+ target: "imagediv"
+ })
+ .image({
+ start: 4,
+ end: 6,
+ src: sources[ 1 ],
+ target: "imagediv"
+ })
+ .image({
+ start: 5,
+ end: 6,
+ src: sources[ 2 ],
+ target: "imagediv"
+ });
+
+ setupId = popped.getLastTrackEventId();
+
+ popped.cue( 2, function() {
+ ok( imagediv.children[ 0 ].style.display !== "none", "inline", "Div contents are displayed" );
+ plus();
+ equal( imagediv.querySelector("img").nodeName, "IMG", "An image exists" );
+ plus();
+ });
+
+ popped.cue( 3, function() {
+ equal( imagediv.children[ 0 ].style.display, "none", "Div contents are hidden again" );
+ plus();
+ });
+
+ popped.cue( 5, function() {
+ [].forEach.call( document.querySelectorAll( "#imagediv a img" ), function( img, idx ) {
+ ok( img.src === sources[ idx ], "Image " + idx + " is in the right order" );
+ plus();
+ });
+ });
+
+ popped.cue( 7, function() {
+ popped.pause().removeTrackEvent( setupId );
+ ok( !imagediv.children[ 2 ], "removed image was properly destroyed" );
+ plus();
+ });
+
+ popped.volume( 0 ).play();
+});
+
+asyncTest( "Zerostart doesn't rehide", 1, function() {
+ var popped = Popcorn( "#video" ),
+ zerostart = document.getElementById( "zerostart" );
+
+ popped.on( "canplayall", function() {
+ popped.currentTime(0);
+
+ popped.image({
+ start: 0,
+ end: 3,
+ src: "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ target: "zerostart"
+ });
+
+ popped.cue( 1, function() {
+ ok( zerostart.children[ 0 ].style.display !== "none", "display area displayed at start: 0 without re-hiding" );
+ popped.destroy();
+ start();
+ });
+
+ popped.play();
+ });
+});
+
+asyncTest( "size test", 4, function() {
+
+ var popped = Popcorn( "#video" ),
+ withsize = document.getElementById( "withsize" ),
+ withoutsizeinsize = document.getElementById( "withoutsizeinsize" );
+
+ popped.on( "canplayall", function() {
+ popped.currentTime(0);
+
+ // images take on the size of the parent, if the parent has a size
+ popped.image({
+ start: 0,
+ end: 3,
+ src: "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ target: "withsize"
+ });
+
+ // multiple images take on the size of the original parent,
+ // and not the size of the parent with an image. Testing 3 images total.
+ popped.image({
+ start: 0,
+ end: 3,
+ src: "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ target: "withsize"
+ }).image({
+ start: 0,
+ end: 3,
+ src: "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ target: "withsize"
+ });
+
+ // should only take on the size if it is explicitly defined in the parent,
+ // so not the parent's parent
+ popped.image({
+ start: 0,
+ end: 3,
+ src: "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ target: "withoutsizeinsize"
+ });
+
+ popped.cue( 1, function() {
+
+ equal( withsize.children[ 0 ].children[ 0 ].offsetWidth, 400, "display area displayed at start: 0 without re-hiding" );
+ equal( withsize.children[ 1 ].children[ 0 ].offsetWidth, 400, "display area displayed at start: 0 without re-hiding" );
+ equal( withsize.children[ 2 ].children[ 0 ].offsetWidth, 400, "display area displayed at start: 0 without re-hiding" );
+ equal( withoutsizeinsize.children[ 0 ].children[ 0 ].offsetWidth, 300, "display area displayed at start: 0 without re-hiding" );
+ start();
+ });
+
+ popped.play();
+ });
+});
+
+asyncTest( "media element target test", 2, function() {
+ var popped = Popcorn( "#video" );
+ popped.on( "canplayall", function() {
+ popped.image({
+ start: 1,
+ end: 4,
+ src: "http://www.drumbeat.org/images/drumbeat-logo-splash.png",
+ target: "video"
+ });
+ popped.pause();
+ popped.currentTime( 2 );
+ ok( document.querySelector( "div[data-popcorn-helper-container]" ), "helper element was created" );
+ popped.currentTime( 5 );
+ popped.destroy();
+ ok( !document.querySelector( "div[data-popcorn-helper-container]" ), "helper element was removed" );
+ start();
+ });
+
+});
+
+asyncTest( "Overriding default toString", 3, function() {
+ var p = Popcorn( "#video" ),
+ srcText = "http://www.stirinoi.com/wp-content/uploads/2012/05/grass-5.jpg",
+ fullURLText = "http://www.testing.com/asdf/",
+ lastEvent;
+
+ function testLastEvent( compareText, message ) {
+ lastEvent = p.getTrackEvent( p.getLastTrackEventId() );
+ equal( lastEvent.toString(), compareText, message );
+ }
+
+ p.image({
+ src: srcText,
+ target: "imagediv"
+ });
+ testLastEvent( srcText.replace( /.*\//, "" ), "Custom text displayed with toString" );
+ p.image({
+ src: fullURLText,
+ target: "imagediv"
+ });
+ testLastEvent( fullURLText, "Custom text displayed with toString" );
+
+ p.image({});
+ testLastEvent( "for_developers.png", "Custom text displayed with toString using default" );
+
+ start();
+});
diff --git a/plugins/footnote/popcorn.footnote.js b/plugins/footnote/popcorn.footnote.js
index d4631fce3..7c10d816a 100755
--- a/plugins/footnote/popcorn.footnote.js
+++ b/plugins/footnote/popcorn.footnote.js
@@ -24,6 +24,7 @@
* });
**/
+ var i = 0;
Popcorn.plugin( "footnote", {
manifest: {
@@ -49,6 +50,11 @@
type: "text",
label: "Text"
},
+ id: {
+ elem: "input",
+ type: "text",
+ label: "ID"
+ },
target: "footnote-container"
}
},
@@ -59,8 +65,10 @@
options._container = document.createElement( "div" );
options._container.style.display = "none";
+ options._container.id = options.id || `footnotediv${i}`;
+ options._container.classList.add("footnote-plugin");
options._container.innerHTML = options.text;
-
+ i++;
target.appendChild( options._container );
},
diff --git a/plugins/googlemap/popcorn.googlemap.html b/plugins/googlemap/popcorn.googlemap.html
index 0f56cba04..060434404 100755
--- a/plugins/googlemap/popcorn.googlemap.html
+++ b/plugins/googlemap/popcorn.googlemap.html
@@ -10,6 +10,10 @@
var p = Popcorn( "#video" )
.volume( 0 )
.play()
+ .defaults ('googlemap', {
+ target: 'map',
+ apiKey: ""
+ })
.googlemap({
start: 0,
end: 10,
@@ -39,8 +43,8 @@
zoom: "1",
heading: "180",
pitch: "1",
- interval: 1000,
- tween: "York university"
+ interval: 1000,
+ tween: "York university"
})
.googlemap({
start: 0,
@@ -150,10 +154,18 @@
});
}, false);
+
-
+
+ The Google Maps API will no longer load without an API Key! Be sure to add yours to the .defaults()
on line 17 of this page.
A Google Map displaying Toronto, Ontario will appear at 0 seconds and disappear at 20 seconds.
A Google Map Streetview image of Toronto, Ontario will appear at 0 seconds and disaapear at 20 seconds
A Google Map displaying Boston will appear at 0 seconds and disappear at 30 seconds.
diff --git a/plugins/googlemap/popcorn.googlemap.js b/plugins/googlemap/popcorn.googlemap.js
index c1af0d8d3..c2722d947 100755
--- a/plugins/googlemap/popcorn.googlemap.js
+++ b/plugins/googlemap/popcorn.googlemap.js
@@ -13,7 +13,7 @@ var googleCallback;
// before setting _maploaded to true
if ( typeof google !== "undefined" && google.maps && google.maps.Geocoder && google.maps.LatLng ) {
geocoder = new google.maps.Geocoder();
- Popcorn.getScript( "//maps.stamen.com/js/tile.stamen.js", function() {
+ Popcorn.getScript( "https://maps.stamen.com/js/tile.stamen.js", function() {
_mapLoaded = true;
});
} else {
@@ -23,11 +23,11 @@ var googleCallback;
}
};
// function that loads the google api
- loadMaps = function () {
+ loadMaps = function (apiKey) {
// for some reason the Google Map API adds content to the body
if ( document.body ) {
_mapFired = true;
- Popcorn.getScript( "//maps.google.com/maps/api/js?sensor=false&callback=googleCallback" );
+ Popcorn.getScript( "https://maps.googleapis.com/maps/api/js?key="+apiKey+"&callback=googleCallback&libraries=geometry");
} else {
setTimeout(function () {
loadMaps();
@@ -81,6 +81,7 @@ var googleCallback;
* -Location: the adress you want the map to display, must be present if lat and lng are not specified.
* Note: using location requires extra loading time, also not specifying both lat/lng and location will
* cause and error.
+ * -apiKey is your Google Maps API key
*
* Tweening works using the following specifications:
* -location is the start point when using an auto generated route
@@ -104,7 +105,8 @@ var googleCallback;
type: "ROADMAP",
target: "map",
lat: 43.665429,
- lng: -79.403323
+ lng: -79.403323,
+ apiKey: "AO3ousdflj4_slkjwmsdmmr"
} )
*
*/
@@ -120,7 +122,7 @@ var googleCallback;
// if this is the firest time running the plugins
// call the function that gets the sctipt
if ( !_mapFired ) {
- loadMaps();
+ loadMaps(options.apiKey);
}
// create a new div this way anything in the target div is left intact
@@ -389,7 +391,7 @@ var googleCallback;
}, {
about: {
name: "Popcorn Google Map Plugin",
- version: "0.1",
+ version: "0.2",
author: "@annasob",
website: "annasob.wordpress.com"
},
@@ -449,6 +451,12 @@ var googleCallback;
label: "Pitch",
"default": 1,
optional: true
+ },
+ apiKey: {
+ elem: "input",
+ type: "text",
+ label: "ApiKey",
+ optional: false
}
}
});
diff --git a/plugins/leaflet/popcorn.leaflet.html b/plugins/leaflet/popcorn.leaflet.html
new file mode 100755
index 000000000..54000a4ca
--- /dev/null
+++ b/plugins/leaflet/popcorn.leaflet.html
@@ -0,0 +1,117 @@
+
+
+
+ Popcorn Leaflet Map Plug-in Demo
+
+
+
+
+
+
+ An OpenLayers + OpenStreetMap Map displaying Toronto, Ontario will appear at 0 seconds and disappear at 20 seconds.
+ An OpenLayers + NASA LandSat Map displaying Boston and a map marker will appear at 0 seconds and disappear at 30 seconds.
+ An OpenLayers + USGS Topographic Map displaying Punxsutawney, PA will appear at 0 seconds and disappear at 20 seconds.
+
+
+
+
+
+
+
+ Your user agent does not support the HTML5 Video element.
+
+
+
+
+
+
+
diff --git a/plugins/leaflet/popcorn.leaflet.js b/plugins/leaflet/popcorn.leaflet.js
new file mode 100755
index 000000000..a161a40ef
--- /dev/null
+++ b/plugins/leaflet/popcorn.leaflet.js
@@ -0,0 +1,367 @@
+// PLUGIN: LEAFLET
+( function ( Popcorn ) {
+
+ /**
+ * leaflet popcorn plug-in
+ * Adds a leaflet map and open map tiles (OpenStreetMap [default], Mapbox Satellite/Terrain,
+ * or Stamen (toner/wateroclor/terrain))
+ *
+ * Based on the openmap popcorn plug-in. No StreetView support
+ *
+ * Options parameter will need a start, end, target, type, zoom, lat and lng, and apikKy
+ * -Start is the time that you want this plug-in to execute
+ * -End is the time that you want this plug-in to stop executing
+ * -Target is the id of the DOM element that you want the map to appear in. This element must be in the DOM
+ * -Type [optional] either: ROADMAP (OpenStreetMap), SATELLITE (Mapbox Satellite), TERRAIN (Mapbox Outdoors), or COMIC (Mapbox Comic).
+ * The Stamen custom map types can also be used (http://maps.stamen.com): STAMEN-TONER,
+ * STAMEN-TERRAIN, or STAMEN-WATERCOLOR.
+ * -Zoom [optional] defaults to 2
+ * -Lat and Lng are the coordinates of the map if location is not named
+ * -Location is a name of a place to center the map, geocoded to coordinates using Mapbox geocoding API
+ * -Markers [optional] is an array of map marker objects, with the following properties:
+ * --Icon is the URL of a map marker image
+ * --Size [optional] is the radius in pixels of the scaled marker image (default is 14)
+ * --Text [optional] is the HTML content of the map marker -- if your popcorn instance is named 'popped', use to control the video
+ * --Lat and Lng are coordinates of the map marker if location is not specified
+ * --Location is a name of a place for the map marker, geocoded to coordinates using mapbox
+ * Note: using location requires extra loading time; also failure to specify one of lat/lng or location will
+ * cause a JavaScript error.
+ * - apiKey is your Mpabox API Key. This is required, and recommended to be set using
+ * Popcorn's `defaults` property
+ * - fly is an object that moves the map from the initial location to a desired endpoint
+ * -- endpoint is either a lat/long array or a string to be geocoded
+ * -- wait is the length of time to wait after the start evnet fires
+ * -- flightLength is the length of time ti take moving the map
+ * @param {Object} options
+ *
+ * Example:
+ var p = Popcorn( '#video' )
+ .leaflet({
+ start: 5,
+ end: 15,
+ type: 'ROADMAP',
+ target: 'map',
+ lat: 43.665429,
+ lng: -79.403323
+ })
+ *
+ */
+
+ //console.log(options);
+ var newdiv, persistentmap , persistentFlyEndpoint,
+ i = 1;
+
+ function toggle( container, display ) {
+ if ( container.map ) {
+ container.map.div.style.display = display;
+ return;
+ }
+
+ setTimeout(function() {
+ toggle( container, display );
+ }, 10 );
+ }
+
+ function flyMap (map, flyOptions) {
+ // console.log(flyOptions);
+ setTimeout ( function () {
+ map.panTo(flyOptions.endpoint, {animate: true, duration: flyOptions.flightLength});
+ }, flyOptions.wait * 1000);
+ }
+
+ Popcorn.plugin( "leaflet", function( options ){
+ var newdiv,
+ centerlonlat,
+ projection,
+ displayProjection,
+ pointLayer,
+ selectControl,
+ popup,
+ isGeoReady,
+ target = document.getElementById( options.target );
+
+ // create a new div within the target div
+ // this is later passed on to the maps api
+ newdiv = document.createElement( "div" );
+ newdiv.style.display = "none";
+ newdiv.id = "leafletdiv" + i;
+ newdiv.classList.add("leaflet-plugin");
+ newdiv.style.width = "100%";
+ newdiv.style.height = "100%";
+ i++;
+
+ if (target)
+ {target.appendChild( newdiv );}
+
+ var center;
+
+
+ return {
+ /**
+ * @member leaflet
+ * The setup function will be executed when the plug-in is instantiated
+ */
+ _setup: async function( options ) {
+ // grab the leafvar css, we need it!
+ var head = document.querySelector('head');
+ var link = document.createElement('link');
+ link.rel = 'stylesheet';
+ link.type = 'text/css';
+ link.href = "https://unpkg.com/leaflet@1.5.1/dist/leaflet.css";
+ // insert leaflet api script once
+ if ( !window.L ) {
+ head.appendChild(link);
+ Popcorn.getScript( "https://unpkg.com/leaflet@1.5.1/dist/leaflet.js" , function() {
+ console.log('empty callback');
+ // insert jquery -- not sure why I need this though!
+ // Popcorn.getScript( "https://code.jquery.com/jquery-3.4.1.min.js" );
+ } );
+ }
+
+ gc = async function( location ) {
+ let point;
+ let url = `https://api.mapbox.com/geocoding/v5/mapbox.places/${location}.json?limit=1&access_token=${options.apiKey}`;
+ return await fetch (url)
+ .then ( (response) => response.json() )
+ .then ( (json) => {let c = json.features[0].center; return L.latLng([ c[1], c[0] ]); } );
+ };
+
+ // callback function fires when the script is run
+ isGeoReady = async function() {
+ if ( ! ( window.L ) ) {
+ setTimeout(function() {
+ isGeoReady();
+ }, 50);
+ } else {
+ if ( options.location ) {
+ center = await gc(options.location);
+ } else {
+ options.lat = options.lat || 51;
+ options.lng = options.lng || -1.5;
+ center = L.latLng( options.lat, options.lng );
+ }
+ // console.log ('center is ' + center + options.location || '');
+
+ persistentmap = options.map = L.map(newdiv).setView(center, options.zoom || 12);
+
+ // persistentmap ? console.log('pmap exists') : console.log ('pmap does NOT exist');
+ options.type = options.type || "ROADMAP";// XXX:
+ switch( options.type ) {
+ case "SATELLITE" :
+ L.tileLayer(`https://api.mapbox.com/v4/mapbox.satellite/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoidGl0YW5pdW1ib25lcyIsImEiOiJjazF0bTdlNXQwM3gxM2hwbXY0bWtiamM3In0.FFPm7UIuj_b15xnd7wOQig`, {
+ attribution: '© OpenStreetMap contributors'
+ })
+ .addTo(options.map);
+ // console.log(options.map);
+ break;
+ case "TERRAIN":
+ // add terrain map ( USGS )
+ L.tileLayer(`https://api.mapbox.com/v4/mapbox.outdoors/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoidGl0YW5pdW1ib25lcyIsImEiOiJjazF0bTdlNXQwM3gxM2hwbXY0bWtiamM3In0.FFPm7UIuj_b15xnd7wOQig`, {
+ attribution: '© OpenStreetMap contributors'
+ })
+ .addTo(options.map);
+ break;
+ case "STAMEN-TONER":
+ L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/toner/{z}/{x}/{y}{r}.{ext}', {
+ attribution: 'Map tiles by Stamen Design , CC BY 3.0 — Map data © OpenStreetMap contributors',
+ subdomains: 'abcd',
+ minZoom: 0,
+ maxZoom: 20,
+ ext: 'png'
+ })
+ .addTo(options.map);
+ break;
+ case "STAMEN-WATERCOLOR":
+ L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/watercolor/{z}/{x}/{y}{r}.{ext}', {
+ attribution: 'Map tiles by Stamen Design , CC BY 3.0 — Map data © OpenStreetMap contributors',
+ subdomains: 'abcd',
+ minZoom: 0,
+ maxZoom: 20,
+ ext: 'png'
+ })
+ // L.tileLayer(`https://api.mapbox.com/v4/mapbox.comic/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoidGl0YW5pdW1ib25lcyIsImEiOiJjazF0bTdlNXQwM3gxM2hwbXY0bWtiamM3In0.FFPm7UIuj_b15xnd7wOQig`, {
+ // attribution: '© OpenStreetMap contributors'
+ // })
+ .addTo(options.map);
+ break;
+ case "STAMEN-TERRAIN":
+ console.log("st terrain")
+ L.tileLayer('https://stamen-tiles-{s}.a.ssl.fastly.net/terrain/{z}/{x}/{y}{r}.{ext}', {
+ attribution: 'Map tiles by Stamen Design , CC BY 3.0 — Map data © OpenStreetMap contributors',
+ subdomains: 'abcd',
+ minZoom: 0,
+ maxZoom: 20,
+ ext: 'png'
+ })
+ .addTo(options.map);
+ break;
+ case "COMIC":
+ L.tileLayer(`https://api.mapbox.com/v4/mapbox.comic/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoidGl0YW5pdW1ib25lcyIsImEiOiJjazF0bTdlNXQwM3gxM2hwbXY0bWtiamM3In0.FFPm7UIuj_b15xnd7wOQig`, {
+ attribution: '© OpenStreetMap contributors'
+ })
+ .addTo(options.map);
+
+ default: /* case "ROADMAP": */
+ // add OpenStreetMap layer
+ L.tileLayer('https://{s}.tile.openstreetmap.org/{z}/{x}/{y}.png', {
+ attribution: '© OpenStreetMap contributors'
+ }).addTo(options.map);
+ break;
+ }
+ if (options.fly && options.fly.endpoint ) {
+ switch (typeof (options.fly.endpoint)) {
+ case "string":
+ persistentFlyEndpoint = await gc(options.fly.endpoint);
+ break;
+ case "array":
+ persistentFlyEndpoint = options.fly.endpoint;
+ break;
+ }
+ }
+
+
+ if ( options.map ) {
+ options.map.div = newdiv;
+ }
+ }
+ };
+
+ await isGeoReady();
+
+ var isReady = async function() {
+ // wait until OpenLayers has been loaded, and the start function is run, before adding map
+ if ( !options.map ) {
+ setTimeout(function() {
+ isReady();
+ }, 13 );
+ } else {
+
+ // default zoom is 2
+ options.zoom = options.zoom || 2;
+
+ // make sure options.zoom is a number
+ if ( options.zoom && typeof options.zoom !== "number" ) {
+ options.zoom = +options.zoom;
+ }
+
+ // reset the location and zoom just in case the user played with the map
+ options.map.setView( center, options.zoom );
+ if ( options.markers ) {
+
+ for (var m of options.markers) {
+ var o;
+ if (m.location) {
+ o = L.marker(await gc(m.location)).addTo(options.map);
+ if (m.text) o.bindPopup(m.text);
+ } else if (m.lat && m.lng) {
+ o = L.marker([m.lat, m.lng]).addTo(options.map);
+ if (m.text) o.bindPopup(m.text);
+ } else {console.log ("marker is missing lat/lng and location, unable to add")}
+ }
+
+ }
+ }
+ //persistentmap ? console.log('pmap exists') : console.log ('pmap does NOT exist');
+
+ };
+
+ await isReady();
+ },
+
+ /**
+ * @member leaflet
+ * 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 ) {
+ //persistentmap ? console.log('pmap exists') : console.log ('pmap does NOT exist');
+
+ toggle( options, "block" );
+ if (options.fly) {
+ //console.log (persistentmap)
+ if (persistentFlyEndpoint) {options.fly.endpoint = persistentFlyEndpoint}
+ flyMap (persistentmap, options.fly);
+ }
+ },
+ /**
+ * @member leaflet
+ * 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 ) {
+ toggle( options, "none" );
+ },
+
+ _teardown: function( options ) {
+
+ target && target.removeChild( newdiv );
+ newdiv = map = centerlonlat = projection = displayProjection = pointLayer = selectControl = popup = null;
+ }
+ };
+ },
+ {
+ about:{
+ name: "Popcorn Leaflet Plugin",
+ version: "0.1",
+ author: "@mapmeld, @titaniumbones",
+ website: "https://digitalhistory.github.io"
+ },
+ options:{
+ start: {
+ elem: "input",
+ type: "number",
+ label: "Start"
+ },
+ end: {
+ elem: "input",
+ type: "number",
+ label: "End"
+ },
+ target: "map-container",
+ type: {
+ elem: "select",
+ options: [ "ROADMAP", "SATELLITE", "TERRAIN" ],
+ label: "Map Type",
+ optional: true
+ },
+ zoom: {
+ elem: "input",
+ type: "number",
+ label: "Zoom",
+ "default": 2
+ },
+ lat: {
+ elem: "input",
+ type: "text",
+ label: "Lat",
+ optional: true
+ },
+ lng: {
+ elem: "input",
+ type: "text",
+ label: "Lng",
+ optional: true
+ },
+ location: {
+ elem: "input",
+ type: "text",
+ label: "Location",
+ "default": "Toronto, Ontario, Canada"
+ },
+ markers: {
+ elem: "input",
+ type: "text",
+ label: "List Markers",
+ optional: true
+ },
+ apiKey: {
+ elem: "input",
+ type: "text",
+ label: "ApiKey",
+ optional: false
+ }
+ }
+ });
+}) ( Popcorn );
diff --git a/plugins/leaflet/popcorn.leaflet.unit.html b/plugins/leaflet/popcorn.leaflet.unit.html
new file mode 100755
index 000000000..ee63ce3e2
--- /dev/null
+++ b/plugins/leaflet/popcorn.leaflet.unit.html
@@ -0,0 +1,51 @@
+
+
+
+ Popcorn Leaflet Test
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your user agent does not support the HTML5 Video element.
+
+
+
+
+
+
+
+
diff --git a/plugins/leaflet/popcorn.leaflet.unit.js b/plugins/leaflet/popcorn.leaflet.unit.js
new file mode 100755
index 000000000..777e78e62
--- /dev/null
+++ b/plugins/leaflet/popcorn.leaflet.unit.js
@@ -0,0 +1,108 @@
+test( "Popcorn Leaflet Plugin", function() {
+
+ var popped = Popcorn( "#video" ),
+ expects = 15,
+ count = 0;
+
+ expect( expects );
+
+ function plus() {
+ if ( ++count === expects ) {
+ start();
+ }
+ }
+
+ stop();
+
+ ok( "leaflet" in popped, "leaflet is a method of the popped instance" );
+ plus();
+
+ ok( document.getElementById( "map" ).innerHTML === "", "initially, there is nothing inside map" );
+ plus();
+
+ ok( document.getElementById( "map2" ).innerHTML === "", "initially, there is nothing inside map2" );
+ plus();
+
+ ok( document.getElementById( "map3" ).innerHTML === "", "initially, there is nothing inside map3" );
+ plus();
+
+ popped.leaflet({
+ start: 0,
+ end: 4,
+ type: "ROADMAP",
+ target: "map",
+ lat: 43.665429,
+ lng: -79.403323,
+ zoom: 10
+ })
+ .leaflet({
+ start: 0,
+ end: 4,
+ type: "SATELLITE",
+ target: "map2",
+ lat: 40.943926,
+ lng: -78.968525,
+ zoom: 9
+ })
+ .leaflet({
+ start: 0,
+ end: 4,
+ type: "TERRAIN",
+ target: "map3",
+ lat: 40.943926,
+ lng: -78.968525,
+ zoom: 14
+ })
+ .leaflet({
+ start: 0,
+ end: 4,
+ target: "map4",
+ lat: 40.943926,
+ lng: -78.968525,
+ zoom: "14"
+ })
+ .volume( 0 )
+ .play();
+
+ setupId = popped.getLastTrackEventId();
+
+ popped.exec( 3, function() {
+ ok( L, "Leaflet is available" );
+ plus();
+
+ ok( document.getElementById( "leafletdiv1" ), "First map is on the page" );
+ plus();
+
+ equal( document.getElementById( "leafletdiv1" ).offsetParent.id, "map", "First map is inside the 'map' div" );
+ plus();
+
+ ok( document.getElementById( "leafletdiv2" ), "Second map is on the page" );
+ plus();
+
+ equal( document.getElementById( "leafletdiv2" ).offsetParent.id, "map2", "Second map is inside the 'map2' div" );
+ plus();
+
+ ok( document.getElementById( "leafletdiv3" ), "Third map is on the page" );
+ plus();
+
+ equal( document.getElementById( "leafletdiv3" ).offsetParent.id, "map3", "Third map is inside the 'map3' div" );
+ plus();
+
+ ok( document.getElementById( "leafletdiv4" ), "Fourth map is on the page" );
+ plus();
+ equal( document.getElementById( "leafletdiv4" ).offsetParent.id, "map4", "Fourth map is inside the 'map4' div" );
+ plus();
+
+ } )
+ .exec( 5, function() {
+ ok( document.getElementById( "leafletdiv2" ).style.display === "none" &&
+ document.getElementById( "leafletdiv3" ).style.display === "none" &&
+ document.getElementById( "leafletdiv4" ).style.display === "none" &&
+ document.getElementById( "leafletdiv1" ).style.display === "none", "All three maps are no longer visible" );
+ plus();
+ popped.pause().removeTrackEvent( setupId );
+ ok( !document.getElementById( "actualmap3" ), "removed map was properly destroyed" );
+ plus();
+
+ });
+});
diff --git a/plugins/markdown/popcorn.markdown.html b/plugins/markdown/popcorn.markdown.html
new file mode 100755
index 000000000..6185ac06e
--- /dev/null
+++ b/plugins/markdown/popcorn.markdown.html
@@ -0,0 +1,69 @@
+
+
+
+ Popcorn Markdown Plugin Demo
+
+
+
+
+
+
+
+ Markdown - http://markdown.github.com/
+
Markdown generated HTML templates, created using various data methods, are shown at 5, 15, and 25 seconds.
+
+
+
+
+
+
+
+ Your user agent does not support the HTML5 Video element.
+
+
+
+
+
+
diff --git a/plugins/markdown/popcorn.markdown.js b/plugins/markdown/popcorn.markdown.js
new file mode 100755
index 000000000..d04229268
--- /dev/null
+++ b/plugins/markdown/popcorn.markdown.js
@@ -0,0 +1,150 @@
+// PLUGIN: Markdown
+
+(function ( Popcorn ) {
+
+ /**
+ * Markdown Popcorn Plug-in
+ *
+ * Adds the ability to render Markdown using markdown-it on the fly rendering.
+ * Largely copied from Mustache plugin by David Humphrey.
+ *
+ * In initial form, uses opinionated defaults consistent with student expectations in
+ * https://github.com/DigitalHistory/advanced-topics. We therefore enable tables & use
+ * `attrs`, `emoji`, and `footnote` plugins.
+ *
+ * @param {Object} options
+ *
+ * Required parameters: start, end, markdown, and target.
+ *
+ * start: the time in seconds when the markdown template should be rendered
+ * in the target div.
+ *
+ * end: the time in seconds when the rendered markdown template should be
+ * removed from the target div.
+ *
+ * target: a String -- the target div's id.
+ *
+ * text: the markdown text to be rendered using the markdown template. Should be a string.
+ *
+ *
+ * Example:
+ var p = Popcorn('#video')
+
+ // Example using strings.
+ .markdown({
+ start: 5, // seconds
+ end: 15, // seconds
+ target: 'markdown',
+ markdown: `## Header 2
+Paragraph with **bold** _ital_ and :rocket: emoji`
+ } )
+
+ *
+ */
+
+
+ // I don't really understand what this external scope context is, but it seems I can
+ // set some variables out here which will persist across all the markdown plugin instances
+ // and that let works as well as var.
+ // It also doesn't seem to pollute the global scope.
+ let i = 0;
+
+ Popcorn.plugin( "markdown" , function( options ){
+
+ var markdown;
+
+ Popcorn.getScript( "https://cdnjs.cloudflare.com/ajax/libs/markdown-it/10.0.0/markdown-it.js",
+ function () {
+ Popcorn.getScript("https://cdn.jsdelivr.net/npm/markdown-it-attrs@2.3.2/markdown-it-attrs.browser.js");
+ Popcorn.getScript("https://cdn.jsdelivr.net/npm/markdown-it-footnote@3.0.1/dist/markdown-it-footnote.min.js");
+ Popcorn.getScript("https://cdn.jsdelivr.net/npm/markdown-it-emoji@1.4.0/dist/markdown-it-emoji.js");
+ });
+
+ var target = document.getElementById( options.target );
+
+ let newdiv;
+
+ newdiv = document.createElement( "div" );
+ newdiv.id = "markdowndiv" + i;
+ newdiv.classList.add("markdown-plugin");
+ newdiv.style.display = "none";
+ i++;
+ if (target)
+ {target.appendChild( newdiv );}
+
+ markdown = options.text || "" ;
+
+ options.container = target || document.createElement( "div" );
+
+ return {
+ start: function( event, options ) {
+
+ var interval = function() {
+
+ if( !window.markdownitEmoji || !window.markdownitFootnote || !window.markdownItAttrs ) {
+ // console.log ('markdownit tests fail');
+ // console.log(window.markdownitEmoji);
+ // console.log(window.markdownitFootnote);
+ // console.log(window.markdownItAttrs);
+ setTimeout( function() {
+ interval();
+ }, 100 );
+ } else {
+ // console.log("md should have all plugins loaded")
+ let parser = window.markdownit('commonmark', {
+ html: true,
+ linkify: true});
+ /* use footnote, attribute and emoji plugins */
+ parser.use(window.markdownItAttrs);
+ parser.use(window.markdownitFootnote);
+ parser.use(window.markdownitEmoji);
+ /* enable tables */
+ parser.enable('table');
+
+ let html = parser.render( markdown
+ ).replace( /^\s*/mg, "" );
+ newdiv.innerHTML = html;
+ newdiv.style.display = "block";
+ }
+ };
+
+ interval();
+
+ },
+
+ end: function( event, options ) {
+ //newdiv.innerHTML = "";
+ newdiv.style.display = "none";
+ },
+ _teardown: function( options ) {
+ markdown = null;
+ }
+ };
+ },
+ {
+ about: {
+ name: "Popcorn Markdown Plugin",
+ version: "0.1",
+ author: "David Humphrey (@humphd), Matt Price (@titaniumbones)",
+ website: "https://github.com/DigitalHistory"
+ },
+ options: {
+ start: {
+ elem: "input",
+ type: "number",
+ label: "Start"
+ },
+ end: {
+ elem: "input",
+ type: "number",
+ label: "End"
+ },
+ target: "markdown-container",
+ text: {
+ elem: "input",
+ type: "text",
+ label: "Markdown Text"
+ }
+ }
+ });
+})( Popcorn );
diff --git a/plugins/markdown/popcorn.markdown.unit.html b/plugins/markdown/popcorn.markdown.unit.html
new file mode 100755
index 000000000..7365c7c14
--- /dev/null
+++ b/plugins/markdown/popcorn.markdown.unit.html
@@ -0,0 +1,52 @@
+
+
+
+ Popcorn API
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+ Your user agent does not support the HTML5 Video element.
+
+
+
+
+
diff --git a/plugins/markdown/popcorn.markdown.unit.js b/plugins/markdown/popcorn.markdown.unit.js
new file mode 100755
index 000000000..46abe38b1
--- /dev/null
+++ b/plugins/markdown/popcorn.markdown.unit.js
@@ -0,0 +1,60 @@
+test( "Popcorn Markdown Plugin", function() {
+
+ var popped = Popcorn( "#video" ),
+ expects = 4,
+ count = 0,
+ markdownDiv = document.querySelector( "#markdown-div" );
+
+ expect( expects );
+
+ function plus() {
+ if ( ++count === expects ) {
+ start();
+ }
+ }
+
+ stop();
+
+ ok( "markdown" in popped, "markdown is a method of the popped instance" );
+ plus();
+
+ equal( markdownDiv.innerHTML, "", "initially, there is nothing inside the markdown-div" );
+ plus();
+
+ // Static strings
+ popped.markdown({
+ start: 0,
+ end: 4,
+ markdown: "# markdown - test 1/2",
+ target: "markdown-div",
+ dynamic: false
+ })
+ .markdown({
+ start: 4,
+ end: 5,
+ markdown: "# markdown - test 2/2" ,
+ target: "markdown-div",
+ dynamic: false
+ });
+
+ function runTest( a, b ) {
+ let thisInstance = markdownDiv.querySelector(`#markdowndiv${a}`)
+ console.log(thisInstance.innerHTML);
+ equal( thisInstance.innerHTML, "markdown - test " + (a + 1) + "/2<\/h1>\n", "Markdown template rendered" );
+ plus();
+ }
+
+ popped.cue( 2.5, function() {
+ runTest( 0 );
+ })
+ .cue( 4.5, function() {
+ runTest( 1 );
+ });
+
+ // empty track events should be safe
+ Popcorn.plugin.debug = true;
+ popped.markdown({});
+
+ popped.volume( 0 );
+ setTimeout (() => popped.play(), 3);
+});
diff --git a/plugins/markdown/quicktest.html b/plugins/markdown/quicktest.html
new file mode 100644
index 000000000..9e0c62d15
--- /dev/null
+++ b/plugins/markdown/quicktest.html
@@ -0,0 +1,22 @@
+
+
+
+
+ Document
+
+
+
+
+
+
+
+
+
+
diff --git a/plugins/text/popcorn.text.js b/plugins/text/popcorn.text.js
index 37f2d9795..47a808b87 100755
--- a/plugins/text/popcorn.text.js
+++ b/plugins/text/popcorn.text.js
@@ -10,6 +10,7 @@
* 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
* Text: Is the text that you want to appear in the target
+ * ID: ID for div (defaults to `textdiv${i}`)
* Escape: {true|false} Whether to escape the text (e.g., html strings)
* Multiline: {true|false} Whether newlines should be turned into s
* Target: Is the ID of the element where the text should be placed. An empty target
@@ -67,7 +68,7 @@
* HTML escape code from mustache.js, used under MIT Licence
* https://github.com/janl/mustache.js/blob/master/mustache.js
**/
- var escapeMap = {
+ const escapeMap = {
"&": "&",
"<": "<",
">": ">",
@@ -117,45 +118,11 @@
return ctxContainer;
}
+ var i = 0;
- Popcorn.plugin( "text", {
-
- manifest: {
- about: {
- name: "Popcorn Text Plugin",
- version: "0.1",
- author: "@humphd"
- },
- options: {
- start: {
- elem: "input",
- type: "number",
- label: "Start"
- },
- end: {
- elem: "input",
- type: "number",
- label: "End"
- },
- text: {
- elem: "input",
- type: "text",
- label: "Text",
- "default": "Popcorn.js"
- },
- escape: {
- elem: "input",
- type: "checkbox",
- label: "Escape"
- },
- multiline: {
- elem: "input",
- type: "checkbox",
- label: "Multiline"
- }
- }
- },
-
+ Popcorn.plugin( "text", function (options ) {
+
+ return {
_setup: function( options ) {
var target,
@@ -163,7 +130,10 @@
container = options._container = document.createElement( "div" );
container.style.display = "none";
-
+ container.id = options.id || `textdiv${i}`;
+ container.classList.add("text-plugin");
+ i++;
+ console.log(container.classList)
if ( options.target ) {
// Try to use supplied target
target = Popcorn.dom.find( options.target );
@@ -228,5 +198,46 @@
target.removeChild( options._container );
}
}
+ }
+ }, {
+ about: {
+ name: "Popcorn Text Plugin",
+ version: "0.1",
+ author: "@humphd"
+ },
+ options: {
+ start: {
+ elem: "input",
+ type: "number",
+ label: "Start"
+ },
+ end: {
+ elem: "input",
+ type: "number",
+ label: "End"
+ },
+ text: {
+ elem: "input",
+ type: "text",
+ label: "Text",
+ "default": "Popcorn.js"
+ },
+ escape: {
+ elem: "input",
+ type: "checkbox",
+ label: "Escape"
+ },
+ multiline: {
+ elem: "input",
+ type: "checkbox",
+ label: "Multiline"
+ },
+ id: {
+ elem: "input",
+ type: "text",
+ label: "ID",
+ optional: true
+ }
+ }
});
})( Popcorn );
diff --git a/plugins/timeline/popcorn.timeline.html b/plugins/timeline/popcorn.timeline.html
index de62239a4..685b1a5b8 100644
--- a/plugins/timeline/popcorn.timeline.html
+++ b/plugins/timeline/popcorn.timeline.html
@@ -6,61 +6,67 @@
diff --git a/plugins/timeline/popcorn.timeline.js b/plugins/timeline/popcorn.timeline.js
index 7d5afac7e..c87f83399 100644
--- a/plugins/timeline/popcorn.timeline.js
+++ b/plugins/timeline/popcorn.timeline.js
@@ -34,50 +34,57 @@
contentDiv = document.createElement( "div" ),
container,
goingUp = true;
+ return {
- if ( target && !target.firstChild ) {
- target.appendChild ( container = document.createElement( "div" ) );
- container.style.width = "inherit";
- container.style.height = "inherit";
- container.style.overflow = "auto";
- } else {
- container = target.firstChild;
- }
-
- contentDiv.style.display = "none";
- contentDiv.id = "timelineDiv" + i;
-
- // Default to up if options.direction is non-existant or not up or down
- options.direction = options.direction || "up";
- if ( options.direction.toLowerCase() === "down" ) {
-
- goingUp = false;
- }
-
- if ( target && container ) {
- // if this isnt the first div added to the target div
- if( goingUp ){
- // insert the current div before the previous div inserted
- container.insertBefore( contentDiv, container.firstChild );
- }
- else {
-
- container.appendChild( contentDiv );
- }
+ _setup: function ( options ) {
+ options.id = options.id || "timelineDiv" + i;
+ if (!target) {
+ target = document.querySelector('body').appendChild(document.createElement('div'));
+ target.id = options.target;
+ }
+ container = target.querySelector(`#${options.id}`);
+
+ if ( !container ) {
+ container = document.createElement( "div" );
+ target.appendChild ( container );
+ container.style.width = "inherit";
+ container.style.height = "inherit";
+ container.style.overflow = "auto";
+ container.id = options.id;
+ container.classList.add("timeline-plugin");
+ }
+
+ // Default to up if options.direction is non-existant or not up or down
+ options.direction = options.direction || "up";
+ if ( options.direction.toLowerCase() === "down" ) {
+ goingUp = false;
+ }
+
+ // if this isnt the first div added to the target div
+ if( goingUp ){
+ console.log('going up');
+ // insert the current div before the previous div inserted
+ container.insertBefore( contentDiv, container.firstChild );
+ }
+ else {
+
+ container.appendChild( contentDiv );
+ }
+ contentDiv.style.display = "none";
+ contentDiv.classList.add("timeline-plugin-item");
- }
+ options.innerHTML = options.innerHTML || "";
- i++;
+ contentDiv.innerHTML =`${options.title ?"" + options.title + " " : ""}
+${options.text? " " + options.text + "
" : ""} ${options.innerHTML}`;
- // Default to empty if not used
- //options.innerHTML = options.innerHTML || "";
- contentDiv.innerHTML = "" + options.title + " " +
- "" + options.text + " " + options.innerHTML;
+ // console.log(contentDiv.textContent);
+ i++;
- return {
+ },
- start: function( event, options ) {
+ start: function( event, options ) {
contentDiv.style.display = "block";
if( options.direction === "down" ) {
diff --git a/plugins/webpage/popcorn.webpage.html b/plugins/webpage/popcorn.webpage.html
index c59cd5b4c..fe44f583e 100644
--- a/plugins/webpage/popcorn.webpage.html
+++ b/plugins/webpage/popcorn.webpage.html
@@ -6,53 +6,69 @@
+
-
- A Webpage displaying webmademovies.org will appear at 0 seconds and disappear at 5 seconds.
- A Webpage displaying zenit Processing.js wiki will appear at 0 seconds and disappear at 15 seconds.
-
+
+
+ A Webpage displaying https://digitalhistory.github.io will appear at 0 seconds and disappear at 40 seconds.
+ A Webpage displaying front page of NYTimes will appear at 0 seconds and disappear at 15 seconds.
+ Video play is delayed 1.5 secs to allow loading of somewhat heavy pages before start.
+
+
-