PageRenderTime 64ms CodeModel.GetById 18ms RepoModel.GetById 1ms app.codeStats 2ms

/external/layouts/bookreport/js/popcorn-complete.js

https://github.com/lulubulb/popcorn-maker
JavaScript | 8646 lines | 5169 code | 1405 blank | 2072 comment | 967 complexity | 33e6a61aeaeac78654bf04862934d33e MD5 | raw file
Possible License(s): Apache-2.0, MIT, BSD-3-Clause

Large files files are truncated, but you can click here to view the full file

  1. /*
  2. * popcorn.js version defaults
  3. * http://popcornjs.org
  4. *
  5. * Copyright 2011, Mozilla Foundation
  6. * Licensed under the MIT license
  7. */
  8. (function(global, document) {
  9. // Cache refs to speed up calls to native utils
  10. var
  11. AP = Array.prototype,
  12. OP = Object.prototype,
  13. forEach = AP.forEach,
  14. slice = AP.slice,
  15. hasOwn = OP.hasOwnProperty,
  16. toString = OP.toString,
  17. // ID string matching
  18. rIdExp = /^(#([\w\-\_\.]+))$/,
  19. // Ready fn cache
  20. readyStack = [],
  21. readyBound = false,
  22. readyFired = false,
  23. // Non-public internal data object
  24. internal = {
  25. events: {
  26. hash: {},
  27. apis: {}
  28. }
  29. },
  30. // Declare constructor
  31. // Returns an instance object.
  32. Popcorn = function( entity, options ) {
  33. // Return new Popcorn object
  34. return new Popcorn.p.init( entity, options || null );
  35. };
  36. // Instance caching
  37. Popcorn.instances = [];
  38. Popcorn.instanceIds = {};
  39. Popcorn.removeInstance = function( instance ) {
  40. // If called prior to any instances being created
  41. // Return early to avoid splicing on nothing
  42. if ( !Popcorn.instances.length ) {
  43. return;
  44. }
  45. // Remove instance from Popcorn.instances
  46. Popcorn.instances.splice( Popcorn.instanceIds[ instance.id ], 1 );
  47. // Delete the instance id key
  48. delete Popcorn.instanceIds[ instance.id ];
  49. // Return current modified instances
  50. return Popcorn.instances;
  51. };
  52. // Addes a Popcorn instance to the Popcorn instance array
  53. Popcorn.addInstance = function( instance ) {
  54. var instanceLen = Popcorn.instances.length,
  55. instanceId = instance.media.id && instance.media.id;
  56. // If the media element has its own `id` use it, otherwise provide one
  57. // Ensure that instances have unique ids and unique entries
  58. // Uses `in` operator to avoid false positives on 0
  59. instance.id = !( instanceId in Popcorn.instanceIds ) && instanceId ||
  60. "__popcorn" + instanceLen;
  61. // Create a reference entry for this instance
  62. Popcorn.instanceIds[ instance.id ] = instanceLen;
  63. // Add this instance to the cache
  64. Popcorn.instances.push( instance );
  65. // Return the current modified instances
  66. return Popcorn.instances;
  67. };
  68. // Request Popcorn object instance by id
  69. Popcorn.getInstanceById = function( id ) {
  70. return Popcorn.instances[ Popcorn.instanceIds[ id ] ];
  71. };
  72. // Remove Popcorn object instance by id
  73. Popcorn.removeInstanceById = function( id ) {
  74. return Popcorn.removeInstance( Popcorn.instances[ Popcorn.instanceIds[ id ] ] );
  75. };
  76. // Declare a shortcut (Popcorn.p) to and a definition of
  77. // the new prototype for our Popcorn constructor
  78. Popcorn.p = Popcorn.prototype = {
  79. init: function( entity, options ) {
  80. var matches;
  81. // Supports Popcorn(function () { /../ })
  82. // Originally proposed by Daniel Brooks
  83. if ( typeof entity === "function" ) {
  84. // If document ready has already fired
  85. if ( document.readyState === "interactive" || document.readyState === "complete" ) {
  86. entity( document, Popcorn );
  87. return;
  88. }
  89. // Add `entity` fn to ready stack
  90. readyStack.push( entity );
  91. // This process should happen once per page load
  92. if ( !readyBound ) {
  93. // set readyBound flag
  94. readyBound = true;
  95. var DOMContentLoaded = function() {
  96. readyFired = true;
  97. // Remove global DOM ready listener
  98. document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  99. // Execute all ready function in the stack
  100. for ( var i = 0, readyStackLength = readyStack.length; i < readyStackLength; i++ ) {
  101. readyStack[ i ].call( document, Popcorn );
  102. }
  103. // GC readyStack
  104. readyStack = null;
  105. };
  106. // Register global DOM ready listener
  107. document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
  108. }
  109. return;
  110. }
  111. // Check if entity is a valid string id
  112. matches = rIdExp.exec( entity );
  113. // Get media element by id or object reference
  114. this.media = matches && matches.length && matches[ 2 ] ?
  115. document.getElementById( matches[ 2 ] ) :
  116. entity;
  117. // Create an audio or video element property reference
  118. this[ ( this.media.nodeName && this.media.nodeName.toLowerCase() ) || "video" ] = this.media;
  119. // Register new instance
  120. Popcorn.addInstance( this );
  121. this.options = options || {};
  122. this.data = {
  123. // Allows disabling a plugin per instance
  124. disabled: [],
  125. // Stores DOM event queues by type
  126. events: {},
  127. // Stores Special event hooks data
  128. hooks: {},
  129. // Store track event history data
  130. history: [],
  131. // Store track event object references by trackId
  132. trackRefs: {},
  133. // Playback track event queues
  134. trackEvents: {
  135. byStart: [{
  136. start: -1,
  137. end: -1
  138. }],
  139. byEnd: [{
  140. start: -1,
  141. end: -1
  142. }],
  143. startIndex: 0,
  144. endIndex: 0,
  145. previousUpdateTime: -1
  146. }
  147. };
  148. // Wrap true ready check
  149. var isReady = function( that ) {
  150. if ( that.media.readyState >= 2 ) {
  151. // Adding padding to the front and end of the arrays
  152. // this is so we do not fall off either end
  153. var duration = that.media.duration,
  154. // Check for no duration info (NaN)
  155. videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
  156. Popcorn.addTrackEvent( that, {
  157. start: videoDurationPlus,
  158. end: videoDurationPlus
  159. });
  160. that.media.addEventListener( "timeupdate", function( event ) {
  161. var currentTime = this.currentTime,
  162. previousTime = that.data.trackEvents.previousUpdateTime,
  163. tracks = that.data.trackEvents,
  164. tracksByEnd = tracks.byEnd,
  165. tracksByStart = tracks.byStart;
  166. // Playbar advancing
  167. if ( previousTime < currentTime ) {
  168. while ( tracksByEnd[ tracks.endIndex ] && tracksByEnd[ tracks.endIndex ].end <= currentTime ) {
  169. // If plugin does not exist on this instance, remove it
  170. if ( !tracksByEnd[ tracks.endIndex ]._natives || !!that[ tracksByEnd[ tracks.endIndex ]._natives.type ] ) {
  171. if ( tracksByEnd[ tracks.endIndex ]._running === true ) {
  172. tracksByEnd[ tracks.endIndex ]._running = false;
  173. tracksByEnd[ tracks.endIndex ]._natives.end.call( that, event, tracksByEnd[ tracks.endIndex ] );
  174. }
  175. tracks.endIndex++;
  176. } else {
  177. // remove track event
  178. Popcorn.removeTrackEvent( that, tracksByEnd[ tracks.endIndex ]._id );
  179. return;
  180. }
  181. }
  182. while ( tracksByStart[ tracks.startIndex ] && tracksByStart[ tracks.startIndex ].start <= currentTime ) {
  183. // If plugin does not exist on this instance, remove it
  184. if ( !tracksByStart[ tracks.startIndex ]._natives || !!that[ tracksByStart[ tracks.startIndex ]._natives.type ] ) {
  185. if ( tracksByStart[ tracks.startIndex ].end > currentTime &&
  186. tracksByStart[ tracks.startIndex ]._running === false &&
  187. that.data.disabled.indexOf( tracksByStart[ tracks.startIndex ]._natives.type ) === -1 ) {
  188. tracksByStart[ tracks.startIndex ]._running = true;
  189. tracksByStart[ tracks.startIndex ]._natives.start.call( that, event, tracksByStart[ tracks.startIndex ] );
  190. }
  191. tracks.startIndex++;
  192. } else {
  193. // remove track event
  194. Popcorn.removeTrackEvent( that, tracksByStart[ tracks.startIndex ]._id );
  195. return;
  196. }
  197. }
  198. // Playbar receding
  199. } else if ( previousTime > currentTime ) {
  200. while ( tracksByStart[ tracks.startIndex ] && tracksByStart[ tracks.startIndex ].start > currentTime ) {
  201. // if plugin does not exist on this instance, remove it
  202. if ( !tracksByStart[ tracks.startIndex ]._natives || !!that[ tracksByStart[ tracks.startIndex ]._natives.type ] ) {
  203. if ( tracksByStart[ tracks.startIndex ]._running === true ) {
  204. tracksByStart[ tracks.startIndex ]._running = false;
  205. tracksByStart[ tracks.startIndex ]._natives.end.call( that, event, tracksByStart[ tracks.startIndex ] );
  206. }
  207. tracks.startIndex--;
  208. } else {
  209. // remove track event
  210. Popcorn.removeTrackEvent( that, tracksByStart[ tracks.startIndex ]._id );
  211. return;
  212. }
  213. }
  214. while ( tracksByEnd[ tracks.endIndex ] && tracksByEnd[ tracks.endIndex ].end > currentTime ) {
  215. // if plugin does not exist on this instance, remove it
  216. if ( !tracksByEnd[ tracks.endIndex ]._natives || !!that[ tracksByEnd[ tracks.endIndex ]._natives.type ] ) {
  217. if ( tracksByEnd[ tracks.endIndex ].start <= currentTime &&
  218. tracksByEnd[ tracks.endIndex ]._running === false &&
  219. that.data.disabled.indexOf( tracksByEnd[ tracks.endIndex ]._natives.type ) === -1 ) {
  220. tracksByEnd[ tracks.endIndex ]._running = true;
  221. tracksByEnd[ tracks.endIndex ]._natives.start.call( that, event, tracksByEnd[tracks.endIndex] );
  222. }
  223. tracks.endIndex--;
  224. } else {
  225. // remove track event
  226. Popcorn.removeTrackEvent( that, tracksByEnd[ tracks.endIndex ]._id );
  227. return;
  228. }
  229. }
  230. }
  231. tracks.previousUpdateTime = currentTime;
  232. }, false );
  233. } else {
  234. global.setTimeout(function() {
  235. isReady( that );
  236. }, 1 );
  237. }
  238. };
  239. isReady( this );
  240. return this;
  241. }
  242. };
  243. // Extend constructor prototype to instance prototype
  244. // Allows chaining methods to instances
  245. Popcorn.p.init.prototype = Popcorn.p;
  246. Popcorn.forEach = function( obj, fn, context ) {
  247. if ( !obj || !fn ) {
  248. return {};
  249. }
  250. context = context || this;
  251. var key, len;
  252. // Use native whenever possible
  253. if ( forEach && obj.forEach === forEach ) {
  254. return obj.forEach( fn, context );
  255. }
  256. if ( toString.call( obj ) === "[object NodeList]" ) {
  257. for ( key = 0, len = obj.length; key < len; key++ ) {
  258. fn.call( context, obj[ key ], key, obj );
  259. }
  260. return obj;
  261. }
  262. for ( key in obj ) {
  263. if ( hasOwn.call( obj, key ) ) {
  264. fn.call( context, obj[ key ], key, obj );
  265. }
  266. }
  267. return obj;
  268. };
  269. Popcorn.extend = function( obj ) {
  270. var dest = obj, src = slice.call( arguments, 1 );
  271. Popcorn.forEach( src, function( copy ) {
  272. for ( var prop in copy ) {
  273. dest[ prop ] = copy[ prop ];
  274. }
  275. });
  276. return dest;
  277. };
  278. // A Few reusable utils, memoized onto Popcorn
  279. Popcorn.extend( Popcorn, {
  280. error: function( msg ) {
  281. throw new Error( msg );
  282. },
  283. guid: function( prefix ) {
  284. Popcorn.guid.counter++;
  285. return ( prefix ? prefix : "" ) + ( +new Date() + Popcorn.guid.counter );
  286. },
  287. sizeOf: function( obj ) {
  288. var size = 0;
  289. for ( var prop in obj ) {
  290. size++;
  291. }
  292. return size;
  293. },
  294. isArray: Array.isArray || function( array ) {
  295. return toString.call( array ) === "[object Array]";
  296. },
  297. nop: function() {},
  298. position: function( elem ) {
  299. var clientRect = elem.getBoundingClientRect(),
  300. bounds = {},
  301. doc = elem.ownerDocument,
  302. docElem = document.documentElement,
  303. body = document.body,
  304. clientTop, clientLeft, scrollTop, scrollLeft, top, left;
  305. // Determine correct clientTop/Left
  306. clientTop = docElem.clientTop || body.clientTop || 0;
  307. clientLeft = docElem.clientLeft || body.clientLeft || 0;
  308. // Determine correct scrollTop/Left
  309. scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop );
  310. scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft );
  311. // Temp top/left
  312. top = Math.ceil( clientRect.top + scrollTop - clientTop );
  313. left = Math.ceil( clientRect.left + scrollLeft - clientLeft );
  314. for ( var p in clientRect ) {
  315. bounds[ p ] = Math.round( clientRect[ p ] );
  316. }
  317. return Popcorn.extend({}, bounds, { top: top, left: left });
  318. },
  319. disable: function( instance, plugin ) {
  320. var disabled = instance.data.disabled;
  321. if ( disabled.indexOf( plugin ) === -1 ) {
  322. disabled.push( plugin );
  323. }
  324. return instance;
  325. },
  326. enable: function( instance, plugin ) {
  327. var disabled = instance.data.disabled,
  328. index = disabled.indexOf( plugin );
  329. if ( index > -1 ) {
  330. disabled.splice( index, 1 );
  331. }
  332. return instance;
  333. }
  334. });
  335. // Memoized GUID Counter
  336. Popcorn.guid.counter = 1;
  337. // Factory to implement getters, setters and controllers
  338. // as Popcorn instance methods. The IIFE will create and return
  339. // an object with defined methods
  340. Popcorn.extend(Popcorn.p, (function() {
  341. var methods = "load play pause currentTime playbackRate mute volume duration",
  342. ret = {};
  343. // Build methods, store in object that is returned and passed to extend
  344. Popcorn.forEach( methods.split( /\s+/g ), function( name ) {
  345. ret[ name ] = function( arg ) {
  346. if ( typeof this.media[ name ] === "function" ) {
  347. this.media[ name ]();
  348. return this;
  349. }
  350. if ( arg !== false && arg !== null && typeof arg !== "undefined" ) {
  351. this.media[ name ] = arg;
  352. return this;
  353. }
  354. return this.media[ name ];
  355. };
  356. });
  357. return ret;
  358. })()
  359. );
  360. Popcorn.forEach( "enable disable".split(" "), function( method ) {
  361. Popcorn.p[ method ] = function( plugin ) {
  362. return Popcorn[ method ]( this, plugin );
  363. };
  364. });
  365. Popcorn.extend(Popcorn.p, {
  366. // Rounded currentTime
  367. roundTime: function() {
  368. return -~this.media.currentTime;
  369. },
  370. // Attach an event to a single point in time
  371. exec: function( time, fn ) {
  372. // Creating a one second track event with an empty end
  373. Popcorn.addTrackEvent( this, {
  374. start: time,
  375. end: time + 1,
  376. _running: false,
  377. _natives: {
  378. start: fn || Popcorn.nop,
  379. end: Popcorn.nop,
  380. type: "exec"
  381. }
  382. });
  383. return this;
  384. },
  385. // Get the client bounding box of an instance element
  386. position: function() {
  387. return Popcorn.position( this.media );
  388. },
  389. // Toggle a plugin's playback behaviour (on or off) per instance
  390. toggle: function( plugin ) {
  391. return Popcorn[ this.data.disabled.indexOf( plugin ) > -1 ? "enable" : "disable" ]( this, plugin );
  392. },
  393. // Set default values for plugin options objects per instance
  394. defaults: function( plugin, defaults ) {
  395. // If an array of default configurations is provided,
  396. // iterate and apply each to this instance
  397. if ( Popcorn.isArray( plugin ) ) {
  398. Popcorn.forEach( plugin, function( obj ) {
  399. for ( var name in obj ) {
  400. this.defaults( name, obj[ name ] );
  401. }
  402. }, this );
  403. return this;
  404. }
  405. if ( !this.options.defaults ) {
  406. this.options.defaults = {};
  407. }
  408. if ( !this.options.defaults[ plugin ] ) {
  409. this.options.defaults[ plugin ] = {};
  410. }
  411. Popcorn.extend( this.options.defaults[ plugin ], defaults );
  412. return this;
  413. }
  414. });
  415. Popcorn.Events = {
  416. UIEvents: "blur focus focusin focusout load resize scroll unload",
  417. MouseEvents: "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave click dblclick",
  418. Events: "loadstart progress suspend emptied stalled play pause " +
  419. "loadedmetadata loadeddata waiting playing canplay canplaythrough " +
  420. "seeking seeked timeupdate ended ratechange durationchange volumechange"
  421. };
  422. Popcorn.Events.Natives = Popcorn.Events.UIEvents + " " +
  423. Popcorn.Events.MouseEvents + " " +
  424. Popcorn.Events.Events;
  425. internal.events.apiTypes = [ "UIEvents", "MouseEvents", "Events" ];
  426. // Privately compile events table at load time
  427. (function( events, data ) {
  428. var apis = internal.events.apiTypes,
  429. eventsList = events.Natives.split( /\s+/g ),
  430. idx = 0, len = eventsList.length, prop;
  431. for( ; idx < len; idx++ ) {
  432. data.hash[ eventsList[idx] ] = true;
  433. }
  434. apis.forEach(function( val, idx ) {
  435. data.apis[ val ] = {};
  436. var apiEvents = events[ val ].split( /\s+/g ),
  437. len = apiEvents.length,
  438. k = 0;
  439. for ( ; k < len; k++ ) {
  440. data.apis[ val ][ apiEvents[ k ] ] = true;
  441. }
  442. });
  443. })( Popcorn.Events, internal.events );
  444. Popcorn.events = {
  445. isNative: function( type ) {
  446. return !!internal.events.hash[ type ];
  447. },
  448. getInterface: function( type ) {
  449. if ( !Popcorn.events.isNative( type ) ) {
  450. return false;
  451. }
  452. var eventApi = internal.events,
  453. apis = eventApi.apiTypes,
  454. apihash = eventApi.apis,
  455. idx = 0, len = apis.length, api, tmp;
  456. for ( ; idx < len; idx++ ) {
  457. tmp = apis[ idx ];
  458. if ( apihash[ tmp ][ type ] ) {
  459. api = tmp;
  460. break;
  461. }
  462. }
  463. return api;
  464. },
  465. // Compile all native events to single array
  466. all: Popcorn.Events.Natives.split( /\s+/g ),
  467. // Defines all Event handling static functions
  468. fn: {
  469. trigger: function( type, data ) {
  470. var eventInterface, evt;
  471. // setup checks for custom event system
  472. if ( this.data.events[ type ] && Popcorn.sizeOf( this.data.events[ type ] ) ) {
  473. eventInterface = Popcorn.events.getInterface( type );
  474. if ( eventInterface ) {
  475. evt = document.createEvent( eventInterface );
  476. evt.initEvent( type, true, true, global, 1 );
  477. this.media.dispatchEvent( evt );
  478. return this;
  479. }
  480. // Custom events
  481. Popcorn.forEach( this.data.events[ type ], function( obj, key ) {
  482. obj.call( this, data );
  483. }, this );
  484. }
  485. return this;
  486. },
  487. listen: function( type, fn ) {
  488. var self = this,
  489. hasEvents = true,
  490. eventHook = Popcorn.events.hooks[ type ],
  491. origType = type,
  492. tmp;
  493. if ( !this.data.events[ type ] ) {
  494. this.data.events[ type ] = {};
  495. hasEvents = false;
  496. }
  497. // Check and setup event hooks
  498. if ( eventHook ) {
  499. // Execute hook add method if defined
  500. if ( eventHook.add ) {
  501. eventHook.add.call( this, {}, fn );
  502. }
  503. // Reassign event type to our piggyback event type if defined
  504. if ( eventHook.bind ) {
  505. type = eventHook.bind;
  506. }
  507. // Reassign handler if defined
  508. if ( eventHook.handler ) {
  509. tmp = fn;
  510. fn = function wrapper( event ) {
  511. eventHook.handler.call( self, event, tmp );
  512. };
  513. }
  514. // assume the piggy back event is registered
  515. hasEvents = true;
  516. // Setup event registry entry
  517. if ( !this.data.events[ type ] ) {
  518. this.data.events[ type ] = {};
  519. // Toggle if the previous assumption was untrue
  520. hasEvents = false;
  521. }
  522. }
  523. // Register event and handler
  524. this.data.events[ type ][ fn.name || ( fn.toString() + Popcorn.guid() ) ] = fn;
  525. // only attach one event of any type
  526. if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) {
  527. this.media.addEventListener( type, function( event ) {
  528. Popcorn.forEach( self.data.events[ type ], function( obj, key ) {
  529. if ( typeof obj === "function" ) {
  530. obj.call( self, event );
  531. }
  532. });
  533. }, false);
  534. }
  535. return this;
  536. },
  537. unlisten: function( type, fn ) {
  538. if ( this.data.events[ type ] && this.data.events[ type ][ fn ] ) {
  539. delete this.data.events[ type ][ fn ];
  540. return this;
  541. }
  542. this.data.events[ type ] = null;
  543. return this;
  544. }
  545. },
  546. hooks: {
  547. canplayall: {
  548. bind: "canplaythrough",
  549. add: function( event, callback ) {
  550. var state = false;
  551. if ( this.media.readyState ) {
  552. callback.call( this, event );
  553. state = true;
  554. }
  555. this.data.hooks.canplayall = {
  556. fired: state
  557. };
  558. },
  559. // declare special handling instructions
  560. handler: function canplayall( event, callback ) {
  561. if ( !this.data.hooks.canplayall.fired ) {
  562. // trigger original user callback once
  563. callback.call( this, event );
  564. this.data.hooks.canplayall.fired = true;
  565. }
  566. }
  567. }
  568. }
  569. };
  570. // Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances
  571. Popcorn.forEach( [ "trigger", "listen", "unlisten" ], function( key ) {
  572. Popcorn.p[ key ] = Popcorn.events.fn[ key ];
  573. });
  574. // Protected API methods
  575. Popcorn.protect = {
  576. natives: "load play pause currentTime playbackRate mute volume duration removePlugin roundTime trigger listen unlisten exec".toLowerCase().split( /\s+/ )
  577. };
  578. // Internal Only - Adds track events to the instance object
  579. Popcorn.addTrackEvent = function( obj, track ) {
  580. // Determine if this track has default options set for it
  581. // If so, apply them to the track object
  582. if ( track && track._natives && track._natives.type &&
  583. ( obj.options.defaults && obj.options.defaults[ track._natives.type ] ) ) {
  584. track = Popcorn.extend( {}, obj.options.defaults[ track._natives.type ], track );
  585. }
  586. if ( track._natives ) {
  587. // Supports user defined track event id
  588. track._id = !track.id ? Popcorn.guid( track._natives.type ) : track.id;
  589. // Push track event ids into the history
  590. obj.data.history.push( track._id );
  591. }
  592. track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate );
  593. track.end = Popcorn.util.toSeconds( track.end, obj.options.framerate );
  594. // Store this definition in an array sorted by times
  595. var byStart = obj.data.trackEvents.byStart,
  596. byEnd = obj.data.trackEvents.byEnd,
  597. idx;
  598. for ( idx = byStart.length - 1; idx >= 0; idx-- ) {
  599. if ( track.start >= byStart[ idx ].start ) {
  600. byStart.splice( idx + 1, 0, track );
  601. break;
  602. }
  603. }
  604. for ( idx = byEnd.length - 1; idx >= 0; idx-- ) {
  605. if ( track.end > byEnd[ idx ].end ) {
  606. byEnd.splice( idx + 1, 0, track );
  607. break;
  608. }
  609. }
  610. // Store references to user added trackevents in ref table
  611. if ( track._id ) {
  612. Popcorn.addTrackEvent.ref( obj, track );
  613. }
  614. };
  615. // Internal Only - Adds track event references to the instance object's trackRefs hash table
  616. Popcorn.addTrackEvent.ref = function( obj, track ) {
  617. obj.data.trackRefs[ track._id ] = track;
  618. return obj;
  619. };
  620. Popcorn.removeTrackEvent = function( obj, trackId ) {
  621. var historyLen = obj.data.history.length,
  622. indexWasAt = 0,
  623. byStart = [],
  624. byEnd = [],
  625. history = [];
  626. Popcorn.forEach( obj.data.trackEvents.byStart, function( o, i, context ) {
  627. // Preserve the original start/end trackEvents
  628. if ( !o._id ) {
  629. byStart.push( obj.data.trackEvents.byStart[i] );
  630. byEnd.push( obj.data.trackEvents.byEnd[i] );
  631. }
  632. // Filter for user track events (vs system track events)
  633. if ( o._id ) {
  634. // Filter for the trackevent to remove
  635. if ( o._id !== trackId ) {
  636. byStart.push( obj.data.trackEvents.byStart[i] );
  637. byEnd.push( obj.data.trackEvents.byEnd[i] );
  638. }
  639. // Capture the position of the track being removed.
  640. if ( o._id === trackId ) {
  641. indexWasAt = i;
  642. o._natives._teardown && o._natives._teardown.call( obj, o );
  643. }
  644. }
  645. });
  646. // Update
  647. if ( indexWasAt <= obj.data.trackEvents.startIndex ) {
  648. obj.data.trackEvents.startIndex--;
  649. }
  650. if ( indexWasAt <= obj.data.trackEvents.endIndex ) {
  651. obj.data.trackEvents.endIndex--;
  652. }
  653. obj.data.trackEvents.byStart = byStart;
  654. obj.data.trackEvents.byEnd = byEnd;
  655. for ( var i = 0; i < historyLen; i++ ) {
  656. if ( obj.data.history[ i ] !== trackId ) {
  657. history.push( obj.data.history[ i ] );
  658. }
  659. }
  660. // Update ordered history array
  661. obj.data.history = history;
  662. // Update track event references
  663. Popcorn.removeTrackEvent.ref( obj, trackId );
  664. };
  665. // Internal Only - Removes track event references from instance object's trackRefs hash table
  666. Popcorn.removeTrackEvent.ref = function( obj, trackId ) {
  667. delete obj.data.trackRefs[ trackId ];
  668. return obj;
  669. };
  670. // Return an array of track events bound to this instance object
  671. Popcorn.getTrackEvents = function( obj ) {
  672. var trackevents = [],
  673. refs = obj.data.trackEvents.byStart,
  674. length = refs.length,
  675. idx = 0,
  676. ref;
  677. for ( ; idx < length; idx++ ) {
  678. ref = refs[ idx ];
  679. // Return only user attributed track event references
  680. if ( ref._id ) {
  681. trackevents.push( ref );
  682. }
  683. }
  684. return trackevents;
  685. };
  686. // Internal Only - Returns an instance object's trackRefs hash table
  687. Popcorn.getTrackEvents.ref = function( obj ) {
  688. return obj.data.trackRefs;
  689. };
  690. // Return a single track event bound to this instance object
  691. Popcorn.getTrackEvent = function( obj, trackId ) {
  692. return obj.data.trackRefs[ trackId ];
  693. };
  694. // Internal Only - Returns an instance object's track reference by track id
  695. Popcorn.getTrackEvent.ref = function( obj, trackId ) {
  696. return obj.data.trackRefs[ trackId ];
  697. };
  698. Popcorn.getLastTrackEventId = function( obj ) {
  699. return obj.data.history[ obj.data.history.length - 1 ];
  700. };
  701. // Map and Extend TrackEvent functions to all Popcorn instances
  702. Popcorn.extend( Popcorn.p, {
  703. getTrackEvents: function() {
  704. return Popcorn.getTrackEvents.call( null, this );
  705. },
  706. getTrackEvent: function( id ) {
  707. return Popcorn.getTrackEvent.call( null, this, id );
  708. },
  709. getLastTrackEventId: function() {
  710. return Popcorn.getLastTrackEventId.call( null, this );
  711. },
  712. removeTrackEvent: function( id ) {
  713. Popcorn.removeTrackEvent.call( null, this, id );
  714. return this;
  715. },
  716. removePlugin: function( name ) {
  717. Popcorn.removePlugin.call( null, this, name );
  718. return this;
  719. }
  720. });
  721. // Plugin manifests
  722. Popcorn.manifest = {};
  723. // Plugins are registered
  724. Popcorn.registry = [];
  725. Popcorn.registryByName = {};
  726. // An interface for extending Popcorn
  727. // with plugin functionality
  728. Popcorn.plugin = function( name, definition, manifest ) {
  729. if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
  730. Popcorn.error( "'" + name + "' is a protected function name" );
  731. return;
  732. }
  733. // Provides some sugar, but ultimately extends
  734. // the definition into Popcorn.p
  735. var reserved = [ "start", "end" ],
  736. plugin = {},
  737. setup,
  738. isfn = typeof definition === "function",
  739. methods = [ "_setup", "_teardown", "start", "end" ];
  740. // combines calls of two function calls into one
  741. var combineFn = function( first, second ) {
  742. first = first || Popcorn.nop;
  743. second = second || Popcorn.nop;
  744. return function() {
  745. first.apply( this, arguments );
  746. second.apply( this, arguments );
  747. };
  748. };
  749. // If `manifest` arg is undefined, check for manifest within the `definition` object
  750. // If no `definition.manifest`, an empty object is a sufficient fallback
  751. if ( !manifest ) {
  752. manifest = definition.manifest || {};
  753. }
  754. // apply safe, and empty default functions
  755. methods.forEach(function( method ) {
  756. definition[ method ] = definition[ method ] || Popcorn.nop;
  757. });
  758. var pluginFn = function( setup, options ) {
  759. if ( !options ) {
  760. return this;
  761. }
  762. // Storing the plugin natives
  763. var natives = options._natives = {},
  764. compose = "",
  765. defaults, originalOpts, manifestOpts, mergedSetupOpts;
  766. Popcorn.extend( natives, setup );
  767. options._natives.type = name;
  768. options._running = false;
  769. // Check for previously set default options
  770. defaults = this.options.defaults && this.options.defaults[ options._natives && options._natives.type ];
  771. // default to an empty string if no effect exists
  772. // split string into an array of effects
  773. options.compose = options.compose && options.compose.split( " " ) || [];
  774. options.effect = options.effect && options.effect.split( " " ) || [];
  775. // join the two arrays together
  776. options.compose = options.compose.concat( options.effect );
  777. options.compose.forEach(function( composeOption ) {
  778. // if the requested compose is garbage, throw it away
  779. compose = Popcorn.compositions[ composeOption ] || {};
  780. // extends previous functions with compose function
  781. methods.forEach(function( method ) {
  782. natives[ method ] = combineFn( natives[ method ], compose[ method ] );
  783. });
  784. });
  785. // Ensure a manifest object, an empty object is a sufficient fallback
  786. options._natives.manifest = manifest;
  787. // Checks for expected properties
  788. if ( !( "start" in options ) ) {
  789. options.start = 0;
  790. }
  791. if ( !( "end" in options ) ) {
  792. options.end = this.duration() || Number.MAX_VALUE;
  793. }
  794. // Merge with defaults if they exist, make sure per call is prioritized
  795. mergedSetupOpts = defaults ? Popcorn.extend( {}, defaults, options ) :
  796. options;
  797. // Resolves 239, 241, 242
  798. if ( !mergedSetupOpts.target ) {
  799. // Sometimes the manifest may be missing entirely
  800. // or it has an options object that doesn't have a `target` property
  801. manifestOpts = "options" in manifest && manifest.options;
  802. mergedSetupOpts.target = manifestOpts && "target" in manifestOpts && manifestOpts.target;
  803. }
  804. // Trigger _setup method if exists
  805. options._natives._setup && options._natives._setup.call( this, mergedSetupOpts );
  806. // Create new track event for this instance
  807. Popcorn.addTrackEvent( this, Popcorn.extend( mergedSetupOpts, options ) );
  808. // Future support for plugin event definitions
  809. // for all of the native events
  810. Popcorn.forEach( setup, function( callback, type ) {
  811. if ( type !== "type" ) {
  812. if ( reserved.indexOf( type ) === -1 ) {
  813. this.listen( type, callback );
  814. }
  815. }
  816. }, this );
  817. return this;
  818. };
  819. // Augment the manifest object
  820. if ( manifest || ( "manifest" in definition ) ) {
  821. Popcorn.manifest[ name ] = manifest || definition.manifest;
  822. }
  823. // Assign new named definition
  824. plugin[ name ] = function( options ) {
  825. return pluginFn.call( this, isfn ? definition.call( this, options ) : definition,
  826. options );
  827. };
  828. // Extend Popcorn.p with new named definition
  829. Popcorn.extend( Popcorn.p, plugin );
  830. // Push into the registry
  831. var entry = {
  832. fn: plugin[ name ],
  833. definition: definition,
  834. base: definition,
  835. parents: [],
  836. name: name
  837. };
  838. Popcorn.registry.push(
  839. Popcorn.extend( plugin, entry, {
  840. type: name
  841. })
  842. );
  843. Popcorn.registryByName[ name ] = entry;
  844. return plugin;
  845. };
  846. Popcorn.plugin.debug = false;
  847. // removePlugin( type ) removes all tracks of that from all instances of popcorn
  848. // removePlugin( obj, type ) removes all tracks of type from obj, where obj is a single instance of popcorn
  849. Popcorn.removePlugin = function( obj, name ) {
  850. // Check if we are removing plugin from an instance or from all of Popcorn
  851. if ( !name ) {
  852. // Fix the order
  853. name = obj;
  854. obj = Popcorn.p;
  855. if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
  856. Popcorn.error( "'" + name + "' is a protected function name" );
  857. return;
  858. }
  859. var registryLen = Popcorn.registry.length,
  860. registryIdx;
  861. // remove plugin reference from registry
  862. for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
  863. if ( Popcorn.registry[ registryIdx ].name === name ) {
  864. Popcorn.registry.splice( registryIdx, 1 );
  865. delete Popcorn.registryByName[ name ];
  866. // delete the plugin
  867. delete obj[ name ];
  868. // plugin found and removed, stop checking, we are done
  869. return;
  870. }
  871. }
  872. }
  873. var byStart = obj.data.trackEvents.byStart,
  874. byEnd = obj.data.trackEvents.byEnd,
  875. idx, sl;
  876. // remove all trackEvents
  877. for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) {
  878. if ( ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) &&
  879. ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) ) {
  880. byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] );
  881. byStart.splice( idx, 1 );
  882. byEnd.splice( idx, 1 );
  883. // update for loop if something removed, but keep checking
  884. idx--; sl--;
  885. if ( obj.data.trackEvents.startIndex <= idx ) {
  886. obj.data.trackEvents.startIndex--;
  887. obj.data.trackEvents.endIndex--;
  888. }
  889. }
  890. }
  891. };
  892. Popcorn.compositions = {};
  893. // Plugin inheritance
  894. Popcorn.compose = function( name, definition, manifest ) {
  895. // If `manifest` arg is undefined, check for manifest within the `definition` object
  896. // If no `definition.manifest`, an empty object is a sufficient fallback
  897. Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
  898. // register the effect by name
  899. Popcorn.compositions[ name ] = definition;
  900. };
  901. Popcorn.plugin.effect = Popcorn.effect = Popcorn.compose;
  902. // stores parsers keyed on filetype
  903. Popcorn.parsers = {};
  904. // An interface for extending Popcorn
  905. // with parser functionality
  906. Popcorn.parser = function( name, type, definition ) {
  907. if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
  908. Popcorn.error( "'" + name + "' is a protected function name" );
  909. return;
  910. }
  911. // fixes parameters for overloaded function call
  912. if ( typeof type === "function" && !definition ) {
  913. definition = type;
  914. type = "";
  915. }
  916. if ( typeof definition !== "function" || typeof type !== "string" ) {
  917. return;
  918. }
  919. // Provides some sugar, but ultimately extends
  920. // the definition into Popcorn.p
  921. var natives = Popcorn.events.all,
  922. parseFn,
  923. parser = {};
  924. parseFn = function( filename, callback ) {
  925. if ( !filename ) {
  926. return this;
  927. }
  928. var that = this;
  929. Popcorn.xhr({
  930. url: filename,
  931. dataType: type,
  932. success: function( data ) {
  933. var tracksObject = definition( data ),
  934. tracksData,
  935. tracksDataLen,
  936. tracksDef,
  937. idx = 0;
  938. tracksData = tracksObject.data || [];
  939. tracksDataLen = tracksData.length;
  940. tracksDef = null;
  941. // If no tracks to process, return immediately
  942. if ( !tracksDataLen ) {
  943. return;
  944. }
  945. // Create tracks out of parsed object
  946. for ( ; idx < tracksDataLen; idx++ ) {
  947. tracksDef = tracksData[ idx ];
  948. for ( var key in tracksDef ) {
  949. if ( hasOwn.call( tracksDef, key ) && !!that[ key ] ) {
  950. that[ key ]( tracksDef[ key ] );
  951. }
  952. }
  953. }
  954. if ( callback ) {
  955. callback();
  956. }
  957. }
  958. });
  959. return this;
  960. };
  961. // Assign new named definition
  962. parser[ name ] = parseFn;
  963. // Extend Popcorn.p with new named definition
  964. Popcorn.extend( Popcorn.p, parser );
  965. // keys the function name by filetype extension
  966. //Popcorn.parsers[ name ] = true;
  967. return parser;
  968. };
  969. // Cache references to reused RegExps
  970. var rparams = /\?/,
  971. // XHR Setup object
  972. setup = {
  973. url: "",
  974. data: "",
  975. dataType: "",
  976. success: Popcorn.nop,
  977. type: "GET",
  978. async: true,
  979. xhr: function() {
  980. return new global.XMLHttpRequest();
  981. }
  982. };
  983. Popcorn.xhr = function( options ) {
  984. options.dataType = options.dataType && options.dataType.toLowerCase() || null;
  985. if ( options.dataType &&
  986. ( options.dataType === "jsonp" || options.dataType === "script" ) ) {
  987. Popcorn.xhr.getJSONP(
  988. options.url,
  989. options.success,
  990. options.dataType === "script"
  991. );
  992. return;
  993. }
  994. var settings = Popcorn.extend( {}, setup, options );
  995. // Create new XMLHttpRequest object
  996. settings.ajax = settings.xhr();
  997. if ( settings.ajax ) {
  998. if ( settings.type === "GET" && settings.data ) {
  999. // append query string
  1000. settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
  1001. // Garbage collect and reset settings.data
  1002. settings.data = null;
  1003. }
  1004. settings.ajax.open( settings.type, settings.url, settings.async );
  1005. settings.ajax.send( settings.data || null );
  1006. return Popcorn.xhr.httpData( settings );
  1007. }
  1008. };
  1009. Popcorn.xhr.httpData = function( settings ) {
  1010. var data, json = null;
  1011. settings.ajax.onreadystatechange = function() {
  1012. if ( settings.ajax.readyState === 4 ) {
  1013. try {
  1014. json = JSON.parse( settings.ajax.responseText );
  1015. } catch( e ) {
  1016. //suppress
  1017. }
  1018. data = {
  1019. xml: settings.ajax.responseXML,
  1020. text: settings.ajax.responseText,
  1021. json: json
  1022. };
  1023. // If a dataType was specified, return that type of data
  1024. if ( settings.dataType ) {
  1025. data = data[ settings.dataType ];
  1026. }
  1027. settings.success.call( settings.ajax, data );
  1028. }
  1029. };
  1030. return data;
  1031. };
  1032. Popcorn.xhr.getJSONP = function( url, success, isScript ) {
  1033. // If this is a script request, ensure that we do not call something that has already been loaded
  1034. if ( isScript ) {
  1035. var scripts = document.querySelectorAll( "script[src=\"" + url + "\"]" );
  1036. // If there are scripts with this url loaded, early return
  1037. if ( scripts.length ) {
  1038. // Execute success callback and pass "exists" flag
  1039. success && success( true );
  1040. return;
  1041. }
  1042. }
  1043. var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
  1044. script = document.createElement( "script" ),
  1045. paramStr = url.split( "?" )[ 1 ],
  1046. isFired = false,
  1047. params = [],
  1048. callback, parts, callparam;
  1049. if ( paramStr && !isScript ) {
  1050. params = paramStr.split( "&" );
  1051. }
  1052. if ( params.length ) {
  1053. parts = params[ params.length - 1 ].split( "=" );
  1054. }
  1055. callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ] ) : "jsonp";
  1056. if ( !paramStr && !isScript ) {
  1057. url += "?callback=" + callback;
  1058. }
  1059. if ( callback && !isScript ) {
  1060. // If a callback name already exists
  1061. if ( !!window[ callback ] ) {
  1062. // Create a new unique callback name
  1063. callback = Popcorn.guid( callback );
  1064. }
  1065. // Define the JSONP success callback globally
  1066. window[ callback ] = function( data ) {
  1067. success && success( data );
  1068. isFired = true;
  1069. };
  1070. // Replace callback param and callback name
  1071. url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
  1072. }
  1073. script.onload = script.onreadystatechange = function() {
  1074. if ( !script.readyState || /loaded|complete/.test( script.readyState ) ) {
  1075. // Handling remote script loading callbacks
  1076. if ( isScript ) {
  1077. // getScript
  1078. success && success();
  1079. }
  1080. // Executing for JSONP requests
  1081. if ( isFired ) {
  1082. // Garbage collect the callback
  1083. delete window[ callback ];
  1084. // Garbage collect the script resource
  1085. head.removeChild( script );
  1086. }
  1087. }
  1088. };
  1089. script.src = url;
  1090. head.insertBefore( script, head.firstChild );
  1091. return;
  1092. };
  1093. Popcorn.getJSONP = Popcorn.xhr.getJSONP;
  1094. Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
  1095. return Popcorn.xhr.getJSONP( url, success, true );
  1096. };
  1097. Popcorn.util = {
  1098. // Simple function to parse a timestamp into seconds
  1099. // Acceptable formats are:
  1100. // HH:MM:SS.MMM
  1101. // HH:MM:SS;FF
  1102. // Hours and minutes are optional. They default to 0
  1103. toSeconds: function( timeStr, framerate ) {
  1104. //Hours and minutes are optional
  1105. //Seconds must be specified
  1106. //Seconds can be followed by milliseconds OR by the frame information
  1107. var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
  1108. errorMessage = "Invalid time format";
  1109. if ( typeof timeStr === "number" ) {
  1110. return timeStr;
  1111. } else if ( typeof timeStr === "string" ) {
  1112. if ( ! validTimeFormat.test( timeStr ) ) {
  1113. Popcorn.error( errorMessage );
  1114. }
  1115. } else {
  1116. Popcorn.error( errorMessage );
  1117. }
  1118. var t = timeStr.split( ":" ),
  1119. lastIndex = t.length - 1,
  1120. lastElement = t[ lastIndex ];
  1121. //Fix last element:
  1122. if ( lastElement.indexOf( ";" ) > -1 ) {
  1123. var frameInfo = lastElement.split( ";" ),
  1124. frameTime = 0;
  1125. if ( framerate && ( typeof framerate === "number" ) ) {
  1126. frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
  1127. }
  1128. t[ lastIndex ] =
  1129. parseInt( frameInfo[ 0 ], 10 ) + frameTime;
  1130. }
  1131. if ( t.length === 1 ) {
  1132. return parseFloat( t[ 0 ], 10 );
  1133. } else if ( t.length === 2 ) {
  1134. return ( parseInt( t[ 0 ], 10 ) * 60 ) + parseFloat( t[ 1 ], 10 );
  1135. } else if ( t.length === 3 ) {
  1136. return ( parseInt( t[ 0 ], 10 ) * 3600 ) +
  1137. ( parseInt( t[ 1 ], 10 ) * 60 ) +
  1138. parseFloat( t[ 2 ], 10 );
  1139. }
  1140. }
  1141. };
  1142. // Exposes Popcorn to global context
  1143. global.Popcorn = Popcorn;
  1144. document.addEventListener( "DOMContentLoaded", function() {
  1145. // Supports non-specific elements
  1146. var dataAttr = "data-timeline-sources",
  1147. medias = document.querySelectorAll( "[" + dataAttr + "]" );
  1148. Popcorn.forEach( medias, function( idx, key ) {
  1149. var media = medias[ key ],
  1150. hasDataSources = false,
  1151. dataSources, data, popcornMedia;
  1152. // Ensure that the DOM has an id
  1153. if ( !media.id ) {
  1154. media.id = Popcorn.guid( "__popcorn" );
  1155. }
  1156. // Ensure we're looking at a dom node
  1157. if ( media.nodeType && media.nodeType === 1 ) {
  1158. popcornMedia = Popcorn( "#" + media.id );
  1159. dataSources = ( media.getAttribute( dataAttr ) || "" ).split( "," );
  1160. if ( dataSources[ 0 ] ) {
  1161. Popcorn.forEach( dataSources, function( source ) {
  1162. // split the parser and data as parser!file
  1163. data = source.split( "!" );
  1164. // if no parser is defined for the file, assume "parse" + file extension
  1165. if ( data.length === 1 ) {
  1166. data = source.split( "." );
  1167. data[ 0 ] = "parse" + data[ data.length - 1 ].toUpperCase();
  1168. data[ 1 ] = source;
  1169. }
  1170. // If the media has data sources and the correct parser is registered, continue to load
  1171. if ( dataSources[ 0 ] && popcornMedia[ data[ 0 ] ] ) {
  1172. // Set up the media and load in the datasources
  1173. popcornMedia[ data[ 0 ] ]( data[ 1 ] );
  1174. }
  1175. });
  1176. }
  1177. // Only play the media if it was specified to do so
  1178. if ( !!popcornMedia.autoplay ) {
  1179. popcornMedia.play();
  1180. }
  1181. }
  1182. });
  1183. }, false );
  1184. })(window, window.document);
  1185. // PLUGIN: Mustache
  1186. (function (Popcorn) {
  1187. /**
  1188. * Mustache Popcorn Plug-in
  1189. *
  1190. * Adds the ability to render JSON using templates via the Mustache templating library.
  1191. *
  1192. * @param {Object} options
  1193. *
  1194. * Required parameters: start, end, template, data, and target.
  1195. * Optional parameter: static.
  1196. *
  1197. * start: the time in seconds when the mustache template should be rendered
  1198. * in the target div.
  1199. *
  1200. * end: the time in seconds when the rendered mustache template should be
  1201. * removed from the target div.
  1202. *
  1203. * target: a String -- the target div's id.
  1204. *
  1205. * template: the mustache template for the plugin to use when rendering. This can be
  1206. * a String containing the template, or a Function that returns the template's
  1207. * String.
  1208. *
  1209. * data: the data to be rendered using the mustache template. This can be a JSON String,
  1210. * a JavaScript Object literal, or a Function returning a String or Literal.
  1211. *
  1212. * dynamic: an optional argument indicating that the template and json data are dynamic
  1213. * and need to be loaded dynamically on every use. Defaults to True.
  1214. *
  1215. * Example:
  1216. var p = Popcorn('#video')
  1217. // Example using template and JSON strings.
  1218. .mustache({
  1219. start: 5, // seconds
  1220. end: 15, // seconds
  1221. target: 'mustache',
  1222. template: '<h1>{{header}}</h1>' +
  1223. '{{#bug}}' +
  1224. '{{/bug}}' +
  1225. '' +
  1226. '{{#items}}' +
  1227. ' {{#first}}' +
  1228. ' <li><strong>{{name}}</strong></li>' +
  1229. ' {{/first}}' +
  1230. ' {{#link}}' +
  1231. ' <li><a href="{{url}}">{{name}}</a></li>' +
  1232. ' {{/link}}' +
  1233. '{{/items}}' +
  1234. '' +
  1235. '{{#empty}}' +
  1236. ' <p>The list is empty.</p>' +
  1237. '{{/empty}}' ,
  1238. data: '{' +
  1239. ' "header": "Colors", ' +
  1240. ' "items": [ ' +
  1241. ' {"name": "red", "first": true, "url": "#Red"}, ' +
  1242. ' {"name": "green", "link": true, "url": "#Green"}, ' +
  1243. ' {"name": "blue", "link": true, "url": "#Blue"} ' +
  1244. ' ],' +
  1245. ' 'empty': false' +
  1246. '}',
  1247. dynamic: false // The json is not going to change, load it early.
  1248. } )
  1249. // Example showing Functions instead of Strings.
  1250. .mustache({
  1251. start: 20, // seconds
  1252. end: 25, // seconds
  1253. target: 'mustache',
  1254. template: function(instance, options) {
  1255. var template = // load your template file here...
  1256. return template;
  1257. },
  1258. data: function(instance, options) {
  1259. var json = // load your json here...
  1260. return json;
  1261. }
  1262. } );
  1263. *
  1264. */
  1265. Popcorn.plugin( 'mustache' , function( options ) {
  1266. var getData, data, getTemplate, template;
  1267. Popcorn.getScript('https://github.com/janl/mustache.js/raw/master/mustache.js');
  1268. var shouldReload = !!options.dynamic,
  1269. typeOfTemplate = typeof options.template,
  1270. typeOfData = typeof options.data,
  1271. target = document.getElementById( options.target );
  1272. if ( !target && Popcorn.plugin.debug ) {
  1273. throw new Error( "target container doesn't exist" );
  1274. }
  1275. options.container = target || document.createElement( "div" );
  1276. if ( typeOfTemplate === 'function' ) {
  1277. if ( !shouldReload ) {
  1278. template = options.template( options );
  1279. } else {
  1280. getTemplate = options.template;
  1281. }
  1282. } else if ( typeOfTemplate === 'string' ) {
  1283. template = options.template;
  1284. } else if ( Popcorn.plugin.debug ) {
  1285. throw new Error( 'Mustache Plugin Error: options.template must be a String or a Function.' );
  1286. } else {

Large files files are truncated, but you can click here to view the full file