wai-website-theme 1.3.1 → 1.4
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.
- checksums.yaml +4 -4
- data/_includes/different.html +2 -1
- data/_includes/external.html +2 -1
- data/_includes/header.html +2 -1
- data/_includes/menuitem.html +6 -2
- data/_includes/peoplelist.html +21 -0
- data/_includes/prevnext-navigation.html +56 -0
- data/_includes/{prevnext.html → prevnext-order.html} +9 -0
- data/_includes/translation-note-msg.html +5 -3
- data/_includes/video-player.html +2 -2
- data/_layouts/default.html +8 -1
- data/_layouts/news.html +7 -1
- data/_layouts/policy.html +7 -1
- data/_layouts/sidenav.html +8 -1
- data/_layouts/sidenavsidebar.html +8 -1
- data/assets/ableplayer/Gruntfile.js +2 -1
- data/assets/ableplayer/README.md +158 -85
- data/assets/ableplayer/build/ableplayer.dist.js +15445 -13823
- data/assets/ableplayer/build/ableplayer.js +15445 -13823
- data/assets/ableplayer/build/ableplayer.min.css +1 -2
- data/assets/ableplayer/build/ableplayer.min.js +3 -10
- data/assets/ableplayer/package-lock.json +944 -346
- data/assets/ableplayer/package.json +8 -8
- data/assets/ableplayer/scripts/ableplayer-base.js +515 -524
- data/assets/ableplayer/scripts/browser.js +158 -158
- data/assets/ableplayer/scripts/buildplayer.js +1750 -1682
- data/assets/ableplayer/scripts/caption.js +424 -401
- data/assets/ableplayer/scripts/chapters.js +259 -259
- data/assets/ableplayer/scripts/control.js +1831 -1594
- data/assets/ableplayer/scripts/description.js +333 -256
- data/assets/ableplayer/scripts/dialog.js +145 -145
- data/assets/ableplayer/scripts/dragdrop.js +746 -749
- data/assets/ableplayer/scripts/event.js +875 -696
- data/assets/ableplayer/scripts/initialize.js +819 -912
- data/assets/ableplayer/scripts/langs.js +979 -743
- data/assets/ableplayer/scripts/metadata.js +124 -124
- data/assets/ableplayer/scripts/misc.js +170 -137
- data/assets/ableplayer/scripts/preference.js +904 -904
- data/assets/ableplayer/scripts/search.js +172 -172
- data/assets/ableplayer/scripts/sign.js +82 -78
- data/assets/ableplayer/scripts/slider.js +449 -448
- data/assets/ableplayer/scripts/track.js +409 -309
- data/assets/ableplayer/scripts/transcript.js +684 -595
- data/assets/ableplayer/scripts/translation.js +63 -67
- data/assets/ableplayer/scripts/ttml2webvtt.js +85 -85
- data/assets/ableplayer/scripts/vimeo.js +448 -0
- data/assets/ableplayer/scripts/volume.js +395 -380
- data/assets/ableplayer/scripts/vts.js +1077 -1077
- data/assets/ableplayer/scripts/webvtt.js +766 -763
- data/assets/ableplayer/scripts/youtube.js +695 -478
- data/assets/ableplayer/styles/ableplayer.css +54 -46
- data/assets/ableplayer/translations/nl.js +54 -54
- data/assets/ableplayer/translations/pt-br.js +311 -0
- data/assets/ableplayer/translations/tr.js +311 -0
- data/assets/ableplayer/translations/zh-tw.js +1 -1
- data/assets/css/style.css +1 -1
- data/assets/css/style.css.map +1 -1
- data/assets/images/icons.svg +5 -5
- data/assets/scripts/main.js +7 -0
- data/assets/search/tipuesearch.js +3 -3
- metadata +8 -3
|
@@ -1,1597 +1,1834 @@
|
|
|
1
1
|
(function ($) {
|
|
2
|
-
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
|
|
6
|
-
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
|
|
57
|
-
|
|
58
|
-
|
|
59
|
-
|
|
60
|
-
|
|
61
|
-
|
|
62
|
-
|
|
63
|
-
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
106
|
-
|
|
107
|
-
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
|
|
123
|
-
|
|
124
|
-
|
|
125
|
-
|
|
126
|
-
|
|
127
|
-
|
|
128
|
-
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
|
|
155
|
-
|
|
156
|
-
|
|
157
|
-
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
|
|
166
|
-
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
188
|
-
|
|
189
|
-
|
|
190
|
-
|
|
191
|
-
|
|
192
|
-
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
196
|
-
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
|
|
213
|
-
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
|
|
238
|
-
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
242
|
-
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
|
|
249
|
-
|
|
250
|
-
|
|
251
|
-
|
|
252
|
-
|
|
253
|
-
|
|
254
|
-
|
|
255
|
-
|
|
256
|
-
|
|
257
|
-
|
|
258
|
-
|
|
259
|
-
|
|
260
|
-
|
|
261
|
-
|
|
262
|
-
|
|
263
|
-
|
|
264
|
-
|
|
265
|
-
|
|
266
|
-
|
|
267
|
-
|
|
268
|
-
|
|
269
|
-
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
|
|
314
|
-
|
|
315
|
-
|
|
316
|
-
|
|
317
|
-
|
|
318
|
-
|
|
319
|
-
|
|
320
|
-
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
|
|
325
|
-
|
|
326
|
-
|
|
327
|
-
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
346
|
-
|
|
347
|
-
|
|
348
|
-
|
|
349
|
-
|
|
350
|
-
|
|
351
|
-
|
|
352
|
-
|
|
353
|
-
|
|
354
|
-
|
|
355
|
-
|
|
356
|
-
|
|
357
|
-
|
|
358
|
-
|
|
359
|
-
|
|
360
|
-
|
|
361
|
-
|
|
362
|
-
|
|
363
|
-
|
|
364
|
-
|
|
365
|
-
|
|
366
|
-
|
|
367
|
-
|
|
368
|
-
|
|
369
|
-
|
|
370
|
-
|
|
371
|
-
|
|
372
|
-
|
|
373
|
-
|
|
374
|
-
|
|
375
|
-
|
|
376
|
-
|
|
377
|
-
|
|
378
|
-
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
|
|
388
|
-
|
|
389
|
-
|
|
390
|
-
|
|
391
|
-
|
|
392
|
-
|
|
393
|
-
|
|
394
|
-
|
|
395
|
-
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
409
|
-
|
|
410
|
-
|
|
411
|
-
|
|
412
|
-
|
|
413
|
-
|
|
414
|
-
|
|
415
|
-
|
|
416
|
-
|
|
417
|
-
|
|
418
|
-
|
|
419
|
-
|
|
420
|
-
|
|
421
|
-
|
|
422
|
-
|
|
423
|
-
|
|
424
|
-
|
|
425
|
-
|
|
426
|
-
|
|
427
|
-
|
|
428
|
-
|
|
429
|
-
|
|
430
|
-
|
|
431
|
-
|
|
432
|
-
|
|
433
|
-
|
|
434
|
-
|
|
435
|
-
|
|
436
|
-
|
|
437
|
-
|
|
438
|
-
|
|
439
|
-
|
|
440
|
-
|
|
441
|
-
|
|
442
|
-
|
|
443
|
-
|
|
444
|
-
|
|
445
|
-
|
|
446
|
-
|
|
447
|
-
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
|
|
458
|
-
|
|
459
|
-
|
|
460
|
-
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
471
|
-
|
|
472
|
-
|
|
473
|
-
|
|
474
|
-
|
|
475
|
-
|
|
476
|
-
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
|
|
482
|
-
|
|
483
|
-
|
|
484
|
-
|
|
485
|
-
|
|
486
|
-
|
|
487
|
-
|
|
488
|
-
|
|
489
|
-
|
|
490
|
-
|
|
491
|
-
|
|
492
|
-
|
|
493
|
-
|
|
494
|
-
|
|
495
|
-
|
|
496
|
-
|
|
497
|
-
|
|
498
|
-
|
|
499
|
-
|
|
500
|
-
|
|
501
|
-
|
|
502
|
-
|
|
503
|
-
|
|
504
|
-
|
|
505
|
-
|
|
506
|
-
|
|
507
|
-
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
|
|
512
|
-
|
|
513
|
-
|
|
514
|
-
|
|
515
|
-
|
|
516
|
-
|
|
517
|
-
|
|
518
|
-
|
|
519
|
-
|
|
520
|
-
|
|
521
|
-
|
|
522
|
-
|
|
523
|
-
|
|
524
|
-
|
|
525
|
-
|
|
526
|
-
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
|
|
530
|
-
|
|
531
|
-
|
|
532
|
-
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
|
|
536
|
-
|
|
537
|
-
|
|
538
|
-
|
|
539
|
-
|
|
540
|
-
|
|
541
|
-
|
|
542
|
-
|
|
543
|
-
|
|
544
|
-
|
|
545
|
-
|
|
546
|
-
|
|
547
|
-
|
|
548
|
-
|
|
549
|
-
|
|
550
|
-
|
|
551
|
-
|
|
552
|
-
|
|
553
|
-
|
|
554
|
-
|
|
555
|
-
|
|
556
|
-
|
|
557
|
-
|
|
558
|
-
|
|
559
|
-
|
|
560
|
-
|
|
561
|
-
|
|
562
|
-
|
|
563
|
-
|
|
564
|
-
|
|
565
|
-
|
|
566
|
-
|
|
567
|
-
|
|
568
|
-
|
|
569
|
-
|
|
570
|
-
|
|
571
|
-
|
|
572
|
-
|
|
573
|
-
|
|
574
|
-
|
|
575
|
-
|
|
576
|
-
|
|
577
|
-
|
|
578
|
-
|
|
579
|
-
|
|
580
|
-
|
|
581
|
-
|
|
582
|
-
|
|
583
|
-
|
|
584
|
-
|
|
585
|
-
|
|
586
|
-
|
|
587
|
-
|
|
588
|
-
|
|
589
|
-
|
|
590
|
-
|
|
591
|
-
|
|
592
|
-
|
|
593
|
-
|
|
594
|
-
|
|
595
|
-
|
|
596
|
-
|
|
597
|
-
|
|
598
|
-
|
|
599
|
-
|
|
600
|
-
|
|
601
|
-
|
|
602
|
-
|
|
603
|
-
|
|
604
|
-
|
|
605
|
-
|
|
606
|
-
|
|
607
|
-
|
|
608
|
-
|
|
609
|
-
|
|
610
|
-
|
|
611
|
-
|
|
612
|
-
|
|
613
|
-
|
|
614
|
-
|
|
615
|
-
|
|
616
|
-
|
|
617
|
-
|
|
618
|
-
|
|
619
|
-
|
|
620
|
-
|
|
621
|
-
|
|
622
|
-
|
|
623
|
-
|
|
624
|
-
|
|
625
|
-
|
|
626
|
-
|
|
627
|
-
|
|
628
|
-
|
|
629
|
-
|
|
630
|
-
|
|
631
|
-
|
|
632
|
-
|
|
633
|
-
|
|
634
|
-
|
|
635
|
-
|
|
636
|
-
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
|
|
640
|
-
|
|
641
|
-
|
|
642
|
-
|
|
643
|
-
|
|
644
|
-
|
|
645
|
-
|
|
646
|
-
|
|
647
|
-
|
|
648
|
-
|
|
649
|
-
|
|
650
|
-
|
|
651
|
-
|
|
652
|
-
|
|
653
|
-
|
|
654
|
-
|
|
655
|
-
|
|
656
|
-
|
|
657
|
-
|
|
658
|
-
|
|
659
|
-
|
|
660
|
-
|
|
661
|
-
|
|
662
|
-
|
|
663
|
-
|
|
664
|
-
|
|
665
|
-
|
|
666
|
-
|
|
667
|
-
|
|
668
|
-
|
|
669
|
-
|
|
670
|
-
|
|
671
|
-
|
|
672
|
-
|
|
673
|
-
|
|
674
|
-
|
|
675
|
-
|
|
676
|
-
|
|
677
|
-
|
|
678
|
-
|
|
679
|
-
|
|
680
|
-
|
|
681
|
-
|
|
682
|
-
|
|
683
|
-
|
|
684
|
-
|
|
685
|
-
|
|
686
|
-
|
|
687
|
-
|
|
688
|
-
|
|
689
|
-
|
|
690
|
-
|
|
691
|
-
|
|
692
|
-
|
|
693
|
-
|
|
694
|
-
|
|
695
|
-
|
|
696
|
-
|
|
697
|
-
|
|
698
|
-
|
|
699
|
-
|
|
700
|
-
|
|
701
|
-
|
|
702
|
-
|
|
703
|
-
|
|
704
|
-
|
|
705
|
-
|
|
706
|
-
|
|
707
|
-
|
|
708
|
-
|
|
709
|
-
|
|
710
|
-
|
|
711
|
-
|
|
712
|
-
|
|
713
|
-
|
|
714
|
-
|
|
715
|
-
|
|
716
|
-
|
|
717
|
-
|
|
718
|
-
|
|
719
|
-
|
|
720
|
-
|
|
721
|
-
|
|
722
|
-
|
|
723
|
-
|
|
724
|
-
|
|
725
|
-
|
|
726
|
-
|
|
727
|
-
|
|
728
|
-
|
|
729
|
-
|
|
730
|
-
|
|
731
|
-
|
|
732
|
-
|
|
733
|
-
|
|
734
|
-
|
|
735
|
-
|
|
736
|
-
|
|
737
|
-
|
|
738
|
-
|
|
739
|
-
|
|
740
|
-
|
|
741
|
-
|
|
742
|
-
|
|
743
|
-
|
|
744
|
-
|
|
745
|
-
|
|
746
|
-
|
|
747
|
-
|
|
748
|
-
|
|
749
|
-
|
|
750
|
-
|
|
751
|
-
|
|
752
|
-
|
|
753
|
-
|
|
754
|
-
|
|
755
|
-
|
|
756
|
-
|
|
757
|
-
|
|
758
|
-
|
|
759
|
-
|
|
760
|
-
|
|
761
|
-
|
|
762
|
-
|
|
763
|
-
|
|
764
|
-
|
|
765
|
-
|
|
766
|
-
|
|
767
|
-
|
|
768
|
-
|
|
769
|
-
|
|
770
|
-
|
|
771
|
-
|
|
772
|
-
|
|
773
|
-
|
|
774
|
-
|
|
775
|
-
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
|
|
779
|
-
|
|
780
|
-
|
|
781
|
-
|
|
782
|
-
|
|
783
|
-
|
|
784
|
-
|
|
785
|
-
|
|
786
|
-
|
|
787
|
-
|
|
788
|
-
|
|
789
|
-
|
|
790
|
-
|
|
791
|
-
|
|
792
|
-
|
|
793
|
-
|
|
794
|
-
|
|
795
|
-
|
|
796
|
-
|
|
797
|
-
|
|
798
|
-
|
|
799
|
-
|
|
800
|
-
|
|
801
|
-
|
|
802
|
-
|
|
803
|
-
|
|
804
|
-
|
|
805
|
-
|
|
806
|
-
|
|
807
|
-
|
|
808
|
-
|
|
809
|
-
|
|
810
|
-
|
|
811
|
-
|
|
812
|
-
|
|
813
|
-
|
|
814
|
-
|
|
815
|
-
|
|
816
|
-
|
|
817
|
-
|
|
818
|
-
|
|
819
|
-
|
|
820
|
-
|
|
821
|
-
|
|
822
|
-
|
|
823
|
-
|
|
824
|
-
|
|
825
|
-
|
|
826
|
-
|
|
827
|
-
|
|
828
|
-
|
|
829
|
-
|
|
830
|
-
|
|
831
|
-
|
|
832
|
-
|
|
833
|
-
|
|
834
|
-
|
|
835
|
-
|
|
836
|
-
|
|
837
|
-
|
|
838
|
-
|
|
839
|
-
|
|
840
|
-
|
|
841
|
-
|
|
842
|
-
|
|
843
|
-
|
|
844
|
-
|
|
845
|
-
|
|
846
|
-
|
|
847
|
-
|
|
848
|
-
|
|
849
|
-
|
|
850
|
-
|
|
851
|
-
|
|
852
|
-
|
|
853
|
-
|
|
854
|
-
|
|
855
|
-
|
|
856
|
-
|
|
857
|
-
|
|
858
|
-
|
|
859
|
-
|
|
860
|
-
|
|
861
|
-
|
|
862
|
-
|
|
863
|
-
|
|
864
|
-
|
|
865
|
-
|
|
866
|
-
|
|
867
|
-
|
|
868
|
-
|
|
869
|
-
|
|
870
|
-
|
|
871
|
-
|
|
872
|
-
|
|
873
|
-
|
|
874
|
-
|
|
875
|
-
|
|
876
|
-
|
|
877
|
-
|
|
878
|
-
|
|
879
|
-
|
|
880
|
-
|
|
881
|
-
|
|
882
|
-
|
|
883
|
-
|
|
884
|
-
|
|
885
|
-
|
|
886
|
-
|
|
887
|
-
|
|
888
|
-
|
|
889
|
-
|
|
890
|
-
|
|
891
|
-
|
|
892
|
-
|
|
893
|
-
|
|
894
|
-
|
|
895
|
-
|
|
896
|
-
|
|
897
|
-
|
|
898
|
-
|
|
899
|
-
|
|
900
|
-
|
|
901
|
-
|
|
902
|
-
|
|
903
|
-
|
|
904
|
-
|
|
905
|
-
|
|
906
|
-
|
|
907
|
-
|
|
908
|
-
|
|
909
|
-
|
|
910
|
-
|
|
911
|
-
|
|
912
|
-
|
|
913
|
-
|
|
914
|
-
|
|
915
|
-
|
|
916
|
-
|
|
917
|
-
|
|
918
|
-
|
|
919
|
-
|
|
920
|
-
|
|
921
|
-
|
|
922
|
-
|
|
923
|
-
|
|
924
|
-
|
|
925
|
-
|
|
926
|
-
|
|
927
|
-
|
|
928
|
-
|
|
929
|
-
|
|
930
|
-
|
|
931
|
-
|
|
932
|
-
|
|
933
|
-
|
|
934
|
-
|
|
935
|
-
|
|
936
|
-
|
|
937
|
-
|
|
938
|
-
|
|
939
|
-
|
|
940
|
-
|
|
941
|
-
|
|
942
|
-
|
|
943
|
-
|
|
944
|
-
|
|
945
|
-
|
|
946
|
-
|
|
947
|
-
|
|
948
|
-
|
|
949
|
-
|
|
950
|
-
|
|
951
|
-
|
|
952
|
-
|
|
953
|
-
|
|
954
|
-
|
|
955
|
-
|
|
956
|
-
|
|
957
|
-
|
|
958
|
-
|
|
959
|
-
|
|
960
|
-
|
|
961
|
-
|
|
962
|
-
|
|
963
|
-
|
|
964
|
-
|
|
965
|
-
|
|
966
|
-
|
|
967
|
-
|
|
968
|
-
|
|
969
|
-
|
|
970
|
-
|
|
971
|
-
|
|
972
|
-
|
|
973
|
-
|
|
974
|
-
|
|
975
|
-
|
|
976
|
-
|
|
977
|
-
|
|
978
|
-
|
|
979
|
-
|
|
980
|
-
|
|
981
|
-
|
|
982
|
-
|
|
983
|
-
|
|
984
|
-
|
|
985
|
-
|
|
986
|
-
|
|
987
|
-
|
|
988
|
-
|
|
989
|
-
|
|
990
|
-
|
|
991
|
-
|
|
992
|
-
|
|
993
|
-
|
|
994
|
-
|
|
995
|
-
|
|
996
|
-
|
|
997
|
-
|
|
998
|
-
|
|
999
|
-
|
|
1000
|
-
|
|
1001
|
-
|
|
1002
|
-
|
|
1003
|
-
|
|
1004
|
-
|
|
1005
|
-
|
|
1006
|
-
|
|
1007
|
-
|
|
1008
|
-
|
|
1009
|
-
|
|
1010
|
-
|
|
1011
|
-
|
|
1012
|
-
|
|
1013
|
-
|
|
1014
|
-
|
|
1015
|
-
|
|
1016
|
-
|
|
1017
|
-
|
|
1018
|
-
|
|
1019
|
-
|
|
1020
|
-
|
|
1021
|
-
|
|
1022
|
-
|
|
1023
|
-
|
|
1024
|
-
|
|
1025
|
-
|
|
1026
|
-
|
|
1027
|
-
|
|
1028
|
-
|
|
1029
|
-
|
|
1030
|
-
|
|
1031
|
-
|
|
1032
|
-
|
|
1033
|
-
|
|
1034
|
-
|
|
1035
|
-
|
|
1036
|
-
|
|
1037
|
-
|
|
1038
|
-
|
|
1039
|
-
|
|
1040
|
-
|
|
1041
|
-
|
|
1042
|
-
|
|
1043
|
-
|
|
1044
|
-
|
|
1045
|
-
|
|
1046
|
-
|
|
1047
|
-
|
|
1048
|
-
|
|
1049
|
-
|
|
1050
|
-
|
|
1051
|
-
|
|
1052
|
-
|
|
1053
|
-
|
|
1054
|
-
|
|
1055
|
-
|
|
1056
|
-
|
|
1057
|
-
|
|
1058
|
-
|
|
1059
|
-
|
|
1060
|
-
|
|
1061
|
-
|
|
1062
|
-
|
|
1063
|
-
|
|
1064
|
-
|
|
1065
|
-
|
|
1066
|
-
|
|
1067
|
-
|
|
1068
|
-
|
|
1069
|
-
|
|
1070
|
-
|
|
1071
|
-
|
|
1072
|
-
|
|
1073
|
-
|
|
1074
|
-
|
|
1075
|
-
|
|
1076
|
-
|
|
1077
|
-
|
|
1078
|
-
|
|
1079
|
-
|
|
1080
|
-
|
|
1081
|
-
|
|
1082
|
-
|
|
1083
|
-
|
|
1084
|
-
|
|
1085
|
-
|
|
1086
|
-
|
|
1087
|
-
|
|
1088
|
-
|
|
1089
|
-
|
|
1090
|
-
|
|
1091
|
-
|
|
1092
|
-
|
|
1093
|
-
|
|
1094
|
-
|
|
1095
|
-
|
|
1096
|
-
|
|
1097
|
-
|
|
1098
|
-
|
|
1099
|
-
|
|
1100
|
-
|
|
1101
|
-
|
|
1102
|
-
|
|
1103
|
-
|
|
1104
|
-
|
|
1105
|
-
|
|
1106
|
-
|
|
1107
|
-
|
|
1108
|
-
|
|
1109
|
-
|
|
1110
|
-
|
|
1111
|
-
|
|
1112
|
-
|
|
1113
|
-
|
|
1114
|
-
|
|
1115
|
-
|
|
1116
|
-
|
|
1117
|
-
|
|
1118
|
-
|
|
1119
|
-
|
|
1120
|
-
|
|
1121
|
-
|
|
1122
|
-
|
|
1123
|
-
|
|
1124
|
-
|
|
1125
|
-
|
|
1126
|
-
|
|
1127
|
-
|
|
1128
|
-
|
|
1129
|
-
|
|
1130
|
-
|
|
1131
|
-
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
|
|
1135
|
-
|
|
1136
|
-
|
|
1137
|
-
|
|
1138
|
-
|
|
1139
|
-
|
|
1140
|
-
|
|
1141
|
-
|
|
1142
|
-
|
|
1143
|
-
|
|
1144
|
-
|
|
1145
|
-
|
|
1146
|
-
|
|
1147
|
-
|
|
1148
|
-
|
|
1149
|
-
|
|
1150
|
-
|
|
1151
|
-
|
|
1152
|
-
|
|
1153
|
-
|
|
1154
|
-
|
|
1155
|
-
|
|
1156
|
-
|
|
1157
|
-
|
|
1158
|
-
|
|
1159
|
-
|
|
1160
|
-
|
|
1161
|
-
|
|
1162
|
-
|
|
1163
|
-
|
|
1164
|
-
|
|
1165
|
-
|
|
1166
|
-
|
|
1167
|
-
|
|
1168
|
-
|
|
1169
|
-
|
|
1170
|
-
|
|
1171
|
-
|
|
1172
|
-
|
|
1173
|
-
|
|
1174
|
-
|
|
1175
|
-
|
|
1176
|
-
|
|
1177
|
-
|
|
1178
|
-
|
|
1179
|
-
|
|
1180
|
-
|
|
1181
|
-
|
|
1182
|
-
|
|
1183
|
-
|
|
1184
|
-
|
|
1185
|
-
|
|
1186
|
-
|
|
1187
|
-
|
|
1188
|
-
|
|
1189
|
-
|
|
1190
|
-
|
|
1191
|
-
|
|
1192
|
-
|
|
1193
|
-
|
|
1194
|
-
|
|
1195
|
-
|
|
1196
|
-
|
|
1197
|
-
|
|
1198
|
-
|
|
1199
|
-
|
|
1200
|
-
|
|
1201
|
-
|
|
1202
|
-
|
|
1203
|
-
|
|
1204
|
-
|
|
1205
|
-
|
|
1206
|
-
|
|
1207
|
-
|
|
1208
|
-
|
|
1209
|
-
|
|
1210
|
-
|
|
1211
|
-
|
|
1212
|
-
|
|
1213
|
-
|
|
1214
|
-
|
|
1215
|
-
|
|
1216
|
-
|
|
1217
|
-
|
|
1218
|
-
|
|
1219
|
-
|
|
1220
|
-
|
|
1221
|
-
|
|
1222
|
-
|
|
1223
|
-
|
|
1224
|
-
|
|
1225
|
-
|
|
1226
|
-
|
|
1227
|
-
|
|
1228
|
-
|
|
1229
|
-
|
|
1230
|
-
|
|
1231
|
-
|
|
1232
|
-
|
|
1233
|
-
|
|
1234
|
-
|
|
1235
|
-
|
|
1236
|
-
|
|
1237
|
-
|
|
1238
|
-
|
|
1239
|
-
|
|
1240
|
-
|
|
1241
|
-
|
|
1242
|
-
|
|
1243
|
-
|
|
1244
|
-
|
|
1245
|
-
|
|
1246
|
-
|
|
1247
|
-
|
|
1248
|
-
|
|
1249
|
-
|
|
1250
|
-
|
|
1251
|
-
|
|
1252
|
-
|
|
1253
|
-
|
|
1254
|
-
|
|
1255
|
-
|
|
1256
|
-
|
|
1257
|
-
|
|
1258
|
-
|
|
1259
|
-
|
|
1260
|
-
|
|
1261
|
-
|
|
1262
|
-
|
|
1263
|
-
|
|
1264
|
-
|
|
1265
|
-
|
|
1266
|
-
|
|
1267
|
-
|
|
1268
|
-
|
|
1269
|
-
|
|
1270
|
-
|
|
1271
|
-
|
|
1272
|
-
|
|
1273
|
-
|
|
1274
|
-
|
|
1275
|
-
|
|
1276
|
-
|
|
1277
|
-
|
|
1278
|
-
|
|
1279
|
-
|
|
1280
|
-
|
|
1281
|
-
|
|
1282
|
-
|
|
1283
|
-
|
|
1284
|
-
|
|
1285
|
-
|
|
1286
|
-
|
|
1287
|
-
|
|
1288
|
-
|
|
1289
|
-
|
|
1290
|
-
|
|
1291
|
-
|
|
1292
|
-
|
|
1293
|
-
|
|
1294
|
-
|
|
1295
|
-
|
|
1296
|
-
|
|
1297
|
-
|
|
1298
|
-
|
|
1299
|
-
|
|
1300
|
-
|
|
1301
|
-
|
|
1302
|
-
|
|
1303
|
-
|
|
1304
|
-
|
|
1305
|
-
|
|
1306
|
-
|
|
1307
|
-
|
|
1308
|
-
|
|
1309
|
-
|
|
1310
|
-
|
|
1311
|
-
|
|
1312
|
-
|
|
1313
|
-
|
|
1314
|
-
|
|
1315
|
-
|
|
1316
|
-
|
|
1317
|
-
|
|
1318
|
-
|
|
1319
|
-
|
|
1320
|
-
|
|
1321
|
-
|
|
1322
|
-
|
|
1323
|
-
|
|
1324
|
-
|
|
1325
|
-
|
|
1326
|
-
|
|
1327
|
-
|
|
1328
|
-
|
|
1329
|
-
|
|
1330
|
-
|
|
1331
|
-
|
|
1332
|
-
|
|
1333
|
-
|
|
1334
|
-
|
|
1335
|
-
|
|
1336
|
-
|
|
1337
|
-
|
|
1338
|
-
|
|
1339
|
-
|
|
1340
|
-
|
|
1341
|
-
|
|
1342
|
-
|
|
1343
|
-
|
|
1344
|
-
|
|
1345
|
-
|
|
1346
|
-
|
|
1347
|
-
|
|
1348
|
-
|
|
1349
|
-
|
|
1350
|
-
|
|
1351
|
-
|
|
1352
|
-
|
|
1353
|
-
|
|
1354
|
-
|
|
1355
|
-
|
|
1356
|
-
|
|
1357
|
-
|
|
1358
|
-
|
|
1359
|
-
|
|
1360
|
-
|
|
1361
|
-
|
|
1362
|
-
|
|
1363
|
-
|
|
1364
|
-
|
|
1365
|
-
|
|
1366
|
-
|
|
1367
|
-
|
|
1368
|
-
|
|
1369
|
-
|
|
1370
|
-
|
|
1371
|
-
|
|
1372
|
-
|
|
1373
|
-
|
|
1374
|
-
|
|
1375
|
-
|
|
1376
|
-
|
|
1377
|
-
|
|
1378
|
-
|
|
1379
|
-
|
|
1380
|
-
|
|
1381
|
-
|
|
1382
|
-
|
|
1383
|
-
|
|
1384
|
-
|
|
1385
|
-
|
|
1386
|
-
|
|
1387
|
-
|
|
1388
|
-
|
|
1389
|
-
|
|
1390
|
-
|
|
1391
|
-
|
|
1392
|
-
|
|
1393
|
-
|
|
1394
|
-
|
|
1395
|
-
|
|
1396
|
-
|
|
1397
|
-
|
|
1398
|
-
|
|
1399
|
-
|
|
1400
|
-
|
|
1401
|
-
|
|
1402
|
-
|
|
1403
|
-
|
|
1404
|
-
|
|
1405
|
-
|
|
1406
|
-
|
|
1407
|
-
|
|
1408
|
-
|
|
1409
|
-
|
|
1410
|
-
|
|
1411
|
-
|
|
1412
|
-
|
|
1413
|
-
|
|
1414
|
-
|
|
1415
|
-
|
|
1416
|
-
|
|
1417
|
-
|
|
1418
|
-
|
|
1419
|
-
|
|
1420
|
-
|
|
1421
|
-
|
|
1422
|
-
|
|
1423
|
-
|
|
1424
|
-
|
|
1425
|
-
|
|
1426
|
-
|
|
1427
|
-
|
|
1428
|
-
|
|
1429
|
-
|
|
1430
|
-
|
|
1431
|
-
|
|
1432
|
-
|
|
1433
|
-
|
|
1434
|
-
|
|
1435
|
-
|
|
1436
|
-
|
|
1437
|
-
|
|
1438
|
-
|
|
1439
|
-
|
|
1440
|
-
|
|
1441
|
-
|
|
1442
|
-
|
|
1443
|
-
|
|
1444
|
-
|
|
1445
|
-
|
|
1446
|
-
|
|
1447
|
-
|
|
1448
|
-
|
|
1449
|
-
|
|
1450
|
-
|
|
1451
|
-
|
|
1452
|
-
|
|
1453
|
-
|
|
1454
|
-
|
|
1455
|
-
|
|
1456
|
-
|
|
1457
|
-
|
|
1458
|
-
|
|
1459
|
-
|
|
1460
|
-
|
|
1461
|
-
|
|
1462
|
-
|
|
1463
|
-
|
|
1464
|
-
|
|
1465
|
-
|
|
1466
|
-
|
|
1467
|
-
|
|
1468
|
-
|
|
1469
|
-
|
|
1470
|
-
|
|
1471
|
-
|
|
1472
|
-
|
|
1473
|
-
|
|
1474
|
-
|
|
1475
|
-
|
|
1476
|
-
|
|
1477
|
-
|
|
1478
|
-
|
|
1479
|
-
|
|
1480
|
-
|
|
1481
|
-
|
|
1482
|
-
|
|
1483
|
-
|
|
1484
|
-
|
|
1485
|
-
|
|
1486
|
-
|
|
1487
|
-
|
|
1488
|
-
|
|
1489
|
-
|
|
1490
|
-
|
|
1491
|
-
|
|
1492
|
-
|
|
1493
|
-
|
|
1494
|
-
|
|
1495
|
-
|
|
1496
|
-
|
|
1497
|
-
|
|
1498
|
-
|
|
1499
|
-
|
|
1500
|
-
|
|
1501
|
-
|
|
1502
|
-
|
|
1503
|
-
|
|
1504
|
-
|
|
1505
|
-
|
|
1506
|
-
|
|
1507
|
-
|
|
1508
|
-
|
|
1509
|
-
|
|
1510
|
-
|
|
1511
|
-
|
|
1512
|
-
|
|
1513
|
-
|
|
1514
|
-
|
|
1515
|
-
|
|
1516
|
-
|
|
1517
|
-
|
|
1518
|
-
|
|
1519
|
-
|
|
1520
|
-
|
|
1521
|
-
|
|
1522
|
-
|
|
1523
|
-
|
|
1524
|
-
|
|
1525
|
-
|
|
1526
|
-
|
|
1527
|
-
|
|
1528
|
-
|
|
1529
|
-
|
|
1530
|
-
|
|
1531
|
-
|
|
1532
|
-
|
|
1533
|
-
|
|
1534
|
-
|
|
1535
|
-
|
|
1536
|
-
|
|
1537
|
-
|
|
1538
|
-
|
|
1539
|
-
|
|
1540
|
-
|
|
1541
|
-
|
|
1542
|
-
|
|
1543
|
-
|
|
1544
|
-
|
|
1545
|
-
|
|
1546
|
-
|
|
1547
|
-
|
|
1548
|
-
|
|
1549
|
-
|
|
1550
|
-
|
|
1551
|
-
|
|
1552
|
-
|
|
1553
|
-
|
|
1554
|
-
|
|
1555
|
-
|
|
1556
|
-
|
|
1557
|
-
|
|
1558
|
-
|
|
1559
|
-
|
|
1560
|
-
|
|
1561
|
-
|
|
1562
|
-
|
|
1563
|
-
|
|
1564
|
-
|
|
1565
|
-
|
|
1566
|
-
|
|
1567
|
-
|
|
1568
|
-
|
|
1569
|
-
|
|
1570
|
-
|
|
1571
|
-
|
|
1572
|
-
|
|
1573
|
-
|
|
1574
|
-
|
|
1575
|
-
|
|
1576
|
-
|
|
1577
|
-
|
|
1578
|
-
|
|
1579
|
-
|
|
1580
|
-
|
|
1581
|
-
|
|
1582
|
-
|
|
1583
|
-
|
|
1584
|
-
|
|
1585
|
-
|
|
1586
|
-
|
|
1587
|
-
|
|
1588
|
-
|
|
1589
|
-
|
|
1590
|
-
|
|
1591
|
-
|
|
1592
|
-
|
|
1593
|
-
|
|
1594
|
-
|
|
1595
|
-
|
|
2
|
+
AblePlayer.prototype.seekTo = function (newTime) {
|
|
3
|
+
|
|
4
|
+
// define variables to be used for analytics
|
|
5
|
+
// e.g., to measure the extent to which users seek back and forward
|
|
6
|
+
this.seekFromTime = this.media.currentTime;
|
|
7
|
+
this.seekToTime = newTime;
|
|
8
|
+
|
|
9
|
+
this.seeking = true;
|
|
10
|
+
this.liveUpdatePending = true;
|
|
11
|
+
|
|
12
|
+
if (this.player === 'html5') {
|
|
13
|
+
var seekable;
|
|
14
|
+
|
|
15
|
+
this.startTime = newTime;
|
|
16
|
+
// Check HTML5 media "seekable" property to be sure media is seekable to startTime
|
|
17
|
+
seekable = this.media.seekable;
|
|
18
|
+
if (seekable.length > 0 && this.startTime >= seekable.start(0) && this.startTime <= seekable.end(0)) {
|
|
19
|
+
// ok to seek to startTime
|
|
20
|
+
// canplaythrough will be triggered when seeking is complete
|
|
21
|
+
// this.seeking will be set to false at that point
|
|
22
|
+
this.media.currentTime = this.startTime;
|
|
23
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
24
|
+
// keep sign languge video in sync
|
|
25
|
+
this.signVideo.currentTime = this.startTime;
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
}
|
|
29
|
+
else if (this.player === 'youtube') {
|
|
30
|
+
this.youTubePlayer.seekTo(newTime,true);
|
|
31
|
+
if (newTime > 0) {
|
|
32
|
+
if (typeof this.$posterImg !== 'undefined') {
|
|
33
|
+
this.$posterImg.hide();
|
|
34
|
+
}
|
|
35
|
+
}
|
|
36
|
+
}
|
|
37
|
+
else if (this.player === 'vimeo') {
|
|
38
|
+
this.vimeoPlayer.setCurrentTime(newTime).then(function() {
|
|
39
|
+
// seek finished.
|
|
40
|
+
// could do something here
|
|
41
|
+
// successful completion also fires a 'seeked' event (see event.js)
|
|
42
|
+
})
|
|
43
|
+
}
|
|
44
|
+
this.refreshControls('timeline');
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
AblePlayer.prototype.getMediaTimes = function (duration, elapsed) {
|
|
48
|
+
|
|
49
|
+
// Returns an array with keys 'duration' and 'elapsed'
|
|
50
|
+
// Vars passed to this function come courtesy of select Vimeo events
|
|
51
|
+
// Use those if they're available.
|
|
52
|
+
// Otherwise, will need to call the relevant media API
|
|
53
|
+
// This function should only be called from onMediaUpdateTime()
|
|
54
|
+
// If duration and elapsed are needed other times, use this.duration and this.elapsed
|
|
55
|
+
|
|
56
|
+
// both values are expressed in seconds, and all player APIs are similar:
|
|
57
|
+
// they return a value that is rounded to the nearest second before playback begins,
|
|
58
|
+
// then to the nearest thousandth of a second after playback begins
|
|
59
|
+
// With HTML5 media API, some browsers are more precise (e.g., Firefox rounds to 6 decimal points)
|
|
60
|
+
// but inconsistent (values with 9 decimal points have been sporadically observed in Safari)
|
|
61
|
+
// For standardization, values are rounded to 6 decimal points before they're returned
|
|
62
|
+
|
|
63
|
+
var deferred, promise, thisObj, mediaTimes;
|
|
64
|
+
mediaTimes = {};
|
|
65
|
+
|
|
66
|
+
deferred = new $.Deferred();
|
|
67
|
+
promise = deferred.promise();
|
|
68
|
+
thisObj = this;
|
|
69
|
+
|
|
70
|
+
if (typeof duration !== 'undefined' && typeof elapsed !== 'undefined') {
|
|
71
|
+
mediaTimes['duration'] = duration;
|
|
72
|
+
mediaTimes['elapsed'] = elapsed;
|
|
73
|
+
deferred.resolve(mediaTimes);
|
|
74
|
+
}
|
|
75
|
+
else {
|
|
76
|
+
this.getDuration().then(function(duration) {
|
|
77
|
+
mediaTimes['duration'] = thisObj.roundDown(duration,6);
|
|
78
|
+
thisObj.getElapsed().then(function(elapsed) {
|
|
79
|
+
mediaTimes['elapsed'] = thisObj.roundDown(elapsed,6);
|
|
80
|
+
deferred.resolve(mediaTimes);
|
|
81
|
+
});
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
return promise;
|
|
85
|
+
};
|
|
86
|
+
|
|
87
|
+
AblePlayer.prototype.getDuration = function () {
|
|
88
|
+
|
|
89
|
+
// returns duration of the current media, expressed in seconds
|
|
90
|
+
// function is called by getMediaTimes, and return value is sanitized there
|
|
91
|
+
|
|
92
|
+
var deferred, promise, thisObj;
|
|
93
|
+
|
|
94
|
+
deferred = new $.Deferred();
|
|
95
|
+
promise = deferred.promise();
|
|
96
|
+
thisObj = this;
|
|
97
|
+
|
|
98
|
+
if (this.player === 'vimeo') {
|
|
99
|
+
if (this.vimeoPlayer) {
|
|
100
|
+
this.vimeoPlayer.getDuration().then(function(duration) {
|
|
101
|
+
if (duration === undefined || isNaN(duration) || duration === -1) {
|
|
102
|
+
deferred.resolve(0);
|
|
103
|
+
}
|
|
104
|
+
else {
|
|
105
|
+
deferred.resolve(duration);
|
|
106
|
+
}
|
|
107
|
+
});
|
|
108
|
+
}
|
|
109
|
+
else { // vimeoPlayer hasn't been initialized yet.
|
|
110
|
+
deferred.resolve(0);
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
else {
|
|
114
|
+
var duration;
|
|
115
|
+
if (this.player === 'html5') {
|
|
116
|
+
duration = this.media.duration;
|
|
117
|
+
}
|
|
118
|
+
else if (this.player === 'youtube') {
|
|
119
|
+
if (this.youTubePlayer) {
|
|
120
|
+
duration = this.youTubePlayer.getDuration();
|
|
121
|
+
}
|
|
122
|
+
else { // the YouTube player hasn't initialized yet
|
|
123
|
+
duration = 0;
|
|
124
|
+
}
|
|
125
|
+
}
|
|
126
|
+
if (duration === undefined || isNaN(duration) || duration === -1) {
|
|
127
|
+
deferred.resolve(0);
|
|
128
|
+
}
|
|
129
|
+
else {
|
|
130
|
+
deferred.resolve(duration);
|
|
131
|
+
}
|
|
132
|
+
}
|
|
133
|
+
return promise;
|
|
134
|
+
};
|
|
135
|
+
|
|
136
|
+
AblePlayer.prototype.getElapsed = function () {
|
|
137
|
+
|
|
138
|
+
// returns elapsed time of the current media, expressed in seconds
|
|
139
|
+
// function is called by getMediaTimes, and return value is sanitized there
|
|
140
|
+
|
|
141
|
+
var deferred, promise, thisObj;
|
|
142
|
+
|
|
143
|
+
deferred = new $.Deferred();
|
|
144
|
+
promise = deferred.promise();
|
|
145
|
+
thisObj = this;
|
|
146
|
+
|
|
147
|
+
if (this.player === 'vimeo') {
|
|
148
|
+
if (this.vimeoPlayer) {
|
|
149
|
+
this.vimeoPlayer.getCurrentTime().then(function(elapsed) {
|
|
150
|
+
if (elapsed === undefined || isNaN(elapsed) || elapsed === -1) {
|
|
151
|
+
deferred.resolve(0);
|
|
152
|
+
}
|
|
153
|
+
else {
|
|
154
|
+
deferred.resolve(elapsed);
|
|
155
|
+
}
|
|
156
|
+
});
|
|
157
|
+
}
|
|
158
|
+
else { // vimeoPlayer hasn't been initialized yet.
|
|
159
|
+
deferred.resolve(0);
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
else {
|
|
163
|
+
var elapsed;
|
|
164
|
+
if (this.player === 'html5') {
|
|
165
|
+
elapsed = this.media.currentTime;
|
|
166
|
+
}
|
|
167
|
+
else if (this.player === 'youtube') {
|
|
168
|
+
if (this.youTubePlayer) {
|
|
169
|
+
elapsed = this.youTubePlayer.getCurrentTime();
|
|
170
|
+
}
|
|
171
|
+
else { // the YouTube player hasn't initialized yet
|
|
172
|
+
elapsed = 0;
|
|
173
|
+
}
|
|
174
|
+
}
|
|
175
|
+
if (elapsed === undefined || isNaN(elapsed) || elapsed === -1) {
|
|
176
|
+
deferred.resolve(0);
|
|
177
|
+
}
|
|
178
|
+
else {
|
|
179
|
+
deferred.resolve(elapsed);
|
|
180
|
+
}
|
|
181
|
+
}
|
|
182
|
+
return promise;
|
|
183
|
+
};
|
|
184
|
+
|
|
185
|
+
AblePlayer.prototype.getPlayerState = function () {
|
|
186
|
+
|
|
187
|
+
// Returns one of the following states:
|
|
188
|
+
// 'stopped' - Not yet played for the first time, or otherwise reset to unplayed.
|
|
189
|
+
// 'ended' - Finished playing.
|
|
190
|
+
// 'paused' - Not playing, but not stopped or ended.
|
|
191
|
+
// 'buffering' - Momentarily paused to load, but will resume once data is loaded.
|
|
192
|
+
// 'playing' - Currently playing.
|
|
193
|
+
|
|
194
|
+
// Commented out the following in 3.2.1 - not sure of its intended purpose
|
|
195
|
+
// It can be useful to know player state even when swapping src
|
|
196
|
+
// and the overhead is seemingly minimal
|
|
197
|
+
/*
|
|
198
|
+
if (this.swappingSrc) {
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
*/
|
|
202
|
+
|
|
203
|
+
var deferred, promise, thisObj, duration, elapsed;
|
|
204
|
+
deferred = new $.Deferred();
|
|
205
|
+
promise = deferred.promise();
|
|
206
|
+
thisObj = this;
|
|
207
|
+
|
|
208
|
+
if (this.player === 'html5') {
|
|
209
|
+
if (this.media.ended) {
|
|
210
|
+
deferred.resolve('ended');
|
|
211
|
+
}
|
|
212
|
+
else if (this.media.paused) {
|
|
213
|
+
deferred.resolve('paused');
|
|
214
|
+
}
|
|
215
|
+
else if (this.media.readyState !== 4) {
|
|
216
|
+
deferred.resolve('buffering');
|
|
217
|
+
}
|
|
218
|
+
else {
|
|
219
|
+
deferred.resolve('playing');
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
else if (this.player === 'youtube' && this.youTubePlayer) {
|
|
223
|
+
var state = this.youTubePlayer.getPlayerState();
|
|
224
|
+
if (state === -1 || state === 5) {
|
|
225
|
+
deferred.resolve('stopped');
|
|
226
|
+
}
|
|
227
|
+
else if (state === 0) {
|
|
228
|
+
deferred.resolve('ended');
|
|
229
|
+
}
|
|
230
|
+
else if (state === 1) {
|
|
231
|
+
deferred.resolve('playing');
|
|
232
|
+
}
|
|
233
|
+
else if (state === 2) {
|
|
234
|
+
deferred.resolve('paused');
|
|
235
|
+
}
|
|
236
|
+
else if (state === 3) {
|
|
237
|
+
deferred.resolve('buffering');
|
|
238
|
+
}
|
|
239
|
+
}
|
|
240
|
+
else if (this.player === 'vimeo' && this.vimeoPlayer) {
|
|
241
|
+
// curiously, Vimeo's API has no getPlaying(), getBuffering(), or getState() methods
|
|
242
|
+
// so maybe if it's neither paused nor ended, it must be playing???
|
|
243
|
+
this.vimeoPlayer.getPaused().then(function(paused) {
|
|
244
|
+
if (paused) {
|
|
245
|
+
deferred.resolve('paused');
|
|
246
|
+
}
|
|
247
|
+
else {
|
|
248
|
+
thisObj.vimeoPlayer.getEnded().then(function(ended) {
|
|
249
|
+
if (ended) {
|
|
250
|
+
deferred.resolve('ended');
|
|
251
|
+
}
|
|
252
|
+
else {
|
|
253
|
+
deferred.resolve('playing');
|
|
254
|
+
}
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
});
|
|
258
|
+
}
|
|
259
|
+
return promise;
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
AblePlayer.prototype.isPlaybackRateSupported = function () {
|
|
263
|
+
|
|
264
|
+
if (this.player === 'html5') {
|
|
265
|
+
if (this.media.playbackRate) {
|
|
266
|
+
return true;
|
|
267
|
+
}
|
|
268
|
+
else {
|
|
269
|
+
return false;
|
|
270
|
+
}
|
|
271
|
+
}
|
|
272
|
+
else if (this.player === 'youtube') {
|
|
273
|
+
// Youtube supports varying playback rates per video. Only expose controls if more than one playback rate is available.
|
|
274
|
+
if (this.youTubePlayer.getAvailablePlaybackRates().length > 1) {
|
|
275
|
+
return true;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
278
|
+
return false;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
else if (this.player === 'vimeo') {
|
|
282
|
+
// since this takes longer to determine, it was set previous in initVimeoPlayer()
|
|
283
|
+
return this.vimeoSupportsPlaybackRateChange;
|
|
284
|
+
}
|
|
285
|
+
};
|
|
286
|
+
|
|
287
|
+
AblePlayer.prototype.setPlaybackRate = function (rate) {
|
|
288
|
+
|
|
289
|
+
rate = Math.max(0.5, rate);
|
|
290
|
+
if (this.player === 'html5') {
|
|
291
|
+
this.media.playbackRate = rate;
|
|
292
|
+
}
|
|
293
|
+
else if (this.player === 'youtube') {
|
|
294
|
+
this.youTubePlayer.setPlaybackRate(rate);
|
|
295
|
+
}
|
|
296
|
+
else if (this.player === 'vimeo') {
|
|
297
|
+
this.vimeoPlayer.setPlaybackRate(rate);
|
|
298
|
+
}
|
|
299
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
300
|
+
this.signVideo.playbackRate = rate;
|
|
301
|
+
}
|
|
302
|
+
this.$speed.text(this.tt.speed + ': ' + rate.toFixed(2).toString() + 'x');
|
|
303
|
+
};
|
|
304
|
+
|
|
305
|
+
AblePlayer.prototype.getPlaybackRate = function () {
|
|
306
|
+
|
|
307
|
+
if (this.player === 'html5') {
|
|
308
|
+
return this.media.playbackRate;
|
|
309
|
+
}
|
|
310
|
+
else if (this.player === 'youtube') {
|
|
311
|
+
return this.youTubePlayer.getPlaybackRate();
|
|
312
|
+
}
|
|
313
|
+
};
|
|
314
|
+
|
|
315
|
+
AblePlayer.prototype.isPaused = function () {
|
|
316
|
+
|
|
317
|
+
// Note there are three player states that count as paused in this sense,
|
|
318
|
+
// and one of them is named 'paused'.
|
|
319
|
+
// A better name would be 'isCurrentlyNotPlayingOrBuffering'
|
|
320
|
+
|
|
321
|
+
var state;
|
|
322
|
+
|
|
323
|
+
if (this.player === 'vimeo') {
|
|
324
|
+
// just rely on value of this.playing
|
|
325
|
+
if (this.playing) {
|
|
326
|
+
return false;
|
|
327
|
+
}
|
|
328
|
+
else {
|
|
329
|
+
return true;
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
else {
|
|
333
|
+
this.getPlayerState().then(function(state) {
|
|
334
|
+
// if any of the following is true, consider the media 'paused'
|
|
335
|
+
return state === 'paused' || state === 'stopped' || state === 'ended';
|
|
336
|
+
});
|
|
337
|
+
}
|
|
338
|
+
};
|
|
339
|
+
|
|
340
|
+
AblePlayer.prototype.pauseMedia = function () {
|
|
341
|
+
|
|
342
|
+
var thisObj = this;
|
|
343
|
+
|
|
344
|
+
if (this.player === 'html5') {
|
|
345
|
+
this.media.pause(true);
|
|
346
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
347
|
+
this.signVideo.pause(true);
|
|
348
|
+
}
|
|
349
|
+
}
|
|
350
|
+
else if (this.player === 'youtube') {
|
|
351
|
+
this.youTubePlayer.pauseVideo();
|
|
352
|
+
}
|
|
353
|
+
else if (this.player === 'vimeo') {
|
|
354
|
+
this.vimeoPlayer.pause();
|
|
355
|
+
}
|
|
356
|
+
};
|
|
357
|
+
|
|
358
|
+
AblePlayer.prototype.playMedia = function () {
|
|
359
|
+
|
|
360
|
+
var thisObj = this;
|
|
361
|
+
|
|
362
|
+
if (this.player === 'html5') {
|
|
363
|
+
this.media.play(true);
|
|
364
|
+
if (this.hasSignLanguage && this.signVideo) {
|
|
365
|
+
this.signVideo.play(true);
|
|
366
|
+
}
|
|
367
|
+
}
|
|
368
|
+
else if (this.player === 'youtube') {
|
|
369
|
+
this.youTubePlayer.playVideo();
|
|
370
|
+
if (typeof this.$posterImg !== 'undefined') {
|
|
371
|
+
this.$posterImg.hide();
|
|
372
|
+
}
|
|
373
|
+
this.stoppingYouTube = false;
|
|
374
|
+
}
|
|
375
|
+
else if (this.player === 'vimeo') {
|
|
376
|
+
this.vimeoPlayer.play();
|
|
377
|
+
}
|
|
378
|
+
this.startedPlaying = true;
|
|
379
|
+
if (this.hideControls) {
|
|
380
|
+
// wait briefly after playback begins, then hide controls
|
|
381
|
+
this.hidingControls = true;
|
|
382
|
+
this.invokeHideControlsTimeout();
|
|
383
|
+
}
|
|
384
|
+
};
|
|
385
|
+
|
|
386
|
+
AblePlayer.prototype.fadeControls = function(direction) {
|
|
387
|
+
|
|
388
|
+
// Visibly fade controls without hiding them from screen reader users
|
|
389
|
+
|
|
390
|
+
// direction is either 'out' or 'in'
|
|
391
|
+
|
|
392
|
+
// TODO: This still needs work.
|
|
393
|
+
// After the player fades, it's replaced by an empty space
|
|
394
|
+
// Would be better if the video and captions expanded to fill the void
|
|
395
|
+
// Attempted to fade out to 0 opacity, then move the playerDiv offscreen
|
|
396
|
+
// and expand the mediaContainer to fill the vacated space
|
|
397
|
+
// However, my attempts to do this have been choppy and buggy
|
|
398
|
+
// Code is preserved below and commented out
|
|
399
|
+
|
|
400
|
+
var thisObj, mediaHeight, playerHeight, newMediaHeight;
|
|
401
|
+
var thisObj = this;
|
|
402
|
+
|
|
403
|
+
if (direction == 'out') {
|
|
404
|
+
// get the original height of two key components:
|
|
405
|
+
mediaHeight = this.$mediaContainer.height();
|
|
406
|
+
playerHeight = this.$playerDiv.height();
|
|
407
|
+
newMediaHeight = mediaHeight + playerHeight;
|
|
408
|
+
|
|
409
|
+
// fade slowly to transparency
|
|
410
|
+
this.$playerDiv.fadeTo(2000,0,function() {
|
|
411
|
+
/*
|
|
412
|
+
// when finished, position playerDiv offscreen
|
|
413
|
+
// thisObj.$playerDiv.addClass('able-offscreen');
|
|
414
|
+
// Expand the height of mediaContainer to fill the void (needs work)
|
|
415
|
+
thisObj.$mediaContainer.animate({
|
|
416
|
+
height: newMediaHeight
|
|
417
|
+
},500);
|
|
418
|
+
*/
|
|
419
|
+
});
|
|
420
|
+
}
|
|
421
|
+
else if (direction == 'in') {
|
|
422
|
+
// restore vidcapContainer to its original height (needs work)
|
|
423
|
+
// this.$mediaContainer.removeAttr('style');
|
|
424
|
+
// fade relatively quickly back to its original position with full opacity
|
|
425
|
+
// this.$playerDiv.removeClass('able-offscreen').fadeTo(100,1);
|
|
426
|
+
this.$playerDiv.fadeTo(100,1);
|
|
427
|
+
}
|
|
428
|
+
};
|
|
429
|
+
|
|
430
|
+
AblePlayer.prototype.invokeHideControlsTimeout = function () {
|
|
431
|
+
|
|
432
|
+
// invoke timeout for waiting a few seconds after a mouse move or key down
|
|
433
|
+
// before hiding controls again
|
|
434
|
+
var thisObj = this;
|
|
435
|
+
this.hideControlsTimeout = window.setTimeout(function() {
|
|
436
|
+
if (typeof thisObj.playing !== 'undefined' && thisObj.playing === true && thisObj.hideControls) {
|
|
437
|
+
thisObj.fadeControls('out');
|
|
438
|
+
thisObj.controlsHidden = true;
|
|
439
|
+
}
|
|
440
|
+
},5000);
|
|
441
|
+
this.hideControlsTimeoutStatus = 'active';
|
|
442
|
+
};
|
|
443
|
+
|
|
444
|
+
AblePlayer.prototype.refreshControls = function(context, duration, elapsed) {
|
|
445
|
+
|
|
446
|
+
// context is one of the following:
|
|
447
|
+
// 'init' - initial build (or subsequent change that requires full rebuild)
|
|
448
|
+
// 'timeline' - a change may effect time-related controls
|
|
449
|
+
// 'captions' - a change may effect caption-related controls
|
|
450
|
+
// 'descriptions' - a change may effect description-related controls
|
|
451
|
+
// 'transcript' - a change may effect the transcript window or button
|
|
452
|
+
// 'fullscreen' - a change has been triggered by full screen toggle
|
|
453
|
+
// 'playpause' - a change triggered by either a 'play' or 'pause' event
|
|
454
|
+
|
|
455
|
+
// NOTE: context is not currently supported.
|
|
456
|
+
// The steps in this function have too many complex interdependencies
|
|
457
|
+
// The gains in efficiency are offset by the possibility of introducing bugs
|
|
458
|
+
// For now, executing everything
|
|
459
|
+
context = 'init';
|
|
460
|
+
|
|
461
|
+
// duration and elapsed are passed from callback functions of Vimeo API events
|
|
462
|
+
// duration is expressed as sss.xxx
|
|
463
|
+
// elapsed is expressed as sss.xxx
|
|
464
|
+
|
|
465
|
+
var thisObj, duration, elapsed, lastChapterIndex, displayElapsed,
|
|
466
|
+
updateLive, textByState, timestamp, widthUsed,
|
|
467
|
+
leftControls, rightControls, seekbarWidth, seekbarSpacer, captionsCount,
|
|
468
|
+
buffered, newTop, statusBarHeight, speedHeight, statusBarWidthBreakpoint,
|
|
469
|
+
newSvgData;
|
|
470
|
+
|
|
471
|
+
thisObj = this;
|
|
472
|
+
if (this.swappingSrc) {
|
|
473
|
+
// wait until new source has loaded before refreshing controls
|
|
474
|
+
return;
|
|
475
|
+
}
|
|
476
|
+
|
|
477
|
+
if (context === 'timeline' || context === 'init') {
|
|
478
|
+
// all timeline-related functionality requires both duration and elapsed
|
|
479
|
+
if (typeof this.duration === 'undefined') {
|
|
480
|
+
// wait until duration is known before proceeding with refresh
|
|
481
|
+
return;
|
|
482
|
+
}
|
|
483
|
+
if (this.useChapterTimes) {
|
|
484
|
+
this.chapterDuration = this.getChapterDuration();
|
|
485
|
+
this.chapterElapsed = this.getChapterElapsed();
|
|
486
|
+
}
|
|
487
|
+
|
|
488
|
+
if (this.useFixedSeekInterval === false && this.seekIntervalCalculated === false && this.duration > 0) {
|
|
489
|
+
// couldn't calculate seekInterval previously; try again.
|
|
490
|
+
this.setSeekInterval();
|
|
491
|
+
}
|
|
492
|
+
|
|
493
|
+
if (this.seekBar) {
|
|
494
|
+
if (this.useChapterTimes) {
|
|
495
|
+
lastChapterIndex = this.selectedChapters.cues.length-1;
|
|
496
|
+
if (this.selectedChapters.cues[lastChapterIndex] == this.currentChapter) {
|
|
497
|
+
// this is the last chapter
|
|
498
|
+
if (this.currentChapter.end !== this.duration) {
|
|
499
|
+
// chapter ends before or after video ends
|
|
500
|
+
// need to adjust seekbar duration to match video end
|
|
501
|
+
this.seekBar.setDuration(this.duration - this.currentChapter.start);
|
|
502
|
+
}
|
|
503
|
+
else {
|
|
504
|
+
this.seekBar.setDuration(this.chapterDuration);
|
|
505
|
+
}
|
|
506
|
+
}
|
|
507
|
+
else {
|
|
508
|
+
// this is not the last chapter
|
|
509
|
+
this.seekBar.setDuration(this.chapterDuration);
|
|
510
|
+
}
|
|
511
|
+
}
|
|
512
|
+
else {
|
|
513
|
+
if (!(this.duration === undefined || isNaN(this.duration) || this.duration === -1)) {
|
|
514
|
+
this.seekBar.setDuration(this.duration);
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
if (!(this.seekBar.tracking)) {
|
|
518
|
+
// Only update the aria live region if we have an update pending
|
|
519
|
+
// (from a seek button control) or if the seekBar has focus.
|
|
520
|
+
// We use document.activeElement instead of $(':focus') due to a strange bug:
|
|
521
|
+
// When the seekHead element is focused, .is(':focus') is failing and $(':focus') is returning an undefined element.
|
|
522
|
+
updateLive = this.liveUpdatePending || this.seekBar.seekHead.is($(document.activeElement));
|
|
523
|
+
this.liveUpdatePending = false;
|
|
524
|
+
if (this.useChapterTimes) {
|
|
525
|
+
this.seekBar.setPosition(this.chapterElapsed, updateLive);
|
|
526
|
+
}
|
|
527
|
+
else {
|
|
528
|
+
this.seekBar.setPosition(this.elapsed, updateLive);
|
|
529
|
+
}
|
|
530
|
+
}
|
|
531
|
+
|
|
532
|
+
// When seeking, display the seek bar time instead of the actual elapsed time.
|
|
533
|
+
if (this.seekBar.tracking) {
|
|
534
|
+
displayElapsed = this.seekBar.lastTrackPosition;
|
|
535
|
+
}
|
|
536
|
+
else {
|
|
537
|
+
if (this.useChapterTimes) {
|
|
538
|
+
displayElapsed = this.chapterElapsed;
|
|
539
|
+
}
|
|
540
|
+
else {
|
|
541
|
+
displayElapsed = this.elapsed;
|
|
542
|
+
}
|
|
543
|
+
}
|
|
544
|
+
}
|
|
545
|
+
// update elapsed & duration
|
|
546
|
+
if (typeof this.$durationContainer !== 'undefined') {
|
|
547
|
+
if (this.useChapterTimes) {
|
|
548
|
+
this.$durationContainer.text(' / ' + this.formatSecondsAsColonTime(this.chapterDuration));
|
|
549
|
+
}
|
|
550
|
+
else {
|
|
551
|
+
this.$durationContainer.text(' / ' + this.formatSecondsAsColonTime(this.duration));
|
|
552
|
+
}
|
|
553
|
+
}
|
|
554
|
+
if (typeof this.$elapsedTimeContainer !== 'undefined') {
|
|
555
|
+
this.$elapsedTimeContainer.text(this.formatSecondsAsColonTime(displayElapsed));
|
|
556
|
+
}
|
|
557
|
+
|
|
558
|
+
// Update seekbar width.
|
|
559
|
+
// To do this, we need to calculate the width of all buttons surrounding it.
|
|
560
|
+
if (this.seekBar) {
|
|
561
|
+
widthUsed = 0;
|
|
562
|
+
seekbarSpacer = 40; // adjust for discrepancies in browsers' calculated button widths
|
|
563
|
+
|
|
564
|
+
leftControls = this.seekBar.wrapperDiv.parent().prev('div.able-left-controls');
|
|
565
|
+
rightControls = leftControls.next('div.able-right-controls');
|
|
566
|
+
leftControls.children().each(function () {
|
|
567
|
+
if ($(this).prop('tagName')=='BUTTON') {
|
|
568
|
+
widthUsed += $(this).width();
|
|
569
|
+
}
|
|
570
|
+
});
|
|
571
|
+
rightControls.children().each(function () {
|
|
572
|
+
if ($(this).prop('tagName')=='BUTTON') {
|
|
573
|
+
widthUsed += $(this).width();
|
|
574
|
+
}
|
|
575
|
+
});
|
|
576
|
+
if (this.fullscreen) {
|
|
577
|
+
seekbarWidth = $(window).width() - widthUsed - seekbarSpacer;
|
|
578
|
+
}
|
|
579
|
+
else {
|
|
580
|
+
seekbarWidth = this.$ableWrapper.width() - widthUsed - seekbarSpacer;
|
|
581
|
+
}
|
|
582
|
+
// Sometimes some minor fluctuations based on browser weirdness, so set a threshold.
|
|
583
|
+
if (Math.abs(seekbarWidth - this.seekBar.getWidth()) > 5) {
|
|
584
|
+
this.seekBar.setWidth(seekbarWidth);
|
|
585
|
+
}
|
|
586
|
+
}
|
|
587
|
+
|
|
588
|
+
// Update buffering progress.
|
|
589
|
+
// TODO: Currently only using the first HTML5 buffered interval,
|
|
590
|
+
// but this fails sometimes when buffering is split into two or more intervals.
|
|
591
|
+
if (this.player === 'html5') {
|
|
592
|
+
if (this.media.buffered.length > 0) {
|
|
593
|
+
buffered = this.media.buffered.end(0);
|
|
594
|
+
if (this.useChapterTimes) {
|
|
595
|
+
if (buffered > this.chapterDuration) {
|
|
596
|
+
buffered = this.chapterDuration;
|
|
597
|
+
}
|
|
598
|
+
if (this.seekBar) {
|
|
599
|
+
this.seekBar.setBuffered(buffered / this.chapterDuration);
|
|
600
|
+
}
|
|
601
|
+
}
|
|
602
|
+
else {
|
|
603
|
+
if (this.seekBar) {
|
|
604
|
+
this.seekBar.setBuffered(buffered / duration);
|
|
605
|
+
}
|
|
606
|
+
}
|
|
607
|
+
}
|
|
608
|
+
}
|
|
609
|
+
else if (this.player === 'youtube') {
|
|
610
|
+
if (this.seekBar) {
|
|
611
|
+
this.seekBar.setBuffered(this.youTubePlayer.getVideoLoadedFraction());
|
|
612
|
+
}
|
|
613
|
+
}
|
|
614
|
+
else if (this.player === 'vimeo') {
|
|
615
|
+
// TODO: Add support for Vimeo buffering update
|
|
616
|
+
}
|
|
617
|
+
} // end if context == 'timeline' or 'init'
|
|
618
|
+
|
|
619
|
+
if (context === 'descriptions' || context == 'init'){
|
|
620
|
+
|
|
621
|
+
if (this.$descButton) {
|
|
622
|
+
if (this.descOn) {
|
|
623
|
+
this.$descButton.removeClass('buttonOff').attr('aria-label',this.tt.turnOffDescriptions);
|
|
624
|
+
this.$descButton.find('span.able-clipped').text(this.tt.turnOffDescriptions);
|
|
625
|
+
}
|
|
626
|
+
else {
|
|
627
|
+
this.$descButton.addClass('buttonOff').attr('aria-label',this.tt.turnOnDescriptions);
|
|
628
|
+
this.$descButton.find('span.able-clipped').text(this.tt.turnOnDescriptions);
|
|
629
|
+
}
|
|
630
|
+
}
|
|
631
|
+
}
|
|
632
|
+
|
|
633
|
+
if (context === 'captions' || context == 'init'){
|
|
634
|
+
|
|
635
|
+
if (this.$ccButton) {
|
|
636
|
+
|
|
637
|
+
captionsCount = this.captions.length;
|
|
638
|
+
|
|
639
|
+
// Button has a different title depending on the number of captions.
|
|
640
|
+
// If only one caption track, this is "Show captions" and "Hide captions"
|
|
641
|
+
// Otherwise, it is just always "Captions"
|
|
642
|
+
if (!this.captionsOn) {
|
|
643
|
+
this.$ccButton.addClass('buttonOff');
|
|
644
|
+
if (captionsCount === 1) {
|
|
645
|
+
this.$ccButton.attr('aria-label',this.tt.showCaptions);
|
|
646
|
+
this.$ccButton.find('span.able-clipped').text(this.tt.showCaptions);
|
|
647
|
+
}
|
|
648
|
+
}
|
|
649
|
+
else {
|
|
650
|
+
this.$ccButton.removeClass('buttonOff');
|
|
651
|
+
if (captionsCount === 1) {
|
|
652
|
+
this.$ccButton.attr('aria-label',this.tt.hideCaptions);
|
|
653
|
+
this.$ccButton.find('span.able-clipped').text(this.tt.hideCaptions);
|
|
654
|
+
}
|
|
655
|
+
}
|
|
656
|
+
|
|
657
|
+
if (captionsCount > 1) {
|
|
658
|
+
this.$ccButton.attr({
|
|
659
|
+
'aria-label': this.tt.captions,
|
|
660
|
+
'aria-haspopup': 'true',
|
|
661
|
+
'aria-controls': this.mediaId + '-captions-menu',
|
|
662
|
+
'aria-expanded': 'false'
|
|
663
|
+
});
|
|
664
|
+
this.$ccButton.find('span.able-clipped').text(this.tt.captions);
|
|
665
|
+
}
|
|
666
|
+
}
|
|
667
|
+
}
|
|
668
|
+
|
|
669
|
+
if (context === 'fullscreen' || context == 'init'){
|
|
670
|
+
|
|
671
|
+
if (this.$fullscreenButton) {
|
|
672
|
+
if (!this.fullscreen) {
|
|
673
|
+
this.$fullscreenButton.attr('aria-label', this.tt.enterFullScreen);
|
|
674
|
+
if (this.iconType === 'font') {
|
|
675
|
+
this.$fullscreenButton.find('span').first().removeClass('icon-fullscreen-collapse').addClass('icon-fullscreen-expand');
|
|
676
|
+
this.$fullscreenButton.find('span.able-clipped').text(this.tt.enterFullScreen);
|
|
677
|
+
}
|
|
678
|
+
else if (this.iconType === 'svg') {
|
|
679
|
+
newSvgData = this.getSvgData('fullscreen-expand');
|
|
680
|
+
this.$fullscreenButton.find('svg').attr('viewBox',newSvgData[0]);
|
|
681
|
+
this.$fullscreenButton.find('path').attr('d',newSvgData[1]);
|
|
682
|
+
}
|
|
683
|
+
else {
|
|
684
|
+
this.$fullscreenButton.find('img').attr('src',this.fullscreenExpandButtonImg);
|
|
685
|
+
}
|
|
686
|
+
}
|
|
687
|
+
else {
|
|
688
|
+
this.$fullscreenButton.attr('aria-label',this.tt.exitFullScreen);
|
|
689
|
+
if (this.iconType === 'font') {
|
|
690
|
+
this.$fullscreenButton.find('span').first().removeClass('icon-fullscreen-expand').addClass('icon-fullscreen-collapse');
|
|
691
|
+
this.$fullscreenButton.find('span.able-clipped').text(this.tt.exitFullScreen);
|
|
692
|
+
}
|
|
693
|
+
else if (this.iconType === 'svg') {
|
|
694
|
+
newSvgData = this.getSvgData('fullscreen-collapse');
|
|
695
|
+
this.$fullscreenButton.find('svg').attr('viewBox',newSvgData[0]);
|
|
696
|
+
this.$fullscreenButton.find('path').attr('d',newSvgData[1]);
|
|
697
|
+
}
|
|
698
|
+
else {
|
|
699
|
+
this.$fullscreenButton.find('img').attr('src',this.fullscreenCollapseButtonImg);
|
|
700
|
+
}
|
|
701
|
+
}
|
|
702
|
+
}
|
|
703
|
+
}
|
|
704
|
+
|
|
705
|
+
if (context === 'playpause' || context == 'init'){
|
|
706
|
+
if (typeof this.$bigPlayButton !== 'undefined' && typeof this.seekBar !== 'undefined') {
|
|
707
|
+
// Choose show/hide for big play button and adjust position.
|
|
708
|
+
if (this.paused && !this.seekBar.tracking) {
|
|
709
|
+
if (!this.hideBigPlayButton) {
|
|
710
|
+
this.$bigPlayButton.show();
|
|
711
|
+
}
|
|
712
|
+
if (this.fullscreen) {
|
|
713
|
+
this.$bigPlayButton.width($(window).width());
|
|
714
|
+
this.$bigPlayButton.height($(window).height());
|
|
715
|
+
}
|
|
716
|
+
else {
|
|
717
|
+
this.$bigPlayButton.width(this.$mediaContainer.width());
|
|
718
|
+
this.$bigPlayButton.height(this.$mediaContainer.height());
|
|
719
|
+
}
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
this.$bigPlayButton.hide();
|
|
723
|
+
}
|
|
724
|
+
}
|
|
725
|
+
}
|
|
726
|
+
|
|
727
|
+
if (context === 'transcript' || context == 'init'){
|
|
728
|
+
|
|
729
|
+
if (this.transcriptType) {
|
|
730
|
+
// Sync checkbox and autoScrollTranscript with user preference
|
|
731
|
+
if (this.prefAutoScrollTranscript === 1) {
|
|
732
|
+
this.autoScrollTranscript = true;
|
|
733
|
+
this.$autoScrollTranscriptCheckbox.prop('checked',true);
|
|
734
|
+
}
|
|
735
|
+
else {
|
|
736
|
+
this.autoScrollTranscript = false;
|
|
737
|
+
this.$autoScrollTranscriptCheckbox.prop('checked',false);
|
|
738
|
+
}
|
|
739
|
+
|
|
740
|
+
// If transcript locked, scroll transcript to current highlight location.
|
|
741
|
+
if (this.autoScrollTranscript && this.currentHighlight) {
|
|
742
|
+
newTop = Math.floor($('.able-transcript').scrollTop() +
|
|
743
|
+
$(this.currentHighlight).position().top -
|
|
744
|
+
($('.able-transcript').height() / 2) +
|
|
745
|
+
($(this.currentHighlight).height() / 2));
|
|
746
|
+
if (newTop !== Math.floor($('.able-transcript').scrollTop())) {
|
|
747
|
+
// Set a flag to ignore the coming scroll event.
|
|
748
|
+
// there's no other way I know of to differentiate programmatic and user-initiated scroll events.
|
|
749
|
+
this.scrollingTranscript = true;
|
|
750
|
+
$('.able-transcript').scrollTop(newTop);
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
}
|
|
755
|
+
|
|
756
|
+
if (context === 'init') {
|
|
757
|
+
|
|
758
|
+
if (this.$chaptersButton) {
|
|
759
|
+
this.$chaptersButton.attr({
|
|
760
|
+
'aria-label': this.tt.chapters,
|
|
761
|
+
'aria-haspopup': 'true',
|
|
762
|
+
'aria-controls': this.mediaId + '-chapters-menu',
|
|
763
|
+
'aria-expanded': 'false'
|
|
764
|
+
});
|
|
765
|
+
}
|
|
766
|
+
}
|
|
767
|
+
|
|
768
|
+
if (context === 'timeline' || context === 'playpause' || context === 'init') {
|
|
769
|
+
|
|
770
|
+
// update status
|
|
771
|
+
textByState = {
|
|
772
|
+
'stopped': this.tt.statusStopped,
|
|
773
|
+
'paused': this.tt.statusPaused,
|
|
774
|
+
'playing': this.tt.statusPlaying,
|
|
775
|
+
'buffering': this.tt.statusBuffering,
|
|
776
|
+
'ended': this.tt.statusEnd
|
|
777
|
+
};
|
|
778
|
+
|
|
779
|
+
if (this.stoppingYouTube) {
|
|
780
|
+
// stoppingYouTube is true temporarily while video is paused and seeking to 0
|
|
781
|
+
// See notes in handleRestart()
|
|
782
|
+
// this.stoppingYouTube will be reset when seek to 0 is finished (in event.js > onMediaUpdateTime())
|
|
783
|
+
if (this.$status.text() !== this.tt.statusStopped) {
|
|
784
|
+
this.$status.text(this.tt.statusStopped);
|
|
785
|
+
}
|
|
786
|
+
if (this.$playpauseButton.find('span').first().hasClass('icon-pause')) {
|
|
787
|
+
if (this.iconType === 'font') {
|
|
788
|
+
this.$playpauseButton.find('span').first().removeClass('icon-pause').addClass('icon-play');
|
|
789
|
+
this.$playpauseButton.find('span.able-clipped').text(this.tt.play);
|
|
790
|
+
}
|
|
791
|
+
else if (this.iconType === 'svg') {
|
|
792
|
+
newSvgData = this.getSvgData('play');
|
|
793
|
+
this.$playpauseButton.find('svg').attr('viewBox',newSvgData[0]);
|
|
794
|
+
this.$playpauseButton.find('path').attr('d',newSvgData[1]);
|
|
795
|
+
}
|
|
796
|
+
else {
|
|
797
|
+
this.$playpauseButton.find('img').attr('src',this.playButtonImg);
|
|
798
|
+
}
|
|
799
|
+
}
|
|
800
|
+
}
|
|
801
|
+
else {
|
|
802
|
+
if (typeof this.$status !== 'undefined' && typeof this.seekBar !== 'undefined') {
|
|
803
|
+
// Update the text only if it's changed since it has role="alert";
|
|
804
|
+
// also don't update while tracking, since this may Pause/Play the player but we don't want to send a Pause/Play update.
|
|
805
|
+
this.getPlayerState().then(function(currentState) {
|
|
806
|
+
if (thisObj.$status.text() !== textByState[currentState] && !thisObj.seekBar.tracking) {
|
|
807
|
+
// Debounce updates; only update after status has stayed steadily different for 250ms.
|
|
808
|
+
timestamp = (new Date()).getTime();
|
|
809
|
+
if (!thisObj.statusDebounceStart) {
|
|
810
|
+
thisObj.statusDebounceStart = timestamp;
|
|
811
|
+
// Make sure refreshControls gets called again at the appropriate time to check.
|
|
812
|
+
thisObj.statusTimeout = setTimeout(function () {
|
|
813
|
+
thisObj.refreshControls(context);
|
|
814
|
+
}, 300);
|
|
815
|
+
}
|
|
816
|
+
else if ((timestamp - thisObj.statusDebounceStart) > 250) {
|
|
817
|
+
thisObj.$status.text(textByState[currentState]);
|
|
818
|
+
thisObj.statusDebounceStart = null;
|
|
819
|
+
clearTimeout(thisObj.statusTimeout);
|
|
820
|
+
thisObj.statusTimeout = null;
|
|
821
|
+
}
|
|
822
|
+
}
|
|
823
|
+
else {
|
|
824
|
+
thisObj.statusDebounceStart = null;
|
|
825
|
+
clearTimeout(thisObj.statusTimeout);
|
|
826
|
+
thisObj.statusTimeout = null;
|
|
827
|
+
}
|
|
828
|
+
|
|
829
|
+
// Don't change play/pause button display while using the seek bar (or if YouTube stopped)
|
|
830
|
+
if (!thisObj.seekBar.tracking && !thisObj.stoppingYouTube) {
|
|
831
|
+
if (currentState === 'paused' || currentState === 'stopped') {
|
|
832
|
+
thisObj.$playpauseButton.attr('aria-label',thisObj.tt.play);
|
|
833
|
+
|
|
834
|
+
if (thisObj.iconType === 'font') {
|
|
835
|
+
thisObj.$playpauseButton.find('span').first().removeClass('icon-pause').addClass('icon-play');
|
|
836
|
+
thisObj.$playpauseButton.find('span.able-clipped').text(thisObj.tt.play);
|
|
837
|
+
}
|
|
838
|
+
else if (thisObj.iconType === 'svg') {
|
|
839
|
+
newSvgData = thisObj.getSvgData('play');
|
|
840
|
+
thisObj.$playpauseButton.find('svg').attr('viewBox',newSvgData[0]);
|
|
841
|
+
thisObj.$playpauseButton.find('path').attr('d',newSvgData[1]);
|
|
842
|
+
}
|
|
843
|
+
else {
|
|
844
|
+
thisObj.$playpauseButton.find('img').attr('src',thisObj.playButtonImg);
|
|
845
|
+
}
|
|
846
|
+
}
|
|
847
|
+
else {
|
|
848
|
+
thisObj.$playpauseButton.attr('aria-label',thisObj.tt.pause);
|
|
849
|
+
|
|
850
|
+
if (thisObj.iconType === 'font') {
|
|
851
|
+
thisObj.$playpauseButton.find('span').first().removeClass('icon-play').addClass('icon-pause');
|
|
852
|
+
thisObj.$playpauseButton.find('span.able-clipped').text(thisObj.tt.pause);
|
|
853
|
+
}
|
|
854
|
+
else if (thisObj.iconType === 'svg') {
|
|
855
|
+
newSvgData = thisObj.getSvgData('pause');
|
|
856
|
+
thisObj.$playpauseButton.find('svg').attr('viewBox',newSvgData[0]);
|
|
857
|
+
thisObj.$playpauseButton.find('path').attr('d',newSvgData[1]);
|
|
858
|
+
}
|
|
859
|
+
else {
|
|
860
|
+
thisObj.$playpauseButton.find('img').attr('src',thisObj.pauseButtonImg);
|
|
861
|
+
}
|
|
862
|
+
}
|
|
863
|
+
}
|
|
864
|
+
});
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
}
|
|
868
|
+
|
|
869
|
+
// Show/hide status bar content conditionally
|
|
870
|
+
if (!this.fullscreen) {
|
|
871
|
+
statusBarWidthBreakpoint = 300;
|
|
872
|
+
statusBarHeight = this.$statusBarDiv.height();
|
|
873
|
+
speedHeight = this.$statusBarDiv.find('span.able-speed').height();
|
|
874
|
+
if (speedHeight > (statusBarHeight + 5)) {
|
|
875
|
+
// speed bar is wrapping (happens often in German player)
|
|
876
|
+
this.$statusBarDiv.find('span.able-speed').hide();
|
|
877
|
+
this.hidingSpeed = true;
|
|
878
|
+
}
|
|
879
|
+
else {
|
|
880
|
+
if (this.hidingSpeed) {
|
|
881
|
+
this.$statusBarDiv.find('span.able-speed').show();
|
|
882
|
+
this.hidingSpeed = false;
|
|
883
|
+
}
|
|
884
|
+
if (this.$statusBarDiv.width() < statusBarWidthBreakpoint) {
|
|
885
|
+
// Player is too small for a speed span
|
|
886
|
+
this.$statusBarDiv.find('span.able-speed').hide();
|
|
887
|
+
this.hidingSpeed = true;
|
|
888
|
+
}
|
|
889
|
+
else {
|
|
890
|
+
if (this.hidingSpeed) {
|
|
891
|
+
this.$statusBarDiv.find('span.able-speed').show();
|
|
892
|
+
this.hidingSpeed = false;
|
|
893
|
+
}
|
|
894
|
+
}
|
|
895
|
+
}
|
|
896
|
+
}
|
|
897
|
+
|
|
898
|
+
};
|
|
899
|
+
|
|
900
|
+
AblePlayer.prototype.getHiddenWidth = function($el) {
|
|
901
|
+
|
|
902
|
+
// jQuery returns for width() if element is hidden
|
|
903
|
+
// this function is a workaround
|
|
904
|
+
|
|
905
|
+
// save a reference to a cloned element that can be measured
|
|
906
|
+
var $hiddenElement = $el.clone().appendTo('body');
|
|
907
|
+
|
|
908
|
+
// calculate the width of the clone
|
|
909
|
+
var width = $hiddenElement.outerWidth();
|
|
910
|
+
|
|
911
|
+
// remove the clone from the DOM
|
|
912
|
+
$hiddenElement.remove();
|
|
913
|
+
|
|
914
|
+
return width;
|
|
915
|
+
};
|
|
916
|
+
|
|
917
|
+
AblePlayer.prototype.handlePlay = function(e) {
|
|
918
|
+
|
|
919
|
+
if (this.paused) {
|
|
920
|
+
this.playMedia();
|
|
921
|
+
}
|
|
922
|
+
else {
|
|
923
|
+
this.pauseMedia();
|
|
924
|
+
}
|
|
925
|
+
};
|
|
926
|
+
|
|
927
|
+
AblePlayer.prototype.handleRestart = function() {
|
|
928
|
+
|
|
929
|
+
this.seekTo(0);
|
|
930
|
+
};
|
|
931
|
+
|
|
932
|
+
AblePlayer.prototype.handleRewind = function() {
|
|
933
|
+
|
|
934
|
+
var targetTime;
|
|
935
|
+
|
|
936
|
+
targetTime = this.elapsed - this.seekInterval;
|
|
937
|
+
if (this.useChapterTimes) {
|
|
938
|
+
if (targetTime < this.currentChapter.start) {
|
|
939
|
+
targetTime = this.currentChapter.start;
|
|
940
|
+
}
|
|
941
|
+
}
|
|
942
|
+
else {
|
|
943
|
+
if (targetTime < 0) {
|
|
944
|
+
targetTime = 0;
|
|
945
|
+
}
|
|
946
|
+
}
|
|
947
|
+
this.seekTo(targetTime);
|
|
948
|
+
};
|
|
949
|
+
|
|
950
|
+
AblePlayer.prototype.handleFastForward = function() {
|
|
951
|
+
|
|
952
|
+
var targetTime, lastChapterIndex;
|
|
953
|
+
|
|
954
|
+
lastChapterIndex = this.chapters.length-1;
|
|
955
|
+
targetTime = this.elapsed + this.seekInterval;
|
|
956
|
+
|
|
957
|
+
if (this.useChapterTimes) {
|
|
958
|
+
if (this.chapters[lastChapterIndex] == this.currentChapter) {
|
|
959
|
+
// this is the last chapter
|
|
960
|
+
if (targetTime > this.duration || targetTime > this.currentChapter.end) {
|
|
961
|
+
// targetTime would exceed the end of the video (or chapter)
|
|
962
|
+
// scrub to end of whichever is earliest
|
|
963
|
+
targetTime = Math.min(this.duration, this.currentChapter.end);
|
|
964
|
+
}
|
|
965
|
+
else if (this.duration % targetTime < this.seekInterval) {
|
|
966
|
+
// nothing left but pocket change after seeking to targetTime
|
|
967
|
+
// go ahead and seek to end of video (or chapter), whichever is earliest
|
|
968
|
+
targetTime = Math.min(this.duration, this.currentChapter.end);
|
|
969
|
+
}
|
|
970
|
+
}
|
|
971
|
+
else {
|
|
972
|
+
// this is not the last chapter
|
|
973
|
+
if (targetTime > this.currentChapter.end) {
|
|
974
|
+
// targetTime would exceed the end of the chapter
|
|
975
|
+
// scrub exactly to end of chapter
|
|
976
|
+
targetTime = this.currentChapter.end;
|
|
977
|
+
}
|
|
978
|
+
}
|
|
979
|
+
}
|
|
980
|
+
else {
|
|
981
|
+
// not using chapter times
|
|
982
|
+
if (targetTime > this.duration) {
|
|
983
|
+
targetTime = this.duration;
|
|
984
|
+
}
|
|
985
|
+
}
|
|
986
|
+
this.seekTo(targetTime);
|
|
987
|
+
};
|
|
988
|
+
|
|
989
|
+
AblePlayer.prototype.handleRateIncrease = function() {
|
|
990
|
+
this.changeRate(1);
|
|
991
|
+
};
|
|
992
|
+
|
|
993
|
+
AblePlayer.prototype.handleRateDecrease = function() {
|
|
994
|
+
this.changeRate(-1);
|
|
995
|
+
};
|
|
996
|
+
|
|
997
|
+
// Increases or decreases playback rate, where dir is 1 or -1 indication direction.
|
|
998
|
+
AblePlayer.prototype.changeRate = function (dir) {
|
|
999
|
+
|
|
1000
|
+
var rates, currentRate, index, newRate, vimeoMin, vimeoMax;
|
|
1001
|
+
|
|
1002
|
+
if (this.player === 'html5') {
|
|
1003
|
+
this.setPlaybackRate(this.getPlaybackRate() + (0.25 * dir));
|
|
1004
|
+
}
|
|
1005
|
+
else if (this.player === 'youtube') {
|
|
1006
|
+
rates = this.youTubePlayer.getAvailablePlaybackRates();
|
|
1007
|
+
currentRate = this.getPlaybackRate();
|
|
1008
|
+
index = rates.indexOf(currentRate);
|
|
1009
|
+
if (index === -1) {
|
|
1010
|
+
console.log('ERROR: Youtube returning unknown playback rate ' + currentRate.toString());
|
|
1011
|
+
}
|
|
1012
|
+
else {
|
|
1013
|
+
index += dir;
|
|
1014
|
+
// Can only increase or decrease rate if there's another rate available.
|
|
1015
|
+
if (index < rates.length && index >= 0) {
|
|
1016
|
+
this.setPlaybackRate(rates[index]);
|
|
1017
|
+
}
|
|
1018
|
+
}
|
|
1019
|
+
}
|
|
1020
|
+
else if (this.player === 'vimeo') {
|
|
1021
|
+
// range is 0.5 to 2
|
|
1022
|
+
// increase/decrease in inrements of 0.5
|
|
1023
|
+
vimeoMin = 0.5;
|
|
1024
|
+
vimeoMax = 2;
|
|
1025
|
+
if (dir === 1) {
|
|
1026
|
+
if (this.vimeoPlaybackRate + 0.5 <= vimeoMax) {
|
|
1027
|
+
newRate = this.vimeoPlaybackRate + 0.5;
|
|
1028
|
+
}
|
|
1029
|
+
else {
|
|
1030
|
+
newRate = vimeoMax;
|
|
1031
|
+
}
|
|
1032
|
+
}
|
|
1033
|
+
else if (dir === -1) {
|
|
1034
|
+
if (this.vimeoPlaybackRate - 0.5 >= vimeoMin) {
|
|
1035
|
+
newRate = this.vimeoPlaybackRate - 0.5;
|
|
1036
|
+
}
|
|
1037
|
+
else {
|
|
1038
|
+
newRate = vimeoMin;
|
|
1039
|
+
}
|
|
1040
|
+
}
|
|
1041
|
+
this.setPlaybackRate(newRate);
|
|
1042
|
+
}
|
|
1043
|
+
};
|
|
1044
|
+
|
|
1045
|
+
AblePlayer.prototype.handleCaptionToggle = function() {
|
|
1046
|
+
|
|
1047
|
+
var captions;
|
|
1048
|
+
if (this.hidingPopup) {
|
|
1049
|
+
// stopgap to prevent spacebar in Firefox from reopening popup
|
|
1050
|
+
// immediately after closing it
|
|
1051
|
+
this.hidingPopup = false;
|
|
1052
|
+
return false;
|
|
1053
|
+
}
|
|
1054
|
+
if (this.captions.length) {
|
|
1055
|
+
captions = this.captions;
|
|
1056
|
+
}
|
|
1057
|
+
else {
|
|
1058
|
+
captions = [];
|
|
1059
|
+
}
|
|
1060
|
+
if (captions.length === 1) {
|
|
1061
|
+
// When there's only one set of captions, just do an on/off toggle.
|
|
1062
|
+
if (this.captionsOn === true) {
|
|
1063
|
+
// turn them off
|
|
1064
|
+
this.captionsOn = false;
|
|
1065
|
+
this.prefCaptions = 0;
|
|
1066
|
+
this.updateCookie('prefCaptions');
|
|
1067
|
+
if (this.usingYouTubeCaptions) {
|
|
1068
|
+
this.youTubePlayer.unloadModule(this.ytCaptionModule);
|
|
1069
|
+
}
|
|
1070
|
+
else {
|
|
1071
|
+
this.$captionsWrapper.hide();
|
|
1072
|
+
}
|
|
1073
|
+
}
|
|
1074
|
+
else {
|
|
1075
|
+
// captions are off. Turn them on.
|
|
1076
|
+
this.captionsOn = true;
|
|
1077
|
+
this.prefCaptions = 1;
|
|
1078
|
+
this.updateCookie('prefCaptions');
|
|
1079
|
+
if (this.usingYouTubeCaptions) {
|
|
1080
|
+
if (typeof this.ytCaptionModule !== 'undefined') {
|
|
1081
|
+
this.youTubePlayer.loadModule(this.ytCaptionModule);
|
|
1082
|
+
}
|
|
1083
|
+
}
|
|
1084
|
+
else {
|
|
1085
|
+
this.$captionsWrapper.show();
|
|
1086
|
+
}
|
|
1087
|
+
for (var i=0; i<captions.length; i++) {
|
|
1088
|
+
if (captions[i].def === true) { // this is the default language
|
|
1089
|
+
this.selectedCaptions = captions[i];
|
|
1090
|
+
}
|
|
1091
|
+
}
|
|
1092
|
+
this.selectedCaptions = this.captions[0];
|
|
1093
|
+
if (this.descriptions.length >= 0) {
|
|
1094
|
+
this.selectedDescriptions = this.descriptions[0];
|
|
1095
|
+
}
|
|
1096
|
+
}
|
|
1097
|
+
this.refreshControls('captions');
|
|
1098
|
+
}
|
|
1099
|
+
else {
|
|
1100
|
+
// there is more than one caption track.
|
|
1101
|
+
// clicking on a track is handled via caption.js > getCaptionClickFunction()
|
|
1102
|
+
if (this.captionsPopup && this.captionsPopup.is(':visible')) {
|
|
1103
|
+
this.captionsPopup.hide();
|
|
1104
|
+
this.hidingPopup = false;
|
|
1105
|
+
this.$ccButton.attr('aria-expanded','false').focus();
|
|
1106
|
+
}
|
|
1107
|
+
else {
|
|
1108
|
+
this.closePopups();
|
|
1109
|
+
if (this.captionsPopup) {
|
|
1110
|
+
this.captionsPopup.show();
|
|
1111
|
+
this.$ccButton.attr('aria-expanded','true');
|
|
1112
|
+
this.captionsPopup.css('top', this.$ccButton.position().top - this.captionsPopup.outerHeight());
|
|
1113
|
+
this.captionsPopup.css('left', this.$ccButton.position().left)
|
|
1114
|
+
// Place focus on the first button (even if another button is checked)
|
|
1115
|
+
this.captionsPopup.find('li').removeClass('able-focus');
|
|
1116
|
+
this.captionsPopup.find('li').first().focus().addClass('able-focus');
|
|
1117
|
+
}
|
|
1118
|
+
}
|
|
1119
|
+
}
|
|
1120
|
+
};
|
|
1121
|
+
|
|
1122
|
+
AblePlayer.prototype.handleChapters = function () {
|
|
1123
|
+
if (this.hidingPopup) {
|
|
1124
|
+
// stopgap to prevent spacebar in Firefox from reopening popup
|
|
1125
|
+
// immediately after closing it
|
|
1126
|
+
this.hidingPopup = false;
|
|
1127
|
+
return false;
|
|
1128
|
+
}
|
|
1129
|
+
if (this.chaptersPopup.is(':visible')) {
|
|
1130
|
+
this.chaptersPopup.hide();
|
|
1131
|
+
this.hidingPopup = false;
|
|
1132
|
+
this.$chaptersButton.attr('aria-expanded','false').focus();
|
|
1133
|
+
}
|
|
1134
|
+
else {
|
|
1135
|
+
this.closePopups();
|
|
1136
|
+
this.chaptersPopup.show();
|
|
1137
|
+
this.$chaptersButton.attr('aria-expanded','true');
|
|
1138
|
+
this.chaptersPopup.css('top', this.$chaptersButton.position().top - this.chaptersPopup.outerHeight());
|
|
1139
|
+
this.chaptersPopup.css('left', this.$chaptersButton.position().left)
|
|
1140
|
+
|
|
1141
|
+
// Highlight the current chapter, if any chapters are checked
|
|
1142
|
+
// Otherwise, place focus on the first chapter
|
|
1143
|
+
this.chaptersPopup.find('li').removeClass('able-focus');
|
|
1144
|
+
if (this.chaptersPopup.find('li[aria-checked="true"]').length) {
|
|
1145
|
+
this.chaptersPopup.find('li[aria-checked="true"]').focus().addClass('able-focus');
|
|
1146
|
+
}
|
|
1147
|
+
else {
|
|
1148
|
+
this.chaptersPopup.find('li').first().addClass('able-focus').attr('aria-checked','true').focus();
|
|
1149
|
+
}
|
|
1150
|
+
}
|
|
1151
|
+
};
|
|
1152
|
+
|
|
1153
|
+
AblePlayer.prototype.handleDescriptionToggle = function() {
|
|
1154
|
+
this.descOn = !this.descOn;
|
|
1155
|
+
this.prefDesc = + this.descOn; // convert boolean to integer
|
|
1156
|
+
this.updateCookie('prefDesc');
|
|
1157
|
+
this.refreshingDesc = true;
|
|
1158
|
+
this.initDescription();
|
|
1159
|
+
this.refreshControls('descriptions');
|
|
1160
|
+
};
|
|
1161
|
+
|
|
1162
|
+
AblePlayer.prototype.handlePrefsClick = function(pref) {
|
|
1163
|
+
// NOTE: the prefs menu is positioned near the right edge of the player
|
|
1164
|
+
// This assumes the Prefs button is also positioned in that vicinity
|
|
1165
|
+
// (last or second-last button the right)
|
|
1166
|
+
|
|
1167
|
+
var prefsButtonPosition, prefsMenuRight, prefsMenuLeft;
|
|
1168
|
+
|
|
1169
|
+
if (this.hidingPopup) {
|
|
1170
|
+
// stopgap to prevent spacebar in Firefox from reopening popup
|
|
1171
|
+
// immediately after closing it
|
|
1172
|
+
this.hidingPopup = false;
|
|
1173
|
+
return false;
|
|
1174
|
+
}
|
|
1175
|
+
if (this.prefsPopup.is(':visible')) {
|
|
1176
|
+
this.prefsPopup.hide();
|
|
1177
|
+
this.hidingPopup = false;
|
|
1178
|
+
this.$prefsButton.attr('aria-expanded','false').focus();
|
|
1179
|
+
// restore each menu item to original hidden state
|
|
1180
|
+
this.prefsPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
|
|
1181
|
+
}
|
|
1182
|
+
else {
|
|
1183
|
+
this.closePopups();
|
|
1184
|
+
this.prefsPopup.show();
|
|
1185
|
+
this.$prefsButton.attr('aria-expanded','true');
|
|
1186
|
+
prefsButtonPosition = this.$prefsButton.position();
|
|
1187
|
+
prefsMenuRight = this.$ableDiv.width() - 5;
|
|
1188
|
+
prefsMenuLeft = prefsMenuRight - this.prefsPopup.width();
|
|
1189
|
+
this.prefsPopup.css('top', prefsButtonPosition.top - this.prefsPopup.outerHeight());
|
|
1190
|
+
this.prefsPopup.css('left', prefsMenuLeft);
|
|
1191
|
+
// remove prior focus and set focus on first item; also change tabindex from -1 to 0
|
|
1192
|
+
this.prefsPopup.find('li').removeClass('able-focus').attr('tabindex','0');
|
|
1193
|
+
this.prefsPopup.find('li').first().focus().addClass('able-focus');
|
|
1194
|
+
}
|
|
1195
|
+
};
|
|
1196
|
+
|
|
1197
|
+
AblePlayer.prototype.handleHelpClick = function() {
|
|
1198
|
+
this.setFullscreen(false);
|
|
1199
|
+
this.helpDialog.show();
|
|
1200
|
+
};
|
|
1201
|
+
|
|
1202
|
+
AblePlayer.prototype.handleTranscriptToggle = function () {
|
|
1203
|
+
if (this.$transcriptDiv.is(':visible')) {
|
|
1204
|
+
this.$transcriptArea.hide();
|
|
1205
|
+
this.$transcriptButton.addClass('buttonOff').attr('aria-label',this.tt.showTranscript);
|
|
1206
|
+
this.$transcriptButton.find('span.able-clipped').text(this.tt.showTranscript);
|
|
1207
|
+
this.prefTranscript = 0;
|
|
1208
|
+
this.$transcriptButton.focus().addClass('able-focus');
|
|
1209
|
+
}
|
|
1210
|
+
else {
|
|
1211
|
+
this.positionDraggableWindow('transcript');
|
|
1212
|
+
this.$transcriptArea.show();
|
|
1213
|
+
this.$transcriptButton.removeClass('buttonOff').attr('aria-label',this.tt.hideTranscript);
|
|
1214
|
+
this.$transcriptButton.find('span.able-clipped').text(this.tt.hideTranscript);
|
|
1215
|
+
this.prefTranscript = 1;
|
|
1216
|
+
}
|
|
1217
|
+
this.updateCookie('prefTranscript');
|
|
1218
|
+
};
|
|
1219
|
+
|
|
1220
|
+
AblePlayer.prototype.handleSignToggle = function () {
|
|
1221
|
+
if (this.$signWindow.is(':visible')) {
|
|
1222
|
+
this.$signWindow.hide();
|
|
1223
|
+
this.$signButton.addClass('buttonOff').attr('aria-label',this.tt.showSign);
|
|
1224
|
+
this.$signButton.find('span.able-clipped').text(this.tt.showSign);
|
|
1225
|
+
this.prefSign = 0;
|
|
1226
|
+
this.$signButton.focus().addClass('able-focus');
|
|
1227
|
+
}
|
|
1228
|
+
else {
|
|
1229
|
+
this.positionDraggableWindow('sign');
|
|
1230
|
+
this.$signWindow.show();
|
|
1231
|
+
this.$signButton.removeClass('buttonOff').attr('aria-label',this.tt.hideSign);
|
|
1232
|
+
this.$signButton.find('span.able-clipped').text(this.tt.hideSign);
|
|
1233
|
+
this.prefSign = 1;
|
|
1234
|
+
}
|
|
1235
|
+
this.updateCookie('prefSign');
|
|
1236
|
+
};
|
|
1237
|
+
|
|
1238
|
+
AblePlayer.prototype.isFullscreen = function () {
|
|
1239
|
+
|
|
1240
|
+
// NOTE: This has been largely replaced as of 3.2.5 with a Boolean this.fullscreen,
|
|
1241
|
+
// which is defined in setFullscreen()
|
|
1242
|
+
// This function returns true if *any* element is fullscreen
|
|
1243
|
+
// but doesn't tell us whether a particular element is in fullscreen
|
|
1244
|
+
// (e.g., if there are multiple players on the page)
|
|
1245
|
+
// The Boolean this.fullscreen is defined separately for each player instance
|
|
1246
|
+
|
|
1247
|
+
if (this.nativeFullscreenSupported()) {
|
|
1248
|
+
return (document.fullscreenElement ||
|
|
1249
|
+
document.webkitFullscreenElement ||
|
|
1250
|
+
document.webkitCurrentFullScreenElement ||
|
|
1251
|
+
document.mozFullScreenElement ||
|
|
1252
|
+
document.msFullscreenElement) ? true : false;
|
|
1253
|
+
}
|
|
1254
|
+
else {
|
|
1255
|
+
return this.modalFullscreenActive ? true : false;
|
|
1256
|
+
}
|
|
1257
|
+
}
|
|
1258
|
+
|
|
1259
|
+
AblePlayer.prototype.setFullscreen = function (fullscreen) {
|
|
1260
|
+
|
|
1261
|
+
if (this.fullscreen == fullscreen) {
|
|
1262
|
+
// replace isFullscreen() with a Boolean. see function for explanation
|
|
1263
|
+
return;
|
|
1264
|
+
}
|
|
1265
|
+
var thisObj = this;
|
|
1266
|
+
var $el = this.$ableWrapper;
|
|
1267
|
+
var el = $el[0];
|
|
1268
|
+
|
|
1269
|
+
if (this.nativeFullscreenSupported()) {
|
|
1270
|
+
// Note: many varying names for options for browser compatibility.
|
|
1271
|
+
if (fullscreen) {
|
|
1272
|
+
// Initialize fullscreen
|
|
1273
|
+
|
|
1274
|
+
// But first, capture current settings so they can be restored later
|
|
1275
|
+
this.preFullScreenWidth = this.$ableWrapper.width();
|
|
1276
|
+
this.preFullScreenHeight = this.$ableWrapper.height();
|
|
1277
|
+
|
|
1278
|
+
if (el.requestFullscreen) {
|
|
1279
|
+
el.requestFullscreen();
|
|
1280
|
+
}
|
|
1281
|
+
else if (el.webkitRequestFullscreen) {
|
|
1282
|
+
el.webkitRequestFullscreen();
|
|
1283
|
+
}
|
|
1284
|
+
else if (el.mozRequestFullScreen) {
|
|
1285
|
+
el.mozRequestFullScreen();
|
|
1286
|
+
}
|
|
1287
|
+
else if (el.msRequestFullscreen) {
|
|
1288
|
+
el.msRequestFullscreen();
|
|
1289
|
+
}
|
|
1290
|
+
this.fullscreen = true;
|
|
1291
|
+
}
|
|
1292
|
+
else {
|
|
1293
|
+
// Exit fullscreen
|
|
1294
|
+
if (document.exitFullscreen) {
|
|
1295
|
+
document.exitFullscreen();
|
|
1296
|
+
}
|
|
1297
|
+
else if (document.webkitExitFullscreen) {
|
|
1298
|
+
document.webkitExitFullscreen();
|
|
1299
|
+
}
|
|
1300
|
+
else if (document.webkitCancelFullScreen) {
|
|
1301
|
+
document.webkitCancelFullScreen();
|
|
1302
|
+
}
|
|
1303
|
+
else if (document.mozCancelFullScreen) {
|
|
1304
|
+
document.mozCancelFullScreen();
|
|
1305
|
+
}
|
|
1306
|
+
else if (document.msExitFullscreen) {
|
|
1307
|
+
document.msExitFullscreen();
|
|
1308
|
+
}
|
|
1309
|
+
this.fullscreen = false;
|
|
1310
|
+
}
|
|
1311
|
+
// add event handlers for changes in full screen mode
|
|
1312
|
+
// currently most changes are made in response to windowResize event
|
|
1313
|
+
// However, that alone is not resulting in a properly restored player size in Opera Mac
|
|
1314
|
+
// More on the Opera Mac bug: https://github.com/ableplayer/ableplayer/issues/162
|
|
1315
|
+
// this fullscreen event handler added specifically for Opera Mac,
|
|
1316
|
+
// but includes event listeners for all browsers in case its functionality could be expanded
|
|
1317
|
+
// Added functionality in 2.3.45 for handling YouTube return from fullscreen as well
|
|
1318
|
+
$(document).on('webkitfullscreenchange mozfullscreenchange fullscreenchange MSFullscreenChange', function(e) {
|
|
1319
|
+
// NOTE: e.type = the specific event that fired (in case needing to control for browser-specific idiosyncrasies)
|
|
1320
|
+
if (!thisObj.fullscreen) {
|
|
1321
|
+
// user has just exited full screen
|
|
1322
|
+
thisObj.restoringAfterFullScreen = true;
|
|
1323
|
+
thisObj.resizePlayer(thisObj.preFullScreenWidth,thisObj.preFullScreenHeight);
|
|
1324
|
+
}
|
|
1325
|
+
else if (!thisObj.clickedFullscreenButton) {
|
|
1326
|
+
// user triggered fullscreenchange without clicking (or pressing) fullscreen button
|
|
1327
|
+
// this is only possible if they pressed Escape to exit fullscreen mode
|
|
1328
|
+
thisObj.fullscreen = false;
|
|
1329
|
+
thisObj.restoringAfterFullScreen = true;
|
|
1330
|
+
thisObj.resizePlayer(thisObj.preFullScreenWidth,thisObj.preFullScreenHeight);
|
|
1331
|
+
}
|
|
1332
|
+
// NOTE: The fullscreenchange (or browser-equivalent) event is triggered twice
|
|
1333
|
+
// when exiting fullscreen via the "Exit fullscreen" button (only once if using Escape)
|
|
1334
|
+
// Not sure why, but consequently we need to be sure thisObj.clickedFullScreenButton
|
|
1335
|
+
// continues to be true through both events
|
|
1336
|
+
// Could use a counter variable to control that (reset to false after the 2nd trigger)
|
|
1337
|
+
// However, since I don't know why it's happening, and whether it's 100% reliable
|
|
1338
|
+
// resetting clickedFullScreenButton after a timeout seems to be better approach
|
|
1339
|
+
setTimeout(function() {
|
|
1340
|
+
thisObj.clickedFullscreenButton = false;
|
|
1341
|
+
},1000);
|
|
1342
|
+
});
|
|
1343
|
+
}
|
|
1344
|
+
else {
|
|
1345
|
+
// Non-native fullscreen support through modal dialog.
|
|
1346
|
+
// Create dialog on first run through.
|
|
1347
|
+
if (!this.fullscreenDialog) {
|
|
1348
|
+
var $dialogDiv = $('<div>');
|
|
1349
|
+
// create a hidden alert, communicated to screen readers via aria-describedby
|
|
1350
|
+
var $fsDialogAlert = $('<p>',{
|
|
1351
|
+
'class': 'able-screenreader-alert'
|
|
1352
|
+
}).text(this.tt.fullscreen); // In English: "Full screen"; TODO: Add alert text that is more descriptive
|
|
1353
|
+
$dialogDiv.append($fsDialogAlert);
|
|
1354
|
+
// now render this as a dialog
|
|
1355
|
+
this.fullscreenDialog = new AccessibleDialog($dialogDiv, this.$fullscreenButton, 'dialog', 'Fullscreen video player', $fsDialogAlert, this.tt.exitFullScreen, '100%', true, function () { thisObj.handleFullscreenToggle() });
|
|
1356
|
+
$('body').append($dialogDiv);
|
|
1357
|
+
}
|
|
1358
|
+
|
|
1359
|
+
// Track whether paused/playing before moving element; moving the element can stop playback.
|
|
1360
|
+
var wasPaused = this.paused;
|
|
1361
|
+
|
|
1362
|
+
if (fullscreen) {
|
|
1363
|
+
this.modalFullscreenActive = true;
|
|
1364
|
+
this.fullscreenDialog.show();
|
|
1365
|
+
|
|
1366
|
+
// Move player element into fullscreen dialog, then show.
|
|
1367
|
+
// Put a placeholder element where player was.
|
|
1368
|
+
this.$modalFullscreenPlaceholder = $('<div class="placeholder">');
|
|
1369
|
+
this.$modalFullscreenPlaceholder.insertAfter($el);
|
|
1370
|
+
$el.appendTo(this.fullscreenDialog.modal);
|
|
1371
|
+
|
|
1372
|
+
// Column left css is 50% by default; set to 100% for full screen.
|
|
1373
|
+
if ($el === this.$ableColumnLeft) {
|
|
1374
|
+
$el.width('100%');
|
|
1375
|
+
}
|
|
1376
|
+
var newHeight = $(window).height() - this.$playerDiv.height();
|
|
1377
|
+
if (!this.$descDiv.is(':hidden')) {
|
|
1378
|
+
newHeight -= this.$descDiv.height();
|
|
1379
|
+
}
|
|
1380
|
+
this.resizePlayer($(window).width(), newHeight);
|
|
1381
|
+
}
|
|
1382
|
+
else {
|
|
1383
|
+
this.modalFullscreenActive = false;
|
|
1384
|
+
if ($el === this.$ableColumnLeft) {
|
|
1385
|
+
$el.width('50%');
|
|
1386
|
+
}
|
|
1387
|
+
$el.insertAfter(this.$modalFullscreenPlaceholder);
|
|
1388
|
+
this.$modalFullscreenPlaceholder.remove();
|
|
1389
|
+
this.fullscreenDialog.hide();
|
|
1390
|
+
this.resizePlayer(this.$ableWrapper.width(), this.$ableWrapper.height());
|
|
1391
|
+
}
|
|
1392
|
+
|
|
1393
|
+
// Resume playback if moving stopped it.
|
|
1394
|
+
if (!wasPaused && this.paused) {
|
|
1395
|
+
this.playMedia();
|
|
1396
|
+
}
|
|
1397
|
+
}
|
|
1398
|
+
this.refreshControls('fullscreen');
|
|
1399
|
+
};
|
|
1400
|
+
|
|
1401
|
+
AblePlayer.prototype.handleFullscreenToggle = function () {
|
|
1402
|
+
|
|
1403
|
+
var stillPaused = this.paused;
|
|
1404
|
+
this.setFullscreen(!this.fullscreen);
|
|
1405
|
+
if (stillPaused) {
|
|
1406
|
+
this.pauseMedia(); // when toggling fullscreen and media is just paused, keep media paused.
|
|
1407
|
+
}
|
|
1408
|
+
else if (!stillPaused) {
|
|
1409
|
+
this.playMedia(); // when toggling fullscreen and media is playing, continue playing.
|
|
1410
|
+
}
|
|
1411
|
+
// automatically hide controller in fullscreen mode
|
|
1412
|
+
// then reset back to original setting after exiting fullscreen mode
|
|
1413
|
+
if (this.fullscreen) {
|
|
1414
|
+
this.hideControls = true;
|
|
1415
|
+
if (this.playing) {
|
|
1416
|
+
// go ahead and hide the controls
|
|
1417
|
+
this.fadeControls('out');
|
|
1418
|
+
this.controlsHidden = true;
|
|
1419
|
+
}
|
|
1420
|
+
}
|
|
1421
|
+
else {
|
|
1422
|
+
// exit fullscreen mode
|
|
1423
|
+
this.hideControls = this.hideControlsOriginal;
|
|
1424
|
+
if (!this.hideControls) { // do not hide controls
|
|
1425
|
+
if (this.controlsHidden) {
|
|
1426
|
+
this.fadeControls('in');
|
|
1427
|
+
this.controlsHidden = false;
|
|
1428
|
+
}
|
|
1429
|
+
// if there's an active timeout to fade controls out again, clear it
|
|
1430
|
+
if (this.hideControlsTimeoutStatus === 'active') {
|
|
1431
|
+
window.clearTimeout(this.hideControlsTimeout);
|
|
1432
|
+
this.hideControlsTimeoutStatus = 'clear';
|
|
1433
|
+
}
|
|
1434
|
+
}
|
|
1435
|
+
}
|
|
1436
|
+
};
|
|
1437
|
+
|
|
1438
|
+
AblePlayer.prototype.handleTranscriptLockToggle = function (val) {
|
|
1439
|
+
|
|
1440
|
+
this.autoScrollTranscript = val; // val is boolean
|
|
1441
|
+
this.prefAutoScrollTranscript = +val; // convert boolean to numeric 1 or 0 for cookie
|
|
1442
|
+
this.updateCookie('prefAutoScrollTranscript');
|
|
1443
|
+
this.refreshControls('transcript');
|
|
1444
|
+
};
|
|
1445
|
+
|
|
1446
|
+
|
|
1447
|
+
AblePlayer.prototype.showTooltip = function($tooltip) {
|
|
1448
|
+
|
|
1449
|
+
if (($tooltip).is(':animated')) {
|
|
1450
|
+
$tooltip.stop(true,true).show().delay(4000).fadeOut(1000);
|
|
1451
|
+
}
|
|
1452
|
+
else {
|
|
1453
|
+
$tooltip.stop().show().delay(4000).fadeOut(1000);
|
|
1454
|
+
}
|
|
1455
|
+
};
|
|
1456
|
+
|
|
1457
|
+
AblePlayer.prototype.showAlert = function( msg, location ) {
|
|
1458
|
+
|
|
1459
|
+
// location is either of the following:
|
|
1460
|
+
// 'main' (default)
|
|
1461
|
+
// 'screenreader (visibly hidden)
|
|
1462
|
+
// 'sign' (sign language window)
|
|
1463
|
+
// 'transcript' (trasncript window)
|
|
1464
|
+
var thisObj, $alertBox, $parentWindow, alertLeft, alertTop;
|
|
1465
|
+
|
|
1466
|
+
thisObj = this;
|
|
1467
|
+
|
|
1468
|
+
if (location === 'transcript') {
|
|
1469
|
+
$alertBox = this.$transcriptAlert;
|
|
1470
|
+
$parentWindow = this.$transcriptArea;
|
|
1471
|
+
}
|
|
1472
|
+
else if (location === 'sign') {
|
|
1473
|
+
$alertBox = this.$signAlert;
|
|
1474
|
+
$parentWindow = this.$signWindow;
|
|
1475
|
+
}
|
|
1476
|
+
else if (location === 'screenreader') {
|
|
1477
|
+
$alertBox = this.$srAlertBox;
|
|
1478
|
+
}
|
|
1479
|
+
else {
|
|
1480
|
+
$alertBox = this.$alertBox;
|
|
1481
|
+
}
|
|
1482
|
+
$alertBox.text(msg).show();
|
|
1483
|
+
if (location == 'transcript' || location === 'sign') {
|
|
1484
|
+
if ($parentWindow.width() > $alertBox.width()) {
|
|
1485
|
+
alertLeft = $parentWindow.width() / 2 - $alertBox.width() / 2;
|
|
1486
|
+
}
|
|
1487
|
+
else {
|
|
1488
|
+
// alert box is wider than its container. Position it far left and let it wrap
|
|
1489
|
+
alertLeft = 10;
|
|
1490
|
+
}
|
|
1491
|
+
if (location === 'sign') {
|
|
1492
|
+
// position alert in the lower third of the sign window (to avoid covering the signer)
|
|
1493
|
+
alertTop = ($parentWindow.height() / 3) * 2;
|
|
1494
|
+
}
|
|
1495
|
+
else if (location === 'transcript') {
|
|
1496
|
+
// position alert just beneath the toolbar to avoid getting lost among transcript text
|
|
1497
|
+
alertTop = this.$transcriptToolbar.height() + 30;
|
|
1498
|
+
}
|
|
1499
|
+
$alertBox.css({
|
|
1500
|
+
top: alertTop + 'px',
|
|
1501
|
+
left: alertLeft + 'px'
|
|
1502
|
+
});
|
|
1503
|
+
}
|
|
1504
|
+
else if (location !== 'screenreader') {
|
|
1505
|
+
// The original formula incorporated offset() into the calculation
|
|
1506
|
+
// but at some point this began resulting in an alert that's off-centered
|
|
1507
|
+
// Changed in v2.2.17, but here's the original for reference in case needed:
|
|
1508
|
+
// left: this.$playerDiv.offset().left + (this.$playerDiv.width() / 2) - ($alertBox.width() / 2)
|
|
1509
|
+
$alertBox.css({
|
|
1510
|
+
left: (this.$playerDiv.width() / 2) - ($alertBox.width() / 2)
|
|
1511
|
+
});
|
|
1512
|
+
}
|
|
1513
|
+
if (location !== 'screenreader') {
|
|
1514
|
+
setTimeout(function () {
|
|
1515
|
+
$alertBox.fadeOut(300);
|
|
1516
|
+
}, 3000);
|
|
1517
|
+
}
|
|
1518
|
+
};
|
|
1519
|
+
|
|
1520
|
+
AblePlayer.prototype.showedAlert = function (which) {
|
|
1521
|
+
|
|
1522
|
+
// returns true if the target alert has already been shown
|
|
1523
|
+
// useful for throttling alerts that only need to be shown once
|
|
1524
|
+
// e.g., move alerts with instructions for dragging a window
|
|
1525
|
+
|
|
1526
|
+
if (which === 'transcript') {
|
|
1527
|
+
if (this.showedTranscriptAlert) {
|
|
1528
|
+
return true;
|
|
1529
|
+
}
|
|
1530
|
+
else {
|
|
1531
|
+
return false;
|
|
1532
|
+
}
|
|
1533
|
+
}
|
|
1534
|
+
else if (which === 'sign') {
|
|
1535
|
+
if (this.showedSignAlert) {
|
|
1536
|
+
return true;
|
|
1537
|
+
}
|
|
1538
|
+
else {
|
|
1539
|
+
return false;
|
|
1540
|
+
}
|
|
1541
|
+
}
|
|
1542
|
+
return false;
|
|
1543
|
+
}
|
|
1544
|
+
|
|
1545
|
+
// Resizes all relevant player attributes.
|
|
1546
|
+
AblePlayer.prototype.resizePlayer = function (width, height) {
|
|
1547
|
+
|
|
1548
|
+
var captionSizeOkMin, captionSizeOkMax, captionSize, newCaptionSize, newLineHeight;
|
|
1549
|
+
|
|
1550
|
+
if (this.fullscreen) { // replace isFullscreen() with a Boolean. see function for explanation
|
|
1551
|
+
if (typeof this.$vidcapContainer !== 'undefined') {
|
|
1552
|
+
this.$ableWrapper.css({
|
|
1553
|
+
'width': width + 'px',
|
|
1554
|
+
'max-width': ''
|
|
1555
|
+
})
|
|
1556
|
+
this.$vidcapContainer.css({
|
|
1557
|
+
'height': height + 'px',
|
|
1558
|
+
'width': width
|
|
1559
|
+
});
|
|
1560
|
+
this.$media.css({
|
|
1561
|
+
'height': height + 'px',
|
|
1562
|
+
'width': width
|
|
1563
|
+
})
|
|
1564
|
+
}
|
|
1565
|
+
if (typeof this.$transcriptArea !== 'undefined') {
|
|
1566
|
+
this.retrieveOffscreenWindow('transcript',width,height);
|
|
1567
|
+
}
|
|
1568
|
+
if (typeof this.$signWindow !== 'undefined') {
|
|
1569
|
+
this.retrieveOffscreenWindow('sign',width,height);
|
|
1570
|
+
}
|
|
1571
|
+
}
|
|
1572
|
+
else {
|
|
1573
|
+
// player resized
|
|
1574
|
+
if (this.restoringAfterFullScreen) {
|
|
1575
|
+
// User has just exited fullscreen mode. Restore to previous settings
|
|
1576
|
+
width = this.preFullScreenWidth;
|
|
1577
|
+
height = this.preFullScreenHeight;
|
|
1578
|
+
this.restoringAfterFullScreen = false;
|
|
1579
|
+
this.$ableWrapper.css({
|
|
1580
|
+
'max-width': width + 'px',
|
|
1581
|
+
'width': ''
|
|
1582
|
+
});
|
|
1583
|
+
if (typeof this.$vidcapContainer !== 'undefined') {
|
|
1584
|
+
this.$vidcapContainer.css({
|
|
1585
|
+
'height': '',
|
|
1586
|
+
'width': ''
|
|
1587
|
+
});
|
|
1588
|
+
}
|
|
1589
|
+
this.$media.css({
|
|
1590
|
+
'width': '100%',
|
|
1591
|
+
'height': 'auto'
|
|
1592
|
+
});
|
|
1593
|
+
}
|
|
1594
|
+
}
|
|
1595
|
+
|
|
1596
|
+
// resize YouTube
|
|
1597
|
+
if (this.player === 'youtube' && this.youTubePlayer) {
|
|
1598
|
+
this.youTubePlayer.setSize(width, height);
|
|
1599
|
+
}
|
|
1600
|
+
|
|
1601
|
+
// Resize captions
|
|
1602
|
+
if (typeof this.$captionsDiv !== 'undefined') {
|
|
1603
|
+
|
|
1604
|
+
// Font-size is too small in full screen view & too large in small-width view
|
|
1605
|
+
// The following vars define a somewhat arbitary zone outside of which
|
|
1606
|
+
// caption size requires adjustment
|
|
1607
|
+
captionSizeOkMin = 400;
|
|
1608
|
+
captionSizeOkMax = 1000;
|
|
1609
|
+
captionSize = parseInt(this.prefCaptionsSize,10);
|
|
1610
|
+
|
|
1611
|
+
// TODO: Need a better formula so that it scales proportionally to viewport
|
|
1612
|
+
if (width > captionSizeOkMax) {
|
|
1613
|
+
newCaptionSize = captionSize * 1.5;
|
|
1614
|
+
}
|
|
1615
|
+
else if (width < captionSizeOkMin) {
|
|
1616
|
+
newCaptionSize = captionSize / 1.5;
|
|
1617
|
+
}
|
|
1618
|
+
else {
|
|
1619
|
+
newCaptionSize = captionSize;
|
|
1620
|
+
}
|
|
1621
|
+
newLineHeight = newCaptionSize + 25;
|
|
1622
|
+
this.$captionsDiv.css('font-size',newCaptionSize + '%');
|
|
1623
|
+
this.$captionsWrapper.css('line-height',newLineHeight + '%');
|
|
1624
|
+
}
|
|
1625
|
+
this.refreshControls('captions');
|
|
1626
|
+
};
|
|
1627
|
+
|
|
1628
|
+
AblePlayer.prototype.retrieveOffscreenWindow = function( which, width, height ) {
|
|
1629
|
+
|
|
1630
|
+
// check to be sure popup windows ('transcript' or 'sign') are positioned on-screen
|
|
1631
|
+
// (they sometimes disappear off-screen when entering fullscreen mode)
|
|
1632
|
+
// if off-screen, recalculate so they are back on screen
|
|
1633
|
+
|
|
1634
|
+
var window, windowPos, windowTop, windowLeft, windowRight, windowWidth, windowBottom, windowHeight;
|
|
1635
|
+
|
|
1636
|
+
if (which == 'transcript') {
|
|
1637
|
+
window = this.$transcriptArea;
|
|
1638
|
+
}
|
|
1639
|
+
else if (which == 'sign') {
|
|
1640
|
+
window = this.$signWindow;
|
|
1641
|
+
}
|
|
1642
|
+
windowWidth = window.width();
|
|
1643
|
+
windowHeight = window.height();
|
|
1644
|
+
windowPos = window.position();
|
|
1645
|
+
windowTop = windowPos.top;
|
|
1646
|
+
windowLeft = windowPos.left;
|
|
1647
|
+
windowRight = windowLeft + windowWidth;
|
|
1648
|
+
windowBottom = windowTop + windowHeight;
|
|
1649
|
+
|
|
1650
|
+
if (windowTop < 0) { // off-screen to the top
|
|
1651
|
+
windowTop = 10;
|
|
1652
|
+
window.css('top',windowTop);
|
|
1653
|
+
}
|
|
1654
|
+
if (windowLeft < 0) { // off-screen to the left
|
|
1655
|
+
windowLeft = 10;
|
|
1656
|
+
window.css('left',windowLeft);
|
|
1657
|
+
}
|
|
1658
|
+
if (windowRight > width) { // off-screen to the right
|
|
1659
|
+
windowLeft = (width - 20) - windowWidth;
|
|
1660
|
+
window.css('left',windowLeft);
|
|
1661
|
+
}
|
|
1662
|
+
if (windowBottom > height) { // off-screen to the bottom
|
|
1663
|
+
windowTop = (height - 10) - windowHeight;
|
|
1664
|
+
window.css('top',windowTop);
|
|
1665
|
+
}
|
|
1666
|
+
};
|
|
1667
|
+
|
|
1668
|
+
AblePlayer.prototype.getHighestZIndex = function() {
|
|
1669
|
+
|
|
1670
|
+
// returns the highest z-index on page
|
|
1671
|
+
// used to ensure dialogs (or potentially other windows) are on top
|
|
1672
|
+
|
|
1673
|
+
var max, $elements, z;
|
|
1674
|
+
max = 0;
|
|
1675
|
+
|
|
1676
|
+
// exclude the Able Player dialogs and windows
|
|
1677
|
+
$elements = $('body *').not('.able-modal-dialog,.able-modal-dialog *,.able-modal-overlay,.able-modal-overlay *,.able-sign-window,.able-transcript-area');
|
|
1678
|
+
|
|
1679
|
+
$elements.each(function(){
|
|
1680
|
+
z = $(this).css('z-index');
|
|
1681
|
+
if (Number.isInteger(+z)) { // work only with integer values, not 'auto'
|
|
1682
|
+
if (parseInt(z) > max) {
|
|
1683
|
+
max = parseInt(z);
|
|
1684
|
+
}
|
|
1685
|
+
}
|
|
1686
|
+
});
|
|
1687
|
+
return max;
|
|
1688
|
+
};
|
|
1689
|
+
|
|
1690
|
+
AblePlayer.prototype.updateZIndex = function(which) {
|
|
1691
|
+
|
|
1692
|
+
// update z-index of 'transcript' or 'sign', relative to each other
|
|
1693
|
+
// direction is always 'up' (i.e., move window to top)
|
|
1694
|
+
// windows come to the top when the user clicks on them
|
|
1695
|
+
var defHighZ, defLowZ, highestZ, transcriptZ, signZ, newHighZ, newLowZ;
|
|
1696
|
+
|
|
1697
|
+
// set the default z-indexes, as defined in ableplayer.css
|
|
1698
|
+
defHighZ = 8000; // by default, assigned to the sign window
|
|
1699
|
+
defLowZ = 7000; // by default, assigned to the transcript area
|
|
1700
|
+
highestZ = this.getHighestZIndex(); // highest z-index on the page, excluding Able Player windows & modals
|
|
1701
|
+
|
|
1702
|
+
// NOTE: Although highestZ is collected here, it currently isn't used.
|
|
1703
|
+
// If something on the page has a higher z-index than the transcript or sign window, do we care?
|
|
1704
|
+
// Excluding it here assumes "No". Our immediate concern is with the relationship between our own components.
|
|
1705
|
+
// If we elevate our z-indexes so our content is on top, we run the risk of starting a z-index war.
|
|
1706
|
+
|
|
1707
|
+
if (typeof this.$transcriptArea === 'undefined' || typeof this.$signWindow === 'undefined' ) {
|
|
1708
|
+
// at least one of the windows doesn't exist, so there's no conflict
|
|
1709
|
+
// since z-index may have been stored to a cookie on another page, need to restore default
|
|
1710
|
+
if (typeof this.$transcriptArea !== 'undefined') {
|
|
1711
|
+
transcriptZ = parseInt(this.$transcriptArea.css('z-index'));
|
|
1712
|
+
if (transcriptZ > defLowZ) {
|
|
1713
|
+
// restore to the default
|
|
1714
|
+
this.$transcriptArea.css('z-index',defLowZ);
|
|
1715
|
+
}
|
|
1716
|
+
}
|
|
1717
|
+
else if (typeof this.$signWindow !== 'undefined') {
|
|
1718
|
+
signZ = parseInt(this.$signWindow.css('z-index'));
|
|
1719
|
+
if (signZ > defHighZ) {
|
|
1720
|
+
// restore to the default
|
|
1721
|
+
this.$signWindow.css('z-index',defHighZ);
|
|
1722
|
+
}
|
|
1723
|
+
}
|
|
1724
|
+
return false;
|
|
1725
|
+
}
|
|
1726
|
+
|
|
1727
|
+
// both windows exist
|
|
1728
|
+
|
|
1729
|
+
// get current values
|
|
1730
|
+
transcriptZ = parseInt(this.$transcriptArea.css('z-index'));
|
|
1731
|
+
signZ = parseInt(this.$signWindow.css('z-index'));
|
|
1732
|
+
|
|
1733
|
+
if (transcriptZ === signZ) {
|
|
1734
|
+
// the two windows are equal; restore defaults (the target window will be on top)
|
|
1735
|
+
newHighZ = defHighZ;
|
|
1736
|
+
newLowZ = defLowZ;
|
|
1737
|
+
}
|
|
1738
|
+
else if (transcriptZ > signZ) {
|
|
1739
|
+
if (which === 'transcript') {
|
|
1740
|
+
// transcript is already on top; nothing to do
|
|
1741
|
+
return false;
|
|
1742
|
+
}
|
|
1743
|
+
else {
|
|
1744
|
+
// swap z's
|
|
1745
|
+
newHighZ = transcriptZ;
|
|
1746
|
+
newLowZ = signZ;
|
|
1747
|
+
}
|
|
1748
|
+
}
|
|
1749
|
+
else { // signZ is greater
|
|
1750
|
+
if (which === 'sign') {
|
|
1751
|
+
// sign is already on top; nothing to do
|
|
1752
|
+
return false;
|
|
1753
|
+
}
|
|
1754
|
+
else {
|
|
1755
|
+
newHighZ = signZ;
|
|
1756
|
+
newLowZ = transcriptZ;
|
|
1757
|
+
}
|
|
1758
|
+
}
|
|
1759
|
+
// now assign the new values
|
|
1760
|
+
if (which === 'transcript') {
|
|
1761
|
+
this.$transcriptArea.css('z-index',newHighZ);
|
|
1762
|
+
this.$signWindow.css('z-index',newLowZ);
|
|
1763
|
+
}
|
|
1764
|
+
else if (which === 'sign') {
|
|
1765
|
+
this.$signWindow.css('z-index',newHighZ);
|
|
1766
|
+
this.$transcriptArea.css('z-index',newLowZ);
|
|
1767
|
+
}
|
|
1768
|
+
};
|
|
1769
|
+
|
|
1770
|
+
AblePlayer.prototype.syncTrackLanguages = function (source, language) {
|
|
1771
|
+
|
|
1772
|
+
// this function is called when the player is built (source == 'init')
|
|
1773
|
+
// and again when user changes the language of either 'captions' or 'transcript'
|
|
1774
|
+
// It syncs the languages of chapters, descriptions, and metadata tracks
|
|
1775
|
+
// NOTE: Caption and transcript languages are somewhat independent from one another
|
|
1776
|
+
// If a user changes the caption language, the transcript follows
|
|
1777
|
+
// However, if a user changes the transcript language, this only affects the transcript
|
|
1778
|
+
// This was a group decision based on the belief that users may want a transcript
|
|
1779
|
+
// that is in a different language than the captions
|
|
1780
|
+
|
|
1781
|
+
var i, captions, descriptions, chapters, meta;
|
|
1782
|
+
|
|
1783
|
+
// Captions
|
|
1784
|
+
for (i = 0; i < this.captions.length; i++) {
|
|
1785
|
+
if (this.captions[i].language === language) {
|
|
1786
|
+
captions = this.captions[i];
|
|
1787
|
+
}
|
|
1788
|
+
}
|
|
1789
|
+
// Chapters
|
|
1790
|
+
for (i = 0; i < this.chapters.length; i++) {
|
|
1791
|
+
if (this.chapters[i].language === language) {
|
|
1792
|
+
chapters = this.chapters[i];
|
|
1793
|
+
}
|
|
1794
|
+
}
|
|
1795
|
+
// Descriptions
|
|
1796
|
+
for (i = 0; i < this.descriptions.length; i++) {
|
|
1797
|
+
if (this.descriptions[i].language === language) {
|
|
1798
|
+
descriptions = this.descriptions[i];
|
|
1799
|
+
}
|
|
1800
|
+
}
|
|
1801
|
+
// Metadata
|
|
1802
|
+
for (i = 0; i < this.meta.length; i++) {
|
|
1803
|
+
if (this.meta[i].language === language) {
|
|
1804
|
+
meta = this.meta[i];
|
|
1805
|
+
}
|
|
1806
|
+
}
|
|
1807
|
+
|
|
1808
|
+
// regardless of source...
|
|
1809
|
+
this.transcriptLang = language;
|
|
1810
|
+
|
|
1811
|
+
if (source === 'init' || source === 'captions') {
|
|
1812
|
+
this.captionLang = language;
|
|
1813
|
+
this.selectedCaptions = captions;
|
|
1814
|
+
this.selectedChapters = chapters;
|
|
1815
|
+
this.selectedDescriptions = descriptions;
|
|
1816
|
+
this.selectedMeta = meta;
|
|
1817
|
+
this.transcriptCaptions = captions;
|
|
1818
|
+
this.transcriptChapters = chapters;
|
|
1819
|
+
this.transcriptDescriptions = descriptions;
|
|
1820
|
+
this.updateChaptersList();
|
|
1821
|
+
// the following was commented out in Oct/Nov 2018.
|
|
1822
|
+
// chapters popup is setup automatically when setupPopups() is called later with no param
|
|
1823
|
+
// not sure why it was included here.
|
|
1824
|
+
// this.setupPopups('chapters');
|
|
1825
|
+
}
|
|
1826
|
+
else if (source === 'transcript') {
|
|
1827
|
+
this.transcriptCaptions = captions;
|
|
1828
|
+
this.transcriptChapters = chapters;
|
|
1829
|
+
this.transcriptDescriptions = descriptions;
|
|
1830
|
+
}
|
|
1831
|
+
this.updateTranscript();
|
|
1832
|
+
};
|
|
1596
1833
|
|
|
1597
1834
|
})(jQuery);
|