wai-website-theme 1.7 → 1.9.pre

Sign up to get free protection for your applications and to get access to all the features.
Files changed (101) hide show
  1. checksums.yaml +4 -4
  2. data/_data/navigation.yml +24 -1
  3. data/_includes/body-class.html +1 -1
  4. data/_includes/ednote.html +32 -0
  5. data/_includes/footer.html +28 -2
  6. data/_includes/icon.html +2 -2
  7. data/_includes/link.html +1 -1
  8. data/_includes/prevnext-navigation.html +0 -8
  9. data/_includes/secondarynav.html +13 -83
  10. data/_includes/subpages.html +35 -0
  11. data/_layouts/default.html +4 -4
  12. data/_layouts/home.html +1 -1
  13. data/_layouts/languagefile.js +7 -0
  14. data/_layouts/news.html +2 -2
  15. data/_layouts/policy.html +2 -2
  16. data/_layouts/sidenav.html +2 -2
  17. data/_layouts/sidenavsidebar.html +2 -2
  18. data/assets/ableplayer/build/ableplayer.dist.js +419 -220
  19. data/assets/ableplayer/build/ableplayer.js +391 -192
  20. data/assets/ableplayer/build/ableplayer.min.js +2 -2
  21. data/assets/ableplayer/button-icons/black/captions.png +0 -0
  22. data/assets/ableplayer/button-icons/black/chapters.png +0 -0
  23. data/assets/ableplayer/button-icons/black/close.png +0 -0
  24. data/assets/ableplayer/button-icons/black/descriptions.png +0 -0
  25. data/assets/ableplayer/button-icons/black/ellipsis.png +0 -0
  26. data/assets/ableplayer/button-icons/black/faster.png +0 -0
  27. data/assets/ableplayer/button-icons/black/forward.png +0 -0
  28. data/assets/ableplayer/button-icons/black/fullscreen-collapse.png +0 -0
  29. data/assets/ableplayer/button-icons/black/fullscreen-expand.png +0 -0
  30. data/assets/ableplayer/button-icons/black/help.png +0 -0
  31. data/assets/ableplayer/button-icons/black/next.png +0 -0
  32. data/assets/ableplayer/button-icons/black/pause.png +0 -0
  33. data/assets/ableplayer/button-icons/black/pipe.png +0 -0
  34. data/assets/ableplayer/button-icons/black/play.png +0 -0
  35. data/assets/ableplayer/button-icons/black/preferences.png +0 -0
  36. data/assets/ableplayer/button-icons/black/previous.png +0 -0
  37. data/assets/ableplayer/button-icons/black/restart.png +0 -0
  38. data/assets/ableplayer/button-icons/black/rewind.png +0 -0
  39. data/assets/ableplayer/button-icons/black/sign.png +0 -0
  40. data/assets/ableplayer/button-icons/black/slower.png +0 -0
  41. data/assets/ableplayer/button-icons/black/stop.png +0 -0
  42. data/assets/ableplayer/button-icons/black/transcript.png +0 -0
  43. data/assets/ableplayer/button-icons/black/volume-loud.png +0 -0
  44. data/assets/ableplayer/button-icons/black/volume-medium.png +0 -0
  45. data/assets/ableplayer/button-icons/black/volume-mute.png +0 -0
  46. data/assets/ableplayer/button-icons/black/volume-soft.png +0 -0
  47. data/assets/ableplayer/button-icons/white/captions.png +0 -0
  48. data/assets/ableplayer/button-icons/white/chapters.png +0 -0
  49. data/assets/ableplayer/button-icons/white/close.png +0 -0
  50. data/assets/ableplayer/button-icons/white/descriptions.png +0 -0
  51. data/assets/ableplayer/button-icons/white/ellipsis.png +0 -0
  52. data/assets/ableplayer/button-icons/white/faster.png +0 -0
  53. data/assets/ableplayer/button-icons/white/forward.png +0 -0
  54. data/assets/ableplayer/button-icons/white/fullscreen-collapse.png +0 -0
  55. data/assets/ableplayer/button-icons/white/fullscreen-expand.png +0 -0
  56. data/assets/ableplayer/button-icons/white/help.png +0 -0
  57. data/assets/ableplayer/button-icons/white/next.png +0 -0
  58. data/assets/ableplayer/button-icons/white/pause.png +0 -0
  59. data/assets/ableplayer/button-icons/white/pipe.png +0 -0
  60. data/assets/ableplayer/button-icons/white/play.png +0 -0
  61. data/assets/ableplayer/button-icons/white/preferences.png +0 -0
  62. data/assets/ableplayer/button-icons/white/previous.png +0 -0
  63. data/assets/ableplayer/button-icons/white/rabbit.png +0 -0
  64. data/assets/ableplayer/button-icons/white/restart.png +0 -0
  65. data/assets/ableplayer/button-icons/white/rewind.png +0 -0
  66. data/assets/ableplayer/button-icons/white/sign.png +0 -0
  67. data/assets/ableplayer/button-icons/white/slower.png +0 -0
  68. data/assets/ableplayer/button-icons/white/stop.png +0 -0
  69. data/assets/ableplayer/button-icons/white/transcript.png +0 -0
  70. data/assets/ableplayer/button-icons/white/turtle.png +0 -0
  71. data/assets/ableplayer/button-icons/white/volume-loud.png +0 -0
  72. data/assets/ableplayer/button-icons/white/volume-medium.png +0 -0
  73. data/assets/ableplayer/button-icons/white/volume-mute.png +0 -0
  74. data/assets/ableplayer/button-icons/white/volume-soft.png +0 -0
  75. data/assets/ableplayer/images/wingrip.png +0 -0
  76. data/assets/css/style.css +1 -1
  77. data/assets/css/style.css.map +1 -1
  78. data/assets/fonts/notosansmono/notosansmono-bold.woff +0 -0
  79. data/assets/fonts/notosansmono/notosansmono-bold.woff2 +0 -0
  80. data/assets/fonts/notosansmono/notosansmono-regular.woff +0 -0
  81. data/assets/fonts/notosansmono/notosansmono-regular.woff2 +0 -0
  82. data/assets/images/icons.svg +3 -0
  83. data/assets/images/inline.png +0 -0
  84. data/assets/images/social-sharing-default.jpg +0 -0
  85. data/assets/scripts/lang/ar.js +4 -0
  86. data/assets/scripts/lang/de.js +4 -0
  87. data/assets/scripts/lang/el.js +4 -0
  88. data/assets/scripts/lang/es.js +4 -0
  89. data/assets/scripts/lang/fr.js +4 -0
  90. data/assets/scripts/lang/ja.js +4 -0
  91. data/assets/scripts/lang/ko.js +4 -0
  92. data/assets/scripts/lang/nl.js +4 -0
  93. data/assets/scripts/lang/pt-BR.js +4 -0
  94. data/assets/scripts/lang/ru.js +4 -0
  95. data/assets/scripts/lang/zh-hans.js +4 -0
  96. data/assets/scripts/main.js +20 -7
  97. metadata +23 -9
  98. data/assets/fonts/notosansmono/notosansmono-semicondensed.woff +0 -0
  99. data/assets/fonts/notosansmono/notosansmono-semicondensed.woff2 +0 -0
  100. data/assets/fonts/notosansmono/notosansmono-semicondensedbold.woff +0 -0
  101. data/assets/fonts/notosansmono/notosansmono-semicondensedbold.woff2 +0 -0
@@ -34,11 +34,15 @@
34
34
  /*global $, jQuery */
35
35
  "use strict";
36
36
 
37
+ // maintain an array of Able Player instances for use globally (e.g., for keeping prefs in sync)
38
+ var AblePlayerInstances = [];
39
+
37
40
  (function ($) {
38
41
  $(document).ready(function () {
42
+
39
43
  $('video, audio').each(function (index, element) {
40
44
  if ($(element).data('able-player') !== undefined) {
41
- new AblePlayer($(this),$(element));
45
+ AblePlayerInstances.push(new AblePlayer($(this),$(element)));
42
46
  }
43
47
  });
44
48
  });
@@ -61,10 +65,8 @@
61
65
  // Parameters are:
62
66
  // media - jQuery selector or element identifying the media.
63
67
  window.AblePlayer = function(media) {
64
-
65
68
  // Keep track of the last player created for use with global events.
66
69
  AblePlayer.lastCreated = this;
67
-
68
70
  this.media = media;
69
71
  if ($(media).length === 0) {
70
72
  this.provideFallback();
@@ -845,7 +847,6 @@
845
847
  // Bootstrap from this.media possibly being an ID or other selector.
846
848
  this.$media = $(this.media).first();
847
849
  this.media = this.$media[0];
848
-
849
850
  // Set media type to 'audio' or 'video'; this determines some of the behavior of player creation.
850
851
  if (this.$media.is('audio')) {
851
852
  this.mediaType = 'audio';
@@ -1395,7 +1396,7 @@
1395
1396
  AblePlayer.prototype.updateCookie = function( setting ) {
1396
1397
 
1397
1398
  // called when a particular setting had been updated
1398
- // useful for settings updated indpedently of Preferences dialog
1399
+ // useful for settings updated indepedently of Preferences dialog
1399
1400
  // e.g., prefAutoScrollTranscript, which is updated in control.js > handleTranscriptLockToggle()
1400
1401
  // setting is any supported preference name (e.g., "prefCaptions")
1401
1402
  // OR 'transcript' or 'sign' (not user-defined preferences, used to save position of draggable windows)
@@ -1608,8 +1609,10 @@
1608
1609
  return prefs;
1609
1610
  };
1610
1611
 
1611
- // Loads current/default preferences from cookie into the AblePlayer object.
1612
1612
  AblePlayer.prototype.loadCurrentPreferences = function () {
1613
+
1614
+ // Load current/default preferences from cookie into the AblePlayer object.
1615
+
1613
1616
  var available = this.getAvailablePreferences();
1614
1617
  var cookie = this.getCookie();
1615
1618
 
@@ -1991,7 +1994,7 @@
1991
1994
  cancelButton = $('<button class="modal-button">' + this.tt.cancel + '</button>');
1992
1995
  saveButton.click(function () {
1993
1996
  dialog.hide();
1994
- thisObj.savePrefsFromForm();
1997
+ thisObj.savePrefsFromForm();
1995
1998
  });
1996
1999
  cancelButton.click(function () {
1997
2000
  dialog.hide();
@@ -2028,19 +2031,22 @@
2028
2031
  });
2029
2032
  };
2030
2033
 
2031
- // Reset preferences form with default values from cookie
2032
- // Called when user clicks cancel or close button in Prefs Dialog
2033
- // also called when user presses Escape
2034
-
2035
2034
  AblePlayer.prototype.resetPrefsForm = function () {
2036
2035
 
2037
- var thisObj, cookie, available, i, prefName, thisDiv, thisId;
2036
+ // Reset preferences form with default values from cookie
2037
+ // Called when:
2038
+ // User clicks cancel or close button in Prefs Dialog
2039
+ // User presses Escape to close Prefs dialog
2040
+ // User clicks Save in Prefs dialog, & there's more than one player on page
2041
+
2042
+ var thisObj, cookie, available, i, prefName, prefId, thisDiv, thisId;
2038
2043
 
2039
2044
  thisObj = this;
2040
2045
  cookie = this.getCookie();
2041
2046
  available = this.getAvailablePreferences();
2042
2047
  for (i=0; i<available.length; i++) {
2043
2048
  prefName = available[i]['name'];
2049
+ prefId = this.mediaId + '_' + prefName;
2044
2050
  if ((prefName.indexOf('Captions') !== -1) && (prefName !== 'prefCaptions')) {
2045
2051
  // this is a caption-related select box
2046
2052
  $('select[name="' + prefName + '"]').val(cookie.preferences[prefName]);
@@ -2058,21 +2064,24 @@
2058
2064
  this.stylizeCaptions(this.$sampleCapsDiv);
2059
2065
  };
2060
2066
 
2061
- // Return a prefs object constructed from the form.
2062
2067
  AblePlayer.prototype.savePrefsFromForm = function () {
2068
+
2069
+ // Return a prefs object constructed from the form.
2063
2070
  // called when user saves the Preferences form
2064
2071
  // update cookie with new value
2065
- var numChanges, numCapChanges, capSizeChanged, capSizeValue, newValue;
2072
+ var cookie, available, prefName, prefId, numChanges,
2073
+ numCapChanges, capSizeChanged, capSizeValue, newValue;
2066
2074
 
2067
2075
  numChanges = 0;
2068
2076
  numCapChanges = 0; // changes to caption-style-related preferences
2069
2077
  capSizeChanged = false;
2070
- var cookie = this.getCookie();
2071
- var available = this.getAvailablePreferences();
2078
+ cookie = this.getCookie();
2079
+ available = this.getAvailablePreferences();
2072
2080
  for (var i=0; i < available.length; i++) {
2073
2081
  // only prefs with labels are used in the Prefs form
2074
2082
  if (available[i]['label']) {
2075
- var prefName = available[i]['name'];
2083
+ prefName = available[i]['name'];
2084
+ prefId = this.mediaId + '_' + prefName;
2076
2085
  if (prefName == 'prefDescFormat') {
2077
2086
  // As of v4.0.10, prefDescFormat is no longer a choice
2078
2087
  // this.prefDescFormat = $('input[name="' + prefName + '"]:checked').val();
@@ -2084,7 +2093,7 @@
2084
2093
  }
2085
2094
  else if ((prefName.indexOf('Captions') !== -1) && (prefName !== 'prefCaptions')) {
2086
2095
  // this is one of the caption-related select fields
2087
- newValue = $('select[name="' + prefName + '"]').val();
2096
+ newValue = $('select[id="' + prefId + '"]').val();
2088
2097
  if (cookie.preferences[prefName] !== newValue) { // user changed setting
2089
2098
  cookie.preferences[prefName] = newValue;
2090
2099
  // also update global var for this pref (for caption fields, not done elsewhere)
@@ -2098,7 +2107,7 @@
2098
2107
  }
2099
2108
  }
2100
2109
  else { // all other fields are checkboxes
2101
- if ($('input[name="' + prefName + '"]').is(':checked')) {
2110
+ if ($('input[id="' + prefId + '"]').is(':checked')) {
2102
2111
  cookie.preferences[prefName] = 1;
2103
2112
  if (this[prefName] === 1) {
2104
2113
  // nothing has changed
@@ -2136,35 +2145,38 @@
2136
2145
  // update font size of YouTube captions
2137
2146
  this.youTubePlayer.setOption(this.ytCaptionModule,'fontSize',this.translatePrefs('size',capSizeValue,'youtube'));
2138
2147
  }
2139
- this.updatePrefs();
2140
- if (numCapChanges > 0) {
2141
- this.stylizeCaptions(this.$captionsDiv);
2142
- // also apply same changes to descriptions, if present
2143
- if (typeof this.$descDiv !== 'undefined') {
2144
- this.stylizeCaptions(this.$descDiv);
2145
- }
2146
- }
2148
+ if (AblePlayerInstances.length > 1) {
2149
+ // there are multiple players on this page.
2150
+ // update prefs for ALL of them
2151
+ for (var i=0; i<AblePlayerInstances.length; i++) {
2152
+ AblePlayerInstances[i].updatePrefs();
2153
+ AblePlayerInstances[i].loadCurrentPreferences();
2154
+ AblePlayerInstances[i].resetPrefsForm();
2155
+ if (numCapChanges > 0) {
2156
+ AblePlayerInstances[i].stylizeCaptions(AblePlayerInstances[i].$captionsDiv);
2157
+ // also apply same changes to descriptions, if present
2158
+ if (typeof AblePlayerInstances[i].$descDiv !== 'undefined') {
2159
+ AblePlayerInstances[i].stylizeCaptions(AblePlayerInstances[i].$descDiv);
2160
+ }
2161
+ }
2162
+ }
2163
+ }
2164
+ else {
2165
+ // there is only one player
2166
+ this.updatePrefs();
2167
+ if (numCapChanges > 0) {
2168
+ this.stylizeCaptions(this.$captionsDiv);
2169
+ // also apply same changes to descriptions, if present
2170
+ if (typeof this.$descDiv !== 'undefined') {
2171
+ this.stylizeCaptions(this.$descDiv);
2172
+ }
2173
+ }
2174
+ }
2147
2175
  }
2148
2176
 
2149
- // Updates player based on current prefs. Safe to call multiple times.
2150
2177
  AblePlayer.prototype.updatePrefs = function () {
2151
2178
 
2152
- var modHelp;
2153
-
2154
- // modifier keys (update help text)
2155
- if (this.prefAltKey === 1) {
2156
- modHelp = 'Alt + ';
2157
- }
2158
- else {
2159
- modHelp = '';
2160
- }
2161
- if (this.prefCtrlKey === 1) {
2162
- modHelp += 'Control + ';
2163
- }
2164
- if (this.prefShiftKey === 1) {
2165
- modHelp += 'Shift + ';
2166
- }
2167
- $('.able-help-modifiers').text(modHelp);
2179
+ // Update player based on current prefs. Safe to call multiple times.
2168
2180
 
2169
2181
  // tabbable transcript
2170
2182
  if (this.prefTabbable === 1) {
@@ -2187,6 +2199,7 @@
2187
2199
  };
2188
2200
 
2189
2201
  AblePlayer.prototype.usingModifierKeys = function(e) {
2202
+
2190
2203
  // return true if user is holding down required modifier keys
2191
2204
  if ((this.prefAltKey === 1) && !e.altKey) {
2192
2205
  return false;
@@ -3374,44 +3387,51 @@
3374
3387
 
3375
3388
  // Populate menu with menu items
3376
3389
  if (which === 'prefs') {
3377
- prefCats = this.getPreferencesGroups();
3378
- for (i = 0; i < prefCats.length; i++) {
3379
- $menuItem = $('<li></li>',{
3380
- 'role': 'menuitem',
3381
- 'tabindex': '-1'
3382
- });
3383
- prefCat = prefCats[i];
3384
- if (prefCat === 'captions') {
3385
- $menuItem.text(this.tt.prefMenuCaptions);
3386
- }
3387
- else if (prefCat === 'descriptions') {
3388
- $menuItem.text(this.tt.prefMenuDescriptions);
3389
- }
3390
- else if (prefCat === 'keyboard') {
3391
- $menuItem.text(this.tt.prefMenuKeyboard);
3392
- }
3393
- else if (prefCat === 'transcript') {
3394
- $menuItem.text(this.tt.prefMenuTranscript);
3395
- }
3396
- $menuItem.on('click',function() {
3397
- whichPref = $(this).text();
3398
- thisObj.setFullscreen(false);
3399
- if (whichPref === thisObj.tt.prefMenuCaptions) {
3400
- thisObj.captionPrefsDialog.show();
3401
- }
3402
- else if (whichPref === thisObj.tt.prefMenuDescriptions) {
3403
- thisObj.descPrefsDialog.show();
3404
- }
3405
- else if (whichPref === thisObj.tt.prefMenuKeyboard) {
3406
- thisObj.keyboardPrefsDialog.show();
3407
- }
3408
- else if (whichPref === thisObj.tt.prefMenuTranscript) {
3409
- thisObj.transcriptPrefsDialog.show();
3410
- }
3411
- thisObj.closePopups();
3412
- });
3413
- $menu.append($menuItem);
3414
- }
3390
+ if (this.prefCats.length > 1) {
3391
+ for (i = 0; i < this.prefCats.length; i++) {
3392
+ $menuItem = $('<li></li>',{
3393
+ 'role': 'menuitem',
3394
+ 'tabindex': '-1'
3395
+ });
3396
+ prefCat = this.prefCats[i];
3397
+ if (prefCat === 'captions') {
3398
+ $menuItem.text(this.tt.prefMenuCaptions);
3399
+ }
3400
+ else if (prefCat === 'descriptions') {
3401
+ $menuItem.text(this.tt.prefMenuDescriptions);
3402
+ }
3403
+ else if (prefCat === 'keyboard') {
3404
+ $menuItem.text(this.tt.prefMenuKeyboard);
3405
+ }
3406
+ else if (prefCat === 'transcript') {
3407
+ $menuItem.text(this.tt.prefMenuTranscript);
3408
+ }
3409
+ $menuItem.on('click',function() {
3410
+ whichPref = $(this).text();
3411
+ thisObj.setFullscreen(false);
3412
+ if (whichPref === thisObj.tt.prefMenuCaptions) {
3413
+ thisObj.captionPrefsDialog.show();
3414
+ }
3415
+ else if (whichPref === thisObj.tt.prefMenuDescriptions) {
3416
+ thisObj.descPrefsDialog.show();
3417
+ }
3418
+ else if (whichPref === thisObj.tt.prefMenuKeyboard) {
3419
+ thisObj.keyboardPrefsDialog.show();
3420
+ }
3421
+ else if (whichPref === thisObj.tt.prefMenuTranscript) {
3422
+ thisObj.transcriptPrefsDialog.show();
3423
+ }
3424
+ thisObj.closePopups();
3425
+ });
3426
+ $menu.append($menuItem);
3427
+ }
3428
+ this.$prefsButton.attr('data-prefs-popup','menu');
3429
+ }
3430
+ else if (this.prefCats.length == 1) {
3431
+ // only 1 category, so don't create a popup menu.
3432
+ // Instead, open dialog directly when user clicks Prefs button
3433
+ this.$prefsButton.attr('data-prefs-popup',this.prefCats[0]);
3434
+ }
3415
3435
  }
3416
3436
  else if (which === 'captions' || which === 'chapters') {
3417
3437
  hasDefault = false;
@@ -3562,34 +3582,34 @@
3562
3582
 
3563
3583
  if (this.chaptersPopup && this.chaptersPopup.is(':visible')) {
3564
3584
  this.chaptersPopup.hide();
3565
- this.$chaptersButton.attr('aria-expanded','false').focus();
3585
+ this.$chaptersButton.removeAttr('aria-expanded').focus();
3566
3586
  }
3567
3587
  if (this.captionsPopup && this.captionsPopup.is(':visible')) {
3568
3588
  this.captionsPopup.hide();
3569
- this.$ccButton.attr('aria-expanded','false').focus();
3589
+ this.$ccButton.removeAttr('aria-expanded').focus();
3570
3590
  }
3571
3591
  if (this.prefsPopup && this.prefsPopup.is(':visible')) {
3572
3592
  this.prefsPopup.hide();
3573
3593
  // restore menu items to their original state
3574
3594
  this.prefsPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
3575
- this.$prefsButton.attr('aria-expanded','false').focus();
3595
+ this.$prefsButton.removeAttr('aria-expanded').focus();
3576
3596
  }
3577
3597
  if (this.$volumeSlider && this.$volumeSlider.is(':visible')) {
3578
3598
  this.$volumeSlider.hide().attr('aria-hidden','true');
3579
3599
  this.$volumeAlert.text(this.tt.volumeSliderClosed);
3580
- this.$volumeButton.attr('aria-expanded','false').focus();
3600
+ this.$volumeButton.removeAttr('aria-expanded').focus();
3581
3601
  }
3582
3602
  if (this.$transcriptPopup && this.$transcriptPopup.is(':visible')) {
3583
3603
  this.$transcriptPopup.hide();
3584
3604
  // restore menu items to their original state
3585
3605
  this.$transcriptPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
3586
- this.$transcriptPopupButton.attr('aria-expanded','false').focus();
3606
+ this.$transcriptPopupButton.removeAttr('aria-expanded').focus();
3587
3607
  }
3588
3608
  if (this.$signPopup && this.$signPopup.is(':visible')) {
3589
3609
  this.$signPopup.hide();
3590
3610
  // restore menu items to their original state
3591
3611
  this.$signPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
3592
- this.$signPopupButton.attr('aria-expanded','false').focus();
3612
+ this.$signPopupButton.removeAttr('aria-expanded').focus();
3593
3613
  }
3594
3614
  };
3595
3615
 
@@ -3973,15 +3993,31 @@
3973
3993
  });
3974
3994
  if (control === 'volume' || control === 'preferences') {
3975
3995
  if (control == 'preferences') {
3976
- popupMenuId = this.mediaId + '-prefs-menu';
3996
+ this.prefCats = this.getPreferencesGroups();
3997
+ if (this.prefCats.length > 1) {
3998
+ // Prefs button will trigger a menu
3999
+ popupMenuId = this.mediaId + '-prefs-menu';
4000
+ $newButton.attr({
4001
+ 'aria-controls': popupMenuId,
4002
+ 'aria-haspopup': 'menu'
4003
+ });
4004
+ }
4005
+ else if (this.prefCats.length === 1) {
4006
+ // Prefs button will trigger a dialog
4007
+ $newButton.attr({
4008
+ 'aria-haspopup': 'dialog'
4009
+ });
4010
+ }
3977
4011
  }
3978
4012
  else if (control === 'volume') {
3979
4013
  popupMenuId = this.mediaId + '-volume-slider';
4014
+ // volume slider popup is not a menu or a dialog
4015
+ // therefore, using aria-expanded rather than aria-haspopup to communicate properties/state
4016
+ $newButton.attr({
4017
+ 'aria-controls': popupMenuId,
4018
+ 'aria-expanded': 'false'
4019
+ });
3980
4020
  }
3981
- $newButton.attr({
3982
- 'aria-controls': popupMenuId,
3983
- 'aria-expanded': 'false'
3984
- });
3985
4021
  }
3986
4022
  if (this.iconType === 'font') {
3987
4023
  if (control === 'volume') {
@@ -5955,6 +5991,121 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
5955
5991
  this.setDuration(max);
5956
5992
  }
5957
5993
 
5994
+ // handle seekHead events
5995
+ this.seekHead.on('mouseenter mouseleave mousemove mousedown mouseup focus blur touchstart touchmove touchend', function (e) {
5996
+
5997
+ if (e.type === 'mouseenter' || e.type === 'focus') {
5998
+ thisObj.overHead = true;
5999
+ }
6000
+ else if (e.type === 'mouseleave' || e.type === 'blur') {
6001
+ thisObj.overHead = false;
6002
+ if (!thisObj.overBody && thisObj.tracking && thisObj.trackDevice === 'mouse') {
6003
+ thisObj.stopTracking(thisObj.pageXToPosition(e.pageX));
6004
+ }
6005
+ }
6006
+ else if (e.type === 'mousemove' || e.type === 'touchmove') {
6007
+ if (thisObj.tracking && thisObj.trackDevice === 'mouse') {
6008
+ thisObj.trackHeadAtPageX(e.pageX);
6009
+ }
6010
+ }
6011
+ else if (e.type === 'mousedown' || e.type === 'touchstart') {
6012
+ thisObj.startTracking('mouse', thisObj.pageXToPosition(thisObj.seekHead.offset() + (thisObj.seekHead.width() / 2)));
6013
+ if (!thisObj.bodyDiv.is(':focus')) {
6014
+ thisObj.bodyDiv.focus();
6015
+ }
6016
+ e.preventDefault();
6017
+ }
6018
+ else if (e.type === 'mouseup' || e.type === 'touchend') {
6019
+ if (thisObj.tracking && thisObj.trackDevice === 'mouse') {
6020
+ thisObj.stopTracking(thisObj.pageXToPosition(e.pageX));
6021
+ }
6022
+ }
6023
+ if (e.type !== 'mousemove' && e.type !== 'mousedown' && e.type !== 'mouseup' && e.type !== 'touchstart' && e.type !== 'touchend') {
6024
+ thisObj.refreshTooltip();
6025
+ }
6026
+ });
6027
+
6028
+ // handle bodyDiv events
6029
+ this.bodyDiv.on(
6030
+ 'mouseenter mouseleave mousemove mousedown mouseup keydown keyup touchstart touchmove touchend', function (e) {
6031
+
6032
+ if (e.type === 'mouseenter') {
6033
+ thisObj.overBody = true;
6034
+ }
6035
+ else if (e.type === 'mouseleave') {
6036
+ thisObj.overBody = false;
6037
+ thisObj.overBodyMousePos = null;
6038
+ if (!thisObj.overHead && thisObj.tracking && thisObj.trackDevice === 'mouse') {
6039
+ thisObj.stopTracking(thisObj.pageXToPosition(e.pageX));
6040
+ }
6041
+ }
6042
+ else if (e.type === 'mousemove' || e.type === 'touchmove') {
6043
+ thisObj.overBodyMousePos = {
6044
+ x: e.pageX,
6045
+ y: e.pageY
6046
+ };
6047
+ if (thisObj.tracking && thisObj.trackDevice === 'mouse') {
6048
+ thisObj.trackHeadAtPageX(e.pageX);
6049
+ }
6050
+ }
6051
+ else if (e.type === 'mousedown' || e.type === 'touchstart') {
6052
+ thisObj.startTracking('mouse', thisObj.pageXToPosition(e.pageX));
6053
+ thisObj.trackHeadAtPageX(e.pageX);
6054
+ if (!thisObj.seekHead.is(':focus')) {
6055
+ thisObj.seekHead.focus();
6056
+ }
6057
+ e.preventDefault();
6058
+ }
6059
+ else if (e.type === 'mouseup' || e.type === 'touchend') {
6060
+ if (thisObj.tracking && thisObj.trackDevice === 'mouse') {
6061
+ thisObj.stopTracking(thisObj.pageXToPosition(e.pageX));
6062
+ }
6063
+ }
6064
+ else if (e.type === 'keydown') {
6065
+ // Home
6066
+ if (e.which === 36) {
6067
+ thisObj.trackImmediatelyTo(0);
6068
+ }
6069
+ // End
6070
+ else if (e.which === 35) {
6071
+ thisObj.trackImmediatelyTo(thisObj.duration);
6072
+ }
6073
+ // Left arrow or down arrow
6074
+ else if (e.which === 37 || e.which === 40) {
6075
+ thisObj.arrowKeyDown(-1);
6076
+ }
6077
+ // Right arrow or up arrow
6078
+ else if (e.which === 39 || e.which === 38) {
6079
+ thisObj.arrowKeyDown(1);
6080
+ }
6081
+ // Page up
6082
+ else if (e.which === 33 && bigInterval > 0) {
6083
+ thisObj.arrowKeyDown(bigInterval);
6084
+ }
6085
+ // Page down
6086
+ else if (e.which === 34 && bigInterval > 0) {
6087
+ thisObj.arrowKeyDown(-bigInterval);
6088
+ }
6089
+ else {
6090
+ return;
6091
+ }
6092
+ e.preventDefault();
6093
+ }
6094
+ else if (e.type === 'keyup') {
6095
+ if (e.which >= 33 && e.which <= 40) {
6096
+ if (thisObj.tracking && thisObj.trackDevice === 'keyboard') {
6097
+ thisObj.stopTracking(thisObj.keyTrackPosition);
6098
+ }
6099
+ e.preventDefault();
6100
+ }
6101
+ }
6102
+ if (e.type !== 'mouseup' && e.type !== 'keydown' && e.type !== 'keydown') {
6103
+ thisObj.refreshTooltip();
6104
+ }
6105
+ });
6106
+
6107
+ /* Old event listeners on seekHead and bodyDiv...
6108
+
5958
6109
  this.seekHead.hover(function (e) {
5959
6110
  thisObj.overHead = true;
5960
6111
  thisObj.refreshTooltip();
@@ -6076,6 +6227,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
6076
6227
  e.preventDefault();
6077
6228
  }
6078
6229
  });
6230
+ */
6079
6231
  }
6080
6232
 
6081
6233
  AccessibleSlider.prototype.arrowKeyDown = function (multiplier) {
@@ -6818,7 +6970,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
6818
6970
  e.stopPropagation();
6819
6971
  });
6820
6972
 
6821
- $('body > *').not('.able-modal-overlay').not('.able-modal-dialog').attr('aria-hidden', 'false');
6973
+ $('body > *').not('.able-modal-overlay').not('.able-modal-dialog').removeAttr('aria-hidden');
6822
6974
  };
6823
6975
 
6824
6976
  AccessibleDialog.prototype.show = function () {
@@ -6865,7 +7017,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
6865
7017
  }
6866
7018
  this.modal.css('display', 'none');
6867
7019
  this.modal.attr('aria-hidden', 'true');
6868
- $('body > *').not('.able-modal-overlay').not('.able-modal-dialog').attr('aria-hidden', 'false');
7020
+ $('body > *').not('.able-modal-overlay').not('.able-modal-dialog').removeAttr('aria-hidden');
6869
7021
 
6870
7022
  this.focusedElementBeforeModal.focus();
6871
7023
  };
@@ -7638,7 +7790,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7638
7790
  deferred = new $.Deferred();
7639
7791
  promise = deferred.promise();
7640
7792
  thisObj = this;
7641
-
7642
7793
  if (typeof duration !== 'undefined' && typeof elapsed !== 'undefined') {
7643
7794
  mediaTimes['duration'] = duration;
7644
7795
  mediaTimes['elapsed'] = elapsed;
@@ -7771,7 +7922,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7771
7922
  return;
7772
7923
  }
7773
7924
  */
7774
-
7775
7925
  var deferred, promise, thisObj, duration, elapsed;
7776
7926
  deferred = new $.Deferred();
7777
7927
  promise = deferred.promise();
@@ -7782,7 +7932,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7782
7932
  deferred.resolve('ended');
7783
7933
  }
7784
7934
  else if (this.media.paused) {
7785
- deferred.resolve('paused');
7935
+ deferred.resolve('paused');
7786
7936
  }
7787
7937
  else if (this.media.readyState !== 4) {
7788
7938
  deferred.resolve('buffering');
@@ -8227,8 +8377,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8227
8377
  this.$ccButton.attr({
8228
8378
  'aria-label': this.tt.captions,
8229
8379
  'aria-haspopup': 'true',
8230
- 'aria-controls': this.mediaId + '-captions-menu',
8231
- 'aria-expanded': 'false'
8380
+ 'aria-controls': this.mediaId + '-captions-menu'
8232
8381
  });
8233
8382
  this.$ccButton.find('span.able-clipped').text(this.tt.captions);
8234
8383
  }
@@ -8270,7 +8419,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8270
8419
  }
8271
8420
  }
8272
8421
  }
8273
-
8274
8422
  if (context === 'playpause' || context == 'init'){
8275
8423
  if (typeof this.$bigPlayButton !== 'undefined' && typeof this.seekBar !== 'undefined') {
8276
8424
  // Choose show/hide for big play button and adjust position.
@@ -8316,7 +8464,11 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8316
8464
  // Set a flag to ignore the coming scroll event.
8317
8465
  // there's no other way I know of to differentiate programmatic and user-initiated scroll events.
8318
8466
  this.scrollingTranscript = true;
8319
- $('.able-transcript').scrollTop(newTop);
8467
+ // only scroll once after moving a highlight
8468
+ if (this.movingHighlight) {
8469
+ $('.able-transcript').scrollTop(newTop);
8470
+ this.movingHighlight = false;
8471
+ }
8320
8472
  }
8321
8473
  }
8322
8474
  }
@@ -8328,8 +8480,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8328
8480
  this.$chaptersButton.attr({
8329
8481
  'aria-label': this.tt.chapters,
8330
8482
  'aria-haspopup': 'true',
8331
- 'aria-controls': this.mediaId + '-chapters-menu',
8332
- 'aria-expanded': 'false'
8483
+ 'aria-controls': this.mediaId + '-chapters-menu'
8333
8484
  });
8334
8485
  }
8335
8486
  }
@@ -8373,28 +8524,45 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8373
8524
  // also don't update while tracking, since this may Pause/Play the player but we don't want to send a Pause/Play update.
8374
8525
  this.getPlayerState().then(function(currentState) {
8375
8526
  if (thisObj.$status.text() !== textByState[currentState] && !thisObj.seekBar.tracking) {
8376
- // Debounce updates; only update after status has stayed steadily different for 250ms.
8377
- timestamp = (new Date()).getTime();
8527
+ // Debounce updates; only update after status has stayed steadily different for a while
8528
+ // "A while" is defined differently depending on context
8529
+ if (thisObj.swappingSrc) {
8530
+ // this is where most of the chatter occurs (e.g., playing, paused, buffering, playing),
8531
+ // so set a longer wait time before writing a status message
8532
+ if (!thisObj.debouncingStatus) {
8533
+ thisObj.statusMessageThreshold = 2000; // in ms (2 seconds)
8534
+ }
8535
+ }
8536
+ else {
8537
+ // for all other contexts (e.g., users clicks Play/Pause)
8538
+ // user should receive more rapid feedback
8539
+ if (!thisObj.debouncingStatus) {
8540
+ thisObj.statusMessageThreshold = 250; // in ms
8541
+ }
8542
+ }
8543
+ timestamp = (new Date()).getTime();
8378
8544
  if (!thisObj.statusDebounceStart) {
8379
8545
  thisObj.statusDebounceStart = timestamp;
8380
- // Make sure refreshControls gets called again at the appropriate time to check.
8546
+ // Call refreshControls() again after allotted time has passed
8547
+ thisObj.debouncingStatus = true;
8381
8548
  thisObj.statusTimeout = setTimeout(function () {
8549
+ thisObj.debouncingStatus = false;
8382
8550
  thisObj.refreshControls(context);
8383
- }, 300);
8384
- }
8385
- else if ((timestamp - thisObj.statusDebounceStart) > 250) {
8386
- thisObj.$status.text(textByState[currentState]);
8387
- thisObj.statusDebounceStart = null;
8388
- clearTimeout(thisObj.statusTimeout);
8389
- thisObj.statusTimeout = null;
8551
+ }, thisObj.statusMessageThreshold);
8390
8552
  }
8553
+ else if ((timestamp - thisObj.statusDebounceStart) > thisObj.statusMessageThreshold) {
8554
+ thisObj.$status.text(textByState[currentState]);
8555
+ thisObj.statusDebounceStart = null;
8556
+ clearTimeout(thisObj.statusTimeout);
8557
+ thisObj.statusTimeout = null;
8558
+ }
8391
8559
  }
8392
8560
  else {
8393
8561
  thisObj.statusDebounceStart = null;
8562
+ thisObj.debouncingStatus = false;
8394
8563
  clearTimeout(thisObj.statusTimeout);
8395
8564
  thisObj.statusTimeout = null;
8396
8565
  }
8397
-
8398
8566
  // Don't change play/pause button display while using the seek bar (or if YouTube stopped)
8399
8567
  if (!thisObj.seekBar.tracking && !thisObj.stoppingYouTube) {
8400
8568
  if (currentState === 'paused' || currentState === 'stopped' || currentState === 'ended') {
@@ -8699,7 +8867,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8699
8867
  if (this.captionsPopup && this.captionsPopup.is(':visible')) {
8700
8868
  this.captionsPopup.hide();
8701
8869
  this.hidingPopup = false;
8702
- this.$ccButton.attr('aria-expanded','false').focus();
8870
+ this.$ccButton.removeAttr('aria-expanded').focus();
8703
8871
  }
8704
8872
  else {
8705
8873
  this.closePopups();
@@ -8726,7 +8894,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8726
8894
  if (this.chaptersPopup.is(':visible')) {
8727
8895
  this.chaptersPopup.hide();
8728
8896
  this.hidingPopup = false;
8729
- this.$chaptersButton.attr('aria-expanded','false').focus();
8897
+ this.$chaptersButton.removeAttr('aria-expanded').focus();
8730
8898
  }
8731
8899
  else {
8732
8900
  this.closePopups();
@@ -8760,6 +8928,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8760
8928
  };
8761
8929
 
8762
8930
  AblePlayer.prototype.handlePrefsClick = function(pref) {
8931
+
8763
8932
  // NOTE: the prefs menu is positioned near the right edge of the player
8764
8933
  // This assumes the Prefs button is also positioned in that vicinity
8765
8934
  // (last or second-last button the right)
@@ -8775,7 +8944,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8775
8944
  if (this.prefsPopup.is(':visible')) {
8776
8945
  this.prefsPopup.hide();
8777
8946
  this.hidingPopup = false;
8778
- this.$prefsButton.attr('aria-expanded','false').focus();
8947
+ this.$prefsButton.removeAttr('aria-expanded').focus();
8779
8948
  // restore each menu item to original hidden state
8780
8949
  this.prefsPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
8781
8950
  }
@@ -9759,6 +9928,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
9759
9928
  }
9760
9929
 
9761
9930
  AblePlayer.prototype.stylizeCaptions = function($element, pref) {
9931
+
9762
9932
  // $element is the jQuery element containing the captions
9763
9933
  // this function handles stylizing of the sample caption text in the Prefs dialog
9764
9934
  // plus the actual production captions
@@ -10573,10 +10743,15 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
10573
10743
  }
10574
10744
 
10575
10745
  if (currentTime >= start && currentTime <= end && !isChapterHeading) {
10576
- // move all previous highlights before adding one to current span
10577
- thisObj.$transcriptArea.find('.able-highlight').removeClass('able-highlight');
10578
- $(this).addClass('able-highlight');
10579
- return false;
10746
+
10747
+ // If this item isn't already highlighted, it should be
10748
+ if (!($(this).hasClass('able-highlight'))) {
10749
+ // remove all previous highlights before adding one to current span
10750
+ thisObj.$transcriptArea.find('.able-highlight').removeClass('able-highlight');
10751
+ $(this).addClass('able-highlight');
10752
+ thisObj.movingHighlight = true;
10753
+ }
10754
+ return false;
10580
10755
  }
10581
10756
  });
10582
10757
  thisObj.currentHighlight = $('.able-highlight');
@@ -11128,11 +11303,11 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11128
11303
  (function ($) {
11129
11304
  // Media events
11130
11305
  AblePlayer.prototype.onMediaUpdateTime = function (duration, elapsed) {
11306
+
11131
11307
  // duration and elapsed are passed from callback functions of Vimeo API events
11132
11308
  // duration is expressed as sss.xxx
11133
11309
  // elapsed is expressed as sss.xxx
11134
11310
  var thisObj = this;
11135
-
11136
11311
  this.getMediaTimes(duration,elapsed).then(function(mediaTimes) {
11137
11312
  thisObj.duration = mediaTimes['duration'];
11138
11313
  thisObj.elapsed = mediaTimes['elapsed'];
@@ -11313,7 +11488,9 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11313
11488
  AblePlayer.prototype.onClickPlayerButton = function (el) {
11314
11489
 
11315
11490
  // TODO: This is super-fragile since we need to know the length of the class name to split off; update this to other way of dispatching?
11316
- var whichButton = $(el).attr('class').split(' ')[0].substr(20);
11491
+
11492
+ var whichButton, prefsPopup;
11493
+ whichButton = $(el).attr('class').split(' ')[0].substr(20);
11317
11494
  if (whichButton === 'play') {
11318
11495
  this.clickedPlay = true;
11319
11496
  this.handlePlay();
@@ -11363,7 +11540,25 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11363
11540
  this.handleSignToggle();
11364
11541
  }
11365
11542
  else if (whichButton === 'preferences') {
11366
- this.handlePrefsClick();
11543
+ if ($(el).attr('data-prefs-popup') === 'menu') {
11544
+ this.handlePrefsClick();
11545
+ }
11546
+ else {
11547
+ this.closePopups();
11548
+ prefsPopup = $(el).attr('data-prefs-popup');
11549
+ if (prefsPopup === 'keyboard') {
11550
+ this.keyboardPrefsDialog.show();
11551
+ }
11552
+ else if (prefsPopup === 'captions') {
11553
+ this.captionPrefsDialog.show();
11554
+ }
11555
+ else if (prefsPopup === 'descriptions') {
11556
+ this.descPrefsDialog.show();
11557
+ }
11558
+ else if (prefsPopup === 'transcript') {
11559
+ this.transcriptPrefsDialog.show();
11560
+ }
11561
+ }
11367
11562
  }
11368
11563
  else if (whichButton === 'help') {
11369
11564
  this.handleHelpClick();
@@ -11414,7 +11609,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11414
11609
  if (which >= 65 && which <= 90) {
11415
11610
  which += 32;
11416
11611
  }
11417
-
11418
11612
  // Only use keypress to control player if focus is NOT on a form field or contenteditable element
11419
11613
  if (!(
11420
11614
  $(':focus').is('[contenteditable]') ||
@@ -11538,7 +11732,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11538
11732
  if (!thisObj.startedPlaying) {
11539
11733
  // start playing; no further user action is required
11540
11734
  thisObj.playMedia();
11541
- }
11735
+ }
11542
11736
  thisObj.userClickedPlaylist = false; // reset
11543
11737
  }
11544
11738
  if (thisObj.seekTrigger == 'restart' || thisObj.seekTrigger == 'chapter' || thisObj.seekTrigger == 'transcript') {
@@ -11582,11 +11776,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11582
11776
  // already started playing
11583
11777
  // we're here because a new media source has been loaded and is ready to resume playback
11584
11778
  thisObj.getPlayerState().then(function(currentState) {
11585
- if (thisObj.swappingSrc && currentState === 'stopped') {
11586
- // Safari is the only browser that returns value of 'stopped' (observed in 12.0.1 on MacOS)
11587
- // This prevents 'timeupdate' events from triggering, which prevents the new media src
11588
- // from resuming playback at swapTime
11589
- // This is a hack to jump start Safari
11779
+ if (thisObj.swappingSrc && (currentState === 'stopped' || currentState === 'paused')) {
11590
11780
  thisObj.startedPlaying = false;
11591
11781
  if (thisObj.swapTime > 0) {
11592
11782
  thisObj.seekTo(thisObj.swapTime);
@@ -11630,8 +11820,8 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11630
11820
  .on('pause',function() {
11631
11821
  if (!thisObj.clickedPlay) {
11632
11822
  // 'pause' was triggered automatically, not initiated by user
11633
- // this happens in some browsers (not Chrome, as of 70.x)
11634
- // when swapping source (e.g., between tracks in a playlist, or swapping description)
11823
+ // this happens in some browsers when swapping source
11824
+ // (e.g., between tracks in a playlist or swapping description)
11635
11825
  if (thisObj.hasPlaylist || thisObj.swappingSrc) {
11636
11826
  // do NOT set playing to false.
11637
11827
  // doing so prevents continual playback after new track is loaded
@@ -12067,44 +12257,42 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12067
12257
 
12068
12258
  // add event listener to toolbar to start and end drag
12069
12259
  // other event listeners will be added when drag starts
12070
- $toolbar.on('mousedown', function(e) {
12071
- e.stopPropagation();
12072
- if (!thisObj.windowMenuClickRegistered) {
12073
- thisObj.windowMenuClickRegistered = true;
12074
- thisObj.startMouseX = e.pageX;
12075
- thisObj.startMouseY = e.pageY;
12076
- thisObj.dragDevice = 'mouse';
12077
- thisObj.startDrag(which, $window);
12078
- }
12079
- return false;
12080
- });
12081
- $toolbar.on('mouseup', function(e) {
12260
+ $toolbar.on('mousedown mouseup touchstart touchend', function(e) {
12082
12261
  e.stopPropagation();
12083
- if (thisObj.dragging && thisObj.dragDevice === 'mouse') {
12084
- thisObj.endDrag(which);
12085
- }
12086
- return false;
12262
+ if (e.type === 'mousedown' || e.type === 'touchstart') {
12263
+ if (!thisObj.windowMenuClickRegistered) {
12264
+ thisObj.windowMenuClickRegistered = true;
12265
+ thisObj.startMouseX = e.pageX;
12266
+ thisObj.startMouseY = e.pageY;
12267
+ thisObj.dragDevice = 'mouse'; // ok to use this even if device is a touchpad
12268
+ thisObj.startDrag(which, $window);
12269
+ }
12270
+ }
12271
+ else if (e.type === 'mouseup' || e.type === 'touchend') {
12272
+ if (thisObj.dragging && thisObj.dragDevice === 'mouse') {
12273
+ thisObj.endDrag(which);
12274
+ }
12275
+ }
12276
+ return false;
12087
12277
  });
12088
12278
 
12089
12279
  // add event listeners for resizing
12090
- $resizeHandle.on('mousedown', function(e) {
12091
-
12092
- e.stopPropagation();
12093
- if (!thisObj.windowMenuClickRegistered) {
12094
- thisObj.windowMenuClickRegistered = true;
12095
- thisObj.startMouseX = e.pageX;
12096
- thisObj.startMouseY = e.pageY;
12097
- thisObj.startResize(which, $window);
12098
- return false;
12099
- }
12100
- });
12101
-
12102
- $resizeHandle.on('mouseup', function(e) {
12280
+ $resizeHandle.on('mousedown mouseup touchstart touchend', function(e) {
12103
12281
  e.stopPropagation();
12104
- if (thisObj.resizing) {
12105
- thisObj.endResize(which);
12106
- }
12107
- return false;
12282
+ if (e.type === 'mousedown' || e.type === 'touchstart') {
12283
+ if (!thisObj.windowMenuClickRegistered) {
12284
+ thisObj.windowMenuClickRegistered = true;
12285
+ thisObj.startMouseX = e.pageX;
12286
+ thisObj.startMouseY = e.pageY;
12287
+ thisObj.startResize(which, $window);
12288
+ }
12289
+ }
12290
+ else if (e.type === 'mouseup' || e.type === 'touchend') {
12291
+ if (thisObj.resizing) {
12292
+ thisObj.endResize(which);
12293
+ }
12294
+ }
12295
+ return false;
12108
12296
  });
12109
12297
 
12110
12298
  // whenever a window is clicked, bring it to the foreground
@@ -12549,8 +12737,8 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12549
12737
  }).focus();
12550
12738
 
12551
12739
  // add device-specific event listeners
12552
- if (this.dragDevice === 'mouse') {
12553
- $(document).on('mousemove',function(e) {
12740
+ if (this.dragDevice === 'mouse') { // might also be a touchpad
12741
+ $(document).on('mousemove touchmove',function(e) {
12554
12742
  if (thisObj.dragging) {
12555
12743
  // calculate new top left based on current mouse position - offset
12556
12744
  newX = e.pageX - thisObj.dragOffsetX;
@@ -12654,7 +12842,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12654
12842
  $windowButton = this.$signPopupButton;
12655
12843
  }
12656
12844
 
12657
- $(document).off('mousemove mouseup');
12845
+ $(document).off('mousemove mouseup touchmove touchup');
12658
12846
  this.$activeWindow.off('keydown').removeClass('able-drag');
12659
12847
 
12660
12848
  if (this.dragDevice === 'keyboard') {
@@ -12731,7 +12919,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12731
12919
  this.dragStartHeight = this.$activeWindow.height();
12732
12920
 
12733
12921
  // add event listeners
12734
- $(document).on('mousemove',function(e) {
12922
+ $(document).on('mousemove touchmove',function(e) {
12735
12923
  if (thisObj.resizing) {
12736
12924
  // calculate new width and height based on changes to mouse position
12737
12925
  newWidth = thisObj.dragStartWidth + (e.pageX - thisObj.startMouseX);
@@ -12755,7 +12943,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12755
12943
  $windowButton = this.$signPopupButton;
12756
12944
  }
12757
12945
 
12758
- $(document).off('mousemove mouseup');
12946
+ $(document).off('mousemove mouseup touchmove touchup');
12759
12947
  this.$activeWindow.off('keydown');
12760
12948
 
12761
12949
  $windowButton.show().focus();
@@ -12790,11 +12978,21 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12790
12978
  // If sign language is provided, it must be provided for all sources
12791
12979
  this.signFile = this.$sources.first().attr('data-sign-src');
12792
12980
  if (this.signFile) {
12793
- if (this.debug) {
12794
- console.log('This video has an accompanying sign language video: ' + this.signFile);
12795
- }
12796
- this.hasSignLanguage = true;
12797
- this.injectSignPlayerCode();
12981
+ if (this.isIOS()) {
12982
+ // IOS does not allow multiple videos to play simultaneously
12983
+ // Therefore, sign language as rendered by Able Player unfortunately won't work
12984
+ this.hasSignLanguage = false;
12985
+ if (this.debug) {
12986
+ console.log('Sign language has been disabled due to IOS restrictions');
12987
+ }
12988
+ }
12989
+ else {
12990
+ if (this.debug) {
12991
+ console.log('This video has an accompanying sign language video: ' + this.signFile);
12992
+ }
12993
+ this.hasSignLanguage = true;
12994
+ this.injectSignPlayerCode();
12995
+ }
12798
12996
  }
12799
12997
  else {
12800
12998
  this.hasSignLanguage = false;
@@ -12808,17 +13006,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12808
13006
  AblePlayer.prototype.injectSignPlayerCode = function() {
12809
13007
 
12810
13008
  // create and inject surrounding HTML structure
12811
- // If IOS:
12812
- // If video:
12813
- // IOS does not support any of the player's functionality
12814
- // - everything plays in its own player
12815
- // Therefore, AblePlayer is not loaded & all functionality is disabled
12816
- // (this all determined. If this is IOS && video, this function is never called)
12817
- // If audio:
12818
- // HTML cannot be injected as a *parent* of the <audio> element
12819
- // It is therefore injected *after* the <audio> element
12820
- // This is only a problem in IOS 6 and earlier,
12821
- // & is a known bug, fixed in IOS 7
12822
13009
 
12823
13010
  var thisObj, signVideoId, signVideoWidth, i, signSrc, srcType, $signSource;
12824
13011
 
@@ -13882,10 +14069,10 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
13882
14069
  thisObj = this;
13883
14070
  // get language of the web page, if specified
13884
14071
  if ($('body').attr('lang')) {
13885
- lang = $('body').attr('lang');
14072
+ lang = $('body').attr('lang').toLowerCase();
13886
14073
  }
13887
14074
  else if ($('html').attr('lang')) {
13888
- lang = $('html').attr('lang');
14075
+ lang = $('html').attr('lang').toLowerCase();
13889
14076
  }
13890
14077
  else {
13891
14078
  lang = null;
@@ -15389,7 +15576,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15389
15576
 
15390
15577
  AblePlayer.prototype.getVimeoDimensions = function (vimeoContainerId) {
15391
15578
 
15392
- // get dimensions of YouTube video, return array with width & height
15579
+ // get dimensions of Vimeo video, return array with width & height
15393
15580
  // Sources, in order of priority:
15394
15581
  // 1. The width and height attributes on <video>
15395
15582
  // 2. YouTube (not yet supported; can't seem to get this data via YouTube Data API without OAuth!)
@@ -15423,6 +15610,9 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15423
15610
 
15424
15611
  AblePlayer.prototype.resizeVimeoPlayer = function(youTubeId, youTubeContainerId) {
15425
15612
 
15613
+ // NOTE: This function is modeled after same function in youtube.js
15614
+ // in case useful for Vimeo, but is not currently used
15615
+
15426
15616
  // called after player is ready, if youTube dimensions were previously unknown
15427
15617
  // Now need to get them from the iframe element that YouTube injected
15428
15618
  // and resize Able Player to match
@@ -15479,6 +15669,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15479
15669
 
15480
15670
  AblePlayer.prototype.setupVimeoCaptions = function () {
15481
15671
 
15672
+ console.log('setupVimeoCaptions');
15482
15673
  // called from setupAltCaptions if player is YouTube and there are no <track> captions
15483
15674
 
15484
15675
  // use YouTube Data API to get caption data from YouTube
@@ -15591,6 +15782,11 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15591
15782
  };
15592
15783
 
15593
15784
  AblePlayer.prototype.initVimeoCaptionModule = function () {
15785
+
15786
+ // NOTE: This function is modeled after same function in youtube.js
15787
+ // in case useful for Vimeo, but is not currently used
15788
+
15789
+
15594
15790
  // This function is called when YouTube onApiChange event fires
15595
15791
  // to indicate that the player has loaded (or unloaded) a module with exposed API methods
15596
15792
  // it isn't fired until the video starts playing
@@ -15658,6 +15854,9 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15658
15854
 
15659
15855
  AblePlayer.prototype.getVimeoPosterUrl = function (youTubeId, width) {
15660
15856
 
15857
+ // NOTE: This function is modeled after same function in youtube.js
15858
+ // in case useful for Vimeo, but is not currently used
15859
+
15661
15860
  // return a URL for retrieving a YouTube poster image
15662
15861
  // supported values of width: 120, 320, 480, 640
15663
15862
 
@@ -15681,4 +15880,4 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15681
15880
  return false;
15682
15881
  };
15683
15882
 
15684
- })(jQuery);
15883
+ })(jQuery);