popcornjs-rails 1.1.2 → 1.2.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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) {