popcornjs-rails 1.1.2 → 1.2.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -1,5 +1,5 @@
1
1
  /*
2
- * popcorn.js version 1.1.2
2
+ * popcorn.js version 1.2
3
3
  * http://popcornjs.org
4
4
  *
5
5
  * Copyright 2011, Mozilla Foundation
@@ -14,9 +14,10 @@
14
14
  isSupported: false
15
15
  };
16
16
 
17
- var methods = ( "forEach extend effects error guid sizeOf isArray nop position disable enable destroy " +
17
+ var methods = ( "removeInstance addInstance getInstanceById removeInstanceById " +
18
+ "forEach extend effects error guid sizeOf isArray nop position disable enable destroy" +
18
19
  "addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " +
19
- "timeUpdate plugin removePlugin compose effect parser xhr getJSONP getScript" ).split(/\s+/);
20
+ "timeUpdate plugin removePlugin compose effect xhr getJSONP getScript" ).split(/\s+/);
20
21
 
21
22
  while ( methods.length ) {
22
23
  global.Popcorn[ methods.shift() ] = function() {};
@@ -66,6 +67,21 @@
66
67
  };
67
68
  }()),
68
69
 
70
+ // Non-public `getKeys`, return an object's keys as an array
71
+ getKeys = function( obj ) {
72
+ return Object.keys ? Object.keys( obj ) : (function( obj ) {
73
+ var item,
74
+ list = [];
75
+
76
+ for ( item in obj ) {
77
+ if ( hasOwn.call( obj, item ) ) {
78
+ list.push( item );
79
+ }
80
+ }
81
+ return list;
82
+ })( obj );
83
+ },
84
+
69
85
  refresh = function( obj ) {
70
86
  var currentTime = obj.media.currentTime,
71
87
  animation = obj.options.frameAnimation,
@@ -129,7 +145,7 @@
129
145
  };
130
146
 
131
147
  // Popcorn API version, automatically inserted via build system.
132
- Popcorn.version = "1.1.2";
148
+ Popcorn.version = "1.2";
133
149
 
134
150
  // Boolean flag allowing a client to determine if Popcorn can be supported
135
151
  Popcorn.isSupported = true;
@@ -143,7 +159,8 @@
143
159
 
144
160
  init: function( entity, options ) {
145
161
 
146
- var matches;
162
+ var matches,
163
+ self = this;
147
164
 
148
165
  // Supports Popcorn(function () { /../ })
149
166
  // Originally proposed by Daniel Brooks
@@ -151,7 +168,7 @@
151
168
  if ( typeof entity === "function" ) {
152
169
 
153
170
  // If document ready has already fired
154
- if ( document.readyState === "interactive" || document.readyState === "complete" ) {
171
+ if ( document.readyState === "complete" ) {
155
172
 
156
173
  entity( document, Popcorn );
157
174
 
@@ -251,58 +268,60 @@
251
268
  }
252
269
  };
253
270
 
254
- // Wrap true ready check
255
- var isReady = function( that ) {
271
+ // function to fire when video is ready
272
+ var isReady = function() {
273
+
274
+ self.media.removeEventListener( "loadeddata", isReady, false );
256
275
 
257
276
  var duration, videoDurationPlus;
258
277
 
259
- if ( that.media.readyState >= 2 ) {
260
- // Adding padding to the front and end of the arrays
261
- // this is so we do not fall off either end
278
+ // Adding padding to the front and end of the arrays
279
+ // this is so we do not fall off either end
280
+ duration = self.media.duration;
262
281
 
263
- duration = that.media.duration;
264
- // Check for no duration info (NaN)
265
- videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
282
+ // Check for no duration info (NaN)
283
+ videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
266
284
 
267
- Popcorn.addTrackEvent( that, {
268
- start: videoDurationPlus,
269
- end: videoDurationPlus
270
- });
285
+ Popcorn.addTrackEvent( self, {
286
+ start: videoDurationPlus,
287
+ end: videoDurationPlus
288
+ });
271
289
 
272
- if ( that.options.frameAnimation ) {
273
- // if Popcorn is created with frameAnimation option set to true,
274
- // requestAnimFrame is used instead of "timeupdate" media event.
275
- // This is for greater frame time accuracy, theoretically up to
276
- // 60 frames per second as opposed to ~4 ( ~every 15-250ms)
277
- that.data.timeUpdate = function () {
290
+ if ( self.options.frameAnimation ) {
291
+ // if Popcorn is created with frameAnimation option set to true,
292
+ // requestAnimFrame is used instead of "timeupdate" media event.
293
+ // This is for greater frame time accuracy, theoretically up to
294
+ // 60 frames per second as opposed to ~4 ( ~every 15-250ms)
295
+ self.data.timeUpdate = function () {
278
296
 
279
- Popcorn.timeUpdate( that, {} );
297
+ Popcorn.timeUpdate( self, {} );
280
298
 
281
- that.trigger( "timeupdate" );
299
+ self.emit( "timeupdate" );
282
300
 
283
- !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
284
- };
301
+ !self.isDestroyed && requestAnimFrame( self.data.timeUpdate );
302
+ };
285
303
 
286
- !that.isDestroyed && requestAnimFrame( that.data.timeUpdate );
304
+ !self.isDestroyed && requestAnimFrame( self.data.timeUpdate );
287
305
 
288
- } else {
306
+ } else {
289
307
 
290
- that.data.timeUpdate = function( event ) {
291
- Popcorn.timeUpdate( that, event );
292
- };
308
+ self.data.timeUpdate = function( event ) {
309
+ Popcorn.timeUpdate( self, event );
310
+ };
293
311
 
294
- if ( !that.isDestroyed ) {
295
- that.media.addEventListener( "timeupdate", that.data.timeUpdate, false );
296
- }
312
+ if ( !self.isDestroyed ) {
313
+ self.media.addEventListener( "timeupdate", self.data.timeUpdate, false );
297
314
  }
298
- } else {
299
- global.setTimeout(function() {
300
- isReady( that );
301
- }, 1 );
302
315
  }
303
316
  };
304
317
 
305
- isReady( this );
318
+ if ( self.media.readyState >= 2 ) {
319
+
320
+ isReady();
321
+ } else {
322
+
323
+ self.media.addEventListener( "loadeddata", isReady, false );
324
+ }
306
325
 
307
326
  return this;
308
327
  }
@@ -534,7 +553,7 @@
534
553
  _natives: {
535
554
  start: fn || Popcorn.nop,
536
555
  end: Popcorn.nop,
537
- type: "exec"
556
+ type: "cue"
538
557
  }
539
558
  });
540
559
 
@@ -561,7 +580,7 @@
561
580
  }
562
581
 
563
582
  // Trigger either muted|unmuted event
564
- this.trigger( event );
583
+ this.emit( event );
565
584
 
566
585
  return this;
567
586
  },
@@ -821,8 +840,9 @@
821
840
  };
822
841
 
823
842
  // Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances
824
- Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) {
825
- Popcorn.p[ key ] = Popcorn.events.fn[ key ];
843
+ // Extend aliases (on, off, emit)
844
+ Popcorn.forEach( [ [ "trigger", "emit" ], [ "listen", "on" ], [ "unlisten", "off" ] ], function( key ) {
845
+ Popcorn.p[ key[ 0 ] ] = Popcorn.p[ key[ 1 ] ] = Popcorn.events.fn[ key[ 0 ] ];
826
846
  });
827
847
 
828
848
  // Internal Only - Adds track events to the instance object
@@ -918,55 +938,83 @@
918
938
  return obj;
919
939
  };
920
940
 
921
- Popcorn.removeTrackEvent = function( obj, trackId ) {
941
+ Popcorn.removeTrackEvent = function( obj, removeId ) {
922
942
 
923
- var historyLen = obj.data.history.length,
943
+ var start, end, animate,
944
+ historyLen = obj.data.history.length,
945
+ length = obj.data.trackEvents.byStart.length,
946
+ index = 0,
924
947
  indexWasAt = 0,
925
948
  byStart = [],
926
949
  byEnd = [],
927
950
  animating = [],
928
951
  history = [];
929
952
 
930
- Popcorn.forEach( obj.data.trackEvents.byStart, function( o, i, context ) {
931
- // Preserve the original start/end trackEvents
932
- if ( !o._id ) {
933
- byStart.push( obj.data.trackEvents.byStart[i] );
934
- byEnd.push( obj.data.trackEvents.byEnd[i] );
953
+ while ( --length > -1 ) {
954
+ start = obj.data.trackEvents.byStart[ index ];
955
+ end = obj.data.trackEvents.byEnd[ index ];
956
+
957
+ // Padding events will not have _id properties.
958
+ // These should be safely pushed onto the front and back of the
959
+ // track event array
960
+ if ( !start._id ) {
961
+ byStart.push( start );
962
+ byEnd.push( end );
935
963
  }
936
964
 
937
965
  // Filter for user track events (vs system track events)
938
- if ( o._id ) {
966
+ if ( start._id ) {
939
967
 
940
- // Filter for the trackevent to remove
941
- if ( o._id !== trackId ) {
942
- byStart.push( obj.data.trackEvents.byStart[i] );
943
- byEnd.push( obj.data.trackEvents.byEnd[i] );
968
+ // If not a matching start event for removal
969
+ if ( start._id !== removeId ) {
970
+ byStart.push( start );
944
971
  }
945
972
 
946
- // Capture the position of the track being removed.
947
- if ( o._id === trackId ) {
948
- indexWasAt = i;
949
- o._natives._teardown && o._natives._teardown.call( obj, o );
973
+ // If not a matching end event for removal
974
+ if ( end._id !== removeId ) {
975
+ byEnd.push( end );
950
976
  }
951
- }
952
977
 
953
- });
978
+ // If the _id is matched, capture the current index
979
+ if ( start._id === removeId ) {
980
+ indexWasAt = index;
954
981
 
955
- if ( obj.data.trackEvents.animating.length ) {
956
- Popcorn.forEach( obj.data.trackEvents.animating, function( o, i, context ) {
957
- // Preserve the original start/end trackEvents
958
- if ( !o._id ) {
959
- animating.push( obj.data.trackEvents.animating[i] );
982
+ // If a _teardown function was defined,
983
+ // enforce for track event removals
984
+ if ( start._natives._teardown ) {
985
+ start._natives._teardown.call( obj, start );
986
+ }
960
987
  }
988
+ }
989
+ // Increment the track index
990
+ index++;
991
+ }
961
992
 
962
- // Filter for user track events (vs system track events)
963
- if ( o._id ) {
964
- // Filter for the trackevent to remove
965
- if ( o._id !== trackId ) {
966
- animating.push( obj.data.trackEvents.animating[i] );
967
- }
993
+ // Reset length to be used by the condition below to determine
994
+ // if animating track events should also be filtered for removal.
995
+ // Reset index below to be used by the reverse while as an
996
+ // incrementing counter
997
+ length = obj.data.trackEvents.animating.length;
998
+ index = 0;
999
+
1000
+ if ( length ) {
1001
+ while ( --length > -1 ) {
1002
+ animate = obj.data.trackEvents.animating[ index ];
1003
+
1004
+ // Padding events will not have _id properties.
1005
+ // These should be safely pushed onto the front and back of the
1006
+ // track event array
1007
+ if ( !animate._id ) {
1008
+ animating.push( animate );
968
1009
  }
969
- });
1010
+
1011
+ // If not a matching animate event for removal
1012
+ if ( animate._id && animate._id !== removeId ) {
1013
+ animating.push( animate );
1014
+ }
1015
+ // Increment the track index
1016
+ index++;
1017
+ }
970
1018
  }
971
1019
 
972
1020
  // Update
@@ -983,7 +1031,7 @@
983
1031
  obj.data.trackEvents.animating = animating;
984
1032
 
985
1033
  for ( var i = 0; i < historyLen; i++ ) {
986
- if ( obj.data.history[ i ] !== trackId ) {
1034
+ if ( obj.data.history[ i ] !== removeId ) {
987
1035
  history.push( obj.data.history[ i ] );
988
1036
  }
989
1037
  }
@@ -992,12 +1040,12 @@
992
1040
  obj.data.history = history;
993
1041
 
994
1042
  // Update track event references
995
- Popcorn.removeTrackEvent.ref( obj, trackId );
1043
+ Popcorn.removeTrackEvent.ref( obj, removeId );
996
1044
  };
997
1045
 
998
1046
  // Internal Only - Removes track event references from instance object's trackRefs hash table
999
- Popcorn.removeTrackEvent.ref = function( obj, trackId ) {
1000
- delete obj.data.trackRefs[ trackId ];
1047
+ Popcorn.removeTrackEvent.ref = function( obj, removeId ) {
1048
+ delete obj.data.trackRefs[ removeId ];
1001
1049
 
1002
1050
  return obj;
1003
1051
  };
@@ -1076,7 +1124,7 @@
1076
1124
  byEnd._running = false;
1077
1125
  natives.end.call( obj, event, byEnd );
1078
1126
 
1079
- obj.trigger( trackend,
1127
+ obj.emit( trackend,
1080
1128
  Popcorn.extend({}, byEnd, {
1081
1129
  plugin: type,
1082
1130
  type: trackend
@@ -1110,7 +1158,7 @@
1110
1158
  byStart._running = true;
1111
1159
  natives.start.call( obj, event, byStart );
1112
1160
 
1113
- obj.trigger( trackstart,
1161
+ obj.emit( trackstart,
1114
1162
  Popcorn.extend({}, byStart, {
1115
1163
  plugin: type,
1116
1164
  type: trackstart
@@ -1167,7 +1215,7 @@
1167
1215
  byStart._running = false;
1168
1216
  natives.end.call( obj, event, byStart );
1169
1217
 
1170
- obj.trigger( trackend,
1218
+ obj.emit( trackend,
1171
1219
  Popcorn.extend({}, byEnd, {
1172
1220
  plugin: type,
1173
1221
  type: trackend
@@ -1200,7 +1248,7 @@
1200
1248
  byEnd._running = true;
1201
1249
  natives.start.call( obj, event, byEnd );
1202
1250
 
1203
- obj.trigger( trackstart,
1251
+ obj.emit( trackstart,
1204
1252
  Popcorn.extend({}, byStart, {
1205
1253
  plugin: type,
1206
1254
  type: trackstart
@@ -1339,7 +1387,7 @@
1339
1387
  // Storing the plugin natives
1340
1388
  var natives = options._natives = {},
1341
1389
  compose = "",
1342
- defaults, originalOpts, manifestOpts, mergedSetupOpts;
1390
+ originalOpts, manifestOpts;
1343
1391
 
1344
1392
  Popcorn.extend( natives, setup );
1345
1393
 
@@ -1362,9 +1410,6 @@
1362
1410
  args[ 1 ]._running && natives.end.apply( this, args );
1363
1411
  }, natives._teardown );
1364
1412
 
1365
- // Check for previously set default options
1366
- defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ];
1367
-
1368
1413
  // default to an empty string if no effect exists
1369
1414
  // split string into an array of effects
1370
1415
  options.compose = options.compose && options.compose.split( " " ) || [];
@@ -1392,29 +1437,44 @@
1392
1437
  options.start = options[ "in" ] || 0;
1393
1438
  }
1394
1439
 
1395
- if ( !( "end" in options ) ) {
1396
- options.end = options[ "out" ] || this.duration() || Number.MAX_VALUE;
1440
+ if ( !options.end && options.end !== 0 ) {
1441
+ options.end = options[ "out" ] || Number.MAX_VALUE;
1397
1442
  }
1398
1443
 
1399
- // Merge with defaults if they exist, make sure per call is prioritized
1400
- mergedSetupOpts = defaults ? Popcorn.extend( {}, defaults, options ) :
1401
- options;
1444
+ // Use hasOwn to detect non-inherited toString, since all
1445
+ // objects will receive a toString - its otherwise undetectable
1446
+ if ( !hasOwn.call( options, "toString" ) ) {
1447
+ options.toString = function() {
1448
+ var props = [
1449
+ "start: " + options.start,
1450
+ "end: " + options.end,
1451
+ "id: " + (options.id || options._id)
1452
+ ];
1453
+
1454
+ // Matches null and undefined, allows: false, 0, "" and truthy
1455
+ if ( options.target != null ) {
1456
+ props.push( "target: " + options.target );
1457
+ }
1458
+
1459
+ return name + " ( " + props.join(", ") + " )";
1460
+ };
1461
+ }
1402
1462
 
1403
1463
  // Resolves 239, 241, 242
1404
- if ( !mergedSetupOpts.target ) {
1464
+ if ( !options.target ) {
1405
1465
 
1406
1466
  // Sometimes the manifest may be missing entirely
1407
1467
  // or it has an options object that doesn't have a `target` property
1408
1468
  manifestOpts = "options" in manifest && manifest.options;
1409
1469
 
1410
- mergedSetupOpts.target = manifestOpts && "target" in manifestOpts && manifestOpts.target;
1470
+ options.target = manifestOpts && "target" in manifestOpts && manifestOpts.target;
1411
1471
  }
1412
1472
 
1413
1473
  // Trigger _setup method if exists
1414
- options._natives._setup && options._natives._setup.call( this, mergedSetupOpts );
1474
+ options._natives._setup && options._natives._setup.call( this, options );
1415
1475
 
1416
1476
  // Create new track event for this instance
1417
- Popcorn.addTrackEvent( this, Popcorn.extend( mergedSetupOpts, options ) );
1477
+ Popcorn.addTrackEvent( this, Popcorn.extend( options, options ) );
1418
1478
 
1419
1479
  // Future support for plugin event definitions
1420
1480
  // for all of the native events
@@ -1424,7 +1484,7 @@
1424
1484
 
1425
1485
  if ( reserved.indexOf( type ) === -1 ) {
1426
1486
 
1427
- this.listen( type, callback );
1487
+ this.on( type, callback );
1428
1488
  }
1429
1489
  }
1430
1490
 
@@ -1433,14 +1493,17 @@
1433
1493
  return this;
1434
1494
  };
1435
1495
 
1496
+ // Extend Popcorn.p with new named definition
1436
1497
  // Assign new named definition
1437
- plugin[ name ] = function( options ) {
1438
- return pluginFn.call( this, isfn ? definition.call( this, options ) : definition,
1439
- options );
1440
- };
1498
+ Popcorn.p[ name ] = plugin[ name ] = function( options ) {
1441
1499
 
1442
- // Extend Popcorn.p with new named definition
1443
- Popcorn.extend( Popcorn.p, plugin );
1500
+ // Merge with defaults if they exist, make sure per call is prioritized
1501
+ var defaults = ( this.options.defaults && this.options.defaults[ name ] ) || {},
1502
+ mergedSetupOpts = Popcorn.extend( {}, defaults, options );
1503
+
1504
+ return pluginFn.call( this, isfn ? definition.call( this, mergedSetupOpts ) : definition,
1505
+ mergedSetupOpts );
1506
+ };
1444
1507
 
1445
1508
  // Push into the registry
1446
1509
  var entry = {
@@ -1485,7 +1548,7 @@
1485
1548
 
1486
1549
  // Trigger an error that the instance can listen for
1487
1550
  // and react to
1488
- this.trigger( "error", Popcorn.plugin.errors );
1551
+ this.emit( "error", Popcorn.plugin.errors );
1489
1552
  }
1490
1553
  };
1491
1554
  }
@@ -1537,13 +1600,11 @@
1537
1600
  // remove all trackEvents
1538
1601
  for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) {
1539
1602
 
1540
- if ( ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) &&
1541
- ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) ) {
1603
+ if ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) {
1542
1604
 
1543
1605
  byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] );
1544
1606
 
1545
1607
  byStart.splice( idx, 1 );
1546
- byEnd.splice( idx, 1 );
1547
1608
 
1548
1609
  // update for loop if something removed, but keep checking
1549
1610
  idx--; sl--;
@@ -1552,6 +1613,13 @@
1552
1613
  obj.data.trackEvents.endIndex--;
1553
1614
  }
1554
1615
  }
1616
+
1617
+ // clean any remaining references in the end index
1618
+ // we do this seperate from the above check because they might not be in the same order
1619
+ if ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) {
1620
+
1621
+ byEnd.splice( idx, 1 );
1622
+ }
1555
1623
  }
1556
1624
 
1557
1625
  //remove all animating events
@@ -1583,653 +1651,281 @@
1583
1651
 
1584
1652
  Popcorn.plugin.effect = Popcorn.effect = Popcorn.compose;
1585
1653
 
1586
- // stores parsers keyed on filetype
1587
- Popcorn.parsers = {};
1654
+ // Cache references to reused RegExps
1655
+ var rparams = /\?/,
1656
+ // XHR Setup object
1657
+ setup = {
1658
+ url: "",
1659
+ data: "",
1660
+ dataType: "",
1661
+ success: Popcorn.nop,
1662
+ type: "GET",
1663
+ async: true,
1664
+ xhr: function() {
1665
+ return new global.XMLHttpRequest();
1666
+ }
1667
+ };
1588
1668
 
1589
- // An interface for extending Popcorn
1590
- // with parser functionality
1591
- Popcorn.parser = function( name, type, definition ) {
1669
+ Popcorn.xhr = function( options ) {
1592
1670
 
1593
- if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
1594
- Popcorn.error( "'" + name + "' is a protected function name" );
1595
- return;
1596
- }
1671
+ options.dataType = options.dataType && options.dataType.toLowerCase() || null;
1597
1672
 
1598
- // fixes parameters for overloaded function call
1599
- if ( typeof type === "function" && !definition ) {
1600
- definition = type;
1601
- type = "";
1602
- }
1673
+ if ( options.dataType &&
1674
+ ( options.dataType === "jsonp" || options.dataType === "script" ) ) {
1603
1675
 
1604
- if ( typeof definition !== "function" || typeof type !== "string" ) {
1676
+ Popcorn.xhr.getJSONP(
1677
+ options.url,
1678
+ options.success,
1679
+ options.dataType === "script"
1680
+ );
1605
1681
  return;
1606
1682
  }
1607
1683
 
1608
- // Provides some sugar, but ultimately extends
1609
- // the definition into Popcorn.p
1684
+ var settings = Popcorn.extend( {}, setup, options );
1610
1685
 
1611
- var natives = Popcorn.events.all,
1612
- parseFn,
1613
- parser = {};
1686
+ // Create new XMLHttpRequest object
1687
+ settings.ajax = settings.xhr();
1614
1688
 
1615
- parseFn = function( filename, callback ) {
1689
+ if ( settings.ajax ) {
1616
1690
 
1617
- if ( !filename ) {
1618
- return this;
1691
+ if ( settings.type === "GET" && settings.data ) {
1692
+
1693
+ // append query string
1694
+ settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
1695
+
1696
+ // Garbage collect and reset settings.data
1697
+ settings.data = null;
1619
1698
  }
1620
1699
 
1621
- var that = this;
1622
1700
 
1623
- Popcorn.xhr({
1624
- url: filename,
1625
- dataType: type,
1626
- success: function( data ) {
1701
+ settings.ajax.open( settings.type, settings.url, settings.async );
1702
+ settings.ajax.send( settings.data || null );
1627
1703
 
1628
- var tracksObject = definition( data ),
1629
- tracksData,
1630
- tracksDataLen,
1631
- tracksDef,
1632
- idx = 0;
1704
+ return Popcorn.xhr.httpData( settings );
1705
+ }
1706
+ };
1633
1707
 
1634
- tracksData = tracksObject.data || [];
1635
- tracksDataLen = tracksData.length;
1636
- tracksDef = null;
1637
1708
 
1638
- // If no tracks to process, return immediately
1639
- if ( !tracksDataLen ) {
1640
- return;
1641
- }
1709
+ Popcorn.xhr.httpData = function( settings ) {
1642
1710
 
1643
- // Create tracks out of parsed object
1644
- for ( ; idx < tracksDataLen; idx++ ) {
1711
+ var data, json = null,
1712
+ parser, xml = null;
1645
1713
 
1646
- tracksDef = tracksData[ idx ];
1714
+ settings.ajax.onreadystatechange = function() {
1647
1715
 
1648
- for ( var key in tracksDef ) {
1716
+ if ( settings.ajax.readyState === 4 ) {
1649
1717
 
1650
- if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) {
1718
+ try {
1719
+ json = JSON.parse( settings.ajax.responseText );
1720
+ } catch( e ) {
1721
+ //suppress
1722
+ }
1651
1723
 
1652
- that[ key ]( tracksDef[ key ] );
1653
- }
1724
+ data = {
1725
+ xml: settings.ajax.responseXML,
1726
+ text: settings.ajax.responseText,
1727
+ json: json
1728
+ };
1729
+
1730
+ // Normalize: data.xml is non-null in IE9 regardless of if response is valid xml
1731
+ if ( !data.xml || !data.xml.documentElement ) {
1732
+ data.xml = null;
1733
+
1734
+ try {
1735
+ parser = new DOMParser();
1736
+ xml = parser.parseFromString( settings.ajax.responseText, "text/xml" );
1737
+
1738
+ if ( !xml.getElementsByTagName( "parsererror" ).length ) {
1739
+ data.xml = xml;
1654
1740
  }
1655
- }
1656
- if ( callback ) {
1657
- callback();
1741
+ } catch ( e ) {
1742
+ // data.xml remains null
1658
1743
  }
1659
1744
  }
1660
- });
1661
-
1662
- return this;
1663
- };
1664
1745
 
1665
- // Assign new named definition
1666
- parser[ name ] = parseFn;
1746
+ // If a dataType was specified, return that type of data
1747
+ if ( settings.dataType ) {
1748
+ data = data[ settings.dataType ];
1749
+ }
1667
1750
 
1668
- // Extend Popcorn.p with new named definition
1669
- Popcorn.extend( Popcorn.p, parser );
1670
1751
 
1671
- // keys the function name by filetype extension
1672
- //Popcorn.parsers[ name ] = true;
1752
+ settings.success.call( settings.ajax, data );
1673
1753
 
1674
- return parser;
1754
+ }
1755
+ };
1756
+ return data;
1675
1757
  };
1676
1758
 
1677
- Popcorn.player = function( name, player ) {
1759
+ Popcorn.xhr.getJSONP = function( url, success, isScript ) {
1678
1760
 
1679
- player = player || {};
1761
+ var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
1762
+ script = document.createElement( "script" ),
1763
+ paramStr = url.split( "?" )[ 1 ],
1764
+ isFired = false,
1765
+ params = [],
1766
+ callback, parts, callparam;
1680
1767
 
1681
- var playerFn = function( target, src, options ) {
1768
+ if ( paramStr && !isScript ) {
1769
+ params = paramStr.split( "&" );
1770
+ }
1682
1771
 
1683
- options = options || {};
1772
+ if ( params.length ) {
1773
+ parts = params[ params.length - 1 ].split( "=" );
1774
+ }
1684
1775
 
1685
- // List of events
1686
- var date = new Date() / 1000,
1687
- baselineTime = date,
1688
- currentTime = 0,
1689
- volume = 1,
1690
- muted = false,
1691
- events = {},
1776
+ callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ] ) : "jsonp";
1692
1777
 
1693
- // The container div of the resource
1694
- container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) ||
1695
- document.getElementById( target ) ||
1696
- target,
1697
- basePlayer = {},
1698
- timeout,
1699
- popcorn;
1778
+ if ( !paramStr && !isScript ) {
1779
+ url += "?callback=" + callback;
1780
+ }
1700
1781
 
1701
- // copies a div into the media object
1702
- for( var val in container ) {
1782
+ if ( callback && !isScript ) {
1703
1783
 
1704
- if ( typeof container[ val ] === "object" ) {
1784
+ // If a callback name already exists
1785
+ if ( !!window[ callback ] ) {
1786
+ // Create a new unique callback name
1787
+ callback = Popcorn.guid( callback );
1788
+ }
1705
1789
 
1706
- basePlayer[ val ] = container[ val ];
1707
- } else if ( typeof container[ val ] === "function" ) {
1790
+ // Define the JSONP success callback globally
1791
+ window[ callback ] = function( data ) {
1792
+ // Fire success callbacks
1793
+ success && success( data );
1794
+ isFired = true;
1795
+ };
1708
1796
 
1709
- basePlayer[ val ] = (function( value ) {
1797
+ // Replace callback param and callback name
1798
+ url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
1799
+ }
1710
1800
 
1711
- // this is a stupid ugly kludgy hack in honour of Safari
1712
- // in Safari a NodeList is a function, not an object
1713
- if ( "length" in container[ value ] && !container[ value ].call ) {
1801
+ script.addEventListener( "load", function() {
1714
1802
 
1715
- return container[ value ];
1716
- } else {
1803
+ // Handling remote script loading callbacks
1804
+ if ( isScript ) {
1805
+ // getScript
1806
+ success && success();
1807
+ }
1717
1808
 
1718
- return function() {
1719
-
1720
- return container[ value ].apply( container, arguments );
1721
- };
1722
- }
1723
- }( val ));
1724
- } else {
1725
-
1726
- Popcorn.player.defineProperty( basePlayer, val, {
1727
- get: (function( value ) {
1728
-
1729
- return function() {
1730
-
1731
- return container[ value ];
1732
- };
1733
- }( val )),
1734
- set: Popcorn.nop,
1735
- configurable: true
1736
- });
1737
- }
1809
+ // Executing for JSONP requests
1810
+ if ( isFired ) {
1811
+ // Garbage collect the callback
1812
+ delete window[ callback ];
1738
1813
  }
1814
+ // Garbage collect the script resource
1815
+ head.removeChild( script );
1816
+ }, false );
1739
1817
 
1740
- var timeupdate = function() {
1741
-
1742
- date = new Date() / 1000;
1743
-
1744
- if ( !basePlayer.paused ) {
1745
-
1746
- basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
1747
- basePlayer.dispatchEvent( "timeupdate" );
1748
- timeout = setTimeout( timeupdate, 10 );
1749
- }
1750
-
1751
- baselineTime = date;
1752
- };
1753
-
1754
- basePlayer.play = function() {
1755
-
1756
- this.paused = false;
1757
-
1758
- if ( basePlayer.readyState >= 4 ) {
1759
-
1760
- baselineTime = new Date() / 1000;
1761
- basePlayer.dispatchEvent( "play" );
1762
- timeupdate();
1763
- }
1764
- };
1765
-
1766
- basePlayer.pause = function() {
1767
-
1768
- this.paused = true;
1769
- basePlayer.dispatchEvent( "pause" );
1770
- };
1771
-
1772
- Popcorn.player.defineProperty( basePlayer, "currentTime", {
1773
- get: function() {
1774
-
1775
- return currentTime;
1776
- },
1777
- set: function( val ) {
1778
-
1779
- // make sure val is a number
1780
- currentTime = +val;
1781
- basePlayer.dispatchEvent( "timeupdate" );
1782
- return currentTime;
1783
- },
1784
- configurable: true
1785
- });
1786
-
1787
- Popcorn.player.defineProperty( basePlayer, "volume", {
1788
- get: function() {
1789
-
1790
- return volume;
1791
- },
1792
- set: function( val ) {
1793
-
1794
- // make sure val is a number
1795
- volume = +val;
1796
- basePlayer.dispatchEvent( "volumechange" );
1797
- return volume;
1798
- },
1799
- configurable: true
1800
- });
1801
-
1802
- Popcorn.player.defineProperty( basePlayer, "muted", {
1803
- get: function() {
1818
+ script.src = url;
1804
1819
 
1805
- return muted;
1806
- },
1807
- set: function( val ) {
1820
+ head.insertBefore( script, head.firstChild );
1808
1821
 
1809
- // make sure val is a number
1810
- muted = +val;
1811
- basePlayer.dispatchEvent( "volumechange" );
1812
- return muted;
1813
- },
1814
- configurable: true
1815
- });
1822
+ return;
1823
+ };
1816
1824
 
1817
- // Adds an event listener to the object
1818
- basePlayer.addEventListener = function( evtName, fn ) {
1825
+ Popcorn.getJSONP = Popcorn.xhr.getJSONP;
1819
1826
 
1820
- if ( !events[ evtName ] ) {
1827
+ Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
1821
1828
 
1822
- events[ evtName ] = [];
1823
- }
1829
+ return Popcorn.xhr.getJSONP( url, success, true );
1830
+ };
1824
1831
 
1825
- events[ evtName ].push( fn );
1826
- return fn;
1827
- };
1832
+ Popcorn.util = {
1833
+ // Simple function to parse a timestamp into seconds
1834
+ // Acceptable formats are:
1835
+ // HH:MM:SS.MMM
1836
+ // HH:MM:SS;FF
1837
+ // Hours and minutes are optional. They default to 0
1838
+ toSeconds: function( timeStr, framerate ) {
1839
+ // Hours and minutes are optional
1840
+ // Seconds must be specified
1841
+ // Seconds can be followed by milliseconds OR by the frame information
1842
+ var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
1843
+ errorMessage = "Invalid time format",
1844
+ digitPairs, lastIndex, lastPair, firstPair,
1845
+ frameInfo, frameTime;
1828
1846
 
1829
- // Can take event object or simple string
1830
- basePlayer.dispatchEvent = function( oEvent ) {
1847
+ if ( typeof timeStr === "number" ) {
1848
+ return timeStr;
1849
+ }
1831
1850
 
1832
- var evt,
1833
- self = this,
1834
- eventInterface,
1835
- eventName = oEvent.type;
1851
+ if ( typeof timeStr === "string" &&
1852
+ !validTimeFormat.test( timeStr ) ) {
1853
+ Popcorn.error( errorMessage );
1854
+ }
1836
1855
 
1837
- // A string was passed, create event object
1838
- if ( !eventName ) {
1856
+ digitPairs = timeStr.split( ":" );
1857
+ lastIndex = digitPairs.length - 1;
1858
+ lastPair = digitPairs[ lastIndex ];
1839
1859
 
1840
- eventName = oEvent;
1841
- eventInterface = Popcorn.events.getInterface( eventName );
1860
+ // Fix last element:
1861
+ if ( lastPair.indexOf( ";" ) > -1 ) {
1842
1862
 
1843
- if ( eventInterface ) {
1863
+ frameInfo = lastPair.split( ";" );
1864
+ frameTime = 0;
1844
1865
 
1845
- evt = document.createEvent( eventInterface );
1846
- evt.initEvent( eventName, true, true, window, 1 );
1847
- }
1866
+ if ( framerate && ( typeof framerate === "number" ) ) {
1867
+ frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
1848
1868
  }
1849
1869
 
1850
- Popcorn.forEach( events[ eventName ], function( val ) {
1851
-
1852
- val.call( self, evt, self );
1853
- });
1854
- };
1855
-
1856
- // Attempt to get src from playerFn parameter
1857
- basePlayer.src = src || "";
1858
- basePlayer.readyState = 0;
1859
- basePlayer.duration = 0;
1860
- basePlayer.paused = true;
1861
- basePlayer.ended = 0;
1862
-
1863
- if ( player._setup ) {
1864
-
1865
- player._setup.call( basePlayer, options );
1866
- } else {
1867
-
1868
- // there is no setup, which means there is nothing to load
1869
- basePlayer.readyState = 4;
1870
- basePlayer.dispatchEvent( "load" );
1871
- basePlayer.dispatchEvent( "loadeddata" );
1870
+ digitPairs[ lastIndex ] = parseInt( frameInfo[ 0 ], 10 ) + frameTime;
1872
1871
  }
1873
1872
 
1874
- // when a custom player is loaded, load basePlayer state into custom player
1875
- basePlayer.addEventListener( "load", function() {
1876
-
1877
- // if a player is not ready before currentTime is called, this will set it after it is ready
1878
- basePlayer.currentTime = currentTime;
1879
-
1880
- // same as above with volume and muted
1881
- basePlayer.volume = volume;
1882
- basePlayer.muted = muted;
1883
- });
1884
-
1885
- basePlayer.addEventListener( "loadeddata", function() {
1886
-
1887
- // if play was called before player ready, start playing video
1888
- !basePlayer.paused && basePlayer.play();
1889
- });
1890
-
1891
- popcorn = new Popcorn.p.init( basePlayer, options );
1873
+ firstPair = digitPairs[ 0 ];
1892
1874
 
1893
- return popcorn;
1894
- };
1875
+ return {
1895
1876
 
1896
- Popcorn[ name ] = Popcorn[ name ] || playerFn;
1897
- };
1877
+ 1: parseFloat( firstPair, 10 ),
1898
1878
 
1899
- Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
1879
+ 2: ( parseInt( firstPair, 10 ) * 60 ) +
1880
+ parseFloat( digitPairs[ 1 ], 10 ),
1900
1881
 
1901
- object.__defineGetter__( description, options.get || Popcorn.nop );
1902
- object.__defineSetter__( description, options.set || Popcorn.nop );
1903
- };
1882
+ 3: ( parseInt( firstPair, 10 ) * 3600 ) +
1883
+ ( parseInt( digitPairs[ 1 ], 10 ) * 60 ) +
1884
+ parseFloat( digitPairs[ 2 ], 10 )
1904
1885
 
1905
- // Cache references to reused RegExps
1906
- var rparams = /\?/,
1907
- // XHR Setup object
1908
- setup = {
1909
- url: "",
1910
- data: "",
1911
- dataType: "",
1912
- success: Popcorn.nop,
1913
- type: "GET",
1914
- async: true,
1915
- xhr: function() {
1916
- return new global.XMLHttpRequest();
1886
+ }[ digitPairs.length || 1 ];
1917
1887
  }
1918
1888
  };
1919
1889
 
1920
- Popcorn.xhr = function( options ) {
1921
-
1922
- options.dataType = options.dataType && options.dataType.toLowerCase() || null;
1923
-
1924
- if ( options.dataType &&
1925
- ( options.dataType === "jsonp" || options.dataType === "script" ) ) {
1926
-
1927
- Popcorn.xhr.getJSONP(
1928
- options.url,
1929
- options.success,
1930
- options.dataType === "script"
1931
- );
1932
- return;
1933
- }
1934
-
1935
- var settings = Popcorn.extend( {}, setup, options );
1936
-
1937
- // Create new XMLHttpRequest object
1938
- settings.ajax = settings.xhr();
1939
-
1940
- if ( settings.ajax ) {
1890
+ // alias for exec function
1891
+ Popcorn.p.cue = Popcorn.p.exec;
1941
1892
 
1942
- if ( settings.type === "GET" && settings.data ) {
1893
+ // Protected API methods
1894
+ Popcorn.protect = {
1895
+ natives: getKeys( Popcorn.p ).map(function( val ) {
1896
+ return val.toLowerCase();
1897
+ })
1898
+ };
1943
1899
 
1944
- // append query string
1945
- settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
1900
+ // Setup logging for deprecated methods
1901
+ Popcorn.forEach({
1902
+ // Deprecated: Recommended
1903
+ "listen": "on",
1904
+ "unlisten": "off",
1905
+ "trigger": "emit",
1906
+ "exec": "cue"
1907
+
1908
+ }, function( recommend, api ) {
1909
+ var original = Popcorn.p[ api ];
1910
+ // Override the deprecated api method with a method of the same name
1911
+ // that logs a warning and defers to the new recommended method
1912
+ Popcorn.p[ api ] = function() {
1913
+ if ( typeof console !== "undefined" && console.warn ) {
1914
+ console.warn(
1915
+ "Deprecated method '" + api + "', " +
1916
+ (recommend == null ? "do not use." : "use '" + recommend + "' instead." )
1917
+ );
1918
+
1919
+ // Restore api after first warning
1920
+ Popcorn.p[ api ] = original;
1921
+ }
1922
+ return Popcorn.p[ recommend ].apply( this, [].slice.call( arguments ) );
1923
+ };
1924
+ });
1946
1925
 
1947
- // Garbage collect and reset settings.data
1948
- settings.data = null;
1949
- }
1950
1926
 
1951
-
1952
- settings.ajax.open( settings.type, settings.url, settings.async );
1953
- settings.ajax.send( settings.data || null );
1954
-
1955
- return Popcorn.xhr.httpData( settings );
1956
- }
1957
- };
1958
-
1959
-
1960
- Popcorn.xhr.httpData = function( settings ) {
1961
-
1962
- var data, json = null,
1963
- parser, xml = null;
1964
-
1965
- settings.ajax.onreadystatechange = function() {
1966
-
1967
- if ( settings.ajax.readyState === 4 ) {
1968
-
1969
- try {
1970
- json = JSON.parse( settings.ajax.responseText );
1971
- } catch( e ) {
1972
- //suppress
1973
- }
1974
-
1975
- data = {
1976
- xml: settings.ajax.responseXML,
1977
- text: settings.ajax.responseText,
1978
- json: json
1979
- };
1980
-
1981
- // Normalize: data.xml is non-null in IE9 regardless of if response is valid xml
1982
- if ( !data.xml || !data.xml.documentElement ) {
1983
- data.xml = null;
1984
-
1985
- try {
1986
- parser = new DOMParser();
1987
- xml = parser.parseFromString( settings.ajax.responseText, "text/xml" );
1988
-
1989
- if ( !xml.getElementsByTagName( "parsererror" ).length ) {
1990
- data.xml = xml;
1991
- }
1992
- } catch ( e ) {
1993
- // data.xml remains null
1994
- }
1995
- }
1996
-
1997
- // If a dataType was specified, return that type of data
1998
- if ( settings.dataType ) {
1999
- data = data[ settings.dataType ];
2000
- }
2001
-
2002
-
2003
- settings.success.call( settings.ajax, data );
2004
-
2005
- }
2006
- };
2007
- return data;
2008
- };
2009
-
2010
- Popcorn.xhr.getJSONP = function( url, success, isScript ) {
2011
-
2012
- var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
2013
- script = document.createElement( "script" ),
2014
- paramStr = url.split( "?" )[ 1 ],
2015
- isFired = false,
2016
- params = [],
2017
- callback, parts, callparam;
2018
-
2019
- if ( paramStr && !isScript ) {
2020
- params = paramStr.split( "&" );
2021
- }
2022
-
2023
- if ( params.length ) {
2024
- parts = params[ params.length - 1 ].split( "=" );
2025
- }
2026
-
2027
- callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ] ) : "jsonp";
2028
-
2029
- if ( !paramStr && !isScript ) {
2030
- url += "?callback=" + callback;
2031
- }
2032
-
2033
- if ( callback && !isScript ) {
2034
-
2035
- // If a callback name already exists
2036
- if ( !!window[ callback ] ) {
2037
- // Create a new unique callback name
2038
- callback = Popcorn.guid( callback );
2039
- }
2040
-
2041
- // Define the JSONP success callback globally
2042
- window[ callback ] = function( data ) {
2043
- // Fire success callbacks
2044
- success && success( data );
2045
- isFired = true;
2046
- };
2047
-
2048
- // Replace callback param and callback name
2049
- url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
2050
- }
2051
-
2052
- script.onload = function() {
2053
-
2054
- // Handling remote script loading callbacks
2055
- if ( isScript ) {
2056
- // getScript
2057
- success && success();
2058
- }
2059
-
2060
- // Executing for JSONP requests
2061
- if ( isFired ) {
2062
- // Garbage collect the callback
2063
- delete window[ callback ];
2064
- }
2065
- // Garbage collect the script resource
2066
- head.removeChild( script );
2067
- };
2068
-
2069
- script.src = url;
2070
-
2071
- head.insertBefore( script, head.firstChild );
2072
-
2073
- return;
2074
- };
2075
-
2076
- Popcorn.getJSONP = Popcorn.xhr.getJSONP;
2077
-
2078
- Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
2079
-
2080
- return Popcorn.xhr.getJSONP( url, success, true );
2081
- };
2082
-
2083
- Popcorn.util = {
2084
- // Simple function to parse a timestamp into seconds
2085
- // Acceptable formats are:
2086
- // HH:MM:SS.MMM
2087
- // HH:MM:SS;FF
2088
- // Hours and minutes are optional. They default to 0
2089
- toSeconds: function( timeStr, framerate ) {
2090
- // Hours and minutes are optional
2091
- // Seconds must be specified
2092
- // Seconds can be followed by milliseconds OR by the frame information
2093
- var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
2094
- errorMessage = "Invalid time format",
2095
- digitPairs, lastIndex, lastPair, firstPair,
2096
- frameInfo, frameTime;
2097
-
2098
- if ( typeof timeStr === "number" ) {
2099
- return timeStr;
2100
- }
2101
-
2102
- if ( typeof timeStr === "string" &&
2103
- !validTimeFormat.test( timeStr ) ) {
2104
- Popcorn.error( errorMessage );
2105
- }
2106
-
2107
- digitPairs = timeStr.split( ":" );
2108
- lastIndex = digitPairs.length - 1;
2109
- lastPair = digitPairs[ lastIndex ];
2110
-
2111
- // Fix last element:
2112
- if ( lastPair.indexOf( ";" ) > -1 ) {
2113
-
2114
- frameInfo = lastPair.split( ";" );
2115
- frameTime = 0;
2116
-
2117
- if ( framerate && ( typeof framerate === "number" ) ) {
2118
- frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
2119
- }
2120
-
2121
- digitPairs[ lastIndex ] = parseInt( frameInfo[ 0 ], 10 ) + frameTime;
2122
- }
2123
-
2124
- firstPair = digitPairs[ 0 ];
2125
-
2126
- return {
2127
-
2128
- 1: parseFloat( firstPair, 10 ),
2129
-
2130
- 2: ( parseInt( firstPair, 10 ) * 60 ) +
2131
- parseFloat( digitPairs[ 1 ], 10 ),
2132
-
2133
- 3: ( parseInt( firstPair, 10 ) * 3600 ) +
2134
- ( parseInt( digitPairs[ 1 ], 10 ) * 60 ) +
2135
- parseFloat( digitPairs[ 2 ], 10 )
2136
-
2137
- }[ digitPairs.length || 1 ];
2138
- }
2139
- };
2140
-
2141
-
2142
- // Initialize locale data
2143
- // Based on http://en.wikipedia.org/wiki/Language_localisation#Language_tags_and_codes
2144
- function initLocale( arg ) {
2145
-
2146
- var locale = typeof arg === "string" ? arg : [ arg.language, arg.region ].join( "-" ),
2147
- parts = locale.split( "-" );
2148
-
2149
- // Setup locale data table
2150
- return {
2151
- iso6391: locale,
2152
- language: parts[ 0 ] || "",
2153
- region: parts[ 1 ] || ""
2154
- };
2155
- }
2156
-
2157
- // Declare locale data table
2158
- var localeData = initLocale( global.navigator.userLanguage || global.navigator.language );
2159
-
2160
- Popcorn.locale = {
2161
-
2162
- // Popcorn.locale.get()
2163
- // returns reference to privately
2164
- // defined localeData
2165
- get: function() {
2166
- return localeData;
2167
- },
2168
-
2169
- // Popcorn.locale.set( string|object );
2170
- set: function( arg ) {
2171
-
2172
- localeData = initLocale( arg );
2173
-
2174
- Popcorn.locale.broadcast();
2175
-
2176
- return localeData;
2177
- },
2178
-
2179
- // Popcorn.locale.broadcast( type )
2180
- // Sends events to all popcorn media instances that are
2181
- // listening for locale events
2182
- broadcast: function( type ) {
2183
-
2184
- var instances = Popcorn.instances,
2185
- length = instances.length,
2186
- idx = 0,
2187
- instance;
2188
-
2189
- type = type || "locale:changed";
2190
-
2191
- // Iterate all current instances
2192
- for ( ; idx < length; idx++ ) {
2193
- instance = instances[ idx ];
2194
-
2195
- // For those instances with locale event listeners,
2196
- // trigger a locale change event
2197
- if ( type in instance.data.events ) {
2198
- instance.trigger( type );
2199
- }
2200
- }
2201
- }
2202
- };
2203
-
2204
- // alias for exec function
2205
- Popcorn.p.cue = Popcorn.p.exec;
2206
-
2207
- function getItems() {
2208
-
2209
- var item,
2210
- list = [];
2211
-
2212
- if ( Object.keys ) {
2213
- list = Object.keys( Popcorn.p );
2214
- } else {
2215
-
2216
- for ( item in Popcorn.p ) {
2217
- if ( hasOwn.call( Popcorn.p, item ) ) {
2218
- list.push( item );
2219
- }
2220
- }
2221
- }
2222
-
2223
- return list.join( "," ).toLowerCase().split( ",");
2224
- }
2225
-
2226
- // Protected API methods
2227
- Popcorn.protect = {
2228
- natives: getItems()
2229
- };
2230
-
2231
- // Exposes Popcorn to global context
2232
- global.Popcorn = Popcorn;
1927
+ // Exposes Popcorn to global context
1928
+ global.Popcorn = Popcorn;
2233
1929
 
2234
1930
  })(window, window.document);
2235
1931
  /*!
@@ -2771,67 +2467,616 @@
2771
2467
  (function( Popcorn ) {
2772
2468
  document.addEventListener( "DOMContentLoaded", function() {
2773
2469
 
2774
- // Supports non-specific elements
2775
- var dataAttr = "data-timeline-sources",
2776
- medias = document.querySelectorAll( "[" + dataAttr + "]" );
2470
+ // Supports non-specific elements
2471
+ var dataAttr = "data-timeline-sources",
2472
+ medias = document.querySelectorAll( "[" + dataAttr + "]" );
2473
+
2474
+ Popcorn.forEach( medias, function( idx, key ) {
2475
+
2476
+ var media = medias[ key ],
2477
+ hasDataSources = false,
2478
+ dataSources, data, popcornMedia;
2479
+
2480
+ // Ensure that the DOM has an id
2481
+ if ( !media.id ) {
2482
+
2483
+ media.id = Popcorn.guid( "__popcorn" );
2484
+ }
2485
+
2486
+ // Ensure we're looking at a dom node
2487
+ if ( media.nodeType && media.nodeType === 1 ) {
2488
+
2489
+ popcornMedia = Popcorn( "#" + media.id );
2490
+
2491
+ dataSources = ( media.getAttribute( dataAttr ) || "" ).split( "," );
2492
+
2493
+ if ( dataSources[ 0 ] ) {
2494
+
2495
+ Popcorn.forEach( dataSources, function( source ) {
2496
+
2497
+ // split the parser and data as parser!file
2498
+ data = source.split( "!" );
2499
+
2500
+ // if no parser is defined for the file, assume "parse" + file extension
2501
+ if ( data.length === 1 ) {
2502
+
2503
+ // parse a relative URL for the filename, split to get extension
2504
+ data = source.match( /(.*)[\/\\]([^\/\\]+\.\w+)$/ )[ 2 ].split( "." );
2505
+
2506
+ data[ 0 ] = "parse" + data[ 1 ].toUpperCase();
2507
+ data[ 1 ] = source;
2508
+ }
2509
+
2510
+ // If the media has data sources and the correct parser is registered, continue to load
2511
+ if ( dataSources[ 0 ] && popcornMedia[ data[ 0 ] ] ) {
2512
+
2513
+ // Set up the media and load in the datasources
2514
+ popcornMedia[ data[ 0 ] ]( data[ 1 ] );
2515
+
2516
+ }
2517
+ });
2518
+
2519
+ }
2520
+
2521
+ // Only play the media if it was specified to do so
2522
+ if ( !!popcornMedia.autoplay ) {
2523
+ popcornMedia.play();
2524
+ }
2525
+
2526
+ }
2527
+ });
2528
+ }, false );
2529
+
2530
+ })( Popcorn );(function( global, Popcorn ) {
2531
+
2532
+ var navigator = global.navigator;
2533
+
2534
+ // Initialize locale data
2535
+ // Based on http://en.wikipedia.org/wiki/Language_localisation#Language_tags_and_codes
2536
+ function initLocale( arg ) {
2537
+
2538
+ var locale = typeof arg === "string" ? arg : [ arg.language, arg.region ].join( "-" ),
2539
+ parts = locale.split( "-" );
2540
+
2541
+ // Setup locale data table
2542
+ return {
2543
+ iso6391: locale,
2544
+ language: parts[ 0 ] || "",
2545
+ region: parts[ 1 ] || ""
2546
+ };
2547
+ }
2548
+
2549
+ // Declare locale data table
2550
+ var localeData = initLocale( navigator.userLanguage || navigator.language );
2551
+
2552
+ Popcorn.locale = {
2553
+
2554
+ // Popcorn.locale.get()
2555
+ // returns reference to privately
2556
+ // defined localeData
2557
+ get: function() {
2558
+ return localeData;
2559
+ },
2560
+
2561
+ // Popcorn.locale.set( string|object );
2562
+ set: function( arg ) {
2563
+
2564
+ localeData = initLocale( arg );
2565
+
2566
+ Popcorn.locale.broadcast();
2567
+
2568
+ return localeData;
2569
+ },
2570
+
2571
+ // Popcorn.locale.broadcast( type )
2572
+ // Sends events to all popcorn media instances that are
2573
+ // listening for locale events
2574
+ broadcast: function( type ) {
2575
+
2576
+ var instances = Popcorn.instances,
2577
+ length = instances.length,
2578
+ idx = 0,
2579
+ instance;
2580
+
2581
+ type = type || "locale:changed";
2582
+
2583
+ // Iterate all current instances
2584
+ for ( ; idx < length; idx++ ) {
2585
+ instance = instances[ idx ];
2586
+
2587
+ // For those instances with locale event listeners,
2588
+ // trigger a locale change event
2589
+ if ( type in instance.data.events ) {
2590
+ instance.trigger( type );
2591
+ }
2592
+ }
2593
+ }
2594
+ };
2595
+ })( this, this.Popcorn );(function( Popcorn ) {
2596
+
2597
+ var
2598
+
2599
+ AP = Array.prototype,
2600
+ OP = Object.prototype,
2601
+
2602
+ forEach = AP.forEach,
2603
+ slice = AP.slice,
2604
+ hasOwn = OP.hasOwnProperty,
2605
+ toString = OP.toString;
2606
+
2607
+ // stores parsers keyed on filetype
2608
+ Popcorn.parsers = {};
2609
+
2610
+ // An interface for extending Popcorn
2611
+ // with parser functionality
2612
+ Popcorn.parser = function( name, type, definition ) {
2613
+
2614
+ if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
2615
+ Popcorn.error( "'" + name + "' is a protected function name" );
2616
+ return;
2617
+ }
2618
+
2619
+ // fixes parameters for overloaded function call
2620
+ if ( typeof type === "function" && !definition ) {
2621
+ definition = type;
2622
+ type = "";
2623
+ }
2624
+
2625
+ if ( typeof definition !== "function" || typeof type !== "string" ) {
2626
+ return;
2627
+ }
2628
+
2629
+ // Provides some sugar, but ultimately extends
2630
+ // the definition into Popcorn.p
2631
+
2632
+ var natives = Popcorn.events.all,
2633
+ parseFn,
2634
+ parser = {};
2635
+
2636
+ parseFn = function( filename, callback ) {
2637
+
2638
+ if ( !filename ) {
2639
+ return this;
2640
+ }
2641
+
2642
+ var that = this;
2643
+
2644
+ Popcorn.xhr({
2645
+ url: filename,
2646
+ dataType: type,
2647
+ success: function( data ) {
2648
+
2649
+ var tracksObject = definition( data ),
2650
+ tracksData,
2651
+ tracksDataLen,
2652
+ tracksDef,
2653
+ idx = 0;
2654
+
2655
+ tracksData = tracksObject.data || [];
2656
+ tracksDataLen = tracksData.length;
2657
+ tracksDef = null;
2658
+
2659
+ // If no tracks to process, return immediately
2660
+ if ( !tracksDataLen ) {
2661
+ return;
2662
+ }
2663
+
2664
+ // Create tracks out of parsed object
2665
+ for ( ; idx < tracksDataLen; idx++ ) {
2666
+
2667
+ tracksDef = tracksData[ idx ];
2668
+
2669
+ for ( var key in tracksDef ) {
2670
+
2671
+ if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) {
2672
+
2673
+ that[ key ]( tracksDef[ key ] );
2674
+ }
2675
+ }
2676
+ }
2677
+ if ( callback ) {
2678
+ callback();
2679
+ }
2680
+ }
2681
+ });
2682
+
2683
+ return this;
2684
+ };
2685
+
2686
+ // Assign new named definition
2687
+ parser[ name ] = parseFn;
2688
+
2689
+ // Extend Popcorn.p with new named definition
2690
+ Popcorn.extend( Popcorn.p, parser );
2691
+
2692
+ // keys the function name by filetype extension
2693
+ //Popcorn.parsers[ name ] = true;
2694
+
2695
+ return parser;
2696
+ };
2697
+ })( Popcorn );(function( Popcorn ) {
2698
+
2699
+ // combines calls of two function calls into one
2700
+ var combineFn = function( first, second ) {
2701
+
2702
+ first = first || Popcorn.nop;
2703
+ second = second || Popcorn.nop;
2704
+
2705
+ return function() {
2706
+
2707
+ first.apply( this, arguments );
2708
+ second.apply( this, arguments );
2709
+ };
2710
+ };
2711
+
2712
+ // ID string matching
2713
+ var rIdExp = /^(#([\w\-\_\.]+))$/;
2714
+
2715
+ Popcorn.player = function( name, player ) {
2716
+
2717
+ // return early if a player already exists under this name
2718
+ if ( Popcorn[ name ] ) {
2719
+
2720
+ return;
2721
+ }
2722
+
2723
+ player = player || {};
2724
+
2725
+ var playerFn = function( target, src, options ) {
2726
+
2727
+ options = options || {};
2728
+
2729
+ // List of events
2730
+ var date = new Date() / 1000,
2731
+ baselineTime = date,
2732
+ currentTime = 0,
2733
+ readyState = 0,
2734
+ volume = 1,
2735
+ muted = false,
2736
+ events = {},
2737
+
2738
+ // The container div of the resource
2739
+ container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) ||
2740
+ document.getElementById( target ) ||
2741
+ target,
2742
+ basePlayer = {},
2743
+ timeout,
2744
+ popcorn;
2745
+
2746
+ if ( !Object.prototype.__defineGetter__ ) {
2747
+
2748
+ basePlayer = container || document.createElement( "div" );
2749
+ }
2750
+
2751
+ // copies a div into the media object
2752
+ for( var val in container ) {
2753
+
2754
+ // don't copy properties if using container as baseplayer
2755
+ if ( val in basePlayer ) {
2756
+
2757
+ continue;
2758
+ }
2759
+
2760
+ if ( typeof container[ val ] === "object" ) {
2761
+
2762
+ basePlayer[ val ] = container[ val ];
2763
+ } else if ( typeof container[ val ] === "function" ) {
2764
+
2765
+ basePlayer[ val ] = (function( value ) {
2766
+
2767
+ // this is a stupid ugly kludgy hack in honour of Safari
2768
+ // in Safari a NodeList is a function, not an object
2769
+ if ( "length" in container[ value ] && !container[ value ].call ) {
2770
+
2771
+ return container[ value ];
2772
+ } else {
2773
+
2774
+ return function() {
2775
+
2776
+ return container[ value ].apply( container, arguments );
2777
+ };
2778
+ }
2779
+ }( val ));
2780
+ } else {
2781
+
2782
+ Popcorn.player.defineProperty( basePlayer, val, {
2783
+ get: (function( value ) {
2784
+
2785
+ return function() {
2786
+
2787
+ return container[ value ];
2788
+ };
2789
+ }( val )),
2790
+ set: Popcorn.nop,
2791
+ configurable: true
2792
+ });
2793
+ }
2794
+ }
2795
+
2796
+ var timeupdate = function() {
2797
+
2798
+ date = new Date() / 1000;
2799
+
2800
+ if ( !basePlayer.paused ) {
2801
+
2802
+ basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
2803
+ basePlayer.dispatchEvent( "timeupdate" );
2804
+ timeout = setTimeout( timeupdate, 10 );
2805
+ }
2806
+
2807
+ baselineTime = date;
2808
+ };
2809
+
2810
+ basePlayer.play = function() {
2811
+
2812
+ this.paused = false;
2813
+
2814
+ if ( basePlayer.readyState >= 4 ) {
2815
+
2816
+ baselineTime = new Date() / 1000;
2817
+ basePlayer.dispatchEvent( "play" );
2818
+ timeupdate();
2819
+ }
2820
+ };
2821
+
2822
+ basePlayer.pause = function() {
2823
+
2824
+ this.paused = true;
2825
+ basePlayer.dispatchEvent( "pause" );
2826
+ };
2827
+
2828
+ Popcorn.player.defineProperty( basePlayer, "currentTime", {
2829
+ get: function() {
2830
+
2831
+ return currentTime;
2832
+ },
2833
+ set: function( val ) {
2834
+
2835
+ // make sure val is a number
2836
+ currentTime = +val;
2837
+ basePlayer.dispatchEvent( "timeupdate" );
2838
+
2839
+ return currentTime;
2840
+ },
2841
+ configurable: true
2842
+ });
2843
+
2844
+ Popcorn.player.defineProperty( basePlayer, "volume", {
2845
+ get: function() {
2846
+
2847
+ return volume;
2848
+ },
2849
+ set: function( val ) {
2850
+
2851
+ // make sure val is a number
2852
+ volume = +val;
2853
+ basePlayer.dispatchEvent( "volumechange" );
2854
+ return volume;
2855
+ },
2856
+ configurable: true
2857
+ });
2858
+
2859
+ Popcorn.player.defineProperty( basePlayer, "muted", {
2860
+ get: function() {
2861
+
2862
+ return muted;
2863
+ },
2864
+ set: function( val ) {
2865
+
2866
+ // make sure val is a number
2867
+ muted = +val;
2868
+ basePlayer.dispatchEvent( "volumechange" );
2869
+ return muted;
2870
+ },
2871
+ configurable: true
2872
+ });
2873
+
2874
+ Popcorn.player.defineProperty( basePlayer, "readyState", {
2875
+ get: function() {
2876
+
2877
+ return readyState;
2878
+ },
2879
+ set: function( val ) {
2880
+
2881
+ readyState = val;
2882
+ return readyState;
2883
+ },
2884
+ configurable: true
2885
+ });
2886
+
2887
+ // Adds an event listener to the object
2888
+ basePlayer.addEventListener = function( evtName, fn ) {
2889
+
2890
+ if ( !events[ evtName ] ) {
2891
+
2892
+ events[ evtName ] = [];
2893
+ }
2894
+
2895
+ events[ evtName ].push( fn );
2896
+ return fn;
2897
+ };
2898
+
2899
+ // Removes an event listener from the object
2900
+ basePlayer.removeEventListener = function( evtName, fn ) {
2901
+
2902
+ var i,
2903
+ listeners = events[ evtName ];
2904
+
2905
+ if ( !listeners ){
2906
+
2907
+ return;
2908
+ }
2909
+
2910
+ // walk backwards so we can safely splice
2911
+ for ( i = events[ evtName ].length - 1; i >= 0; i-- ) {
2912
+
2913
+ if( fn === listeners[ i ] ) {
2914
+
2915
+ listeners.splice(i, 1);
2916
+ }
2917
+ }
2918
+
2919
+ return fn;
2920
+ };
2921
+
2922
+ // Can take event object or simple string
2923
+ basePlayer.dispatchEvent = function( oEvent ) {
2924
+
2925
+ var evt,
2926
+ self = this,
2927
+ eventInterface,
2928
+ eventName = oEvent.type;
2929
+
2930
+ // A string was passed, create event object
2931
+ if ( !eventName ) {
2932
+
2933
+ eventName = oEvent;
2934
+ eventInterface = Popcorn.events.getInterface( eventName );
2935
+
2936
+ if ( eventInterface ) {
2937
+
2938
+ evt = document.createEvent( eventInterface );
2939
+ evt.initEvent( eventName, true, true, window, 1 );
2940
+ }
2941
+ }
2942
+
2943
+ if ( events[ eventName ] ) {
2944
+
2945
+ for ( var i = events[ eventName ].length - 1; i >= 0; i-- ) {
2946
+
2947
+ events[ eventName ][ i ].call( self, evt, self );
2948
+ }
2949
+ }
2950
+ };
2951
+
2952
+ // Attempt to get src from playerFn parameter
2953
+ basePlayer.src = src || "";
2954
+ basePlayer.duration = 0;
2955
+ basePlayer.paused = true;
2956
+ basePlayer.ended = 0;
2957
+
2958
+ options && options.events && Popcorn.forEach( options.events, function( val, key ) {
2959
+
2960
+ basePlayer.addEventListener( key, val, false );
2961
+ });
2962
+
2963
+ // true and undefined returns on canPlayType means we should attempt to use it,
2964
+ // false means we cannot play this type
2965
+ if ( player._canPlayType( container.nodeName, src ) !== false ) {
2966
+
2967
+ if ( player._setup ) {
2968
+
2969
+ player._setup.call( basePlayer, options );
2970
+ } else {
2971
+
2972
+ // there is no setup, which means there is nothing to load
2973
+ basePlayer.readyState = 4;
2974
+ basePlayer.dispatchEvent( "loadedmetadata" );
2975
+ basePlayer.dispatchEvent( "loadeddata" );
2976
+ basePlayer.dispatchEvent( "canplaythrough" );
2977
+ }
2978
+ } else {
2979
+
2980
+ basePlayer.dispatchEvent( "error" );
2981
+ }
2982
+
2983
+ // when a custom player is loaded, load basePlayer state into custom player
2984
+ basePlayer.addEventListener( "loadedmetadata", function() {
2985
+
2986
+ // if a player is not ready before currentTime is called, this will set it after it is ready
2987
+ basePlayer.currentTime = currentTime;
2988
+
2989
+ // same as above with volume and muted
2990
+ basePlayer.volume = volume;
2991
+ basePlayer.muted = muted;
2992
+ });
2777
2993
 
2778
- Popcorn.forEach( medias, function( idx, key ) {
2994
+ basePlayer.addEventListener( "loadeddata", function() {
2779
2995
 
2780
- var media = medias[ key ],
2781
- hasDataSources = false,
2782
- dataSources, data, popcornMedia;
2996
+ // if play was called before player ready, start playing video
2997
+ !basePlayer.paused && basePlayer.play();
2998
+ });
2783
2999
 
2784
- // Ensure that the DOM has an id
2785
- if ( !media.id ) {
3000
+ popcorn = new Popcorn.p.init( basePlayer, options );
2786
3001
 
2787
- media.id = Popcorn.guid( "__popcorn" );
3002
+ if ( player._teardown ) {
3003
+
3004
+ popcorn.destroy = combineFn( popcorn.destroy, function() {
3005
+
3006
+ player._teardown.call( basePlayer, options );
3007
+ });
2788
3008
  }
2789
3009
 
2790
- // Ensure we're looking at a dom node
2791
- if ( media.nodeType && media.nodeType === 1 ) {
3010
+ return popcorn;
3011
+ };
2792
3012
 
2793
- popcornMedia = Popcorn( "#" + media.id );
3013
+ playerFn.canPlayType = player._canPlayType = player._canPlayType || Popcorn.nop;
2794
3014
 
2795
- dataSources = ( media.getAttribute( dataAttr ) || "" ).split( "," );
3015
+ Popcorn[ name ] = Popcorn.player.registry[ name ] = playerFn;
3016
+ };
2796
3017
 
2797
- if ( dataSources[ 0 ] ) {
3018
+ Popcorn.player.registry = {};
2798
3019
 
2799
- Popcorn.forEach( dataSources, function( source ) {
3020
+ Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
2800
3021
 
2801
- // split the parser and data as parser!file
2802
- data = source.split( "!" );
3022
+ object.__defineGetter__( description, options.get || Popcorn.nop );
3023
+ object.__defineSetter__( description, options.set || Popcorn.nop );
3024
+ };
2803
3025
 
2804
- // if no parser is defined for the file, assume "parse" + file extension
2805
- if ( data.length === 1 ) {
3026
+ // smart will attempt to find you a match, if it does not find a match,
3027
+ // it will attempt to create a video element with the source,
3028
+ // if that failed, it will throw.
3029
+ Popcorn.smart = function( target, src, options ) {
2806
3030
 
2807
- // parse a relative URL for the filename, split to get extension
2808
- data = source.match( /(.*)[\/\\]([^\/\\]+\.\w+)$/ )[ 2 ].split( "." );
3031
+ var nodeId = rIdExp.exec( target ),
3032
+ playerType,
3033
+ node = nodeId && nodeId.length && nodeId[ 2 ] ?
3034
+ document.getElementById( nodeId[ 2 ] ) :
3035
+ target;
2809
3036
 
2810
- data[ 0 ] = "parse" + data[ 1 ].toUpperCase();
2811
- data[ 1 ] = source;
2812
- }
3037
+ // Popcorn.smart( video, /* options */ )
3038
+ if ( node.nodeType === "VIDEO" && !src ) {
2813
3039
 
2814
- // If the media has data sources and the correct parser is registered, continue to load
2815
- if ( dataSources[ 0 ] && popcornMedia[ data[ 0 ] ] ) {
3040
+ if ( typeof src === "object" ) {
2816
3041
 
2817
- // Set up the media and load in the datasources
2818
- popcornMedia[ data[ 0 ] ]( data[ 1 ] );
3042
+ options = src;
3043
+ src = undefined;
3044
+ }
2819
3045
 
2820
- }
2821
- });
3046
+ return Popcorn( node, options );
3047
+ }
2822
3048
 
2823
- }
3049
+ // for now we loop through and use the first valid player we find.
3050
+ for ( var key in Popcorn.player.registry ) {
2824
3051
 
2825
- // Only play the media if it was specified to do so
2826
- if ( !!popcornMedia.autoplay ) {
2827
- popcornMedia.play();
2828
- }
3052
+ if ( Popcorn.player.registry.hasOwnProperty( key ) ) {
2829
3053
 
3054
+ if ( Popcorn.player.registry[ key ].canPlayType( node.nodeName, src ) ) {
3055
+
3056
+ // Popcorn.smart( player, src, /* options */ )
3057
+ return Popcorn[ key ]( target, src, options );
3058
+ }
2830
3059
  }
2831
- });
2832
- }, false );
3060
+ }
3061
+
3062
+ // Popcorn.smart( div, src, /* options */ )
3063
+ // attempting to create a video in a container
3064
+ if ( node.nodeType !== "VIDEO" ) {
2833
3065
 
2834
- })( Popcorn );// PLUGIN: Attribution
3066
+ target = document.createElement( "video" );
3067
+
3068
+ node.appendChild( target );
3069
+ node = target;
3070
+ }
3071
+
3072
+ options && options.events && options.events.error && node.addEventListener( "error", options.events.error, false );
3073
+ node.src = src;
3074
+
3075
+ return Popcorn( node, options );
3076
+ };
3077
+
3078
+ })( Popcorn );
3079
+ // PLUGIN: Attribution
2835
3080
 
2836
3081
  (function( Popcorn ) {
2837
3082
 
@@ -2993,7 +3238,8 @@
2993
3238
  nameofworkurl: {
2994
3239
  elem: "input",
2995
3240
  type: "url",
2996
- label: "Url of Work"
3241
+ label: "Url of Work",
3242
+ optional: true
2997
3243
  },
2998
3244
  copyrightholder: {
2999
3245
  elem: "input",
@@ -3003,7 +3249,8 @@
3003
3249
  copyrightholderurl: {
3004
3250
  elem: "input",
3005
3251
  type: "url",
3006
- label: "Copyright Holder Url"
3252
+ label: "Copyright Holder Url",
3253
+ optional: true
3007
3254
  },
3008
3255
  license: {
3009
3256
  elem: "input",
@@ -3013,7 +3260,8 @@
3013
3260
  licenseurl: {
3014
3261
  elem: "input",
3015
3262
  type: "url",
3016
- label: "License URL"
3263
+ label: "License URL",
3264
+ optional: true
3017
3265
  },
3018
3266
  target: "attribution-container"
3019
3267
  }
@@ -3191,7 +3439,8 @@
3191
3439
  onFrame: {
3192
3440
  elem: "input",
3193
3441
  type: "function",
3194
- label: "onFrame"
3442
+ label: "onFrame",
3443
+ optional: true
3195
3444
  },
3196
3445
  onEnd: {
3197
3446
  elem: "input",
@@ -3310,7 +3559,7 @@
3310
3559
 
3311
3560
  Popcorn.xhr.getJSONP( _uri, function( data ) {
3312
3561
 
3313
- var fragment = document.createElement( "p" );
3562
+ var fragment = document.createElement( "div" );
3314
3563
 
3315
3564
  fragment.innerHTML = "<p style='padding:" + _padding + ";'>" + data.title + "<p/>";
3316
3565
 
@@ -3390,7 +3639,8 @@
3390
3639
  userid: {
3391
3640
  elem: "input",
3392
3641
  type: "text",
3393
- label: "UserID"
3642
+ label: "UserID",
3643
+ optional: true
3394
3644
  },
3395
3645
  tags: {
3396
3646
  elem: "input",
@@ -3400,33 +3650,39 @@
3400
3650
  username: {
3401
3651
  elem: "input",
3402
3652
  type: "text",
3403
- label: "Username"
3653
+ label: "Username",
3654
+ optional: true
3404
3655
  },
3405
3656
  apikey: {
3406
3657
  elem: "input",
3407
3658
  type: "text",
3408
- label: "Api_key"
3659
+ label: "Api_key",
3660
+ optional: true
3409
3661
  },
3410
3662
  target: "flickr-container",
3411
3663
  height: {
3412
3664
  elem: "input",
3413
3665
  type: "text",
3414
- label: "Height"
3666
+ label: "Height",
3667
+ optional: true
3415
3668
  },
3416
3669
  width: {
3417
3670
  elem: "input",
3418
3671
  type: "text",
3419
- label: "Width"
3672
+ label: "Width",
3673
+ optional: true
3420
3674
  },
3421
3675
  padding: {
3422
3676
  elem: "input",
3423
3677
  type: "text",
3424
- label: "Padding"
3678
+ label: "Padding",
3679
+ optional: true
3425
3680
  },
3426
3681
  border: {
3427
3682
  elem: "input",
3428
3683
  type: "text",
3429
- label: "Border"
3684
+ label: "Border",
3685
+ optional: true
3430
3686
  },
3431
3687
  numberofimages: {
3432
3688
  elem: "input",
@@ -3610,92 +3866,110 @@
3610
3866
  font: {
3611
3867
  elem: "input",
3612
3868
  type: "text",
3613
- label: "font"
3869
+ label: "font",
3870
+ optional: true
3614
3871
  },
3615
3872
  xid: {
3616
3873
  elem: "input",
3617
3874
  type: "text",
3618
- label: "Xid"
3875
+ label: "Xid",
3876
+ optional: true
3619
3877
  },
3620
3878
  href: {
3621
3879
  elem: "input",
3622
3880
  type: "url",
3623
- label: "Href"
3881
+ label: "Href",
3882
+ optional: true
3624
3883
  },
3625
3884
  site: {
3626
3885
  elem: "input",
3627
3886
  type: "url",
3628
- label:"Site"
3887
+ label:"Site",
3888
+ optional: true
3629
3889
  },
3630
3890
  height: {
3631
3891
  elem: "input",
3632
3892
  type: "text",
3633
- label: "Height"
3893
+ label: "Height",
3894
+ optional: true
3634
3895
  },
3635
3896
  width: {
3636
3897
  elem: "input",
3637
3898
  type: "text",
3638
- label: "Width"
3899
+ label: "Width",
3900
+ optional: true
3639
3901
  },
3640
3902
  action: {
3641
3903
  elem: "select",
3642
3904
  options: [ "like", "recommend" ],
3643
- label: "Action"
3905
+ label: "Action",
3906
+ optional: true
3644
3907
  },
3645
3908
  stream: {
3646
3909
  elem: "select",
3647
3910
  options: [ "false", "true" ],
3648
- label: "Stream"
3911
+ label: "Stream",
3912
+ optional: true
3649
3913
  },
3650
3914
  header: {
3651
3915
  elem: "select",
3652
3916
  options: [ "false", "true" ],
3653
- label: "Header"
3917
+ label: "Header",
3918
+ optional: true
3654
3919
  },
3655
3920
  layout: {
3656
3921
  elem: "select",
3657
3922
  options: [ "standard", "button_count", "box_count" ],
3658
- label: "Layout"
3923
+ label: "Layout",
3924
+ optional: true
3659
3925
  },
3660
3926
  max_rows: {
3661
3927
  elem: "input",
3662
3928
  type: "text",
3663
- label: "Max_rows"
3929
+ label: "Max_rows",
3930
+ optional: true
3664
3931
  },
3665
3932
  border_color: {
3666
3933
  elem: "input",
3667
3934
  type: "text",
3668
- label: "Border_color"
3935
+ label: "Border_color",
3936
+ optional: true
3669
3937
  },
3670
3938
  event_app_id: {
3671
3939
  elem: "input",
3672
3940
  type: "text",
3673
- label: "Event_app_id"
3941
+ label: "Event_app_id",
3942
+ optional: true
3674
3943
  },
3675
3944
  colorscheme: {
3676
3945
  elem: "select",
3677
3946
  options: [ "light", "dark" ],
3678
- label: "Colorscheme"
3947
+ label: "Colorscheme",
3948
+ optional: true
3679
3949
  },
3680
3950
  show_faces: {
3681
3951
  elem: "select",
3682
3952
  options: [ "false", "true" ],
3683
- label: "Showfaces"
3953
+ label: "Showfaces",
3954
+ optional: true
3684
3955
  },
3685
3956
  recommendations: {
3686
3957
  elem: "select",
3687
3958
  options: [ "false", "true" ],
3688
- label: "Recommendations"
3959
+ label: "Recommendations",
3960
+ optional: true
3689
3961
  },
3690
3962
  always_post_to_friends: {
3691
3963
  elem: "input",
3692
3964
  options: [ "false", "true" ],
3693
- label: "Always_post_to_friends"
3965
+ label: "Always_post_to_friends",
3966
+ optional: true
3694
3967
  },
3695
3968
  num_posts: {
3696
3969
  elem: "input",
3697
3970
  type: "text",
3698
- label: "Number_of_Comments"
3971
+ label: "Number_of_Comments",
3972
+ optional: true
3699
3973
  }
3700
3974
  }
3701
3975
  },
@@ -3743,6 +4017,7 @@
3743
4017
  options._container.id = "facebookdiv-" + Popcorn.guid();
3744
4018
  options._facebookdiv = document.createElement( "fb:" + _type );
3745
4019
  options._container.appendChild( options._facebookdiv );
4020
+ options._container.style.display = "none";
3746
4021
 
3747
4022
  // All the the "types" for facebook share largely identical attributes, for loop suffices.
3748
4023
  // ** Credit to Rick Waldron, it's essentially all his code in this function.
@@ -4154,22 +4429,26 @@ var googleCallback;
4154
4429
  type: {
4155
4430
  elem: "select",
4156
4431
  options: [ "ROADMAP", "SATELLITE", "STREETVIEW", "HYBRID", "TERRAIN" ],
4157
- label: "Type"
4432
+ label: "Type",
4433
+ optional: true
4158
4434
  },
4159
4435
  zoom: {
4160
4436
  elem: "input",
4161
4437
  type: "text",
4162
- label: "Zoom"
4438
+ label: "Zoom",
4439
+ optional: true
4163
4440
  },
4164
4441
  lat: {
4165
4442
  elem: "input",
4166
4443
  type: "text",
4167
- label: "Lat"
4444
+ label: "Lat",
4445
+ optional: true
4168
4446
  },
4169
4447
  lng: {
4170
4448
  elem: "input",
4171
4449
  type: "text",
4172
- label: "Lng"
4450
+ label: "Lng",
4451
+ optional: true
4173
4452
  },
4174
4453
  location: {
4175
4454
  elem: "input",
@@ -4179,12 +4458,14 @@ var googleCallback;
4179
4458
  heading: {
4180
4459
  elem: "input",
4181
4460
  type: "text",
4182
- label: "Heading"
4461
+ label: "Heading",
4462
+ optional: true
4183
4463
  },
4184
4464
  pitch: {
4185
4465
  elem: "input",
4186
4466
  type: "text",
4187
- label: "Pitch"
4467
+ label: "Pitch",
4468
+ optional: true
4188
4469
  }
4189
4470
  }
4190
4471
  });
@@ -4200,7 +4481,7 @@ var googleCallback;
4200
4481
  * Options parameter will need a start, end, href, target and src.
4201
4482
  * Start is the time that you want this plug-in to execute
4202
4483
  * End is the time that you want this plug-in to stop executing
4203
- * href is the url of the destination of a link - optional
4484
+ * href is the url of the destination of a anchor - optional
4204
4485
  * Target is the id of the document element that the iframe needs to be attached to,
4205
4486
  * this target element must exist on the DOM
4206
4487
  * Src is the url of the image that you want to display
@@ -4242,7 +4523,8 @@ var googleCallback;
4242
4523
  href: {
4243
4524
  elem: "input",
4244
4525
  type: "url",
4245
- label: "Link URL"
4526
+ label: "anchor URL",
4527
+ optional: true
4246
4528
  },
4247
4529
  target: "image-container",
4248
4530
  src: {
@@ -4253,7 +4535,8 @@ var googleCallback;
4253
4535
  text: {
4254
4536
  elem: "input",
4255
4537
  type: "text",
4256
- label: "TEXT"
4538
+ label: "TEXT",
4539
+ optional: true
4257
4540
  }
4258
4541
  }
4259
4542
  },
@@ -4261,47 +4544,50 @@ var googleCallback;
4261
4544
  var img = document.createElement( "img" ),
4262
4545
  target = document.getElementById( options.target );
4263
4546
 
4264
- options.link = document.createElement( "a" );
4265
- options.link.style.position = "relative";
4266
- options.link.style.textDecoration = "none";
4547
+ options.anchor = document.createElement( "a" );
4548
+ options.anchor.style.position = "relative";
4549
+ options.anchor.style.textDecoration = "none";
4550
+ options.anchor.style.display = "none";
4267
4551
 
4268
4552
 
4269
4553
  if ( !target && Popcorn.plugin.debug ) {
4270
4554
  throw new Error( "target container doesn't exist" );
4271
4555
  }
4272
4556
  // add the widget's div to the target div
4273
- target && target.appendChild( options.link );
4557
+ target && target.appendChild( options.anchor );
4274
4558
 
4275
4559
  img.addEventListener( "load", function() {
4276
4560
 
4277
4561
  // borders look really bad, if someone wants it they can put it on their div target
4278
4562
  img.style.borderStyle = "none";
4279
4563
 
4280
- if ( options.href ) {
4281
- options.link.href = options.href;
4282
- }
4564
+ options.anchor.href = options.href || options.src || "#";
4565
+ options.anchor.target = "_blank";
4283
4566
 
4284
- options.link.target = "_blank";
4567
+ var fontHeight, divText;
4285
4568
 
4286
- var fontHeight = ( img.height / 12 ) + "px",
4287
- divText = document.createElement( "div" );
4288
4569
 
4289
- Popcorn.extend( divText.style, {
4570
+ // If display text was provided, display it:
4571
+ if ( options.text ) {
4572
+ fontHeight = ( img.height / 12 ) + "px";
4573
+ divText = document.createElement( "div" );
4290
4574
 
4291
- color: "black",
4292
- fontSize: fontHeight,
4293
- fontWeight: "bold",
4294
- position: "relative",
4295
- textAlign: "center",
4296
- width: img.width + "px",
4297
- zIndex: "10"
4298
- });
4575
+ Popcorn.extend( divText.style, {
4576
+ color: "black",
4577
+ fontSize: fontHeight,
4578
+ fontWeight: "bold",
4579
+ position: "relative",
4580
+ textAlign: "center",
4581
+ width: img.width + "px",
4582
+ zIndex: "10"
4583
+ });
4584
+
4585
+ divText.innerHTML = options.text || "";
4586
+ divText.style.top = ( img.height / 2 ) - ( divText.offsetHeight / 2 ) + "px";
4587
+ options.anchor.appendChild( divText );
4588
+ }
4299
4589
 
4300
- divText.innerHTML = options.text || "";
4301
- options.link.appendChild( divText );
4302
- options.link.appendChild( img );
4303
- divText.style.top = ( img.height / 2 ) - ( divText.offsetHeight / 2 ) + "px";
4304
- options.link.style.display = "none";
4590
+ options.anchor.appendChild( img );
4305
4591
  }, false );
4306
4592
 
4307
4593
  img.src = options.src;
@@ -4314,7 +4600,7 @@ var googleCallback;
4314
4600
  * options variable
4315
4601
  */
4316
4602
  start: function( event, options ) {
4317
- options.link.style.display = "block";
4603
+ options.anchor.style.display = "inline";
4318
4604
  },
4319
4605
  /**
4320
4606
  * @member image
@@ -4323,10 +4609,10 @@ var googleCallback;
4323
4609
  * options variable
4324
4610
  */
4325
4611
  end: function( event, options ) {
4326
- options.link.style.display = "none";
4612
+ options.anchor.style.display = "none";
4327
4613
  },
4328
4614
  _teardown: function( options ) {
4329
- document.getElementById( options.target ) && document.getElementById( options.target ).removeChild( options.link );
4615
+ document.getElementById( options.target ) && document.getElementById( options.target ).removeChild( options.anchor );
4330
4616
  }
4331
4617
  });
4332
4618
  })( Popcorn );
@@ -4495,6 +4781,7 @@ var googleCallback;
4495
4781
  target && target.appendChild( options.container );
4496
4782
 
4497
4783
  var scriptReady = function() {
4784
+
4498
4785
  Popcorn.getJSONP( "//000000book.com/data/" + options.gmltag + ".json?callback=", function( data ) {
4499
4786
 
4500
4787
  options.pjsInstance = new Processing( options.container, gmlPlayer );
@@ -4505,7 +4792,7 @@ var googleCallback;
4505
4792
 
4506
4793
  if ( !window.Processing ) {
4507
4794
 
4508
- Popcorn.getScript( "//processingjs.org/content/download/processing-js-1.3.0/processing-1.3.0.min.js", scriptReady );
4795
+ Popcorn.getScript( "//cloud.github.com/downloads/processing-js/processing-js/processing-1.3.6.min.js", scriptReady );
4509
4796
  } else {
4510
4797
 
4511
4798
  scriptReady();
@@ -4735,7 +5022,8 @@ var googleCallback;
4735
5022
  salutation : {
4736
5023
  elem: "input",
4737
5024
  type: "text",
4738
- label: "Text"
5025
+ label: "Text",
5026
+ optional: true
4739
5027
  },
4740
5028
  name: {
4741
5029
  elem: "input",
@@ -4745,7 +5033,8 @@ var googleCallback;
4745
5033
  role: {
4746
5034
  elem: "input",
4747
5035
  type: "text",
4748
- label: "Text"
5036
+ label: "Text",
5037
+ optional: true
4749
5038
  }
4750
5039
  }
4751
5040
  },
@@ -4802,7 +5091,10 @@ var googleCallback;
4802
5091
  * options variable
4803
5092
  */
4804
5093
  end: function( event, options ) {
4805
- options.container.innerHTML = "";
5094
+ // Empty child nodes
5095
+ while ( options.container.firstChild ) {
5096
+ options.container.removeChild( options.container.firstChild );
5097
+ }
4806
5098
  }
4807
5099
 
4808
5100
  });
@@ -4981,16 +5273,190 @@ var googleCallback;
4981
5273
  title: {
4982
5274
  elem: "input",
4983
5275
  type: "text",
4984
- label: "title"
5276
+ label: "title",
5277
+ optional: true
4985
5278
  },
4986
5279
  orientation: {
4987
5280
  elem: "select",
4988
5281
  options: [ "Vertical", "Horizontal" ],
4989
- label: "orientation"
5282
+ label: "orientation",
5283
+ optional: true
4990
5284
  }
4991
5285
  }
4992
5286
  });
4993
5287
  })( Popcorn );
5288
+ // Rdio Plug-in
5289
+ /**
5290
+ * Rdio popcorn plug-in
5291
+ * Appends Rdio album track listings to an element on the page.
5292
+ * Can also append a user's playlist to an element on the page.
5293
+ * Option paramter can be in two forms:
5294
+ * Options parameter will take a start, end, target, artist, album, and type or
5295
+ * Options parameter will take a start, end, target, person, id, playlist, and type
5296
+ * Start is the time that you want this plug-in to execute
5297
+ * End is the time that you want this plug-in to stop executing
5298
+ * Target is the id of the document element that the images are appended to
5299
+ * Artist is the name of who's album image will display
5300
+ * Album is the album that will display of the specified Artist
5301
+ * Person is the Rdio member who's playlist will display
5302
+ * ID is the playlist's unqiue Rdio playlist identifier
5303
+ * Playlist is the name of the playlist
5304
+ * Type specifies if the element is an album or playlist
5305
+ *
5306
+
5307
+ *
5308
+ * @param {Object} options
5309
+ *
5310
+ * Example 1:
5311
+ var p = Popcorn( "#video" )
5312
+ .rdio({
5313
+ start: 2,
5314
+ end: 10,
5315
+ target: "rdiodiv",
5316
+ artist: "Jamiroquai",
5317
+ album: "Synkronized",
5318
+ type: "album"
5319
+ })
5320
+ *
5321
+ * Example 2:
5322
+ var p = Popcorn( "#video" )
5323
+ .rdio({
5324
+ start: 10,
5325
+ end: 20,
5326
+ target: "rdiodiv",
5327
+ person: "diggywiggy",
5328
+ id: 413517,
5329
+ playlist: "sunday",
5330
+ type: "playlist"
5331
+ })
5332
+ **/
5333
+
5334
+ (function( Popcorn ) {
5335
+ var _album = {},
5336
+ _container = {},
5337
+ _target = {},
5338
+ _rdioURL = "http://www.rdio.com/api/oembed/?format=json&url=http://www.rdio.com/%23";
5339
+
5340
+ Popcorn.plugin( "rdio", (function( options ) {
5341
+ var _loadResults = function( data, options ) {
5342
+ var title = data.title,
5343
+ html = data.html;
5344
+ if ( data && title && html ) {
5345
+ _album[ options.containerid ].htmlString = "<div>" + html + "</div>";
5346
+ } else {
5347
+ if ( Popcorn.plugin.debug ) {
5348
+ throw new Error( "Did not receive data from server." );
5349
+ }
5350
+ }
5351
+ },
5352
+
5353
+ // Handle AJAX Request
5354
+ _getResults = function( options ) {
5355
+ var urlBuilder = function( type ) {
5356
+ var path = {
5357
+ playlist: function() {
5358
+ return "/people/" + ( options.person ) + "/playlists/" + options.id + "/";
5359
+ },
5360
+ album: function() {
5361
+ return "/artist/" + ( options.artist ) + "/album/";
5362
+ }
5363
+ }[ type ]();
5364
+
5365
+ return _rdioURL + path + options[ type ] + "/&callback=_loadResults";
5366
+ },
5367
+ url = urlBuilder( options.type );
5368
+ Popcorn.getJSONP( url, function( data ) {
5369
+ _loadResults( data, options );
5370
+ }, false );
5371
+ };
5372
+
5373
+ return {
5374
+ _setup: function( options ) {
5375
+ var key = options.containerid = Popcorn.guid(),
5376
+ container = _container[ key ] = document.createElement( "div" ),
5377
+ target = _target[ key ] = document.getElementById( options.target );
5378
+ if ( !target && Popcorn.plugin.debug ) {
5379
+ throw new Error( "Target container could not be found." );
5380
+ }
5381
+ container.style.display = "none";
5382
+ container.innerHTML = "";
5383
+ target.appendChild( container );
5384
+ _album[ key ] = {
5385
+ htmlString: ( options.playlist || "Unknown Source" ) || ( options.album || "Unknown Source" )
5386
+ };
5387
+ _getResults( options );
5388
+ },
5389
+ start: function( event, options ) {
5390
+ var key = options.containerid,
5391
+ container = _container[ key ];
5392
+ container.innerHTML = _album[ key ].htmlString;
5393
+ container.style.display = "inline";
5394
+ },
5395
+ end: function( event, options ) {
5396
+ container = _container[ options.containerid ];
5397
+ container.style.display = "none";
5398
+ container.innerHTML = "";
5399
+ },
5400
+ _teardown: function( options ) {
5401
+ var key = options.containerid,
5402
+ target = _target[ key ];
5403
+ if ( _album[ key ] ) {
5404
+ delete _album[ key ];
5405
+ }
5406
+ target && target.removeChild( _container[ key ] );
5407
+ delete _target[ key ];
5408
+ delete _container[ key ];
5409
+ }
5410
+ };
5411
+ })(),
5412
+ {
5413
+ manifest: {
5414
+ about: {
5415
+ name: "Popcorn Rdio Plugin",
5416
+ version: "0.1",
5417
+ author: "Denise Rigato"
5418
+ },
5419
+ options: {
5420
+ start: {
5421
+ elem: "input",
5422
+ type: "text",
5423
+ label: "In"
5424
+ },
5425
+ end: {
5426
+ elem: "input",
5427
+ type: "text",
5428
+ label: "Out"
5429
+ },
5430
+ target: "rdio",
5431
+ artist: {
5432
+ elem: "input",
5433
+ type: "text",
5434
+ label: "Artist"
5435
+ },
5436
+ album: {
5437
+ elem: "input",
5438
+ type: "text",
5439
+ label: "Album"
5440
+ },
5441
+ person: {
5442
+ elem: "input",
5443
+ type: "text",
5444
+ label: "Person"
5445
+ },
5446
+ id: {
5447
+ elem: "input",
5448
+ type: "text",
5449
+ label: "Id"
5450
+ },
5451
+ playlist: {
5452
+ elem: "input",
5453
+ type: "text",
5454
+ label: "Playlist"
5455
+ }
5456
+ }
5457
+ }
5458
+ });
5459
+ }( Popcorn ));
4994
5460
  // PLUGIN: Subtitle
4995
5461
 
4996
5462
  (function ( Popcorn ) {
@@ -5259,12 +5725,14 @@ var googleCallback;
5259
5725
  image: {
5260
5726
  elem: "input",
5261
5727
  type: "url",
5262
- label: "Image Src"
5728
+ label: "Image Src",
5729
+ optional: true
5263
5730
  },
5264
5731
  href: {
5265
5732
  elem: "input",
5266
5733
  type: "url",
5267
- label: "URL"
5734
+ label: "URL",
5735
+ optional: true
5268
5736
  }
5269
5737
  }
5270
5738
  });
@@ -5332,12 +5800,14 @@ var googleCallback;
5332
5800
  height: {
5333
5801
  elem: "input",
5334
5802
  type: "number",
5335
- label: "Height"
5803
+ label: "Height",
5804
+ optional: true
5336
5805
  },
5337
5806
  width: {
5338
5807
  elem: "input",
5339
5808
  type: "number",
5340
- label: "Width"
5809
+ label: "Width",
5810
+ optional: true
5341
5811
  }
5342
5812
  }
5343
5813
  },
@@ -5494,7 +5964,8 @@ var googleCallback;
5494
5964
  id: {
5495
5965
  elem: "input",
5496
5966
  type: "text",
5497
- label: "Id"
5967
+ label: "Id",
5968
+ optional: true
5498
5969
  },
5499
5970
  start: {
5500
5971
  elem: "input",
@@ -5620,7 +6091,8 @@ var wikiCallback;
5620
6091
  lang: {
5621
6092
  elem: "input",
5622
6093
  type: "text",
5623
- label: "Language"
6094
+ label: "Language",
6095
+ optional: true
5624
6096
  },
5625
6097
  src: {
5626
6098
  elem: "input",
@@ -5630,12 +6102,14 @@ var wikiCallback;
5630
6102
  title: {
5631
6103
  elem: "input",
5632
6104
  type: "text",
5633
- label: "Title"
6105
+ label: "Title",
6106
+ optional: true
5634
6107
  },
5635
6108
  numberofwords: {
5636
6109
  elem: "input",
5637
6110
  type: "text",
5638
- label: "Num Of Words"
6111
+ label: "Num Of Words",
6112
+ optional: true
5639
6113
  },
5640
6114
  target: "wikipedia-container"
5641
6115
  }
@@ -6019,17 +6493,20 @@ var wikiCallback;
6019
6493
  api_key: { // Required for Blog Info and Blog Post retrievals
6020
6494
  elem: "input",
6021
6495
  type: "text",
6022
- label: "Application_Key"
6496
+ label: "Application_Key",
6497
+ optional: true
6023
6498
  },
6024
6499
  size: {
6025
6500
  elem: "select",
6026
6501
  options: [ 16, 24, 30, 40, 48, 64, 96, 128, 512 ],
6027
- label: "avatarSize"
6502
+ label: "avatarSize",
6503
+ optional: true
6028
6504
  },
6029
6505
  blogId: { // Required for BLOGPOST requests
6030
6506
  elem: "input",
6031
6507
  type: "number",
6032
- label: "Blog_ID"
6508
+ label: "Blog_ID",
6509
+ optional: true
6033
6510
  },
6034
6511
  /* Optional for Photo and Video BlogPosts, defaulted to 250 pixels for photos and 400 for videos if not provided or provided width
6035
6512
  * is not found in their arrays. If multiple videos or photos are in the blogpost then it will use this same size for all of them unless
@@ -6038,7 +6515,8 @@ var wikiCallback;
6038
6515
  width: {
6039
6516
  elem: "input",
6040
6517
  type: "number",
6041
- label: "Photo_Width"
6518
+ label: "Photo_Width",
6519
+ optional: true
6042
6520
  }
6043
6521
  }
6044
6522
  },
@@ -6049,7 +6527,8 @@ var wikiCallback;
6049
6527
  blogHTTPHeader,
6050
6528
  uriNoHeader,
6051
6529
  uriFinal,
6052
- type;
6530
+ type,
6531
+ that = this;
6053
6532
 
6054
6533
  // Valid types of retrieval requests
6055
6534
  var validType = function( type ) {
@@ -6096,6 +6575,10 @@ var wikiCallback;
6096
6575
  }
6097
6576
  requestString = "http://api.tumblr.com/v2/blog/" + options.base_hostname + "/" + type + "?api_key=" + options.api_key + "&id=" + options.blogId +
6098
6577
  "&jsonp=tumblrCallBack";
6578
+
6579
+ this.listen( "tumblrError", function( e ){
6580
+ Popcorn.error( e );
6581
+ });
6099
6582
 
6100
6583
  Popcorn.getJSONP( requestString, function( data ) {
6101
6584
  if ( data.meta.msg === "OK" ) {
@@ -6136,8 +6619,7 @@ var wikiCallback;
6136
6619
  options._container.appendChild( commonDiv );
6137
6620
  }
6138
6621
  } else {
6139
- // There was an error somewhere down the line that caused the request to fail.
6140
- Popcorn.error( "Error. Request failed. Status code: " + data.meta.status + " - Message: " + data.meta.msg );
6622
+ that.trigger( "tumblrError", "Error. Request failed. Status code: " + data.meta.status + " - Message: " + data.meta.msg );
6141
6623
  }
6142
6624
  }, false );
6143
6625
  }
@@ -6231,32 +6713,38 @@ var wikiCallback;
6231
6713
  memberid: {
6232
6714
  elem: "input",
6233
6715
  type: "text",
6234
- label: "Member ID"
6716
+ label: "Member ID",
6717
+ optional: true
6235
6718
  },
6236
6719
  format: {
6237
6720
  elem: "input",
6238
6721
  type: "text",
6239
- label: "Format"
6722
+ label: "Format",
6723
+ optional: true
6240
6724
  },
6241
6725
  companyid: {
6242
6726
  elem: "input",
6243
6727
  type: "text",
6244
- label: "Company ID"
6728
+ label: "Company ID",
6729
+ optional: true
6245
6730
  },
6246
6731
  modules: {
6247
6732
  elem: "input",
6248
6733
  type: "text",
6249
- label: "Modules"
6734
+ label: "Modules",
6735
+ optional: true
6250
6736
  },
6251
6737
  productid: {
6252
6738
  elem: "input",
6253
6739
  type: "text",
6254
- label: "productid"
6740
+ label: "productid",
6741
+ optional: true
6255
6742
  },
6256
6743
  related: {
6257
6744
  elem: "input",
6258
6745
  type: "text",
6259
- label: "related"
6746
+ label: "related",
6747
+ optional: true
6260
6748
  },
6261
6749
  start: {
6262
6750
  elem: "input",
@@ -6485,7 +6973,7 @@ var wikiCallback;
6485
6973
 
6486
6974
  var getData, data, getTemplate, template;
6487
6975
 
6488
- Popcorn.getScript( "https://github.com/janl/mustache.js/raw/master/mustache.js" );
6976
+ Popcorn.getScript( "http://mustache.github.com/extras/mustache.js" );
6489
6977
 
6490
6978
  var shouldReload = !!options.dynamic,
6491
6979
  typeOfTemplate = typeof options.template,
@@ -6599,7 +7087,8 @@ var wikiCallback;
6599
7087
  dynamic: {
6600
7088
  elem: "input",
6601
7089
  type: "text",
6602
- label: "Dynamic"
7090
+ label: "Dynamic",
7091
+ optional: true
6603
7092
  }
6604
7093
  }
6605
7094
  });
@@ -6911,22 +7400,26 @@ var wikiCallback;
6911
7400
  type: {
6912
7401
  elem: "select",
6913
7402
  options: [ "ROADMAP", "SATELLITE", "TERRAIN" ],
6914
- label: "Type"
7403
+ label: "Type",
7404
+ optional: true
6915
7405
  },
6916
7406
  zoom: {
6917
7407
  elem: "input",
6918
7408
  type: "text",
6919
- label: "Zoom"
7409
+ label: "Zoom",
7410
+ optional: true
6920
7411
  },
6921
7412
  lat: {
6922
7413
  elem: "input",
6923
7414
  type: "text",
6924
- label: "Lat"
7415
+ label: "Lat",
7416
+ optional: true
6925
7417
  },
6926
7418
  lng: {
6927
7419
  elem: "input",
6928
7420
  type: "text",
6929
- label: "Lng"
7421
+ label: "Lng",
7422
+ optional: true
6930
7423
  },
6931
7424
  location: {
6932
7425
  elem: "input",
@@ -6936,7 +7429,8 @@ var wikiCallback;
6936
7429
  markers: {
6937
7430
  elem: "input",
6938
7431
  type: "text",
6939
- label: "List Markers"
7432
+ label: "List Markers",
7433
+ optional: true
6940
7434
  }
6941
7435
  }
6942
7436
  });
@@ -7060,7 +7554,8 @@ document.addEventListener( "click", function( event ) {
7060
7554
  color: {
7061
7555
  elem: "input",
7062
7556
  type: "text",
7063
- label: "Color"
7557
+ label: "Color",
7558
+ optional: true
7064
7559
  }
7065
7560
  }
7066
7561
  },
@@ -7210,7 +7705,7 @@ document.addEventListener( "click", function( event ) {
7210
7705
  }
7211
7706
 
7212
7707
  if ( !window.Processing ) {
7213
- Popcorn.getScript( "//wac.1237.edgecastcdn.net/801237/cdn.processingjs.org/content/download/processing-js-1.3.6/processing-1.3.6.min.js", function() {
7708
+ Popcorn.getScript( "//cloud.github.com/downloads/processing-js/processing-js/processing-1.3.6.min.js", function() {
7214
7709
  scriptReady( options );
7215
7710
  });
7216
7711
  } else {
@@ -7291,7 +7786,8 @@ document.addEventListener( "click", function( event ) {
7291
7786
  noPause: {
7292
7787
  elem: "select",
7293
7788
  options: [ "TRUE", "FALSE" ],
7294
- label: "No Loop"
7789
+ label: "No Loop",
7790
+ optional: true
7295
7791
  }
7296
7792
  }
7297
7793
  });
@@ -7429,12 +7925,14 @@ document.addEventListener( "click", function( event ) {
7429
7925
  innerHTML: {
7430
7926
  elem: "input",
7431
7927
  type: "text",
7432
- label: "innerHTML"
7928
+ label: "innerHTML",
7929
+ optional: true
7433
7930
  },
7434
7931
  direction: {
7435
7932
  elem: "input",
7436
7933
  type: "text",
7437
- label: "direction"
7934
+ label: "direction",
7935
+ optional: true
7438
7936
  }
7439
7937
  }
7440
7938
  });
@@ -7493,12 +7991,14 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc
7493
7991
  width: {
7494
7992
  elem: "input",
7495
7993
  type: "text",
7496
- label: "Width"
7994
+ label: "Width",
7995
+ optional: true
7497
7996
  },
7498
7997
  height: {
7499
7998
  elem: "input",
7500
7999
  type: "text",
7501
- label: "Height"
8000
+ label: "Height",
8001
+ optional: true
7502
8002
  },
7503
8003
  src: {
7504
8004
  elem: "input",
@@ -7508,17 +8008,20 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc
7508
8008
  preload: {
7509
8009
  elem: "input",
7510
8010
  type: "boolean",
7511
- label: "Preload"
8011
+ label: "Preload",
8012
+ optional: true
7512
8013
  },
7513
8014
  page: {
7514
8015
  elem: "input",
7515
8016
  type: "number",
7516
- label: "Page Number"
8017
+ label: "Page Number",
8018
+ optional: true
7517
8019
  },
7518
8020
  aid: {
7519
8021
  elem: "input",
7520
8022
  type: "number",
7521
- label: "Annotation Id"
8023
+ label: "Annotation Id",
8024
+ optional: true
7522
8025
  }
7523
8026
  }
7524
8027
  },
@@ -9311,6 +9814,10 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc
9311
9814
  vimeo_player_loaded.pause = {};
9312
9815
 
9313
9816
  Popcorn.player( "vimeo", {
9817
+ _canPlayType: function( nodeName, url ) {
9818
+
9819
+ return (/(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(vimeo)/).test( url ) && nodeName.toLowerCase() !== "video";
9820
+ },
9314
9821
  _setup: function( options ) {
9315
9822
 
9316
9823
  var media = this,
@@ -9330,8 +9837,8 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc
9330
9837
  media.appendChild( vimeoContainer );
9331
9838
 
9332
9839
  // setting vimeo player's height and width, default to 560 x 315
9333
- width = media.style.width ? ""+media.offsetWidth : "560";
9334
- height = media.style.height ? ""+media.offsetHeight : "315";
9840
+ width = media.style.width ? "" + media.offsetWidth : "560";
9841
+ height = media.style.height ? "" + media.offsetHeight : "315";
9335
9842
 
9336
9843
  var vimeoInit = function() {
9337
9844
 
@@ -9503,37 +10010,22 @@ api - https://github.com/documentcloud/document-viewer/blob/master/public/javasc
9503
10010
  }
9504
10011
  });
9505
10012
 
9506
- media.readyState = 4;
9507
- media.dispatchEvent( "canplaythrough" );
9508
- media.dispatchEvent( "load" );
10013
+ media.dispatchEvent( "loadedmetadata" );
10014
+ media.dispatchEvent( "loadeddata" );
10015
+
9509
10016
  media.duration = vimeoObject.api_getDuration();
9510
10017
  media.dispatchEvent( "durationchange" );
9511
10018
  volumeUpdate();
9512
-
9513
- media.dispatchEvent( "loadeddata" );
10019
+ media.readyState = 4;
10020
+ media.dispatchEvent( "canplaythrough" );
9514
10021
  };
9515
10022
 
9516
- function extractId( videoUrl ) {
9517
-
9518
- if ( !videoUrl ) {
9519
- return;
9520
- }
9521
-
9522
- var rPlayerUri = /^http:\/\/player\.vimeo\.com\/video\/[\d]+/i,
9523
- rWebUrl = /vimeo\.com\/[\d]+/;
9524
-
9525
- var matches = videoUrl.match( rPlayerUri ) ? videoUrl.match( rPlayerUri )[ 0 ].substr( 30 ) : "";
9526
- return matches ? matches : videoUrl.match( rWebUrl ) ? videoUrl.match( rWebUrl )[ 0 ].substr( 10 ) : "";
9527
- }
9528
-
9529
- if ( !( src = extractId( src ) ) ) {
9530
-
9531
- throw "Invalid Video Id";
9532
- }
10023
+ var clip_id = ( /\d+$/ ).exec( src );
9533
10024
 
9534
10025
  flashvars = {
9535
- clip_id: src,
9536
- js_api: 1,
10026
+ // Load a video not found poster if the url does not contain a valid id
10027
+ clip_id: clip_id ? clip_id[ 0 ] : 0,
10028
+ api: 1,
9537
10029
  js_swf_id: vimeoContainer.id
9538
10030
  };
9539
10031
 
@@ -9570,13 +10062,18 @@ onYouTubePlayerReady.stateChangeEventHandler = {};
9570
10062
  onYouTubePlayerReady.onErrorEventHandler = {};
9571
10063
 
9572
10064
  Popcorn.player( "youtube", {
10065
+ _canPlayType: function( nodeName, url ) {
10066
+
10067
+ return (/(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(youtu)/).test( url ) && nodeName.toLowerCase() !== "video";
10068
+ },
9573
10069
  _setup: function( options ) {
9574
10070
 
9575
10071
  var media = this,
9576
- youtubeObject,
10072
+ autoPlay = false,
9577
10073
  container = document.createElement( "div" ),
9578
10074
  currentTime = 0,
9579
10075
  seekTime = 0,
10076
+ firstGo = true,
9580
10077
  seeking = false,
9581
10078
 
9582
10079
  // state code for volume changed polling
@@ -9584,8 +10081,12 @@ Popcorn.player( "youtube", {
9584
10081
  lastMuted = false,
9585
10082
  lastVolume = 100;
9586
10083
 
10084
+ // setting paused to undefined because youtube has state for not paused or playing
10085
+ media.paused = undefined;
9587
10086
  container.id = media.id + Popcorn.guid();
9588
10087
 
10088
+ options._container = container;
10089
+
9589
10090
  media.appendChild( container );
9590
10091
 
9591
10092
  var youtubeInit = function() {
@@ -9601,33 +10102,83 @@ Popcorn.player( "youtube", {
9601
10102
  // expose a callback to this scope, that is called from the global callback youtube calls
9602
10103
  onYouTubePlayerReady[ container.id ] = function() {
9603
10104
 
9604
- youtubeObject = document.getElementById( container.id );
10105
+ options.youtubeObject = document.getElementById( container.id );
9605
10106
 
9606
10107
  // more youtube callback nonsense
9607
10108
  onYouTubePlayerReady.stateChangeEventHandler[ container.id ] = function( state ) {
9608
10109
 
9609
- // playing is state 1
9610
- // paused is state 2
9611
- if ( state === 1 ) {
10110
+ if ( options.destroyed ) {
10111
+
10112
+ return;
10113
+ }
9612
10114
 
9613
- media.paused && media.play();
9614
10115
  // youtube fires paused events while seeking
9615
10116
  // this is the only way to get seeking events
9616
- } else if ( state === 2 ) {
10117
+ if ( state === 2 ) {
9617
10118
 
9618
10119
  // silly logic forced on me by the youtube API
9619
10120
  // calling youtube.seekTo triggers multiple events
9620
10121
  // with the second events getCurrentTime being the old time
9621
- if ( seeking && seekTime === currentTime && seekTime !== youtubeObject.getCurrentTime() ) {
10122
+ if ( seeking && seekTime === currentTime && seekTime !== options.youtubeObject.getCurrentTime() ) {
9622
10123
 
9623
10124
  seeking = false;
9624
- youtubeObject.seekTo( currentTime );
10125
+ options.youtubeObject.seekTo( currentTime );
9625
10126
  return;
9626
10127
  }
9627
10128
 
9628
- currentTime = youtubeObject.getCurrentTime();
10129
+ currentTime = options.youtubeObject.getCurrentTime();
9629
10130
  media.dispatchEvent( "timeupdate" );
9630
10131
  !media.paused && media.pause();
10132
+
10133
+ return;
10134
+ } else
10135
+ // playing is state 1
10136
+ // paused is state 2
10137
+ if ( state === 1 && !firstGo ) {
10138
+
10139
+ media.paused && media.play();
10140
+ return;
10141
+ } else
10142
+ // this is the real player ready check
10143
+ // -1 is for unstarted, but ready to go videos
10144
+ // before this the player object exists, but calls to it may go unheard
10145
+ if ( state === -1 ) {
10146
+
10147
+ options.youtubeObject.playVideo();
10148
+ return;
10149
+ } else
10150
+ if ( state === 1 && firstGo ) {
10151
+
10152
+ firstGo = false;
10153
+
10154
+ if ( media.paused === true ) {
10155
+
10156
+ media.pause();
10157
+ } else if ( media.paused === false ) {
10158
+
10159
+ media.play();
10160
+ } else if ( autoPlay ) {
10161
+
10162
+ media.play();
10163
+ } else if ( !autoPlay ) {
10164
+
10165
+ media.pause();
10166
+ }
10167
+
10168
+ media.duration = options.youtubeObject.getDuration();
10169
+
10170
+ media.dispatchEvent( "durationchange" );
10171
+ volumeupdate();
10172
+
10173
+ media.dispatchEvent( "loadedmetadata" );
10174
+ media.dispatchEvent( "loadeddata" );
10175
+
10176
+ media.readyState = 4;
10177
+ media.dispatchEvent( "canplaythrough" );
10178
+
10179
+ return;
10180
+ } else if ( state === 0 ) {
10181
+ media.dispatchEvent( "ended" );
9631
10182
  }
9632
10183
  };
9633
10184
 
@@ -9638,15 +10189,20 @@ Popcorn.player( "youtube", {
9638
10189
  };
9639
10190
 
9640
10191
  // youtube requires callbacks to be a string to a function path from the global scope
9641
- youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id );
10192
+ options.youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id );
9642
10193
 
9643
- youtubeObject.addEventListener( "onError", "onYouTubePlayerReady.onErrorEventHandler." + container.id );
10194
+ options.youtubeObject.addEventListener( "onError", "onYouTubePlayerReady.onErrorEventHandler." + container.id );
9644
10195
 
9645
10196
  var timeupdate = function() {
9646
10197
 
10198
+ if ( options.destroyed ) {
10199
+
10200
+ return;
10201
+ }
10202
+
9647
10203
  if ( !media.paused ) {
9648
10204
 
9649
- currentTime = youtubeObject.getCurrentTime();
10205
+ currentTime = options.youtubeObject.getCurrentTime();
9650
10206
  media.dispatchEvent( "timeupdate" );
9651
10207
  setTimeout( timeupdate, 10 );
9652
10208
  }
@@ -9654,15 +10210,20 @@ Popcorn.player( "youtube", {
9654
10210
 
9655
10211
  var volumeupdate = function() {
9656
10212
 
9657
- if ( lastMuted !== youtubeObject.isMuted() ) {
10213
+ if ( options.destroyed ) {
10214
+
10215
+ return;
10216
+ }
9658
10217
 
9659
- lastMuted = youtubeObject.isMuted();
10218
+ if ( lastMuted !== options.youtubeObject.isMuted() ) {
10219
+
10220
+ lastMuted = options.youtubeObject.isMuted();
9660
10221
  media.dispatchEvent( "volumechange" );
9661
10222
  }
9662
10223
 
9663
- if ( lastVolume !== youtubeObject.getVolume() ) {
10224
+ if ( lastVolume !== options.youtubeObject.getVolume() ) {
9664
10225
 
9665
- lastVolume = youtubeObject.getVolume();
10226
+ lastVolume = options.youtubeObject.getVolume();
9666
10227
  media.dispatchEvent( "volumechange" );
9667
10228
  }
9668
10229
 
@@ -9671,21 +10232,35 @@ Popcorn.player( "youtube", {
9671
10232
 
9672
10233
  media.play = function() {
9673
10234
 
9674
- media.paused = false;
9675
- media.dispatchEvent( "play" );
10235
+ if ( options.destroyed ) {
10236
+
10237
+ return;
10238
+ }
10239
+
10240
+ if ( media.paused !== false || options.youtubeObject.getPlayerState() !== 1 ) {
10241
+
10242
+ media.paused = false;
10243
+ media.dispatchEvent( "play" );
10244
+
10245
+ media.dispatchEvent( "playing" );
10246
+ }
9676
10247
 
9677
- media.dispatchEvent( "playing" );
9678
10248
  timeupdate();
9679
- youtubeObject.playVideo();
10249
+ options.youtubeObject.playVideo();
9680
10250
  };
9681
10251
 
9682
10252
  media.pause = function() {
9683
10253
 
9684
- if ( !media.paused ) {
10254
+ if ( options.destroyed ) {
10255
+
10256
+ return;
10257
+ }
10258
+
10259
+ if ( media.paused !== true || options.youtubeObject.getPlayerState() !== 2 ) {
9685
10260
 
9686
10261
  media.paused = true;
9687
10262
  media.dispatchEvent( "pause" );
9688
- youtubeObject.pauseVideo();
10263
+ options.youtubeObject.pauseVideo();
9689
10264
  }
9690
10265
  };
9691
10266
 
@@ -9695,9 +10270,17 @@ Popcorn.player( "youtube", {
9695
10270
  // make sure val is a number
9696
10271
  currentTime = seekTime = +val;
9697
10272
  seeking = true;
10273
+
10274
+ if ( options.destroyed ) {
10275
+
10276
+ return currentTime;
10277
+ }
10278
+
9698
10279
  media.dispatchEvent( "seeked" );
9699
10280
  media.dispatchEvent( "timeupdate" );
9700
- youtubeObject.seekTo( currentTime );
10281
+
10282
+ options.youtubeObject.seekTo( currentTime );
10283
+
9701
10284
  return currentTime;
9702
10285
  },
9703
10286
  get: function() {
@@ -9709,54 +10292,65 @@ Popcorn.player( "youtube", {
9709
10292
  Popcorn.player.defineProperty( media, "muted", {
9710
10293
  set: function( val ) {
9711
10294
 
9712
- if ( youtubeObject.isMuted() !== val ) {
10295
+ if ( options.destroyed ) {
10296
+
10297
+ return val;
10298
+ }
10299
+
10300
+ if ( options.youtubeObject.isMuted() !== val ) {
9713
10301
 
9714
10302
  if ( val ) {
9715
10303
 
9716
- youtubeObject.mute();
10304
+ options.youtubeObject.mute();
9717
10305
  } else {
9718
10306
 
9719
- youtubeObject.unMute();
10307
+ options.youtubeObject.unMute();
9720
10308
  }
9721
10309
 
9722
- lastMuted = youtubeObject.isMuted();
10310
+ lastMuted = options.youtubeObject.isMuted();
9723
10311
  media.dispatchEvent( "volumechange" );
9724
10312
  }
9725
10313
 
9726
- return youtubeObject.isMuted();
10314
+ return options.youtubeObject.isMuted();
9727
10315
  },
9728
10316
  get: function() {
9729
10317
 
9730
- return youtubeObject.isMuted();
10318
+ if ( options.destroyed ) {
10319
+
10320
+ return 0;
10321
+ }
10322
+
10323
+ return options.youtubeObject.isMuted();
9731
10324
  }
9732
10325
  });
9733
10326
 
9734
10327
  Popcorn.player.defineProperty( media, "volume", {
9735
10328
  set: function( val ) {
9736
10329
 
9737
- if ( youtubeObject.getVolume() / 100 !== val ) {
10330
+ if ( options.destroyed ) {
10331
+
10332
+ return val;
10333
+ }
10334
+
10335
+ if ( options.youtubeObject.getVolume() / 100 !== val ) {
9738
10336
 
9739
- youtubeObject.setVolume( val * 100 );
9740
- lastVolume = youtubeObject.getVolume();
10337
+ options.youtubeObject.setVolume( val * 100 );
10338
+ lastVolume = options.youtubeObject.getVolume();
9741
10339
  media.dispatchEvent( "volumechange" );
9742
10340
  }
9743
10341
 
9744
- return youtubeObject.getVolume() / 100;
10342
+ return options.youtubeObject.getVolume() / 100;
9745
10343
  },
9746
10344
  get: function() {
9747
10345
 
9748
- return youtubeObject.getVolume() / 100;
9749
- }
9750
- });
10346
+ if ( options.destroyed ) {
9751
10347
 
9752
- media.readyState = 4;
9753
- media.dispatchEvent( "canplaythrough" );
9754
- media.dispatchEvent( "load" );
9755
- media.duration = youtubeObject.getDuration();
9756
- media.dispatchEvent( "durationchange" );
9757
- volumeupdate();
10348
+ return 0;
10349
+ }
9758
10350
 
9759
- media.dispatchEvent( "loadeddata" );
10351
+ return options.youtubeObject.getVolume() / 100;
10352
+ }
10353
+ });
9760
10354
  };
9761
10355
 
9762
10356
  options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1;
@@ -9771,19 +10365,21 @@ Popcorn.player( "youtube", {
9771
10365
  allowScriptAccess: "always"
9772
10366
  };
9773
10367
 
9774
- attributes = {
9775
- id: container.id
9776
- };
9777
-
9778
10368
  src = /^.*(?:\/|v=)(.{11})/.exec( media.src )[ 1 ];
10369
+
9779
10370
  query = ( media.src.split( "?" )[ 1 ] || "" ).replace( /v=.{11}/, "" );
10371
+ autoPlay = ( /autoplay=1/.test( query ) );
9780
10372
 
9781
10373
  // setting youtube player's height and width, default to 560 x 315
9782
- width = media.style.width ? ""+media.offsetWidth : "560";
9783
- height = media.style.height ? ""+media.offsetHeight : "315";
10374
+ width = media.style.width ? "" + media.offsetWidth : "560";
10375
+ height = media.style.height ? "" + media.offsetHeight : "315";
10376
+
10377
+ attributes = {
10378
+ id: container.id,
10379
+ "data-youtube-player": "//www.youtube.com/e/" + src + "?" + query + "&enablejsapi=1&playerapiid=" + container.id + "&version=3"
10380
+ };
9784
10381
 
9785
- swfobject.embedSWF( "//www.youtube.com/e/" + src + "?" + query + "&enablejsapi=1&playerapiid=" + container.id + "&version=3",
9786
- container.id, width, height, "8", null, flashvars, params, attributes );
10382
+ swfobject.embedSWF( attributes[ "data-youtube-player" ], container.id, width, height, "8", undefined, flashvars, params, attributes );
9787
10383
  };
9788
10384
 
9789
10385
  if ( !window.swfobject ) {
@@ -9793,9 +10389,15 @@ Popcorn.player( "youtube", {
9793
10389
 
9794
10390
  youtubeInit();
9795
10391
  }
10392
+ },
10393
+ _teardown: function( options ) {
10394
+
10395
+ options.destroyed = true;
10396
+ options.youtubeObject.stopVideo();
10397
+ options.youtubeObject.clearVideo();
10398
+ this.removeChild( document.getElementById( options._container.id ) );
9796
10399
  }
9797
10400
  });
9798
-
9799
10401
  // EFFECT: applyclass
9800
10402
 
9801
10403
  (function (Popcorn) {