popcornjs-rails 1.1.2 → 1.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/.gitignore +0 -0
- data/Gemfile +0 -0
- data/README.md +10 -4
- data/Rakefile +0 -0
- data/lib/popcornjs-rails/version.rb +1 -1
- data/lib/popcornjs-rails.rb +0 -0
- data/popcornjs-rails.gemspec +0 -0
- data/vendor/assets/javascripts/popcorn-complete.js +1512 -910
- data/vendor/assets/javascripts/popcorn-complete.min.js +188 -178
- data/vendor/assets/javascripts/popcorn-ie8.js +2849 -0
- data/vendor/assets/javascripts/popcorn-ie8.min.js +64 -0
- data/vendor/assets/javascripts/popcorn.js +207 -511
- data/vendor/assets/javascripts/popcorn.min.js +37 -42
- metadata +5 -3
@@ -0,0 +1,2849 @@
|
|
1
|
+
/*
|
2
|
+
* popcorn.js version 1.2
|
3
|
+
* http://popcornjs.org
|
4
|
+
*
|
5
|
+
* Copyright 2011, Mozilla Foundation
|
6
|
+
* Licensed under the MIT license
|
7
|
+
*/
|
8
|
+
|
9
|
+
(function() {
|
10
|
+
|
11
|
+
document.addEventListener = document.addEventListener || function( event, callBack ) {
|
12
|
+
|
13
|
+
event = ( event === "DOMContentLoaded" ) ? "onreadystatechange" : "on" + event;
|
14
|
+
|
15
|
+
document.attachEvent( event, callBack );
|
16
|
+
};
|
17
|
+
|
18
|
+
document.removeEventListener = document.removeEventListener || function( event, callBack ) {
|
19
|
+
|
20
|
+
event = ( event === "DOMContentLoaded" ) ? "onreadystatechange" : "on" + event;
|
21
|
+
|
22
|
+
document.detachEvent( event, callBack );
|
23
|
+
};
|
24
|
+
|
25
|
+
HTMLScriptElement.prototype.addEventListener = HTMLScriptElement.prototype.addEventListener || function( event, callBack ) {
|
26
|
+
|
27
|
+
event = ( event === "load" ) ? "onreadystatechange" : "on" + event;
|
28
|
+
|
29
|
+
this.attachEvent( event, callBack );
|
30
|
+
};
|
31
|
+
|
32
|
+
HTMLScriptElement.prototype.removeEventListener = HTMLScriptElement.prototype.removeEventListener || function( event, callBack ) {
|
33
|
+
|
34
|
+
event = ( event === "load" ) ? "onreadystatechange" : "on" + event;
|
35
|
+
|
36
|
+
this.detachEvent( event, callBack );
|
37
|
+
};
|
38
|
+
|
39
|
+
document.createEvent = document.createEvent || function ( type ) {
|
40
|
+
|
41
|
+
return {
|
42
|
+
type : null,
|
43
|
+
target : null,
|
44
|
+
currentTarget : null,
|
45
|
+
cancelable : false,
|
46
|
+
bubbles : false,
|
47
|
+
initEvent : function (type, bubbles, cancelable) {
|
48
|
+
this.type = type;
|
49
|
+
},
|
50
|
+
stopPropagation : function () {},
|
51
|
+
stopImmediatePropagation : function () {}
|
52
|
+
}
|
53
|
+
};
|
54
|
+
|
55
|
+
Array.prototype.forEach = Array.prototype.forEach || function( fn, context ) {
|
56
|
+
|
57
|
+
var obj = this,
|
58
|
+
hasOwn = Object.prototype.hasOwnProperty;
|
59
|
+
|
60
|
+
if ( !obj || !fn ) {
|
61
|
+
return {};
|
62
|
+
}
|
63
|
+
|
64
|
+
context = context || this;
|
65
|
+
|
66
|
+
var key, len;
|
67
|
+
|
68
|
+
for ( key in obj ) {
|
69
|
+
if ( hasOwn.call( obj, key ) ) {
|
70
|
+
fn.call( context, obj[ key ], key, obj );
|
71
|
+
}
|
72
|
+
}
|
73
|
+
return obj;
|
74
|
+
};
|
75
|
+
|
76
|
+
// Production steps of ECMA-262, Edition 5, 15.4.4.19
|
77
|
+
// Reference: http://es5.github.com/#x15.4.4.19
|
78
|
+
if ( !Array.prototype.map ) {
|
79
|
+
|
80
|
+
Array.prototype.map = function( callback, thisArg ) {
|
81
|
+
|
82
|
+
var T, A, k;
|
83
|
+
|
84
|
+
if ( this == null ) {
|
85
|
+
throw new TypeError( "this is null or not defined" );
|
86
|
+
}
|
87
|
+
|
88
|
+
// 1. Let O be the result of calling ToObject passing the |this| value as the argument.
|
89
|
+
var O = Object( this );
|
90
|
+
|
91
|
+
// 2. Let lenValue be the result of calling the Get internal method of O with the argument "length".
|
92
|
+
// 3. Let len be ToUint32(lenValue).
|
93
|
+
var len = O.length >>> 0;
|
94
|
+
|
95
|
+
// 4. If IsCallable(callback) is false, throw a TypeError exception.
|
96
|
+
// See: http://es5.github.com/#x9.11
|
97
|
+
if ( {}.toString.call( callback ) != "[object Function]" ) {
|
98
|
+
throw new TypeError( callback + " is not a function" );
|
99
|
+
}
|
100
|
+
|
101
|
+
// 5. If thisArg was supplied, let T be thisArg; else let T be undefined.
|
102
|
+
if ( thisArg ) {
|
103
|
+
T = thisArg;
|
104
|
+
}
|
105
|
+
|
106
|
+
// 6. Let A be a new array created as if by the expression new Array(len) where Array is
|
107
|
+
// the standard built-in constructor with that name and len is the value of len.
|
108
|
+
A = new Array( len );
|
109
|
+
|
110
|
+
// 7. Let k be 0
|
111
|
+
k = 0;
|
112
|
+
|
113
|
+
// 8. Repeat, while k < len
|
114
|
+
while( k < len ) {
|
115
|
+
|
116
|
+
var kValue, mappedValue;
|
117
|
+
|
118
|
+
// a. Let Pk be ToString(k).
|
119
|
+
// This is implicit for LHS operands of the in operator
|
120
|
+
// b. Let kPresent be the result of calling the HasProperty internal method of O with argument Pk.
|
121
|
+
// This step can be combined with c
|
122
|
+
// c. If kPresent is true, then
|
123
|
+
if ( k in O ) {
|
124
|
+
|
125
|
+
// i. Let kValue be the result of calling the Get internal method of O with argument Pk.
|
126
|
+
kValue = O[ k ];
|
127
|
+
|
128
|
+
// ii. Let mappedValue be the result of calling the Call internal method of callback
|
129
|
+
// with T as the this value and argument list containing kValue, k, and O.
|
130
|
+
mappedValue = callback.call( T, kValue, k, O );
|
131
|
+
|
132
|
+
// iii. Call the DefineOwnProperty internal method of A with arguments
|
133
|
+
// Pk, Property Descriptor {Value: mappedValue, Writable: true, Enumerable: true, Configurable: true},
|
134
|
+
// and false.
|
135
|
+
|
136
|
+
// In browsers that support Object.defineProperty, use the following:
|
137
|
+
// Object.defineProperty(A, Pk, { value: mappedValue, writable: true, enumerable: true, configurable: true });
|
138
|
+
|
139
|
+
// For best browser support, use the following:
|
140
|
+
A[ k ] = mappedValue;
|
141
|
+
}
|
142
|
+
// d. Increase k by 1.
|
143
|
+
k++;
|
144
|
+
}
|
145
|
+
|
146
|
+
// 9. return A
|
147
|
+
return A;
|
148
|
+
};
|
149
|
+
}
|
150
|
+
|
151
|
+
if ( !Array.prototype.indexOf ) {
|
152
|
+
|
153
|
+
Array.prototype.indexOf = function ( searchElement /*, fromIndex */ ) {
|
154
|
+
|
155
|
+
if ( this == null) {
|
156
|
+
|
157
|
+
throw new TypeError();
|
158
|
+
}
|
159
|
+
|
160
|
+
var t = Object( this ),
|
161
|
+
len = t.length >>> 0;
|
162
|
+
|
163
|
+
if ( len === 0 ) {
|
164
|
+
|
165
|
+
return -1;
|
166
|
+
}
|
167
|
+
|
168
|
+
var n = 0;
|
169
|
+
|
170
|
+
if ( arguments.length > 0 ) {
|
171
|
+
|
172
|
+
n = Number( arguments[ 1 ] );
|
173
|
+
|
174
|
+
if ( n != n ) { // shortcut for verifying if it's NaN
|
175
|
+
|
176
|
+
n = 0;
|
177
|
+
} else if ( n != 0 && n != Infinity && n != -Infinity ) {
|
178
|
+
|
179
|
+
n = ( n > 0 || -1 ) * Math.floor( Math.abs( n ) );
|
180
|
+
}
|
181
|
+
}
|
182
|
+
|
183
|
+
if ( n >= len ) {
|
184
|
+
return -1;
|
185
|
+
}
|
186
|
+
|
187
|
+
var k = n >= 0 ? n : Math.max( len - Math.abs( n ), 0 );
|
188
|
+
|
189
|
+
for (; k < len; k++ ) {
|
190
|
+
|
191
|
+
if ( k in t && t[ k ] === searchElement ) {
|
192
|
+
|
193
|
+
return k;
|
194
|
+
}
|
195
|
+
}
|
196
|
+
|
197
|
+
return -1;
|
198
|
+
}
|
199
|
+
}
|
200
|
+
})();
|
201
|
+
(function(global, document) {
|
202
|
+
|
203
|
+
// Popcorn.js does not support archaic browsers
|
204
|
+
if ( !document.addEventListener ) {
|
205
|
+
global.Popcorn = {
|
206
|
+
isSupported: false
|
207
|
+
};
|
208
|
+
|
209
|
+
var methods = ( "removeInstance addInstance getInstanceById removeInstanceById " +
|
210
|
+
"forEach extend effects error guid sizeOf isArray nop position disable enable destroy" +
|
211
|
+
"addTrackEvent removeTrackEvent getTrackEvents getTrackEvent getLastTrackEventId " +
|
212
|
+
"timeUpdate plugin removePlugin compose effect xhr getJSONP getScript" ).split(/\s+/);
|
213
|
+
|
214
|
+
while ( methods.length ) {
|
215
|
+
global.Popcorn[ methods.shift() ] = function() {};
|
216
|
+
}
|
217
|
+
return;
|
218
|
+
}
|
219
|
+
|
220
|
+
var
|
221
|
+
|
222
|
+
AP = Array.prototype,
|
223
|
+
OP = Object.prototype,
|
224
|
+
|
225
|
+
forEach = AP.forEach,
|
226
|
+
slice = AP.slice,
|
227
|
+
hasOwn = OP.hasOwnProperty,
|
228
|
+
toString = OP.toString,
|
229
|
+
|
230
|
+
// Copy global Popcorn (may not exist)
|
231
|
+
_Popcorn = global.Popcorn,
|
232
|
+
|
233
|
+
// ID string matching
|
234
|
+
rIdExp = /^(#([\w\-\_\.]+))$/,
|
235
|
+
|
236
|
+
// Ready fn cache
|
237
|
+
readyStack = [],
|
238
|
+
readyBound = false,
|
239
|
+
readyFired = false,
|
240
|
+
|
241
|
+
// Non-public internal data object
|
242
|
+
internal = {
|
243
|
+
events: {
|
244
|
+
hash: {},
|
245
|
+
apis: {}
|
246
|
+
}
|
247
|
+
},
|
248
|
+
|
249
|
+
// Non-public `requestAnimFrame`
|
250
|
+
// http://paulirish.com/2011/requestanimationframe-for-smart-animating/
|
251
|
+
requestAnimFrame = (function(){
|
252
|
+
return global.requestAnimationFrame ||
|
253
|
+
global.webkitRequestAnimationFrame ||
|
254
|
+
global.mozRequestAnimationFrame ||
|
255
|
+
global.oRequestAnimationFrame ||
|
256
|
+
global.msRequestAnimationFrame ||
|
257
|
+
function( callback, element ) {
|
258
|
+
global.setTimeout( callback, 16 );
|
259
|
+
};
|
260
|
+
}()),
|
261
|
+
|
262
|
+
// Non-public `getKeys`, return an object's keys as an array
|
263
|
+
getKeys = function( obj ) {
|
264
|
+
return Object.keys ? Object.keys( obj ) : (function( obj ) {
|
265
|
+
var item,
|
266
|
+
list = [];
|
267
|
+
|
268
|
+
for ( item in obj ) {
|
269
|
+
if ( hasOwn.call( obj, item ) ) {
|
270
|
+
list.push( item );
|
271
|
+
}
|
272
|
+
}
|
273
|
+
return list;
|
274
|
+
})( obj );
|
275
|
+
},
|
276
|
+
|
277
|
+
refresh = function( obj ) {
|
278
|
+
var currentTime = obj.media.currentTime,
|
279
|
+
animation = obj.options.frameAnimation,
|
280
|
+
disabled = obj.data.disabled,
|
281
|
+
tracks = obj.data.trackEvents,
|
282
|
+
animating = tracks.animating,
|
283
|
+
start = tracks.startIndex,
|
284
|
+
registryByName = Popcorn.registryByName,
|
285
|
+
animIndex = 0,
|
286
|
+
byStart, natives, type;
|
287
|
+
|
288
|
+
start = Math.min( start + 1, tracks.byStart.length - 2 );
|
289
|
+
|
290
|
+
while ( start > 0 && tracks.byStart[ start ] ) {
|
291
|
+
|
292
|
+
byStart = tracks.byStart[ start ];
|
293
|
+
natives = byStart._natives;
|
294
|
+
type = natives && natives.type;
|
295
|
+
|
296
|
+
if ( !natives ||
|
297
|
+
( !!registryByName[ type ] || !!obj[ type ] ) ) {
|
298
|
+
|
299
|
+
if ( ( byStart.start <= currentTime && byStart.end > currentTime ) &&
|
300
|
+
disabled.indexOf( type ) === -1 ) {
|
301
|
+
|
302
|
+
if ( !byStart._running ) {
|
303
|
+
byStart._running = true;
|
304
|
+
natives.start.call( obj, null, byStart );
|
305
|
+
|
306
|
+
// if the 'frameAnimation' option is used,
|
307
|
+
// push the current byStart object into the `animating` cue
|
308
|
+
if ( animation &&
|
309
|
+
( byStart && byStart._running && byStart.natives.frame ) ) {
|
310
|
+
|
311
|
+
natives.frame.call( obj, null, byStart, currentTime );
|
312
|
+
}
|
313
|
+
}
|
314
|
+
} else if ( byStart._running === true ) {
|
315
|
+
|
316
|
+
byStart._running = false;
|
317
|
+
natives.end.call( obj, null, byStart );
|
318
|
+
|
319
|
+
if ( animation && byStart._natives.frame ) {
|
320
|
+
animIndex = animating.indexOf( byStart );
|
321
|
+
if ( animIndex >= 0 ) {
|
322
|
+
animating.splice( animIndex, 1 );
|
323
|
+
}
|
324
|
+
}
|
325
|
+
}
|
326
|
+
}
|
327
|
+
|
328
|
+
start--;
|
329
|
+
}
|
330
|
+
},
|
331
|
+
|
332
|
+
// Declare constructor
|
333
|
+
// Returns an instance object.
|
334
|
+
Popcorn = function( entity, options ) {
|
335
|
+
// Return new Popcorn object
|
336
|
+
return new Popcorn.p.init( entity, options || null );
|
337
|
+
};
|
338
|
+
|
339
|
+
// Popcorn API version, automatically inserted via build system.
|
340
|
+
Popcorn.version = "1.2";
|
341
|
+
|
342
|
+
// Boolean flag allowing a client to determine if Popcorn can be supported
|
343
|
+
Popcorn.isSupported = true;
|
344
|
+
|
345
|
+
// Instance caching
|
346
|
+
Popcorn.instances = [];
|
347
|
+
|
348
|
+
// Declare a shortcut (Popcorn.p) to and a definition of
|
349
|
+
// the new prototype for our Popcorn constructor
|
350
|
+
Popcorn.p = Popcorn.prototype = {
|
351
|
+
|
352
|
+
init: function( entity, options ) {
|
353
|
+
|
354
|
+
var matches,
|
355
|
+
self = this;
|
356
|
+
|
357
|
+
// Supports Popcorn(function () { /../ })
|
358
|
+
// Originally proposed by Daniel Brooks
|
359
|
+
|
360
|
+
if ( typeof entity === "function" ) {
|
361
|
+
|
362
|
+
// If document ready has already fired
|
363
|
+
if ( document.readyState === "complete" ) {
|
364
|
+
|
365
|
+
entity( document, Popcorn );
|
366
|
+
|
367
|
+
return;
|
368
|
+
}
|
369
|
+
// Add `entity` fn to ready stack
|
370
|
+
readyStack.push( entity );
|
371
|
+
|
372
|
+
// This process should happen once per page load
|
373
|
+
if ( !readyBound ) {
|
374
|
+
|
375
|
+
// set readyBound flag
|
376
|
+
readyBound = true;
|
377
|
+
|
378
|
+
var DOMContentLoaded = function() {
|
379
|
+
|
380
|
+
readyFired = true;
|
381
|
+
|
382
|
+
// Remove global DOM ready listener
|
383
|
+
document.removeEventListener( "DOMContentLoaded", DOMContentLoaded, false );
|
384
|
+
|
385
|
+
// Execute all ready function in the stack
|
386
|
+
for ( var i = 0, readyStackLength = readyStack.length; i < readyStackLength; i++ ) {
|
387
|
+
|
388
|
+
readyStack[ i ].call( document, Popcorn );
|
389
|
+
|
390
|
+
}
|
391
|
+
// GC readyStack
|
392
|
+
readyStack = null;
|
393
|
+
};
|
394
|
+
|
395
|
+
// Register global DOM ready listener
|
396
|
+
document.addEventListener( "DOMContentLoaded", DOMContentLoaded, false );
|
397
|
+
}
|
398
|
+
|
399
|
+
return;
|
400
|
+
}
|
401
|
+
|
402
|
+
// Check if entity is a valid string id
|
403
|
+
matches = rIdExp.exec( entity );
|
404
|
+
|
405
|
+
// Get media element by id or object reference
|
406
|
+
this.media = matches && matches.length && matches[ 2 ] ?
|
407
|
+
document.getElementById( matches[ 2 ] ) :
|
408
|
+
entity;
|
409
|
+
|
410
|
+
// Create an audio or video element property reference
|
411
|
+
this[ ( this.media.nodeName && this.media.nodeName.toLowerCase() ) || "video" ] = this.media;
|
412
|
+
|
413
|
+
// Register new instance
|
414
|
+
Popcorn.instances.push( this );
|
415
|
+
|
416
|
+
this.options = options || {};
|
417
|
+
|
418
|
+
this.isDestroyed = false;
|
419
|
+
|
420
|
+
this.data = {
|
421
|
+
|
422
|
+
// Executed by either timeupdate event or in rAF loop
|
423
|
+
timeUpdate: Popcorn.nop,
|
424
|
+
|
425
|
+
// Allows disabling a plugin per instance
|
426
|
+
disabled: [],
|
427
|
+
|
428
|
+
// Stores DOM event queues by type
|
429
|
+
events: {},
|
430
|
+
|
431
|
+
// Stores Special event hooks data
|
432
|
+
hooks: {},
|
433
|
+
|
434
|
+
// Store track event history data
|
435
|
+
history: [],
|
436
|
+
|
437
|
+
// Stores ad-hoc state related data]
|
438
|
+
state: {
|
439
|
+
volume: this.media.volume
|
440
|
+
},
|
441
|
+
|
442
|
+
// Store track event object references by trackId
|
443
|
+
trackRefs: {},
|
444
|
+
|
445
|
+
// Playback track event queues
|
446
|
+
trackEvents: {
|
447
|
+
byStart: [{
|
448
|
+
|
449
|
+
start: -1,
|
450
|
+
end: -1
|
451
|
+
}],
|
452
|
+
byEnd: [{
|
453
|
+
start: -1,
|
454
|
+
end: -1
|
455
|
+
}],
|
456
|
+
animating: [],
|
457
|
+
startIndex: 0,
|
458
|
+
endIndex: 0,
|
459
|
+
previousUpdateTime: -1
|
460
|
+
}
|
461
|
+
};
|
462
|
+
|
463
|
+
// function to fire when video is ready
|
464
|
+
var isReady = function() {
|
465
|
+
|
466
|
+
self.media.removeEventListener( "loadeddata", isReady, false );
|
467
|
+
|
468
|
+
var duration, videoDurationPlus;
|
469
|
+
|
470
|
+
// Adding padding to the front and end of the arrays
|
471
|
+
// this is so we do not fall off either end
|
472
|
+
duration = self.media.duration;
|
473
|
+
|
474
|
+
// Check for no duration info (NaN)
|
475
|
+
videoDurationPlus = duration != duration ? Number.MAX_VALUE : duration + 1;
|
476
|
+
|
477
|
+
Popcorn.addTrackEvent( self, {
|
478
|
+
start: videoDurationPlus,
|
479
|
+
end: videoDurationPlus
|
480
|
+
});
|
481
|
+
|
482
|
+
if ( self.options.frameAnimation ) {
|
483
|
+
// if Popcorn is created with frameAnimation option set to true,
|
484
|
+
// requestAnimFrame is used instead of "timeupdate" media event.
|
485
|
+
// This is for greater frame time accuracy, theoretically up to
|
486
|
+
// 60 frames per second as opposed to ~4 ( ~every 15-250ms)
|
487
|
+
self.data.timeUpdate = function () {
|
488
|
+
|
489
|
+
Popcorn.timeUpdate( self, {} );
|
490
|
+
|
491
|
+
self.emit( "timeupdate" );
|
492
|
+
|
493
|
+
!self.isDestroyed && requestAnimFrame( self.data.timeUpdate );
|
494
|
+
};
|
495
|
+
|
496
|
+
!self.isDestroyed && requestAnimFrame( self.data.timeUpdate );
|
497
|
+
|
498
|
+
} else {
|
499
|
+
|
500
|
+
self.data.timeUpdate = function( event ) {
|
501
|
+
Popcorn.timeUpdate( self, event );
|
502
|
+
};
|
503
|
+
|
504
|
+
if ( !self.isDestroyed ) {
|
505
|
+
self.media.addEventListener( "timeupdate", self.data.timeUpdate, false );
|
506
|
+
}
|
507
|
+
}
|
508
|
+
};
|
509
|
+
|
510
|
+
if ( self.media.readyState >= 2 ) {
|
511
|
+
|
512
|
+
isReady();
|
513
|
+
} else {
|
514
|
+
|
515
|
+
self.media.addEventListener( "loadeddata", isReady, false );
|
516
|
+
}
|
517
|
+
|
518
|
+
return this;
|
519
|
+
}
|
520
|
+
};
|
521
|
+
|
522
|
+
// Extend constructor prototype to instance prototype
|
523
|
+
// Allows chaining methods to instances
|
524
|
+
Popcorn.p.init.prototype = Popcorn.p;
|
525
|
+
|
526
|
+
Popcorn.forEach = function( obj, fn, context ) {
|
527
|
+
|
528
|
+
if ( !obj || !fn ) {
|
529
|
+
return {};
|
530
|
+
}
|
531
|
+
|
532
|
+
context = context || this;
|
533
|
+
|
534
|
+
var key, len;
|
535
|
+
|
536
|
+
// Use native whenever possible
|
537
|
+
if ( forEach && obj.forEach === forEach ) {
|
538
|
+
return obj.forEach( fn, context );
|
539
|
+
}
|
540
|
+
|
541
|
+
if ( toString.call( obj ) === "[object NodeList]" ) {
|
542
|
+
for ( key = 0, len = obj.length; key < len; key++ ) {
|
543
|
+
fn.call( context, obj[ key ], key, obj );
|
544
|
+
}
|
545
|
+
return obj;
|
546
|
+
}
|
547
|
+
|
548
|
+
for ( key in obj ) {
|
549
|
+
if ( hasOwn.call( obj, key ) ) {
|
550
|
+
fn.call( context, obj[ key ], key, obj );
|
551
|
+
}
|
552
|
+
}
|
553
|
+
return obj;
|
554
|
+
};
|
555
|
+
|
556
|
+
Popcorn.extend = function( obj ) {
|
557
|
+
var dest = obj, src = slice.call( arguments, 1 );
|
558
|
+
|
559
|
+
Popcorn.forEach( src, function( copy ) {
|
560
|
+
for ( var prop in copy ) {
|
561
|
+
dest[ prop ] = copy[ prop ];
|
562
|
+
}
|
563
|
+
});
|
564
|
+
|
565
|
+
return dest;
|
566
|
+
};
|
567
|
+
|
568
|
+
|
569
|
+
// A Few reusable utils, memoized onto Popcorn
|
570
|
+
Popcorn.extend( Popcorn, {
|
571
|
+
noConflict: function( deep ) {
|
572
|
+
|
573
|
+
if ( deep ) {
|
574
|
+
global.Popcorn = _Popcorn;
|
575
|
+
}
|
576
|
+
|
577
|
+
return Popcorn;
|
578
|
+
},
|
579
|
+
error: function( msg ) {
|
580
|
+
throw new Error( msg );
|
581
|
+
},
|
582
|
+
guid: function( prefix ) {
|
583
|
+
Popcorn.guid.counter++;
|
584
|
+
return ( prefix ? prefix : "" ) + ( +new Date() + Popcorn.guid.counter );
|
585
|
+
},
|
586
|
+
sizeOf: function( obj ) {
|
587
|
+
var size = 0;
|
588
|
+
|
589
|
+
for ( var prop in obj ) {
|
590
|
+
size++;
|
591
|
+
}
|
592
|
+
|
593
|
+
return size;
|
594
|
+
},
|
595
|
+
isArray: Array.isArray || function( array ) {
|
596
|
+
return toString.call( array ) === "[object Array]";
|
597
|
+
},
|
598
|
+
|
599
|
+
nop: function() {},
|
600
|
+
|
601
|
+
position: function( elem ) {
|
602
|
+
|
603
|
+
var clientRect = elem.getBoundingClientRect(),
|
604
|
+
bounds = {},
|
605
|
+
doc = elem.ownerDocument,
|
606
|
+
docElem = document.documentElement,
|
607
|
+
body = document.body,
|
608
|
+
clientTop, clientLeft, scrollTop, scrollLeft, top, left;
|
609
|
+
|
610
|
+
// Determine correct clientTop/Left
|
611
|
+
clientTop = docElem.clientTop || body.clientTop || 0;
|
612
|
+
clientLeft = docElem.clientLeft || body.clientLeft || 0;
|
613
|
+
|
614
|
+
// Determine correct scrollTop/Left
|
615
|
+
scrollTop = ( global.pageYOffset && docElem.scrollTop || body.scrollTop );
|
616
|
+
scrollLeft = ( global.pageXOffset && docElem.scrollLeft || body.scrollLeft );
|
617
|
+
|
618
|
+
// Temp top/left
|
619
|
+
top = Math.ceil( clientRect.top + scrollTop - clientTop );
|
620
|
+
left = Math.ceil( clientRect.left + scrollLeft - clientLeft );
|
621
|
+
|
622
|
+
for ( var p in clientRect ) {
|
623
|
+
bounds[ p ] = Math.round( clientRect[ p ] );
|
624
|
+
}
|
625
|
+
|
626
|
+
return Popcorn.extend({}, bounds, { top: top, left: left });
|
627
|
+
},
|
628
|
+
|
629
|
+
disable: function( instance, plugin ) {
|
630
|
+
|
631
|
+
var disabled = instance.data.disabled;
|
632
|
+
|
633
|
+
if ( disabled.indexOf( plugin ) === -1 ) {
|
634
|
+
disabled.push( plugin );
|
635
|
+
}
|
636
|
+
|
637
|
+
refresh( instance );
|
638
|
+
|
639
|
+
return instance;
|
640
|
+
},
|
641
|
+
enable: function( instance, plugin ) {
|
642
|
+
|
643
|
+
var disabled = instance.data.disabled,
|
644
|
+
index = disabled.indexOf( plugin );
|
645
|
+
|
646
|
+
if ( index > -1 ) {
|
647
|
+
disabled.splice( index, 1 );
|
648
|
+
}
|
649
|
+
|
650
|
+
refresh( instance );
|
651
|
+
|
652
|
+
return instance;
|
653
|
+
},
|
654
|
+
destroy: function( instance ) {
|
655
|
+
var events = instance.data.events,
|
656
|
+
singleEvent, item, fn;
|
657
|
+
|
658
|
+
// Iterate through all events and remove them
|
659
|
+
for ( item in events ) {
|
660
|
+
singleEvent = events[ item ];
|
661
|
+
for ( fn in singleEvent ) {
|
662
|
+
delete singleEvent[ fn ];
|
663
|
+
}
|
664
|
+
events[ item ] = null;
|
665
|
+
}
|
666
|
+
|
667
|
+
if ( !instance.isDestroyed ) {
|
668
|
+
instance.data.timeUpdate && instance.media.removeEventListener( "timeupdate", instance.data.timeUpdate, false );
|
669
|
+
instance.isDestroyed = true;
|
670
|
+
}
|
671
|
+
}
|
672
|
+
});
|
673
|
+
|
674
|
+
// Memoized GUID Counter
|
675
|
+
Popcorn.guid.counter = 1;
|
676
|
+
|
677
|
+
// Factory to implement getters, setters and controllers
|
678
|
+
// as Popcorn instance methods. The IIFE will create and return
|
679
|
+
// an object with defined methods
|
680
|
+
Popcorn.extend(Popcorn.p, (function() {
|
681
|
+
|
682
|
+
var methods = "load play pause currentTime playbackRate volume duration preload playbackRate " +
|
683
|
+
"autoplay loop controls muted buffered readyState seeking paused played seekable ended",
|
684
|
+
ret = {};
|
685
|
+
|
686
|
+
|
687
|
+
// Build methods, store in object that is returned and passed to extend
|
688
|
+
Popcorn.forEach( methods.split( /\s+/g ), function( name ) {
|
689
|
+
|
690
|
+
ret[ name ] = function( arg ) {
|
691
|
+
|
692
|
+
if ( typeof this.media[ name ] === "function" ) {
|
693
|
+
|
694
|
+
// Support for shorthanded play(n)/pause(n) jump to currentTime
|
695
|
+
// If arg is not null or undefined and called by one of the
|
696
|
+
// allowed shorthandable methods, then set the currentTime
|
697
|
+
// Supports time as seconds or SMPTE
|
698
|
+
if ( arg != null && /play|pause/.test( name ) ) {
|
699
|
+
this.media.currentTime = Popcorn.util.toSeconds( arg );
|
700
|
+
}
|
701
|
+
|
702
|
+
this.media[ name ]();
|
703
|
+
|
704
|
+
return this;
|
705
|
+
}
|
706
|
+
|
707
|
+
|
708
|
+
if ( arg != null ) {
|
709
|
+
|
710
|
+
this.media[ name ] = arg;
|
711
|
+
|
712
|
+
return this;
|
713
|
+
}
|
714
|
+
|
715
|
+
return this.media[ name ];
|
716
|
+
};
|
717
|
+
});
|
718
|
+
|
719
|
+
return ret;
|
720
|
+
|
721
|
+
})()
|
722
|
+
);
|
723
|
+
|
724
|
+
Popcorn.forEach( "enable disable".split(" "), function( method ) {
|
725
|
+
Popcorn.p[ method ] = function( plugin ) {
|
726
|
+
return Popcorn[ method ]( this, plugin );
|
727
|
+
};
|
728
|
+
});
|
729
|
+
|
730
|
+
Popcorn.extend(Popcorn.p, {
|
731
|
+
|
732
|
+
// Rounded currentTime
|
733
|
+
roundTime: function() {
|
734
|
+
return -~this.media.currentTime;
|
735
|
+
},
|
736
|
+
|
737
|
+
// Attach an event to a single point in time
|
738
|
+
exec: function( time, fn ) {
|
739
|
+
|
740
|
+
// Creating a one second track event with an empty end
|
741
|
+
Popcorn.addTrackEvent( this, {
|
742
|
+
start: time,
|
743
|
+
end: time + 1,
|
744
|
+
_running: false,
|
745
|
+
_natives: {
|
746
|
+
start: fn || Popcorn.nop,
|
747
|
+
end: Popcorn.nop,
|
748
|
+
type: "cue"
|
749
|
+
}
|
750
|
+
});
|
751
|
+
|
752
|
+
return this;
|
753
|
+
},
|
754
|
+
|
755
|
+
// Mute the calling media, optionally toggle
|
756
|
+
mute: function( toggle ) {
|
757
|
+
|
758
|
+
var event = toggle == null || toggle === true ? "muted" : "unmuted";
|
759
|
+
|
760
|
+
// If `toggle` is explicitly `false`,
|
761
|
+
// unmute the media and restore the volume level
|
762
|
+
if ( event === "unmuted" ) {
|
763
|
+
this.media.muted = false;
|
764
|
+
this.media.volume = this.data.state.volume;
|
765
|
+
}
|
766
|
+
|
767
|
+
// If `toggle` is either null or undefined,
|
768
|
+
// save the current volume and mute the media element
|
769
|
+
if ( event === "muted" ) {
|
770
|
+
this.data.state.volume = this.media.volume;
|
771
|
+
this.media.muted = true;
|
772
|
+
}
|
773
|
+
|
774
|
+
// Trigger either muted|unmuted event
|
775
|
+
this.emit( event );
|
776
|
+
|
777
|
+
return this;
|
778
|
+
},
|
779
|
+
|
780
|
+
// Convenience method, unmute the calling media
|
781
|
+
unmute: function( toggle ) {
|
782
|
+
|
783
|
+
return this.mute( toggle == null ? false : !toggle );
|
784
|
+
},
|
785
|
+
|
786
|
+
// Get the client bounding box of an instance element
|
787
|
+
position: function() {
|
788
|
+
return Popcorn.position( this.media );
|
789
|
+
},
|
790
|
+
|
791
|
+
// Toggle a plugin's playback behaviour (on or off) per instance
|
792
|
+
toggle: function( plugin ) {
|
793
|
+
return Popcorn[ this.data.disabled.indexOf( plugin ) > -1 ? "enable" : "disable" ]( this, plugin );
|
794
|
+
},
|
795
|
+
|
796
|
+
// Set default values for plugin options objects per instance
|
797
|
+
defaults: function( plugin, defaults ) {
|
798
|
+
|
799
|
+
// If an array of default configurations is provided,
|
800
|
+
// iterate and apply each to this instance
|
801
|
+
if ( Popcorn.isArray( plugin ) ) {
|
802
|
+
|
803
|
+
Popcorn.forEach( plugin, function( obj ) {
|
804
|
+
for ( var name in obj ) {
|
805
|
+
this.defaults( name, obj[ name ] );
|
806
|
+
}
|
807
|
+
}, this );
|
808
|
+
|
809
|
+
return this;
|
810
|
+
}
|
811
|
+
|
812
|
+
if ( !this.options.defaults ) {
|
813
|
+
this.options.defaults = {};
|
814
|
+
}
|
815
|
+
|
816
|
+
if ( !this.options.defaults[ plugin ] ) {
|
817
|
+
this.options.defaults[ plugin ] = {};
|
818
|
+
}
|
819
|
+
|
820
|
+
Popcorn.extend( this.options.defaults[ plugin ], defaults );
|
821
|
+
|
822
|
+
return this;
|
823
|
+
}
|
824
|
+
});
|
825
|
+
|
826
|
+
Popcorn.Events = {
|
827
|
+
UIEvents: "blur focus focusin focusout load resize scroll unload",
|
828
|
+
MouseEvents: "mousedown mouseup mousemove mouseover mouseout mouseenter mouseleave click dblclick",
|
829
|
+
Events: "loadstart progress suspend emptied stalled play pause " +
|
830
|
+
"loadedmetadata loadeddata waiting playing canplay canplaythrough " +
|
831
|
+
"seeking seeked timeupdate ended ratechange durationchange volumechange"
|
832
|
+
};
|
833
|
+
|
834
|
+
Popcorn.Events.Natives = Popcorn.Events.UIEvents + " " +
|
835
|
+
Popcorn.Events.MouseEvents + " " +
|
836
|
+
Popcorn.Events.Events;
|
837
|
+
|
838
|
+
internal.events.apiTypes = [ "UIEvents", "MouseEvents", "Events" ];
|
839
|
+
|
840
|
+
// Privately compile events table at load time
|
841
|
+
(function( events, data ) {
|
842
|
+
|
843
|
+
var apis = internal.events.apiTypes,
|
844
|
+
eventsList = events.Natives.split( /\s+/g ),
|
845
|
+
idx = 0, len = eventsList.length, prop;
|
846
|
+
|
847
|
+
for( ; idx < len; idx++ ) {
|
848
|
+
data.hash[ eventsList[idx] ] = true;
|
849
|
+
}
|
850
|
+
|
851
|
+
apis.forEach(function( val, idx ) {
|
852
|
+
|
853
|
+
data.apis[ val ] = {};
|
854
|
+
|
855
|
+
var apiEvents = events[ val ].split( /\s+/g ),
|
856
|
+
len = apiEvents.length,
|
857
|
+
k = 0;
|
858
|
+
|
859
|
+
for ( ; k < len; k++ ) {
|
860
|
+
data.apis[ val ][ apiEvents[ k ] ] = true;
|
861
|
+
}
|
862
|
+
});
|
863
|
+
})( Popcorn.Events, internal.events );
|
864
|
+
|
865
|
+
Popcorn.events = {
|
866
|
+
|
867
|
+
isNative: function( type ) {
|
868
|
+
return !!internal.events.hash[ type ];
|
869
|
+
},
|
870
|
+
getInterface: function( type ) {
|
871
|
+
|
872
|
+
if ( !Popcorn.events.isNative( type ) ) {
|
873
|
+
return false;
|
874
|
+
}
|
875
|
+
|
876
|
+
var eventApi = internal.events,
|
877
|
+
apis = eventApi.apiTypes,
|
878
|
+
apihash = eventApi.apis,
|
879
|
+
idx = 0, len = apis.length, api, tmp;
|
880
|
+
|
881
|
+
for ( ; idx < len; idx++ ) {
|
882
|
+
tmp = apis[ idx ];
|
883
|
+
|
884
|
+
if ( apihash[ tmp ][ type ] ) {
|
885
|
+
api = tmp;
|
886
|
+
break;
|
887
|
+
}
|
888
|
+
}
|
889
|
+
return api;
|
890
|
+
},
|
891
|
+
// Compile all native events to single array
|
892
|
+
all: Popcorn.Events.Natives.split( /\s+/g ),
|
893
|
+
// Defines all Event handling static functions
|
894
|
+
fn: {
|
895
|
+
trigger: function( type, data ) {
|
896
|
+
|
897
|
+
var eventInterface, evt;
|
898
|
+
// setup checks for custom event system
|
899
|
+
if ( this.data.events[ type ] && Popcorn.sizeOf( this.data.events[ type ] ) ) {
|
900
|
+
|
901
|
+
eventInterface = Popcorn.events.getInterface( type );
|
902
|
+
|
903
|
+
if ( eventInterface ) {
|
904
|
+
|
905
|
+
evt = document.createEvent( eventInterface );
|
906
|
+
evt.initEvent( type, true, true, global, 1 );
|
907
|
+
|
908
|
+
this.media.dispatchEvent( evt );
|
909
|
+
|
910
|
+
return this;
|
911
|
+
}
|
912
|
+
|
913
|
+
// Custom events
|
914
|
+
Popcorn.forEach( this.data.events[ type ], function( obj, key ) {
|
915
|
+
|
916
|
+
obj.call( this, data );
|
917
|
+
|
918
|
+
}, this );
|
919
|
+
|
920
|
+
}
|
921
|
+
|
922
|
+
return this;
|
923
|
+
},
|
924
|
+
listen: function( type, fn ) {
|
925
|
+
|
926
|
+
var self = this,
|
927
|
+
hasEvents = true,
|
928
|
+
eventHook = Popcorn.events.hooks[ type ],
|
929
|
+
origType = type,
|
930
|
+
tmp;
|
931
|
+
|
932
|
+
if ( !this.data.events[ type ] ) {
|
933
|
+
this.data.events[ type ] = {};
|
934
|
+
hasEvents = false;
|
935
|
+
}
|
936
|
+
|
937
|
+
// Check and setup event hooks
|
938
|
+
if ( eventHook ) {
|
939
|
+
|
940
|
+
// Execute hook add method if defined
|
941
|
+
if ( eventHook.add ) {
|
942
|
+
eventHook.add.call( this, {}, fn );
|
943
|
+
}
|
944
|
+
|
945
|
+
// Reassign event type to our piggyback event type if defined
|
946
|
+
if ( eventHook.bind ) {
|
947
|
+
type = eventHook.bind;
|
948
|
+
}
|
949
|
+
|
950
|
+
// Reassign handler if defined
|
951
|
+
if ( eventHook.handler ) {
|
952
|
+
tmp = fn;
|
953
|
+
|
954
|
+
fn = function wrapper( event ) {
|
955
|
+
eventHook.handler.call( self, event, tmp );
|
956
|
+
};
|
957
|
+
}
|
958
|
+
|
959
|
+
// assume the piggy back event is registered
|
960
|
+
hasEvents = true;
|
961
|
+
|
962
|
+
// Setup event registry entry
|
963
|
+
if ( !this.data.events[ type ] ) {
|
964
|
+
this.data.events[ type ] = {};
|
965
|
+
// Toggle if the previous assumption was untrue
|
966
|
+
hasEvents = false;
|
967
|
+
}
|
968
|
+
}
|
969
|
+
|
970
|
+
// Register event and handler
|
971
|
+
this.data.events[ type ][ fn.name || ( fn.toString() + Popcorn.guid() ) ] = fn;
|
972
|
+
|
973
|
+
// only attach one event of any type
|
974
|
+
if ( !hasEvents && Popcorn.events.all.indexOf( type ) > -1 ) {
|
975
|
+
|
976
|
+
this.media.addEventListener( type, function( event ) {
|
977
|
+
|
978
|
+
Popcorn.forEach( self.data.events[ type ], function( obj, key ) {
|
979
|
+
if ( typeof obj === "function" ) {
|
980
|
+
obj.call( self, event );
|
981
|
+
}
|
982
|
+
});
|
983
|
+
|
984
|
+
}, false);
|
985
|
+
}
|
986
|
+
return this;
|
987
|
+
},
|
988
|
+
unlisten: function( type, fn ) {
|
989
|
+
|
990
|
+
if ( this.data.events[ type ] && this.data.events[ type ][ fn ] ) {
|
991
|
+
|
992
|
+
delete this.data.events[ type ][ fn ];
|
993
|
+
|
994
|
+
return this;
|
995
|
+
}
|
996
|
+
|
997
|
+
this.data.events[ type ] = null;
|
998
|
+
|
999
|
+
return this;
|
1000
|
+
}
|
1001
|
+
},
|
1002
|
+
hooks: {
|
1003
|
+
canplayall: {
|
1004
|
+
bind: "canplaythrough",
|
1005
|
+
add: function( event, callback ) {
|
1006
|
+
|
1007
|
+
var state = false;
|
1008
|
+
|
1009
|
+
if ( this.media.readyState ) {
|
1010
|
+
|
1011
|
+
callback.call( this, event );
|
1012
|
+
|
1013
|
+
state = true;
|
1014
|
+
}
|
1015
|
+
|
1016
|
+
this.data.hooks.canplayall = {
|
1017
|
+
fired: state
|
1018
|
+
};
|
1019
|
+
},
|
1020
|
+
// declare special handling instructions
|
1021
|
+
handler: function canplayall( event, callback ) {
|
1022
|
+
|
1023
|
+
if ( !this.data.hooks.canplayall.fired ) {
|
1024
|
+
// trigger original user callback once
|
1025
|
+
callback.call( this, event );
|
1026
|
+
|
1027
|
+
this.data.hooks.canplayall.fired = true;
|
1028
|
+
}
|
1029
|
+
}
|
1030
|
+
}
|
1031
|
+
}
|
1032
|
+
};
|
1033
|
+
|
1034
|
+
// Extend Popcorn.events.fns (listen, unlisten, trigger) to all Popcorn instances
|
1035
|
+
// Extend aliases (on, off, emit)
|
1036
|
+
Popcorn.forEach( [ [ "trigger", "emit" ], [ "listen", "on" ], [ "unlisten", "off" ] ], function( key ) {
|
1037
|
+
Popcorn.p[ key[ 0 ] ] = Popcorn.p[ key[ 1 ] ] = Popcorn.events.fn[ key[ 0 ] ];
|
1038
|
+
});
|
1039
|
+
|
1040
|
+
// Internal Only - Adds track events to the instance object
|
1041
|
+
Popcorn.addTrackEvent = function( obj, track ) {
|
1042
|
+
|
1043
|
+
// Determine if this track has default options set for it
|
1044
|
+
// If so, apply them to the track object
|
1045
|
+
if ( track && track._natives && track._natives.type &&
|
1046
|
+
( obj.options.defaults && obj.options.defaults[ track._natives.type ] ) ) {
|
1047
|
+
|
1048
|
+
track = Popcorn.extend( {}, obj.options.defaults[ track._natives.type ], track );
|
1049
|
+
}
|
1050
|
+
|
1051
|
+
if ( track._natives ) {
|
1052
|
+
// Supports user defined track event id
|
1053
|
+
track._id = !track.id ? Popcorn.guid( track._natives.type ) : track.id;
|
1054
|
+
|
1055
|
+
// Push track event ids into the history
|
1056
|
+
obj.data.history.push( track._id );
|
1057
|
+
}
|
1058
|
+
|
1059
|
+
track.start = Popcorn.util.toSeconds( track.start, obj.options.framerate );
|
1060
|
+
track.end = Popcorn.util.toSeconds( track.end, obj.options.framerate );
|
1061
|
+
|
1062
|
+
// Store this definition in an array sorted by times
|
1063
|
+
var byStart = obj.data.trackEvents.byStart,
|
1064
|
+
byEnd = obj.data.trackEvents.byEnd,
|
1065
|
+
startIndex, endIndex,
|
1066
|
+
currentTime;
|
1067
|
+
|
1068
|
+
for ( startIndex = byStart.length - 1; startIndex >= 0; startIndex-- ) {
|
1069
|
+
|
1070
|
+
if ( track.start >= byStart[ startIndex ].start ) {
|
1071
|
+
byStart.splice( startIndex + 1, 0, track );
|
1072
|
+
break;
|
1073
|
+
}
|
1074
|
+
}
|
1075
|
+
|
1076
|
+
for ( endIndex = byEnd.length - 1; endIndex >= 0; endIndex-- ) {
|
1077
|
+
|
1078
|
+
if ( track.end > byEnd[ endIndex ].end ) {
|
1079
|
+
byEnd.splice( endIndex + 1, 0, track );
|
1080
|
+
break;
|
1081
|
+
}
|
1082
|
+
}
|
1083
|
+
|
1084
|
+
// Display track event immediately if it's enabled and current
|
1085
|
+
if ( track._natives &&
|
1086
|
+
( !!Popcorn.registryByName[ track._natives.type ] || !!obj[ track._natives.type ] ) ) {
|
1087
|
+
|
1088
|
+
currentTime = obj.media.currentTime;
|
1089
|
+
if ( track.end > currentTime &&
|
1090
|
+
track.start <= currentTime &&
|
1091
|
+
obj.data.disabled.indexOf( track._natives.type ) === -1 ) {
|
1092
|
+
|
1093
|
+
track._running = true;
|
1094
|
+
track._natives.start.call( obj, null, track );
|
1095
|
+
|
1096
|
+
if ( obj.options.frameAnimation &&
|
1097
|
+
track._natives.frame ) {
|
1098
|
+
|
1099
|
+
obj.data.trackEvents.animating.push( track );
|
1100
|
+
track._natives.frame.call( obj, null, track, currentTime );
|
1101
|
+
}
|
1102
|
+
}
|
1103
|
+
}
|
1104
|
+
|
1105
|
+
// update startIndex and endIndex
|
1106
|
+
if ( startIndex <= obj.data.trackEvents.startIndex &&
|
1107
|
+
track.start <= obj.data.trackEvents.previousUpdateTime ) {
|
1108
|
+
|
1109
|
+
obj.data.trackEvents.startIndex++;
|
1110
|
+
}
|
1111
|
+
|
1112
|
+
if ( endIndex <= obj.data.trackEvents.endIndex &&
|
1113
|
+
track.end < obj.data.trackEvents.previousUpdateTime ) {
|
1114
|
+
|
1115
|
+
obj.data.trackEvents.endIndex++;
|
1116
|
+
}
|
1117
|
+
|
1118
|
+
this.timeUpdate( obj, null, true );
|
1119
|
+
|
1120
|
+
// Store references to user added trackevents in ref table
|
1121
|
+
if ( track._id ) {
|
1122
|
+
Popcorn.addTrackEvent.ref( obj, track );
|
1123
|
+
}
|
1124
|
+
};
|
1125
|
+
|
1126
|
+
// Internal Only - Adds track event references to the instance object's trackRefs hash table
|
1127
|
+
Popcorn.addTrackEvent.ref = function( obj, track ) {
|
1128
|
+
obj.data.trackRefs[ track._id ] = track;
|
1129
|
+
|
1130
|
+
return obj;
|
1131
|
+
};
|
1132
|
+
|
1133
|
+
Popcorn.removeTrackEvent = function( obj, removeId ) {
|
1134
|
+
|
1135
|
+
var start, end, animate,
|
1136
|
+
historyLen = obj.data.history.length,
|
1137
|
+
length = obj.data.trackEvents.byStart.length,
|
1138
|
+
index = 0,
|
1139
|
+
indexWasAt = 0,
|
1140
|
+
byStart = [],
|
1141
|
+
byEnd = [],
|
1142
|
+
animating = [],
|
1143
|
+
history = [];
|
1144
|
+
|
1145
|
+
while ( --length > -1 ) {
|
1146
|
+
start = obj.data.trackEvents.byStart[ index ];
|
1147
|
+
end = obj.data.trackEvents.byEnd[ index ];
|
1148
|
+
|
1149
|
+
// Padding events will not have _id properties.
|
1150
|
+
// These should be safely pushed onto the front and back of the
|
1151
|
+
// track event array
|
1152
|
+
if ( !start._id ) {
|
1153
|
+
byStart.push( start );
|
1154
|
+
byEnd.push( end );
|
1155
|
+
}
|
1156
|
+
|
1157
|
+
// Filter for user track events (vs system track events)
|
1158
|
+
if ( start._id ) {
|
1159
|
+
|
1160
|
+
// If not a matching start event for removal
|
1161
|
+
if ( start._id !== removeId ) {
|
1162
|
+
byStart.push( start );
|
1163
|
+
}
|
1164
|
+
|
1165
|
+
// If not a matching end event for removal
|
1166
|
+
if ( end._id !== removeId ) {
|
1167
|
+
byEnd.push( end );
|
1168
|
+
}
|
1169
|
+
|
1170
|
+
// If the _id is matched, capture the current index
|
1171
|
+
if ( start._id === removeId ) {
|
1172
|
+
indexWasAt = index;
|
1173
|
+
|
1174
|
+
// If a _teardown function was defined,
|
1175
|
+
// enforce for track event removals
|
1176
|
+
if ( start._natives._teardown ) {
|
1177
|
+
start._natives._teardown.call( obj, start );
|
1178
|
+
}
|
1179
|
+
}
|
1180
|
+
}
|
1181
|
+
// Increment the track index
|
1182
|
+
index++;
|
1183
|
+
}
|
1184
|
+
|
1185
|
+
// Reset length to be used by the condition below to determine
|
1186
|
+
// if animating track events should also be filtered for removal.
|
1187
|
+
// Reset index below to be used by the reverse while as an
|
1188
|
+
// incrementing counter
|
1189
|
+
length = obj.data.trackEvents.animating.length;
|
1190
|
+
index = 0;
|
1191
|
+
|
1192
|
+
if ( length ) {
|
1193
|
+
while ( --length > -1 ) {
|
1194
|
+
animate = obj.data.trackEvents.animating[ index ];
|
1195
|
+
|
1196
|
+
// Padding events will not have _id properties.
|
1197
|
+
// These should be safely pushed onto the front and back of the
|
1198
|
+
// track event array
|
1199
|
+
if ( !animate._id ) {
|
1200
|
+
animating.push( animate );
|
1201
|
+
}
|
1202
|
+
|
1203
|
+
// If not a matching animate event for removal
|
1204
|
+
if ( animate._id && animate._id !== removeId ) {
|
1205
|
+
animating.push( animate );
|
1206
|
+
}
|
1207
|
+
// Increment the track index
|
1208
|
+
index++;
|
1209
|
+
}
|
1210
|
+
}
|
1211
|
+
|
1212
|
+
// Update
|
1213
|
+
if ( indexWasAt <= obj.data.trackEvents.startIndex ) {
|
1214
|
+
obj.data.trackEvents.startIndex--;
|
1215
|
+
}
|
1216
|
+
|
1217
|
+
if ( indexWasAt <= obj.data.trackEvents.endIndex ) {
|
1218
|
+
obj.data.trackEvents.endIndex--;
|
1219
|
+
}
|
1220
|
+
|
1221
|
+
obj.data.trackEvents.byStart = byStart;
|
1222
|
+
obj.data.trackEvents.byEnd = byEnd;
|
1223
|
+
obj.data.trackEvents.animating = animating;
|
1224
|
+
|
1225
|
+
for ( var i = 0; i < historyLen; i++ ) {
|
1226
|
+
if ( obj.data.history[ i ] !== removeId ) {
|
1227
|
+
history.push( obj.data.history[ i ] );
|
1228
|
+
}
|
1229
|
+
}
|
1230
|
+
|
1231
|
+
// Update ordered history array
|
1232
|
+
obj.data.history = history;
|
1233
|
+
|
1234
|
+
// Update track event references
|
1235
|
+
Popcorn.removeTrackEvent.ref( obj, removeId );
|
1236
|
+
};
|
1237
|
+
|
1238
|
+
// Internal Only - Removes track event references from instance object's trackRefs hash table
|
1239
|
+
Popcorn.removeTrackEvent.ref = function( obj, removeId ) {
|
1240
|
+
delete obj.data.trackRefs[ removeId ];
|
1241
|
+
|
1242
|
+
return obj;
|
1243
|
+
};
|
1244
|
+
|
1245
|
+
// Return an array of track events bound to this instance object
|
1246
|
+
Popcorn.getTrackEvents = function( obj ) {
|
1247
|
+
|
1248
|
+
var trackevents = [],
|
1249
|
+
refs = obj.data.trackEvents.byStart,
|
1250
|
+
length = refs.length,
|
1251
|
+
idx = 0,
|
1252
|
+
ref;
|
1253
|
+
|
1254
|
+
for ( ; idx < length; idx++ ) {
|
1255
|
+
ref = refs[ idx ];
|
1256
|
+
// Return only user attributed track event references
|
1257
|
+
if ( ref._id ) {
|
1258
|
+
trackevents.push( ref );
|
1259
|
+
}
|
1260
|
+
}
|
1261
|
+
|
1262
|
+
return trackevents;
|
1263
|
+
};
|
1264
|
+
|
1265
|
+
// Internal Only - Returns an instance object's trackRefs hash table
|
1266
|
+
Popcorn.getTrackEvents.ref = function( obj ) {
|
1267
|
+
return obj.data.trackRefs;
|
1268
|
+
};
|
1269
|
+
|
1270
|
+
// Return a single track event bound to this instance object
|
1271
|
+
Popcorn.getTrackEvent = function( obj, trackId ) {
|
1272
|
+
return obj.data.trackRefs[ trackId ];
|
1273
|
+
};
|
1274
|
+
|
1275
|
+
// Internal Only - Returns an instance object's track reference by track id
|
1276
|
+
Popcorn.getTrackEvent.ref = function( obj, trackId ) {
|
1277
|
+
return obj.data.trackRefs[ trackId ];
|
1278
|
+
};
|
1279
|
+
|
1280
|
+
Popcorn.getLastTrackEventId = function( obj ) {
|
1281
|
+
return obj.data.history[ obj.data.history.length - 1 ];
|
1282
|
+
};
|
1283
|
+
|
1284
|
+
Popcorn.timeUpdate = function( obj, event ) {
|
1285
|
+
|
1286
|
+
var currentTime = obj.media.currentTime,
|
1287
|
+
previousTime = obj.data.trackEvents.previousUpdateTime,
|
1288
|
+
tracks = obj.data.trackEvents,
|
1289
|
+
animating = tracks.animating,
|
1290
|
+
end = tracks.endIndex,
|
1291
|
+
start = tracks.startIndex,
|
1292
|
+
animIndex = 0,
|
1293
|
+
byStartLen = tracks.byStart.length,
|
1294
|
+
byEndLen = tracks.byEnd.length,
|
1295
|
+
registryByName = Popcorn.registryByName,
|
1296
|
+
trackstart = "trackstart",
|
1297
|
+
trackend = "trackend",
|
1298
|
+
|
1299
|
+
byEnd, byStart, byAnimate, natives, type;
|
1300
|
+
|
1301
|
+
// Playbar advancing
|
1302
|
+
if ( previousTime <= currentTime ) {
|
1303
|
+
|
1304
|
+
while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end <= currentTime ) {
|
1305
|
+
|
1306
|
+
byEnd = tracks.byEnd[ end ];
|
1307
|
+
natives = byEnd._natives;
|
1308
|
+
type = natives && natives.type;
|
1309
|
+
|
1310
|
+
// If plugin does not exist on this instance, remove it
|
1311
|
+
if ( !natives ||
|
1312
|
+
( !!registryByName[ type ] ||
|
1313
|
+
!!obj[ type ] ) ) {
|
1314
|
+
|
1315
|
+
if ( byEnd._running === true ) {
|
1316
|
+
byEnd._running = false;
|
1317
|
+
natives.end.call( obj, event, byEnd );
|
1318
|
+
|
1319
|
+
obj.emit( trackend,
|
1320
|
+
Popcorn.extend({}, byEnd, {
|
1321
|
+
plugin: type,
|
1322
|
+
type: trackend
|
1323
|
+
})
|
1324
|
+
);
|
1325
|
+
}
|
1326
|
+
|
1327
|
+
end++;
|
1328
|
+
} else {
|
1329
|
+
// remove track event
|
1330
|
+
Popcorn.removeTrackEvent( obj, byEnd._id );
|
1331
|
+
return;
|
1332
|
+
}
|
1333
|
+
}
|
1334
|
+
|
1335
|
+
while ( tracks.byStart[ start ] && tracks.byStart[ start ].start <= currentTime ) {
|
1336
|
+
|
1337
|
+
byStart = tracks.byStart[ start ];
|
1338
|
+
natives = byStart._natives;
|
1339
|
+
type = natives && natives.type;
|
1340
|
+
|
1341
|
+
// If plugin does not exist on this instance, remove it
|
1342
|
+
if ( !natives ||
|
1343
|
+
( !!registryByName[ type ] ||
|
1344
|
+
!!obj[ type ] ) ) {
|
1345
|
+
|
1346
|
+
if ( byStart.end > currentTime &&
|
1347
|
+
byStart._running === false &&
|
1348
|
+
obj.data.disabled.indexOf( type ) === -1 ) {
|
1349
|
+
|
1350
|
+
byStart._running = true;
|
1351
|
+
natives.start.call( obj, event, byStart );
|
1352
|
+
|
1353
|
+
obj.emit( trackstart,
|
1354
|
+
Popcorn.extend({}, byStart, {
|
1355
|
+
plugin: type,
|
1356
|
+
type: trackstart
|
1357
|
+
})
|
1358
|
+
);
|
1359
|
+
|
1360
|
+
// If the `frameAnimation` option is used,
|
1361
|
+
// push the current byStart object into the `animating` cue
|
1362
|
+
if ( obj.options.frameAnimation &&
|
1363
|
+
( byStart && byStart._running && byStart._natives.frame ) ) {
|
1364
|
+
|
1365
|
+
animating.push( byStart );
|
1366
|
+
}
|
1367
|
+
}
|
1368
|
+
start++;
|
1369
|
+
} else {
|
1370
|
+
// remove track event
|
1371
|
+
Popcorn.removeTrackEvent( obj, byStart._id );
|
1372
|
+
return;
|
1373
|
+
}
|
1374
|
+
}
|
1375
|
+
|
1376
|
+
// If the `frameAnimation` option is used, iterate the animating track
|
1377
|
+
// and execute the `frame` callback
|
1378
|
+
if ( obj.options.frameAnimation ) {
|
1379
|
+
while ( animIndex < animating.length ) {
|
1380
|
+
|
1381
|
+
byAnimate = animating[ animIndex ];
|
1382
|
+
|
1383
|
+
if ( !byAnimate._running ) {
|
1384
|
+
animating.splice( animIndex, 1 );
|
1385
|
+
} else {
|
1386
|
+
byAnimate._natives.frame.call( obj, event, byAnimate, currentTime );
|
1387
|
+
animIndex++;
|
1388
|
+
}
|
1389
|
+
}
|
1390
|
+
}
|
1391
|
+
|
1392
|
+
// Playbar receding
|
1393
|
+
} else if ( previousTime > currentTime ) {
|
1394
|
+
|
1395
|
+
while ( tracks.byStart[ start ] && tracks.byStart[ start ].start > currentTime ) {
|
1396
|
+
|
1397
|
+
byStart = tracks.byStart[ start ];
|
1398
|
+
natives = byStart._natives;
|
1399
|
+
type = natives && natives.type;
|
1400
|
+
|
1401
|
+
// if plugin does not exist on this instance, remove it
|
1402
|
+
if ( !natives ||
|
1403
|
+
( !!registryByName[ type ] ||
|
1404
|
+
!!obj[ type ] ) ) {
|
1405
|
+
|
1406
|
+
if ( byStart._running === true ) {
|
1407
|
+
byStart._running = false;
|
1408
|
+
natives.end.call( obj, event, byStart );
|
1409
|
+
|
1410
|
+
obj.emit( trackend,
|
1411
|
+
Popcorn.extend({}, byEnd, {
|
1412
|
+
plugin: type,
|
1413
|
+
type: trackend
|
1414
|
+
})
|
1415
|
+
);
|
1416
|
+
}
|
1417
|
+
start--;
|
1418
|
+
} else {
|
1419
|
+
// remove track event
|
1420
|
+
Popcorn.removeTrackEvent( obj, byStart._id );
|
1421
|
+
return;
|
1422
|
+
}
|
1423
|
+
}
|
1424
|
+
|
1425
|
+
while ( tracks.byEnd[ end ] && tracks.byEnd[ end ].end > currentTime ) {
|
1426
|
+
|
1427
|
+
byEnd = tracks.byEnd[ end ];
|
1428
|
+
natives = byEnd._natives;
|
1429
|
+
type = natives && natives.type;
|
1430
|
+
|
1431
|
+
// if plugin does not exist on this instance, remove it
|
1432
|
+
if ( !natives ||
|
1433
|
+
( !!registryByName[ type ] ||
|
1434
|
+
!!obj[ type ] ) ) {
|
1435
|
+
|
1436
|
+
if ( byEnd.start <= currentTime &&
|
1437
|
+
byEnd._running === false &&
|
1438
|
+
obj.data.disabled.indexOf( type ) === -1 ) {
|
1439
|
+
|
1440
|
+
byEnd._running = true;
|
1441
|
+
natives.start.call( obj, event, byEnd );
|
1442
|
+
|
1443
|
+
obj.emit( trackstart,
|
1444
|
+
Popcorn.extend({}, byStart, {
|
1445
|
+
plugin: type,
|
1446
|
+
type: trackstart
|
1447
|
+
})
|
1448
|
+
);
|
1449
|
+
// If the `frameAnimation` option is used,
|
1450
|
+
// push the current byEnd object into the `animating` cue
|
1451
|
+
if ( obj.options.frameAnimation &&
|
1452
|
+
( byEnd && byEnd._running && byEnd._natives.frame ) ) {
|
1453
|
+
|
1454
|
+
animating.push( byEnd );
|
1455
|
+
}
|
1456
|
+
}
|
1457
|
+
end--;
|
1458
|
+
} else {
|
1459
|
+
// remove track event
|
1460
|
+
Popcorn.removeTrackEvent( obj, byEnd._id );
|
1461
|
+
return;
|
1462
|
+
}
|
1463
|
+
}
|
1464
|
+
|
1465
|
+
// If the `frameAnimation` option is used, iterate the animating track
|
1466
|
+
// and execute the `frame` callback
|
1467
|
+
if ( obj.options.frameAnimation ) {
|
1468
|
+
while ( animIndex < animating.length ) {
|
1469
|
+
|
1470
|
+
byAnimate = animating[ animIndex ];
|
1471
|
+
|
1472
|
+
if ( !byAnimate._running ) {
|
1473
|
+
animating.splice( animIndex, 1 );
|
1474
|
+
} else {
|
1475
|
+
byAnimate._natives.frame.call( obj, event, byAnimate, currentTime );
|
1476
|
+
animIndex++;
|
1477
|
+
}
|
1478
|
+
}
|
1479
|
+
}
|
1480
|
+
// time bar is not moving ( video is paused )
|
1481
|
+
}
|
1482
|
+
|
1483
|
+
tracks.endIndex = end;
|
1484
|
+
tracks.startIndex = start;
|
1485
|
+
tracks.previousUpdateTime = currentTime;
|
1486
|
+
|
1487
|
+
//enforce index integrity if trackRemoved
|
1488
|
+
tracks.byStart.length < byStartLen && tracks.startIndex--;
|
1489
|
+
tracks.byEnd.length < byEndLen && tracks.endIndex--;
|
1490
|
+
|
1491
|
+
};
|
1492
|
+
|
1493
|
+
// Map and Extend TrackEvent functions to all Popcorn instances
|
1494
|
+
Popcorn.extend( Popcorn.p, {
|
1495
|
+
|
1496
|
+
getTrackEvents: function() {
|
1497
|
+
return Popcorn.getTrackEvents.call( null, this );
|
1498
|
+
},
|
1499
|
+
|
1500
|
+
getTrackEvent: function( id ) {
|
1501
|
+
return Popcorn.getTrackEvent.call( null, this, id );
|
1502
|
+
},
|
1503
|
+
|
1504
|
+
getLastTrackEventId: function() {
|
1505
|
+
return Popcorn.getLastTrackEventId.call( null, this );
|
1506
|
+
},
|
1507
|
+
|
1508
|
+
removeTrackEvent: function( id ) {
|
1509
|
+
|
1510
|
+
Popcorn.removeTrackEvent.call( null, this, id );
|
1511
|
+
return this;
|
1512
|
+
},
|
1513
|
+
|
1514
|
+
removePlugin: function( name ) {
|
1515
|
+
Popcorn.removePlugin.call( null, this, name );
|
1516
|
+
return this;
|
1517
|
+
},
|
1518
|
+
|
1519
|
+
timeUpdate: function( event ) {
|
1520
|
+
Popcorn.timeUpdate.call( null, this, event );
|
1521
|
+
return this;
|
1522
|
+
},
|
1523
|
+
|
1524
|
+
destroy: function() {
|
1525
|
+
Popcorn.destroy.call( null, this );
|
1526
|
+
return this;
|
1527
|
+
}
|
1528
|
+
});
|
1529
|
+
|
1530
|
+
// Plugin manifests
|
1531
|
+
Popcorn.manifest = {};
|
1532
|
+
// Plugins are registered
|
1533
|
+
Popcorn.registry = [];
|
1534
|
+
Popcorn.registryByName = {};
|
1535
|
+
// An interface for extending Popcorn
|
1536
|
+
// with plugin functionality
|
1537
|
+
Popcorn.plugin = function( name, definition, manifest ) {
|
1538
|
+
|
1539
|
+
if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
|
1540
|
+
Popcorn.error( "'" + name + "' is a protected function name" );
|
1541
|
+
return;
|
1542
|
+
}
|
1543
|
+
|
1544
|
+
// Provides some sugar, but ultimately extends
|
1545
|
+
// the definition into Popcorn.p
|
1546
|
+
var reserved = [ "start", "end" ],
|
1547
|
+
plugin = {},
|
1548
|
+
setup,
|
1549
|
+
isfn = typeof definition === "function",
|
1550
|
+
methods = [ "_setup", "_teardown", "start", "end", "frame" ];
|
1551
|
+
|
1552
|
+
// combines calls of two function calls into one
|
1553
|
+
var combineFn = function( first, second ) {
|
1554
|
+
|
1555
|
+
first = first || Popcorn.nop;
|
1556
|
+
second = second || Popcorn.nop;
|
1557
|
+
|
1558
|
+
return function() {
|
1559
|
+
first.apply( this, arguments );
|
1560
|
+
second.apply( this, arguments );
|
1561
|
+
};
|
1562
|
+
};
|
1563
|
+
|
1564
|
+
// If `manifest` arg is undefined, check for manifest within the `definition` object
|
1565
|
+
// If no `definition.manifest`, an empty object is a sufficient fallback
|
1566
|
+
Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
|
1567
|
+
|
1568
|
+
// apply safe, and empty default functions
|
1569
|
+
methods.forEach(function( method ) {
|
1570
|
+
definition[ method ] = safeTry( definition[ method ] || Popcorn.nop, name );
|
1571
|
+
});
|
1572
|
+
|
1573
|
+
var pluginFn = function( setup, options ) {
|
1574
|
+
|
1575
|
+
if ( !options ) {
|
1576
|
+
return this;
|
1577
|
+
}
|
1578
|
+
|
1579
|
+
// Storing the plugin natives
|
1580
|
+
var natives = options._natives = {},
|
1581
|
+
compose = "",
|
1582
|
+
originalOpts, manifestOpts;
|
1583
|
+
|
1584
|
+
Popcorn.extend( natives, setup );
|
1585
|
+
|
1586
|
+
options._natives.type = name;
|
1587
|
+
options._running = false;
|
1588
|
+
|
1589
|
+
natives.start = natives.start || natives[ "in" ];
|
1590
|
+
natives.end = natives.end || natives[ "out" ];
|
1591
|
+
|
1592
|
+
// extend teardown to always call end if running
|
1593
|
+
natives._teardown = combineFn(function() {
|
1594
|
+
|
1595
|
+
var args = slice.call( arguments );
|
1596
|
+
|
1597
|
+
// end function signature is not the same as teardown,
|
1598
|
+
// put null on the front of arguments for the event parameter
|
1599
|
+
args.unshift( null );
|
1600
|
+
|
1601
|
+
// only call end if event is running
|
1602
|
+
args[ 1 ]._running && natives.end.apply( this, args );
|
1603
|
+
}, natives._teardown );
|
1604
|
+
|
1605
|
+
// default to an empty string if no effect exists
|
1606
|
+
// split string into an array of effects
|
1607
|
+
options.compose = options.compose && options.compose.split( " " ) || [];
|
1608
|
+
options.effect = options.effect && options.effect.split( " " ) || [];
|
1609
|
+
|
1610
|
+
// join the two arrays together
|
1611
|
+
options.compose = options.compose.concat( options.effect );
|
1612
|
+
|
1613
|
+
options.compose.forEach(function( composeOption ) {
|
1614
|
+
|
1615
|
+
// if the requested compose is garbage, throw it away
|
1616
|
+
compose = Popcorn.compositions[ composeOption ] || {};
|
1617
|
+
|
1618
|
+
// extends previous functions with compose function
|
1619
|
+
methods.forEach(function( method ) {
|
1620
|
+
natives[ method ] = combineFn( natives[ method ], compose[ method ] );
|
1621
|
+
});
|
1622
|
+
});
|
1623
|
+
|
1624
|
+
// Ensure a manifest object, an empty object is a sufficient fallback
|
1625
|
+
options._natives.manifest = manifest;
|
1626
|
+
|
1627
|
+
// Checks for expected properties
|
1628
|
+
if ( !( "start" in options ) ) {
|
1629
|
+
options.start = options[ "in" ] || 0;
|
1630
|
+
}
|
1631
|
+
|
1632
|
+
if ( !options.end && options.end !== 0 ) {
|
1633
|
+
options.end = options[ "out" ] || Number.MAX_VALUE;
|
1634
|
+
}
|
1635
|
+
|
1636
|
+
// Use hasOwn to detect non-inherited toString, since all
|
1637
|
+
// objects will receive a toString - its otherwise undetectable
|
1638
|
+
if ( !hasOwn.call( options, "toString" ) ) {
|
1639
|
+
options.toString = function() {
|
1640
|
+
var props = [
|
1641
|
+
"start: " + options.start,
|
1642
|
+
"end: " + options.end,
|
1643
|
+
"id: " + (options.id || options._id)
|
1644
|
+
];
|
1645
|
+
|
1646
|
+
// Matches null and undefined, allows: false, 0, "" and truthy
|
1647
|
+
if ( options.target != null ) {
|
1648
|
+
props.push( "target: " + options.target );
|
1649
|
+
}
|
1650
|
+
|
1651
|
+
return name + " ( " + props.join(", ") + " )";
|
1652
|
+
};
|
1653
|
+
}
|
1654
|
+
|
1655
|
+
// Resolves 239, 241, 242
|
1656
|
+
if ( !options.target ) {
|
1657
|
+
|
1658
|
+
// Sometimes the manifest may be missing entirely
|
1659
|
+
// or it has an options object that doesn't have a `target` property
|
1660
|
+
manifestOpts = "options" in manifest && manifest.options;
|
1661
|
+
|
1662
|
+
options.target = manifestOpts && "target" in manifestOpts && manifestOpts.target;
|
1663
|
+
}
|
1664
|
+
|
1665
|
+
// Trigger _setup method if exists
|
1666
|
+
options._natives._setup && options._natives._setup.call( this, options );
|
1667
|
+
|
1668
|
+
// Create new track event for this instance
|
1669
|
+
Popcorn.addTrackEvent( this, Popcorn.extend( options, options ) );
|
1670
|
+
|
1671
|
+
// Future support for plugin event definitions
|
1672
|
+
// for all of the native events
|
1673
|
+
Popcorn.forEach( setup, function( callback, type ) {
|
1674
|
+
|
1675
|
+
if ( type !== "type" ) {
|
1676
|
+
|
1677
|
+
if ( reserved.indexOf( type ) === -1 ) {
|
1678
|
+
|
1679
|
+
this.on( type, callback );
|
1680
|
+
}
|
1681
|
+
}
|
1682
|
+
|
1683
|
+
}, this );
|
1684
|
+
|
1685
|
+
return this;
|
1686
|
+
};
|
1687
|
+
|
1688
|
+
// Extend Popcorn.p with new named definition
|
1689
|
+
// Assign new named definition
|
1690
|
+
Popcorn.p[ name ] = plugin[ name ] = function( options ) {
|
1691
|
+
|
1692
|
+
// Merge with defaults if they exist, make sure per call is prioritized
|
1693
|
+
var defaults = ( this.options.defaults && this.options.defaults[ name ] ) || {},
|
1694
|
+
mergedSetupOpts = Popcorn.extend( {}, defaults, options );
|
1695
|
+
|
1696
|
+
return pluginFn.call( this, isfn ? definition.call( this, mergedSetupOpts ) : definition,
|
1697
|
+
mergedSetupOpts );
|
1698
|
+
};
|
1699
|
+
|
1700
|
+
// Push into the registry
|
1701
|
+
var entry = {
|
1702
|
+
fn: plugin[ name ],
|
1703
|
+
definition: definition,
|
1704
|
+
base: definition,
|
1705
|
+
parents: [],
|
1706
|
+
name: name
|
1707
|
+
};
|
1708
|
+
Popcorn.registry.push(
|
1709
|
+
Popcorn.extend( plugin, entry, {
|
1710
|
+
type: name
|
1711
|
+
})
|
1712
|
+
);
|
1713
|
+
Popcorn.registryByName[ name ] = entry;
|
1714
|
+
|
1715
|
+
return plugin;
|
1716
|
+
};
|
1717
|
+
|
1718
|
+
// Storage for plugin function errors
|
1719
|
+
Popcorn.plugin.errors = [];
|
1720
|
+
|
1721
|
+
// Returns wrapped plugin function
|
1722
|
+
function safeTry( fn, pluginName ) {
|
1723
|
+
return function() {
|
1724
|
+
|
1725
|
+
// When Popcorn.plugin.debug is true, do not suppress errors
|
1726
|
+
if ( Popcorn.plugin.debug ) {
|
1727
|
+
return fn.apply( this, arguments );
|
1728
|
+
}
|
1729
|
+
|
1730
|
+
try {
|
1731
|
+
return fn.apply( this, arguments );
|
1732
|
+
} catch ( ex ) {
|
1733
|
+
|
1734
|
+
// Push plugin function errors into logging queue
|
1735
|
+
Popcorn.plugin.errors.push({
|
1736
|
+
plugin: pluginName,
|
1737
|
+
thrown: ex,
|
1738
|
+
source: fn.toString()
|
1739
|
+
});
|
1740
|
+
|
1741
|
+
// Trigger an error that the instance can listen for
|
1742
|
+
// and react to
|
1743
|
+
this.emit( "error", Popcorn.plugin.errors );
|
1744
|
+
}
|
1745
|
+
};
|
1746
|
+
}
|
1747
|
+
|
1748
|
+
// Debug-mode flag for plugin development
|
1749
|
+
Popcorn.plugin.debug = false;
|
1750
|
+
|
1751
|
+
// removePlugin( type ) removes all tracks of that from all instances of popcorn
|
1752
|
+
// removePlugin( obj, type ) removes all tracks of type from obj, where obj is a single instance of popcorn
|
1753
|
+
Popcorn.removePlugin = function( obj, name ) {
|
1754
|
+
|
1755
|
+
// Check if we are removing plugin from an instance or from all of Popcorn
|
1756
|
+
if ( !name ) {
|
1757
|
+
|
1758
|
+
// Fix the order
|
1759
|
+
name = obj;
|
1760
|
+
obj = Popcorn.p;
|
1761
|
+
|
1762
|
+
if ( Popcorn.protect.natives.indexOf( name.toLowerCase() ) >= 0 ) {
|
1763
|
+
Popcorn.error( "'" + name + "' is a protected function name" );
|
1764
|
+
return;
|
1765
|
+
}
|
1766
|
+
|
1767
|
+
var registryLen = Popcorn.registry.length,
|
1768
|
+
registryIdx;
|
1769
|
+
|
1770
|
+
// remove plugin reference from registry
|
1771
|
+
for ( registryIdx = 0; registryIdx < registryLen; registryIdx++ ) {
|
1772
|
+
if ( Popcorn.registry[ registryIdx ].name === name ) {
|
1773
|
+
Popcorn.registry.splice( registryIdx, 1 );
|
1774
|
+
delete Popcorn.registryByName[ name ];
|
1775
|
+
delete Popcorn.manifest[ name ];
|
1776
|
+
|
1777
|
+
// delete the plugin
|
1778
|
+
delete obj[ name ];
|
1779
|
+
|
1780
|
+
// plugin found and removed, stop checking, we are done
|
1781
|
+
return;
|
1782
|
+
}
|
1783
|
+
}
|
1784
|
+
|
1785
|
+
}
|
1786
|
+
|
1787
|
+
var byStart = obj.data.trackEvents.byStart,
|
1788
|
+
byEnd = obj.data.trackEvents.byEnd,
|
1789
|
+
animating = obj.data.trackEvents.animating,
|
1790
|
+
idx, sl;
|
1791
|
+
|
1792
|
+
// remove all trackEvents
|
1793
|
+
for ( idx = 0, sl = byStart.length; idx < sl; idx++ ) {
|
1794
|
+
|
1795
|
+
if ( byStart[ idx ] && byStart[ idx ]._natives && byStart[ idx ]._natives.type === name ) {
|
1796
|
+
|
1797
|
+
byStart[ idx ]._natives._teardown && byStart[ idx ]._natives._teardown.call( obj, byStart[ idx ] );
|
1798
|
+
|
1799
|
+
byStart.splice( idx, 1 );
|
1800
|
+
|
1801
|
+
// update for loop if something removed, but keep checking
|
1802
|
+
idx--; sl--;
|
1803
|
+
if ( obj.data.trackEvents.startIndex <= idx ) {
|
1804
|
+
obj.data.trackEvents.startIndex--;
|
1805
|
+
obj.data.trackEvents.endIndex--;
|
1806
|
+
}
|
1807
|
+
}
|
1808
|
+
|
1809
|
+
// clean any remaining references in the end index
|
1810
|
+
// we do this seperate from the above check because they might not be in the same order
|
1811
|
+
if ( byEnd[ idx ] && byEnd[ idx ]._natives && byEnd[ idx ]._natives.type === name ) {
|
1812
|
+
|
1813
|
+
byEnd.splice( idx, 1 );
|
1814
|
+
}
|
1815
|
+
}
|
1816
|
+
|
1817
|
+
//remove all animating events
|
1818
|
+
for ( idx = 0, sl = animating.length; idx < sl; idx++ ) {
|
1819
|
+
|
1820
|
+
if ( animating[ idx ] && animating[ idx ]._natives && animating[ idx ]._natives.type === name ) {
|
1821
|
+
|
1822
|
+
animating.splice( idx, 1 );
|
1823
|
+
|
1824
|
+
// update for loop if something removed, but keep checking
|
1825
|
+
idx--; sl--;
|
1826
|
+
}
|
1827
|
+
}
|
1828
|
+
|
1829
|
+
};
|
1830
|
+
|
1831
|
+
Popcorn.compositions = {};
|
1832
|
+
|
1833
|
+
// Plugin inheritance
|
1834
|
+
Popcorn.compose = function( name, definition, manifest ) {
|
1835
|
+
|
1836
|
+
// If `manifest` arg is undefined, check for manifest within the `definition` object
|
1837
|
+
// If no `definition.manifest`, an empty object is a sufficient fallback
|
1838
|
+
Popcorn.manifest[ name ] = manifest = manifest || definition.manifest || {};
|
1839
|
+
|
1840
|
+
// register the effect by name
|
1841
|
+
Popcorn.compositions[ name ] = definition;
|
1842
|
+
};
|
1843
|
+
|
1844
|
+
Popcorn.plugin.effect = Popcorn.effect = Popcorn.compose;
|
1845
|
+
|
1846
|
+
// Cache references to reused RegExps
|
1847
|
+
var rparams = /\?/,
|
1848
|
+
// XHR Setup object
|
1849
|
+
setup = {
|
1850
|
+
url: "",
|
1851
|
+
data: "",
|
1852
|
+
dataType: "",
|
1853
|
+
success: Popcorn.nop,
|
1854
|
+
type: "GET",
|
1855
|
+
async: true,
|
1856
|
+
xhr: function() {
|
1857
|
+
return new global.XMLHttpRequest();
|
1858
|
+
}
|
1859
|
+
};
|
1860
|
+
|
1861
|
+
Popcorn.xhr = function( options ) {
|
1862
|
+
|
1863
|
+
options.dataType = options.dataType && options.dataType.toLowerCase() || null;
|
1864
|
+
|
1865
|
+
if ( options.dataType &&
|
1866
|
+
( options.dataType === "jsonp" || options.dataType === "script" ) ) {
|
1867
|
+
|
1868
|
+
Popcorn.xhr.getJSONP(
|
1869
|
+
options.url,
|
1870
|
+
options.success,
|
1871
|
+
options.dataType === "script"
|
1872
|
+
);
|
1873
|
+
return;
|
1874
|
+
}
|
1875
|
+
|
1876
|
+
var settings = Popcorn.extend( {}, setup, options );
|
1877
|
+
|
1878
|
+
// Create new XMLHttpRequest object
|
1879
|
+
settings.ajax = settings.xhr();
|
1880
|
+
|
1881
|
+
if ( settings.ajax ) {
|
1882
|
+
|
1883
|
+
if ( settings.type === "GET" && settings.data ) {
|
1884
|
+
|
1885
|
+
// append query string
|
1886
|
+
settings.url += ( rparams.test( settings.url ) ? "&" : "?" ) + settings.data;
|
1887
|
+
|
1888
|
+
// Garbage collect and reset settings.data
|
1889
|
+
settings.data = null;
|
1890
|
+
}
|
1891
|
+
|
1892
|
+
|
1893
|
+
settings.ajax.open( settings.type, settings.url, settings.async );
|
1894
|
+
settings.ajax.send( settings.data || null );
|
1895
|
+
|
1896
|
+
return Popcorn.xhr.httpData( settings );
|
1897
|
+
}
|
1898
|
+
};
|
1899
|
+
|
1900
|
+
|
1901
|
+
Popcorn.xhr.httpData = function( settings ) {
|
1902
|
+
|
1903
|
+
var data, json = null,
|
1904
|
+
parser, xml = null;
|
1905
|
+
|
1906
|
+
settings.ajax.onreadystatechange = function() {
|
1907
|
+
|
1908
|
+
if ( settings.ajax.readyState === 4 ) {
|
1909
|
+
|
1910
|
+
try {
|
1911
|
+
json = JSON.parse( settings.ajax.responseText );
|
1912
|
+
} catch( e ) {
|
1913
|
+
//suppress
|
1914
|
+
}
|
1915
|
+
|
1916
|
+
data = {
|
1917
|
+
xml: settings.ajax.responseXML,
|
1918
|
+
text: settings.ajax.responseText,
|
1919
|
+
json: json
|
1920
|
+
};
|
1921
|
+
|
1922
|
+
// Normalize: data.xml is non-null in IE9 regardless of if response is valid xml
|
1923
|
+
if ( !data.xml || !data.xml.documentElement ) {
|
1924
|
+
data.xml = null;
|
1925
|
+
|
1926
|
+
try {
|
1927
|
+
parser = new DOMParser();
|
1928
|
+
xml = parser.parseFromString( settings.ajax.responseText, "text/xml" );
|
1929
|
+
|
1930
|
+
if ( !xml.getElementsByTagName( "parsererror" ).length ) {
|
1931
|
+
data.xml = xml;
|
1932
|
+
}
|
1933
|
+
} catch ( e ) {
|
1934
|
+
// data.xml remains null
|
1935
|
+
}
|
1936
|
+
}
|
1937
|
+
|
1938
|
+
// If a dataType was specified, return that type of data
|
1939
|
+
if ( settings.dataType ) {
|
1940
|
+
data = data[ settings.dataType ];
|
1941
|
+
}
|
1942
|
+
|
1943
|
+
|
1944
|
+
settings.success.call( settings.ajax, data );
|
1945
|
+
|
1946
|
+
}
|
1947
|
+
};
|
1948
|
+
return data;
|
1949
|
+
};
|
1950
|
+
|
1951
|
+
Popcorn.xhr.getJSONP = function( url, success, isScript ) {
|
1952
|
+
|
1953
|
+
var head = document.head || document.getElementsByTagName( "head" )[ 0 ] || document.documentElement,
|
1954
|
+
script = document.createElement( "script" ),
|
1955
|
+
paramStr = url.split( "?" )[ 1 ],
|
1956
|
+
isFired = false,
|
1957
|
+
params = [],
|
1958
|
+
callback, parts, callparam;
|
1959
|
+
|
1960
|
+
if ( paramStr && !isScript ) {
|
1961
|
+
params = paramStr.split( "&" );
|
1962
|
+
}
|
1963
|
+
|
1964
|
+
if ( params.length ) {
|
1965
|
+
parts = params[ params.length - 1 ].split( "=" );
|
1966
|
+
}
|
1967
|
+
|
1968
|
+
callback = params.length ? ( parts[ 1 ] ? parts[ 1 ] : parts[ 0 ] ) : "jsonp";
|
1969
|
+
|
1970
|
+
if ( !paramStr && !isScript ) {
|
1971
|
+
url += "?callback=" + callback;
|
1972
|
+
}
|
1973
|
+
|
1974
|
+
if ( callback && !isScript ) {
|
1975
|
+
|
1976
|
+
// If a callback name already exists
|
1977
|
+
if ( !!window[ callback ] ) {
|
1978
|
+
// Create a new unique callback name
|
1979
|
+
callback = Popcorn.guid( callback );
|
1980
|
+
}
|
1981
|
+
|
1982
|
+
// Define the JSONP success callback globally
|
1983
|
+
window[ callback ] = function( data ) {
|
1984
|
+
// Fire success callbacks
|
1985
|
+
success && success( data );
|
1986
|
+
isFired = true;
|
1987
|
+
};
|
1988
|
+
|
1989
|
+
// Replace callback param and callback name
|
1990
|
+
url = url.replace( parts.join( "=" ), parts[ 0 ] + "=" + callback );
|
1991
|
+
}
|
1992
|
+
|
1993
|
+
script.addEventListener( "load", function() {
|
1994
|
+
|
1995
|
+
// Handling remote script loading callbacks
|
1996
|
+
if ( isScript ) {
|
1997
|
+
// getScript
|
1998
|
+
success && success();
|
1999
|
+
}
|
2000
|
+
|
2001
|
+
// Executing for JSONP requests
|
2002
|
+
if ( isFired ) {
|
2003
|
+
// Garbage collect the callback
|
2004
|
+
delete window[ callback ];
|
2005
|
+
}
|
2006
|
+
// Garbage collect the script resource
|
2007
|
+
head.removeChild( script );
|
2008
|
+
}, false );
|
2009
|
+
|
2010
|
+
script.src = url;
|
2011
|
+
|
2012
|
+
head.insertBefore( script, head.firstChild );
|
2013
|
+
|
2014
|
+
return;
|
2015
|
+
};
|
2016
|
+
|
2017
|
+
Popcorn.getJSONP = Popcorn.xhr.getJSONP;
|
2018
|
+
|
2019
|
+
Popcorn.getScript = Popcorn.xhr.getScript = function( url, success ) {
|
2020
|
+
|
2021
|
+
return Popcorn.xhr.getJSONP( url, success, true );
|
2022
|
+
};
|
2023
|
+
|
2024
|
+
Popcorn.util = {
|
2025
|
+
// Simple function to parse a timestamp into seconds
|
2026
|
+
// Acceptable formats are:
|
2027
|
+
// HH:MM:SS.MMM
|
2028
|
+
// HH:MM:SS;FF
|
2029
|
+
// Hours and minutes are optional. They default to 0
|
2030
|
+
toSeconds: function( timeStr, framerate ) {
|
2031
|
+
// Hours and minutes are optional
|
2032
|
+
// Seconds must be specified
|
2033
|
+
// Seconds can be followed by milliseconds OR by the frame information
|
2034
|
+
var validTimeFormat = /^([0-9]+:){0,2}[0-9]+([.;][0-9]+)?$/,
|
2035
|
+
errorMessage = "Invalid time format",
|
2036
|
+
digitPairs, lastIndex, lastPair, firstPair,
|
2037
|
+
frameInfo, frameTime;
|
2038
|
+
|
2039
|
+
if ( typeof timeStr === "number" ) {
|
2040
|
+
return timeStr;
|
2041
|
+
}
|
2042
|
+
|
2043
|
+
if ( typeof timeStr === "string" &&
|
2044
|
+
!validTimeFormat.test( timeStr ) ) {
|
2045
|
+
Popcorn.error( errorMessage );
|
2046
|
+
}
|
2047
|
+
|
2048
|
+
digitPairs = timeStr.split( ":" );
|
2049
|
+
lastIndex = digitPairs.length - 1;
|
2050
|
+
lastPair = digitPairs[ lastIndex ];
|
2051
|
+
|
2052
|
+
// Fix last element:
|
2053
|
+
if ( lastPair.indexOf( ";" ) > -1 ) {
|
2054
|
+
|
2055
|
+
frameInfo = lastPair.split( ";" );
|
2056
|
+
frameTime = 0;
|
2057
|
+
|
2058
|
+
if ( framerate && ( typeof framerate === "number" ) ) {
|
2059
|
+
frameTime = parseFloat( frameInfo[ 1 ], 10 ) / framerate;
|
2060
|
+
}
|
2061
|
+
|
2062
|
+
digitPairs[ lastIndex ] = parseInt( frameInfo[ 0 ], 10 ) + frameTime;
|
2063
|
+
}
|
2064
|
+
|
2065
|
+
firstPair = digitPairs[ 0 ];
|
2066
|
+
|
2067
|
+
return {
|
2068
|
+
|
2069
|
+
1: parseFloat( firstPair, 10 ),
|
2070
|
+
|
2071
|
+
2: ( parseInt( firstPair, 10 ) * 60 ) +
|
2072
|
+
parseFloat( digitPairs[ 1 ], 10 ),
|
2073
|
+
|
2074
|
+
3: ( parseInt( firstPair, 10 ) * 3600 ) +
|
2075
|
+
( parseInt( digitPairs[ 1 ], 10 ) * 60 ) +
|
2076
|
+
parseFloat( digitPairs[ 2 ], 10 )
|
2077
|
+
|
2078
|
+
}[ digitPairs.length || 1 ];
|
2079
|
+
}
|
2080
|
+
};
|
2081
|
+
|
2082
|
+
// alias for exec function
|
2083
|
+
Popcorn.p.cue = Popcorn.p.exec;
|
2084
|
+
|
2085
|
+
// Protected API methods
|
2086
|
+
Popcorn.protect = {
|
2087
|
+
natives: getKeys( Popcorn.p ).map(function( val ) {
|
2088
|
+
return val.toLowerCase();
|
2089
|
+
})
|
2090
|
+
};
|
2091
|
+
|
2092
|
+
// Setup logging for deprecated methods
|
2093
|
+
Popcorn.forEach({
|
2094
|
+
// Deprecated: Recommended
|
2095
|
+
"listen": "on",
|
2096
|
+
"unlisten": "off",
|
2097
|
+
"trigger": "emit",
|
2098
|
+
"exec": "cue"
|
2099
|
+
|
2100
|
+
}, function( recommend, api ) {
|
2101
|
+
var original = Popcorn.p[ api ];
|
2102
|
+
// Override the deprecated api method with a method of the same name
|
2103
|
+
// that logs a warning and defers to the new recommended method
|
2104
|
+
Popcorn.p[ api ] = function() {
|
2105
|
+
if ( typeof console !== "undefined" && console.warn ) {
|
2106
|
+
console.warn(
|
2107
|
+
"Deprecated method '" + api + "', " +
|
2108
|
+
(recommend == null ? "do not use." : "use '" + recommend + "' instead." )
|
2109
|
+
);
|
2110
|
+
|
2111
|
+
// Restore api after first warning
|
2112
|
+
Popcorn.p[ api ] = original;
|
2113
|
+
}
|
2114
|
+
return Popcorn.p[ recommend ].apply( this, [].slice.call( arguments ) );
|
2115
|
+
};
|
2116
|
+
});
|
2117
|
+
|
2118
|
+
|
2119
|
+
// Exposes Popcorn to global context
|
2120
|
+
global.Popcorn = Popcorn;
|
2121
|
+
|
2122
|
+
})(window, window.document);
|
2123
|
+
(function( Popcorn ) {
|
2124
|
+
|
2125
|
+
// combines calls of two function calls into one
|
2126
|
+
var combineFn = function( first, second ) {
|
2127
|
+
|
2128
|
+
first = first || Popcorn.nop;
|
2129
|
+
second = second || Popcorn.nop;
|
2130
|
+
|
2131
|
+
return function() {
|
2132
|
+
|
2133
|
+
first.apply( this, arguments );
|
2134
|
+
second.apply( this, arguments );
|
2135
|
+
};
|
2136
|
+
};
|
2137
|
+
|
2138
|
+
// ID string matching
|
2139
|
+
var rIdExp = /^(#([\w\-\_\.]+))$/;
|
2140
|
+
|
2141
|
+
Popcorn.player = function( name, player ) {
|
2142
|
+
|
2143
|
+
// return early if a player already exists under this name
|
2144
|
+
if ( Popcorn[ name ] ) {
|
2145
|
+
|
2146
|
+
return;
|
2147
|
+
}
|
2148
|
+
|
2149
|
+
player = player || {};
|
2150
|
+
|
2151
|
+
var playerFn = function( target, src, options ) {
|
2152
|
+
|
2153
|
+
options = options || {};
|
2154
|
+
|
2155
|
+
// List of events
|
2156
|
+
var date = new Date() / 1000,
|
2157
|
+
baselineTime = date,
|
2158
|
+
currentTime = 0,
|
2159
|
+
readyState = 0,
|
2160
|
+
volume = 1,
|
2161
|
+
muted = false,
|
2162
|
+
events = {},
|
2163
|
+
|
2164
|
+
// The container div of the resource
|
2165
|
+
container = document.getElementById( rIdExp.exec( target ) && rIdExp.exec( target )[ 2 ] ) ||
|
2166
|
+
document.getElementById( target ) ||
|
2167
|
+
target,
|
2168
|
+
basePlayer = {},
|
2169
|
+
timeout,
|
2170
|
+
popcorn;
|
2171
|
+
|
2172
|
+
if ( !Object.prototype.__defineGetter__ ) {
|
2173
|
+
|
2174
|
+
basePlayer = container || document.createElement( "div" );
|
2175
|
+
}
|
2176
|
+
|
2177
|
+
// copies a div into the media object
|
2178
|
+
for( var val in container ) {
|
2179
|
+
|
2180
|
+
// don't copy properties if using container as baseplayer
|
2181
|
+
if ( val in basePlayer ) {
|
2182
|
+
|
2183
|
+
continue;
|
2184
|
+
}
|
2185
|
+
|
2186
|
+
if ( typeof container[ val ] === "object" ) {
|
2187
|
+
|
2188
|
+
basePlayer[ val ] = container[ val ];
|
2189
|
+
} else if ( typeof container[ val ] === "function" ) {
|
2190
|
+
|
2191
|
+
basePlayer[ val ] = (function( value ) {
|
2192
|
+
|
2193
|
+
// this is a stupid ugly kludgy hack in honour of Safari
|
2194
|
+
// in Safari a NodeList is a function, not an object
|
2195
|
+
if ( "length" in container[ value ] && !container[ value ].call ) {
|
2196
|
+
|
2197
|
+
return container[ value ];
|
2198
|
+
} else {
|
2199
|
+
|
2200
|
+
return function() {
|
2201
|
+
|
2202
|
+
return container[ value ].apply( container, arguments );
|
2203
|
+
};
|
2204
|
+
}
|
2205
|
+
}( val ));
|
2206
|
+
} else {
|
2207
|
+
|
2208
|
+
Popcorn.player.defineProperty( basePlayer, val, {
|
2209
|
+
get: (function( value ) {
|
2210
|
+
|
2211
|
+
return function() {
|
2212
|
+
|
2213
|
+
return container[ value ];
|
2214
|
+
};
|
2215
|
+
}( val )),
|
2216
|
+
set: Popcorn.nop,
|
2217
|
+
configurable: true
|
2218
|
+
});
|
2219
|
+
}
|
2220
|
+
}
|
2221
|
+
|
2222
|
+
var timeupdate = function() {
|
2223
|
+
|
2224
|
+
date = new Date() / 1000;
|
2225
|
+
|
2226
|
+
if ( !basePlayer.paused ) {
|
2227
|
+
|
2228
|
+
basePlayer.currentTime = basePlayer.currentTime + ( date - baselineTime );
|
2229
|
+
basePlayer.dispatchEvent( "timeupdate" );
|
2230
|
+
timeout = setTimeout( timeupdate, 10 );
|
2231
|
+
}
|
2232
|
+
|
2233
|
+
baselineTime = date;
|
2234
|
+
};
|
2235
|
+
|
2236
|
+
basePlayer.play = function() {
|
2237
|
+
|
2238
|
+
this.paused = false;
|
2239
|
+
|
2240
|
+
if ( basePlayer.readyState >= 4 ) {
|
2241
|
+
|
2242
|
+
baselineTime = new Date() / 1000;
|
2243
|
+
basePlayer.dispatchEvent( "play" );
|
2244
|
+
timeupdate();
|
2245
|
+
}
|
2246
|
+
};
|
2247
|
+
|
2248
|
+
basePlayer.pause = function() {
|
2249
|
+
|
2250
|
+
this.paused = true;
|
2251
|
+
basePlayer.dispatchEvent( "pause" );
|
2252
|
+
};
|
2253
|
+
|
2254
|
+
Popcorn.player.defineProperty( basePlayer, "currentTime", {
|
2255
|
+
get: function() {
|
2256
|
+
|
2257
|
+
return currentTime;
|
2258
|
+
},
|
2259
|
+
set: function( val ) {
|
2260
|
+
|
2261
|
+
// make sure val is a number
|
2262
|
+
currentTime = +val;
|
2263
|
+
basePlayer.dispatchEvent( "timeupdate" );
|
2264
|
+
|
2265
|
+
return currentTime;
|
2266
|
+
},
|
2267
|
+
configurable: true
|
2268
|
+
});
|
2269
|
+
|
2270
|
+
Popcorn.player.defineProperty( basePlayer, "volume", {
|
2271
|
+
get: function() {
|
2272
|
+
|
2273
|
+
return volume;
|
2274
|
+
},
|
2275
|
+
set: function( val ) {
|
2276
|
+
|
2277
|
+
// make sure val is a number
|
2278
|
+
volume = +val;
|
2279
|
+
basePlayer.dispatchEvent( "volumechange" );
|
2280
|
+
return volume;
|
2281
|
+
},
|
2282
|
+
configurable: true
|
2283
|
+
});
|
2284
|
+
|
2285
|
+
Popcorn.player.defineProperty( basePlayer, "muted", {
|
2286
|
+
get: function() {
|
2287
|
+
|
2288
|
+
return muted;
|
2289
|
+
},
|
2290
|
+
set: function( val ) {
|
2291
|
+
|
2292
|
+
// make sure val is a number
|
2293
|
+
muted = +val;
|
2294
|
+
basePlayer.dispatchEvent( "volumechange" );
|
2295
|
+
return muted;
|
2296
|
+
},
|
2297
|
+
configurable: true
|
2298
|
+
});
|
2299
|
+
|
2300
|
+
Popcorn.player.defineProperty( basePlayer, "readyState", {
|
2301
|
+
get: function() {
|
2302
|
+
|
2303
|
+
return readyState;
|
2304
|
+
},
|
2305
|
+
set: function( val ) {
|
2306
|
+
|
2307
|
+
readyState = val;
|
2308
|
+
return readyState;
|
2309
|
+
},
|
2310
|
+
configurable: true
|
2311
|
+
});
|
2312
|
+
|
2313
|
+
// Adds an event listener to the object
|
2314
|
+
basePlayer.addEventListener = function( evtName, fn ) {
|
2315
|
+
|
2316
|
+
if ( !events[ evtName ] ) {
|
2317
|
+
|
2318
|
+
events[ evtName ] = [];
|
2319
|
+
}
|
2320
|
+
|
2321
|
+
events[ evtName ].push( fn );
|
2322
|
+
return fn;
|
2323
|
+
};
|
2324
|
+
|
2325
|
+
// Removes an event listener from the object
|
2326
|
+
basePlayer.removeEventListener = function( evtName, fn ) {
|
2327
|
+
|
2328
|
+
var i,
|
2329
|
+
listeners = events[ evtName ];
|
2330
|
+
|
2331
|
+
if ( !listeners ){
|
2332
|
+
|
2333
|
+
return;
|
2334
|
+
}
|
2335
|
+
|
2336
|
+
// walk backwards so we can safely splice
|
2337
|
+
for ( i = events[ evtName ].length - 1; i >= 0; i-- ) {
|
2338
|
+
|
2339
|
+
if( fn === listeners[ i ] ) {
|
2340
|
+
|
2341
|
+
listeners.splice(i, 1);
|
2342
|
+
}
|
2343
|
+
}
|
2344
|
+
|
2345
|
+
return fn;
|
2346
|
+
};
|
2347
|
+
|
2348
|
+
// Can take event object or simple string
|
2349
|
+
basePlayer.dispatchEvent = function( oEvent ) {
|
2350
|
+
|
2351
|
+
var evt,
|
2352
|
+
self = this,
|
2353
|
+
eventInterface,
|
2354
|
+
eventName = oEvent.type;
|
2355
|
+
|
2356
|
+
// A string was passed, create event object
|
2357
|
+
if ( !eventName ) {
|
2358
|
+
|
2359
|
+
eventName = oEvent;
|
2360
|
+
eventInterface = Popcorn.events.getInterface( eventName );
|
2361
|
+
|
2362
|
+
if ( eventInterface ) {
|
2363
|
+
|
2364
|
+
evt = document.createEvent( eventInterface );
|
2365
|
+
evt.initEvent( eventName, true, true, window, 1 );
|
2366
|
+
}
|
2367
|
+
}
|
2368
|
+
|
2369
|
+
if ( events[ eventName ] ) {
|
2370
|
+
|
2371
|
+
for ( var i = events[ eventName ].length - 1; i >= 0; i-- ) {
|
2372
|
+
|
2373
|
+
events[ eventName ][ i ].call( self, evt, self );
|
2374
|
+
}
|
2375
|
+
}
|
2376
|
+
};
|
2377
|
+
|
2378
|
+
// Attempt to get src from playerFn parameter
|
2379
|
+
basePlayer.src = src || "";
|
2380
|
+
basePlayer.duration = 0;
|
2381
|
+
basePlayer.paused = true;
|
2382
|
+
basePlayer.ended = 0;
|
2383
|
+
|
2384
|
+
options && options.events && Popcorn.forEach( options.events, function( val, key ) {
|
2385
|
+
|
2386
|
+
basePlayer.addEventListener( key, val, false );
|
2387
|
+
});
|
2388
|
+
|
2389
|
+
// true and undefined returns on canPlayType means we should attempt to use it,
|
2390
|
+
// false means we cannot play this type
|
2391
|
+
if ( player._canPlayType( container.nodeName, src ) !== false ) {
|
2392
|
+
|
2393
|
+
if ( player._setup ) {
|
2394
|
+
|
2395
|
+
player._setup.call( basePlayer, options );
|
2396
|
+
} else {
|
2397
|
+
|
2398
|
+
// there is no setup, which means there is nothing to load
|
2399
|
+
basePlayer.readyState = 4;
|
2400
|
+
basePlayer.dispatchEvent( "loadedmetadata" );
|
2401
|
+
basePlayer.dispatchEvent( "loadeddata" );
|
2402
|
+
basePlayer.dispatchEvent( "canplaythrough" );
|
2403
|
+
}
|
2404
|
+
} else {
|
2405
|
+
|
2406
|
+
basePlayer.dispatchEvent( "error" );
|
2407
|
+
}
|
2408
|
+
|
2409
|
+
// when a custom player is loaded, load basePlayer state into custom player
|
2410
|
+
basePlayer.addEventListener( "loadedmetadata", function() {
|
2411
|
+
|
2412
|
+
// if a player is not ready before currentTime is called, this will set it after it is ready
|
2413
|
+
basePlayer.currentTime = currentTime;
|
2414
|
+
|
2415
|
+
// same as above with volume and muted
|
2416
|
+
basePlayer.volume = volume;
|
2417
|
+
basePlayer.muted = muted;
|
2418
|
+
});
|
2419
|
+
|
2420
|
+
basePlayer.addEventListener( "loadeddata", function() {
|
2421
|
+
|
2422
|
+
// if play was called before player ready, start playing video
|
2423
|
+
!basePlayer.paused && basePlayer.play();
|
2424
|
+
});
|
2425
|
+
|
2426
|
+
popcorn = new Popcorn.p.init( basePlayer, options );
|
2427
|
+
|
2428
|
+
if ( player._teardown ) {
|
2429
|
+
|
2430
|
+
popcorn.destroy = combineFn( popcorn.destroy, function() {
|
2431
|
+
|
2432
|
+
player._teardown.call( basePlayer, options );
|
2433
|
+
});
|
2434
|
+
}
|
2435
|
+
|
2436
|
+
return popcorn;
|
2437
|
+
};
|
2438
|
+
|
2439
|
+
playerFn.canPlayType = player._canPlayType = player._canPlayType || Popcorn.nop;
|
2440
|
+
|
2441
|
+
Popcorn[ name ] = Popcorn.player.registry[ name ] = playerFn;
|
2442
|
+
};
|
2443
|
+
|
2444
|
+
Popcorn.player.registry = {};
|
2445
|
+
|
2446
|
+
Popcorn.player.defineProperty = Object.defineProperty || function( object, description, options ) {
|
2447
|
+
|
2448
|
+
object.__defineGetter__( description, options.get || Popcorn.nop );
|
2449
|
+
object.__defineSetter__( description, options.set || Popcorn.nop );
|
2450
|
+
};
|
2451
|
+
|
2452
|
+
// smart will attempt to find you a match, if it does not find a match,
|
2453
|
+
// it will attempt to create a video element with the source,
|
2454
|
+
// if that failed, it will throw.
|
2455
|
+
Popcorn.smart = function( target, src, options ) {
|
2456
|
+
|
2457
|
+
var nodeId = rIdExp.exec( target ),
|
2458
|
+
playerType,
|
2459
|
+
node = nodeId && nodeId.length && nodeId[ 2 ] ?
|
2460
|
+
document.getElementById( nodeId[ 2 ] ) :
|
2461
|
+
target;
|
2462
|
+
|
2463
|
+
// Popcorn.smart( video, /* options */ )
|
2464
|
+
if ( node.nodeType === "VIDEO" && !src ) {
|
2465
|
+
|
2466
|
+
if ( typeof src === "object" ) {
|
2467
|
+
|
2468
|
+
options = src;
|
2469
|
+
src = undefined;
|
2470
|
+
}
|
2471
|
+
|
2472
|
+
return Popcorn( node, options );
|
2473
|
+
}
|
2474
|
+
|
2475
|
+
// for now we loop through and use the first valid player we find.
|
2476
|
+
for ( var key in Popcorn.player.registry ) {
|
2477
|
+
|
2478
|
+
if ( Popcorn.player.registry.hasOwnProperty( key ) ) {
|
2479
|
+
|
2480
|
+
if ( Popcorn.player.registry[ key ].canPlayType( node.nodeName, src ) ) {
|
2481
|
+
|
2482
|
+
// Popcorn.smart( player, src, /* options */ )
|
2483
|
+
return Popcorn[ key ]( target, src, options );
|
2484
|
+
}
|
2485
|
+
}
|
2486
|
+
}
|
2487
|
+
|
2488
|
+
// Popcorn.smart( div, src, /* options */ )
|
2489
|
+
// attempting to create a video in a container
|
2490
|
+
if ( node.nodeType !== "VIDEO" ) {
|
2491
|
+
|
2492
|
+
target = document.createElement( "video" );
|
2493
|
+
|
2494
|
+
node.appendChild( target );
|
2495
|
+
node = target;
|
2496
|
+
}
|
2497
|
+
|
2498
|
+
options && options.events && options.events.error && node.addEventListener( "error", options.events.error, false );
|
2499
|
+
node.src = src;
|
2500
|
+
|
2501
|
+
return Popcorn( node, options );
|
2502
|
+
};
|
2503
|
+
|
2504
|
+
})( Popcorn );
|
2505
|
+
// A global callback for youtube... that makes me angry
|
2506
|
+
var onYouTubePlayerReady = function( containerId ) {
|
2507
|
+
|
2508
|
+
onYouTubePlayerReady[ containerId ] && onYouTubePlayerReady[ containerId ]();
|
2509
|
+
};
|
2510
|
+
onYouTubePlayerReady.stateChangeEventHandler = {};
|
2511
|
+
onYouTubePlayerReady.onErrorEventHandler = {};
|
2512
|
+
|
2513
|
+
Popcorn.player( "youtube", {
|
2514
|
+
_canPlayType: function( nodeName, url ) {
|
2515
|
+
|
2516
|
+
return (/(?:http:\/\/www\.|http:\/\/|www\.|\.|^)(youtu)/).test( url ) && nodeName.toLowerCase() !== "video";
|
2517
|
+
},
|
2518
|
+
_setup: function( options ) {
|
2519
|
+
|
2520
|
+
var media = this,
|
2521
|
+
autoPlay = false,
|
2522
|
+
container = document.createElement( "div" ),
|
2523
|
+
currentTime = 0,
|
2524
|
+
seekTime = 0,
|
2525
|
+
firstGo = true,
|
2526
|
+
seeking = false,
|
2527
|
+
|
2528
|
+
// state code for volume changed polling
|
2529
|
+
volumeChanged = false,
|
2530
|
+
lastMuted = false,
|
2531
|
+
lastVolume = 100;
|
2532
|
+
|
2533
|
+
// setting paused to undefined because youtube has state for not paused or playing
|
2534
|
+
media.paused = undefined;
|
2535
|
+
container.id = media.id + Popcorn.guid();
|
2536
|
+
|
2537
|
+
options._container = container;
|
2538
|
+
|
2539
|
+
media.appendChild( container );
|
2540
|
+
|
2541
|
+
var youtubeInit = function() {
|
2542
|
+
|
2543
|
+
var flashvars,
|
2544
|
+
params,
|
2545
|
+
attributes,
|
2546
|
+
src,
|
2547
|
+
width,
|
2548
|
+
height,
|
2549
|
+
query;
|
2550
|
+
|
2551
|
+
// expose a callback to this scope, that is called from the global callback youtube calls
|
2552
|
+
onYouTubePlayerReady[ container.id ] = function() {
|
2553
|
+
|
2554
|
+
options.youtubeObject = document.getElementById( container.id );
|
2555
|
+
|
2556
|
+
// more youtube callback nonsense
|
2557
|
+
onYouTubePlayerReady.stateChangeEventHandler[ container.id ] = function( state ) {
|
2558
|
+
|
2559
|
+
if ( options.destroyed ) {
|
2560
|
+
|
2561
|
+
return;
|
2562
|
+
}
|
2563
|
+
|
2564
|
+
// youtube fires paused events while seeking
|
2565
|
+
// this is the only way to get seeking events
|
2566
|
+
if ( state === 2 ) {
|
2567
|
+
|
2568
|
+
// silly logic forced on me by the youtube API
|
2569
|
+
// calling youtube.seekTo triggers multiple events
|
2570
|
+
// with the second events getCurrentTime being the old time
|
2571
|
+
if ( seeking && seekTime === currentTime && seekTime !== options.youtubeObject.getCurrentTime() ) {
|
2572
|
+
|
2573
|
+
seeking = false;
|
2574
|
+
options.youtubeObject.seekTo( currentTime );
|
2575
|
+
return;
|
2576
|
+
}
|
2577
|
+
|
2578
|
+
currentTime = options.youtubeObject.getCurrentTime();
|
2579
|
+
media.dispatchEvent( "timeupdate" );
|
2580
|
+
!media.paused && media.pause();
|
2581
|
+
|
2582
|
+
return;
|
2583
|
+
} else
|
2584
|
+
// playing is state 1
|
2585
|
+
// paused is state 2
|
2586
|
+
if ( state === 1 && !firstGo ) {
|
2587
|
+
|
2588
|
+
media.paused && media.play();
|
2589
|
+
return;
|
2590
|
+
} else
|
2591
|
+
// this is the real player ready check
|
2592
|
+
// -1 is for unstarted, but ready to go videos
|
2593
|
+
// before this the player object exists, but calls to it may go unheard
|
2594
|
+
if ( state === -1 ) {
|
2595
|
+
|
2596
|
+
options.youtubeObject.playVideo();
|
2597
|
+
return;
|
2598
|
+
} else
|
2599
|
+
if ( state === 1 && firstGo ) {
|
2600
|
+
|
2601
|
+
firstGo = false;
|
2602
|
+
|
2603
|
+
if ( media.paused === true ) {
|
2604
|
+
|
2605
|
+
media.pause();
|
2606
|
+
} else if ( media.paused === false ) {
|
2607
|
+
|
2608
|
+
media.play();
|
2609
|
+
} else if ( autoPlay ) {
|
2610
|
+
|
2611
|
+
media.play();
|
2612
|
+
} else if ( !autoPlay ) {
|
2613
|
+
|
2614
|
+
media.pause();
|
2615
|
+
}
|
2616
|
+
|
2617
|
+
media.duration = options.youtubeObject.getDuration();
|
2618
|
+
|
2619
|
+
media.dispatchEvent( "durationchange" );
|
2620
|
+
volumeupdate();
|
2621
|
+
|
2622
|
+
media.dispatchEvent( "loadedmetadata" );
|
2623
|
+
media.dispatchEvent( "loadeddata" );
|
2624
|
+
|
2625
|
+
media.readyState = 4;
|
2626
|
+
media.dispatchEvent( "canplaythrough" );
|
2627
|
+
|
2628
|
+
return;
|
2629
|
+
} else if ( state === 0 ) {
|
2630
|
+
media.dispatchEvent( "ended" );
|
2631
|
+
}
|
2632
|
+
};
|
2633
|
+
|
2634
|
+
onYouTubePlayerReady.onErrorEventHandler[ container.id ] = function( errorCode ) {
|
2635
|
+
if ( [ 2, 100, 101, 150 ].indexOf( errorCode ) !== -1 ) {
|
2636
|
+
media.dispatchEvent( "error" );
|
2637
|
+
}
|
2638
|
+
};
|
2639
|
+
|
2640
|
+
// youtube requires callbacks to be a string to a function path from the global scope
|
2641
|
+
options.youtubeObject.addEventListener( "onStateChange", "onYouTubePlayerReady.stateChangeEventHandler." + container.id );
|
2642
|
+
|
2643
|
+
options.youtubeObject.addEventListener( "onError", "onYouTubePlayerReady.onErrorEventHandler." + container.id );
|
2644
|
+
|
2645
|
+
var timeupdate = function() {
|
2646
|
+
|
2647
|
+
if ( options.destroyed ) {
|
2648
|
+
|
2649
|
+
return;
|
2650
|
+
}
|
2651
|
+
|
2652
|
+
if ( !media.paused ) {
|
2653
|
+
|
2654
|
+
currentTime = options.youtubeObject.getCurrentTime();
|
2655
|
+
media.dispatchEvent( "timeupdate" );
|
2656
|
+
setTimeout( timeupdate, 10 );
|
2657
|
+
}
|
2658
|
+
};
|
2659
|
+
|
2660
|
+
var volumeupdate = function() {
|
2661
|
+
|
2662
|
+
if ( options.destroyed ) {
|
2663
|
+
|
2664
|
+
return;
|
2665
|
+
}
|
2666
|
+
|
2667
|
+
if ( lastMuted !== options.youtubeObject.isMuted() ) {
|
2668
|
+
|
2669
|
+
lastMuted = options.youtubeObject.isMuted();
|
2670
|
+
media.dispatchEvent( "volumechange" );
|
2671
|
+
}
|
2672
|
+
|
2673
|
+
if ( lastVolume !== options.youtubeObject.getVolume() ) {
|
2674
|
+
|
2675
|
+
lastVolume = options.youtubeObject.getVolume();
|
2676
|
+
media.dispatchEvent( "volumechange" );
|
2677
|
+
}
|
2678
|
+
|
2679
|
+
setTimeout( volumeupdate, 250 );
|
2680
|
+
};
|
2681
|
+
|
2682
|
+
media.play = function() {
|
2683
|
+
|
2684
|
+
if ( options.destroyed ) {
|
2685
|
+
|
2686
|
+
return;
|
2687
|
+
}
|
2688
|
+
|
2689
|
+
if ( media.paused !== false || options.youtubeObject.getPlayerState() !== 1 ) {
|
2690
|
+
|
2691
|
+
media.paused = false;
|
2692
|
+
media.dispatchEvent( "play" );
|
2693
|
+
|
2694
|
+
media.dispatchEvent( "playing" );
|
2695
|
+
}
|
2696
|
+
|
2697
|
+
timeupdate();
|
2698
|
+
options.youtubeObject.playVideo();
|
2699
|
+
};
|
2700
|
+
|
2701
|
+
media.pause = function() {
|
2702
|
+
|
2703
|
+
if ( options.destroyed ) {
|
2704
|
+
|
2705
|
+
return;
|
2706
|
+
}
|
2707
|
+
|
2708
|
+
if ( media.paused !== true || options.youtubeObject.getPlayerState() !== 2 ) {
|
2709
|
+
|
2710
|
+
media.paused = true;
|
2711
|
+
media.dispatchEvent( "pause" );
|
2712
|
+
options.youtubeObject.pauseVideo();
|
2713
|
+
}
|
2714
|
+
};
|
2715
|
+
|
2716
|
+
Popcorn.player.defineProperty( media, "currentTime", {
|
2717
|
+
set: function( val ) {
|
2718
|
+
|
2719
|
+
// make sure val is a number
|
2720
|
+
currentTime = seekTime = +val;
|
2721
|
+
seeking = true;
|
2722
|
+
|
2723
|
+
if ( options.destroyed ) {
|
2724
|
+
|
2725
|
+
return currentTime;
|
2726
|
+
}
|
2727
|
+
|
2728
|
+
media.dispatchEvent( "seeked" );
|
2729
|
+
media.dispatchEvent( "timeupdate" );
|
2730
|
+
|
2731
|
+
options.youtubeObject.seekTo( currentTime );
|
2732
|
+
|
2733
|
+
return currentTime;
|
2734
|
+
},
|
2735
|
+
get: function() {
|
2736
|
+
|
2737
|
+
return currentTime;
|
2738
|
+
}
|
2739
|
+
});
|
2740
|
+
|
2741
|
+
Popcorn.player.defineProperty( media, "muted", {
|
2742
|
+
set: function( val ) {
|
2743
|
+
|
2744
|
+
if ( options.destroyed ) {
|
2745
|
+
|
2746
|
+
return val;
|
2747
|
+
}
|
2748
|
+
|
2749
|
+
if ( options.youtubeObject.isMuted() !== val ) {
|
2750
|
+
|
2751
|
+
if ( val ) {
|
2752
|
+
|
2753
|
+
options.youtubeObject.mute();
|
2754
|
+
} else {
|
2755
|
+
|
2756
|
+
options.youtubeObject.unMute();
|
2757
|
+
}
|
2758
|
+
|
2759
|
+
lastMuted = options.youtubeObject.isMuted();
|
2760
|
+
media.dispatchEvent( "volumechange" );
|
2761
|
+
}
|
2762
|
+
|
2763
|
+
return options.youtubeObject.isMuted();
|
2764
|
+
},
|
2765
|
+
get: function() {
|
2766
|
+
|
2767
|
+
if ( options.destroyed ) {
|
2768
|
+
|
2769
|
+
return 0;
|
2770
|
+
}
|
2771
|
+
|
2772
|
+
return options.youtubeObject.isMuted();
|
2773
|
+
}
|
2774
|
+
});
|
2775
|
+
|
2776
|
+
Popcorn.player.defineProperty( media, "volume", {
|
2777
|
+
set: function( val ) {
|
2778
|
+
|
2779
|
+
if ( options.destroyed ) {
|
2780
|
+
|
2781
|
+
return val;
|
2782
|
+
}
|
2783
|
+
|
2784
|
+
if ( options.youtubeObject.getVolume() / 100 !== val ) {
|
2785
|
+
|
2786
|
+
options.youtubeObject.setVolume( val * 100 );
|
2787
|
+
lastVolume = options.youtubeObject.getVolume();
|
2788
|
+
media.dispatchEvent( "volumechange" );
|
2789
|
+
}
|
2790
|
+
|
2791
|
+
return options.youtubeObject.getVolume() / 100;
|
2792
|
+
},
|
2793
|
+
get: function() {
|
2794
|
+
|
2795
|
+
if ( options.destroyed ) {
|
2796
|
+
|
2797
|
+
return 0;
|
2798
|
+
}
|
2799
|
+
|
2800
|
+
return options.youtubeObject.getVolume() / 100;
|
2801
|
+
}
|
2802
|
+
});
|
2803
|
+
};
|
2804
|
+
|
2805
|
+
options.controls = +options.controls === 0 || +options.controls === 1 ? options.controls : 1;
|
2806
|
+
options.annotations = +options.annotations === 1 || +options.annotations === 3 ? options.annotations : 1;
|
2807
|
+
|
2808
|
+
flashvars = {
|
2809
|
+
playerapiid: container.id
|
2810
|
+
};
|
2811
|
+
|
2812
|
+
params = {
|
2813
|
+
wmode: "transparent",
|
2814
|
+
allowScriptAccess: "always"
|
2815
|
+
};
|
2816
|
+
|
2817
|
+
src = /^.*(?:\/|v=)(.{11})/.exec( media.src )[ 1 ];
|
2818
|
+
|
2819
|
+
query = ( media.src.split( "?" )[ 1 ] || "" ).replace( /v=.{11}/, "" );
|
2820
|
+
autoPlay = ( /autoplay=1/.test( query ) );
|
2821
|
+
|
2822
|
+
// setting youtube player's height and width, default to 560 x 315
|
2823
|
+
width = media.style.width ? "" + media.offsetWidth : "560";
|
2824
|
+
height = media.style.height ? "" + media.offsetHeight : "315";
|
2825
|
+
|
2826
|
+
attributes = {
|
2827
|
+
id: container.id,
|
2828
|
+
"data-youtube-player": "//www.youtube.com/e/" + src + "?" + query + "&enablejsapi=1&playerapiid=" + container.id + "&version=3"
|
2829
|
+
};
|
2830
|
+
|
2831
|
+
swfobject.embedSWF( attributes[ "data-youtube-player" ], container.id, width, height, "8", undefined, flashvars, params, attributes );
|
2832
|
+
};
|
2833
|
+
|
2834
|
+
if ( !window.swfobject ) {
|
2835
|
+
|
2836
|
+
Popcorn.getScript( "//ajax.googleapis.com/ajax/libs/swfobject/2.2/swfobject.js", youtubeInit );
|
2837
|
+
} else {
|
2838
|
+
|
2839
|
+
youtubeInit();
|
2840
|
+
}
|
2841
|
+
},
|
2842
|
+
_teardown: function( options ) {
|
2843
|
+
|
2844
|
+
options.destroyed = true;
|
2845
|
+
options.youtubeObject.stopVideo();
|
2846
|
+
options.youtubeObject.clearVideo();
|
2847
|
+
this.removeChild( document.getElementById( options._container.id ) );
|
2848
|
+
}
|
2849
|
+
});
|