wai-website-theme 1.2 → 1.3

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.
Files changed (127) hide show
  1. checksums.yaml +4 -4
  2. data/_includes/backtotop.html +1 -1
  3. data/_includes/excol.html +1 -1
  4. data/_includes/feedback-box.html +13 -10
  5. data/_includes/footer.html +20 -6
  6. data/_includes/header.html +73 -35
  7. data/_includes/inpl.html +6 -0
  8. data/_includes/lang.html +7 -0
  9. data/_includes/link.html +92 -0
  10. data/_includes/menuitem.html +5 -3
  11. data/_includes/multilang-list-policy-links.html +1 -1
  12. data/_includes/navlist.html +1 -1
  13. data/_includes/prevnext.html +5 -4
  14. data/_includes/secondarynav.html +34 -6
  15. data/_includes/t.html +33 -0
  16. data/_includes/translation-note-msg.html +45 -0
  17. data/_includes/video-player.html +50 -9
  18. data/_layouts/default.html +32 -9
  19. data/_layouts/home.html +29 -5
  20. data/_layouts/news.html +27 -6
  21. data/_layouts/policy.html +27 -6
  22. data/_layouts/sidenav.html +27 -6
  23. data/_layouts/sidenavsidebar.html +27 -6
  24. data/assets/ableplayer/.gitattributes +0 -0
  25. data/assets/ableplayer/.gitignore +3 -1
  26. data/assets/ableplayer/Gruntfile.js +3 -1
  27. data/assets/ableplayer/LICENSE +0 -0
  28. data/assets/ableplayer/README.md +214 -170
  29. data/assets/ableplayer/_config.yml +1 -0
  30. data/assets/ableplayer/build/ableplayer.dist.js +2637 -744
  31. data/assets/ableplayer/build/ableplayer.js +2637 -744
  32. data/assets/ableplayer/build/ableplayer.min.css +2 -2
  33. data/assets/ableplayer/build/ableplayer.min.js +9 -7
  34. data/assets/ableplayer/button-icons/able-icons.svg +0 -0
  35. data/assets/ableplayer/button-icons/black/rabbit.png +0 -0
  36. data/assets/ableplayer/button-icons/black/turtle.png +0 -0
  37. data/assets/ableplayer/button-icons/white/rabbit.png +0 -0
  38. data/assets/ableplayer/button-icons/white/turtle.png +0 -0
  39. data/assets/ableplayer/images/wingrip.png +0 -0
  40. data/assets/ableplayer/package-lock.json +705 -0
  41. data/assets/ableplayer/package.json +11 -2
  42. data/assets/ableplayer/scripts/JQuery.doWhen.js +0 -0
  43. data/assets/ableplayer/scripts/ableplayer-base.js +129 -29
  44. data/assets/ableplayer/scripts/browser.js +0 -0
  45. data/assets/ableplayer/scripts/buildplayer.js +342 -262
  46. data/assets/ableplayer/scripts/caption.js +19 -0
  47. data/assets/ableplayer/scripts/chapters.js +21 -0
  48. data/assets/ableplayer/scripts/control.js +139 -56
  49. data/assets/ableplayer/scripts/description.js +0 -0
  50. data/assets/ableplayer/scripts/dialog.js +13 -13
  51. data/assets/ableplayer/scripts/dragdrop.js +102 -109
  52. data/assets/ableplayer/scripts/event.js +186 -83
  53. data/assets/ableplayer/scripts/initialize.js +261 -71
  54. data/assets/ableplayer/scripts/langs.js +4 -0
  55. data/assets/ableplayer/scripts/metadata.js +0 -0
  56. data/assets/ableplayer/scripts/misc.js +76 -7
  57. data/assets/ableplayer/scripts/preference.js +2 -2
  58. data/assets/ableplayer/scripts/search.js +10 -7
  59. data/assets/ableplayer/scripts/sign.js +0 -0
  60. data/assets/ableplayer/scripts/slider.js +35 -34
  61. data/assets/ableplayer/scripts/track.js +38 -22
  62. data/assets/ableplayer/scripts/transcript.js +15 -6
  63. data/assets/ableplayer/scripts/translation.js +29 -20
  64. data/assets/ableplayer/scripts/ttml2webvtt.js +87 -0
  65. data/assets/ableplayer/scripts/volume.js +16 -15
  66. data/assets/ableplayer/scripts/vts.js +1093 -0
  67. data/assets/ableplayer/scripts/webvtt.js +0 -0
  68. data/assets/ableplayer/scripts/youtube.js +16 -5
  69. data/assets/ableplayer/styles/ableplayer.css +125 -22
  70. data/assets/ableplayer/thirdparty/js.cookie.js +0 -0
  71. data/assets/ableplayer/thirdparty/modernizr.custom.js +0 -0
  72. data/assets/ableplayer/translations/ca.js +311 -1
  73. data/assets/ableplayer/translations/de.js +1 -1
  74. data/assets/ableplayer/translations/en.js +6 -0
  75. data/assets/ableplayer/translations/es.js +6 -0
  76. data/assets/ableplayer/translations/fr.js +6 -0
  77. data/assets/ableplayer/translations/he.js +311 -0
  78. data/assets/ableplayer/translations/it.js +7 -1
  79. data/assets/ableplayer/translations/ja.js +6 -0
  80. data/assets/ableplayer/translations/nb.js +311 -0
  81. data/assets/ableplayer/translations/nl.js +6 -0
  82. data/assets/ableplayer/translations/zh-tw.js +311 -0
  83. data/assets/css/style.css +1 -1
  84. data/assets/css/style.css.map +1 -1
  85. data/assets/fonts/{anonymouspro-bold.woff → anonymouspro/anonymouspro-bold.woff} +0 -0
  86. data/assets/fonts/{anonymouspro-bold.woff2 → anonymouspro/anonymouspro-bold.woff2} +0 -0
  87. data/assets/fonts/{anonymouspro-bolditalic.woff → anonymouspro/anonymouspro-bolditalic.woff} +0 -0
  88. data/assets/fonts/{anonymouspro-bolditalic.woff2 → anonymouspro/anonymouspro-bolditalic.woff2} +0 -0
  89. data/assets/fonts/{anonymouspro-italic.woff → anonymouspro/anonymouspro-italic.woff} +0 -0
  90. data/assets/fonts/{anonymouspro-italic.woff2 → anonymouspro/anonymouspro-italic.woff2} +0 -0
  91. data/assets/fonts/{anonymouspro-regular.woff → anonymouspro/anonymouspro-regular.woff} +0 -0
  92. data/assets/fonts/{anonymouspro-regular.woff2 → anonymouspro/anonymouspro-regular.woff2} +0 -0
  93. data/assets/fonts/notonaskh/bold-minimal.woff +0 -0
  94. data/assets/fonts/notonaskh/bold-minimal.woff2 +0 -0
  95. data/assets/fonts/notonaskh/bold.woff +0 -0
  96. data/assets/fonts/notonaskh/bold.woff2 +0 -0
  97. data/assets/fonts/notonaskh/regular-minimal.woff +0 -0
  98. data/assets/fonts/notonaskh/regular-minimal.woff2 +0 -0
  99. data/assets/fonts/notonaskh/regular.woff +0 -0
  100. data/assets/fonts/notonaskh/regular.woff2 +0 -0
  101. data/assets/fonts/{notosans-bold-subset.woff → notosans/notosans-bold-subset.woff} +0 -0
  102. data/assets/fonts/{notosans-bold-subset.woff2 → notosans/notosans-bold-subset.woff2} +0 -0
  103. data/assets/fonts/{notosans-bold.woff → notosans/notosans-bold.woff} +0 -0
  104. data/assets/fonts/{notosans-bold.woff2 → notosans/notosans-bold.woff2} +0 -0
  105. data/assets/fonts/{notosans-bolditalic-subset.woff → notosans/notosans-bolditalic-subset.woff} +0 -0
  106. data/assets/fonts/{notosans-bolditalic-subset.woff2 → notosans/notosans-bolditalic-subset.woff2} +0 -0
  107. data/assets/fonts/{notosans-bolditalic.woff → notosans/notosans-bolditalic.woff} +0 -0
  108. data/assets/fonts/{notosans-bolditalic.woff2 → notosans/notosans-bolditalic.woff2} +0 -0
  109. data/assets/fonts/{notosans-italic-subset.woff → notosans/notosans-italic-subset.woff} +0 -0
  110. data/assets/fonts/{notosans-italic-subset.woff2 → notosans/notosans-italic-subset.woff2} +0 -0
  111. data/assets/fonts/{notosans-italic.woff → notosans/notosans-italic.woff} +0 -0
  112. data/assets/fonts/{notosans-italic.woff2 → notosans/notosans-italic.woff2} +0 -0
  113. data/assets/fonts/{notosans-regular-subset.woff → notosans/notosans-regular-subset.woff} +0 -0
  114. data/assets/fonts/{notosans-regular-subset.woff2 → notosans/notosans-regular-subset.woff2} +0 -0
  115. data/assets/fonts/{notosans-regular.woff → notosans/notosans-regular.woff} +0 -0
  116. data/assets/fonts/{notosans-regular.woff2 → notosans/notosans-regular.woff2} +0 -0
  117. data/assets/fonts/notosansmono/notosansmono-semicondensed.woff +0 -0
  118. data/assets/fonts/notosansmono/notosansmono-semicondensed.woff2 +0 -0
  119. data/assets/fonts/notosansmono/notosansmono-semicondensedbold.woff +0 -0
  120. data/assets/fonts/notosansmono/notosansmono-semicondensedbold.woff2 +0 -0
  121. data/assets/images/icons.svg +24 -0
  122. data/assets/scripts/main.js +10 -3
  123. metadata +66 -33
  124. data/_data/lang.json +0 -730
  125. data/_data/techniques.yml +0 -180
  126. data/_data/wcag.yml +0 -125
  127. data/_includes/.DS_Store +0 -0
File without changes
@@ -24,9 +24,9 @@
24
24
  'title': thisObj.closeButtonLabel,
25
25
  'aria-label': thisObj.closeButtonLabel
26
26
  }).text('X');
27
- closeButton.keydown(function (event) {
27
+ closeButton.keydown(function (e) {
28
28
  // Space key down
29
- if (event.which === 32) {
29
+ if (e.which === 32) {
30
30
  thisObj.hide();
31
31
  }
32
32
  }).click(function () {
@@ -53,19 +53,19 @@
53
53
  'role': dialogRole
54
54
  });
55
55
 
56
- modal.keydown(function (event) {
56
+ modal.keydown(function (e) {
57
57
  // Escape
58
- if (event.which === 27) {
58
+ if (e.which === 27) {
59
59
  if (thisObj.escapeHook) {
60
- thisObj.escapeHook(event, this);
60
+ thisObj.escapeHook(e, this);
61
61
  }
62
62
  else {
63
63
  thisObj.hide();
64
- event.preventDefault();
64
+ e.preventDefault();
65
65
  }
66
66
  }
67
67
  // Tab
68
- else if (event.which === 9) {
68
+ else if (e.which === 9) {
69
69
  // Manually loop tab navigation inside the modal.
70
70
  var parts = modal.find('*');
71
71
  var focusable = parts.filter(focusableElementsSelector).filter(':visible');
@@ -76,21 +76,21 @@
76
76
 
77
77
  var focused = $(':focus');
78
78
  var currentIndex = focusable.index(focused);
79
- if (event.shiftKey) {
79
+ if (e.shiftKey) {
80
80
  // If backwards from first element, go to last.
81
81
  if (currentIndex === 0) {
82
82
  focusable.get(focusable.length - 1).focus();
83
- event.preventDefault();
83
+ e.preventDefault();
84
84
  }
85
85
  }
86
86
  else {
87
87
  if (currentIndex === focusable.length - 1) {
88
88
  focusable.get(0).focus();
89
- event.preventDefault();
89
+ e.preventDefault();
90
90
  }
91
91
  }
92
92
  }
93
- event.stopPropagation();
93
+ e.stopPropagation();
94
94
  });
95
95
 
96
96
  $('body > *').not('.able-modal-overlay').not('.able-modal-dialog').attr('aria-hidden', 'false');
@@ -107,8 +107,8 @@
107
107
  $('body').append(overlay);
108
108
 
109
109
  // Keep from moving focus out of dialog when clicking outside of it.
110
- overlay.on('mousedown.accessibleModal', function (event) {
111
- event.preventDefault();
110
+ overlay.on('mousedown.accessibleModal', function (e) {
111
+ e.preventDefault();
112
112
  });
113
113
  }
114
114
 
@@ -42,19 +42,19 @@
42
42
 
43
43
  // add event listener to toolbar to start and end drag
44
44
  // other event listeners will be added when drag starts
45
- $toolbar.on('mousedown', function(event) {
46
- event.stopPropagation();
45
+ $toolbar.on('mousedown', function(e) {
46
+ e.stopPropagation();
47
47
  if (!thisObj.windowMenuClickRegistered) {
48
48
  thisObj.windowMenuClickRegistered = true;
49
- thisObj.startMouseX = event.pageX;
50
- thisObj.startMouseY = event.pageY;
49
+ thisObj.startMouseX = e.pageX;
50
+ thisObj.startMouseY = e.pageY;
51
51
  thisObj.dragDevice = 'mouse';
52
52
  thisObj.startDrag(which, $window);
53
53
  }
54
54
  return false;
55
55
  });
56
- $toolbar.on('mouseup', function(event) {
57
- event.stopPropagation();
56
+ $toolbar.on('mouseup', function(e) {
57
+ e.stopPropagation();
58
58
  if (thisObj.dragging && thisObj.dragDevice === 'mouse') {
59
59
  thisObj.endDrag(which);
60
60
  }
@@ -62,18 +62,20 @@
62
62
  });
63
63
 
64
64
  // add event listeners for resizing
65
- $resizeHandle.on('mousedown', function(event) {
66
- event.stopPropagation();
65
+ $resizeHandle.on('mousedown', function(e) {
66
+
67
+ e.stopPropagation();
67
68
  if (!thisObj.windowMenuClickRegistered) {
68
69
  thisObj.windowMenuClickRegistered = true;
69
- thisObj.startMouseX = event.pageX;
70
- thisObj.startMouseY = event.pageY;
70
+ thisObj.startMouseX = e.pageX;
71
+ thisObj.startMouseY = e.pageY;
71
72
  thisObj.startResize(which, $window);
72
73
  return false;
73
74
  }
74
75
  });
75
- $resizeHandle.on('mouseup', function(event) {
76
- event.stopPropagation();
76
+
77
+ $resizeHandle.on('mouseup', function(e) {
78
+ e.stopPropagation();
77
79
  if (thisObj.resizing) {
78
80
  thisObj.endResize(which);
79
81
  }
@@ -82,6 +84,7 @@
82
84
 
83
85
  // whenever a window is clicked, bring it to the foreground
84
86
  $window.on('click', function() {
87
+
85
88
  if (!thisObj.windowMenuClickRegistered && !thisObj.finishingDrag) {
86
89
  thisObj.updateZIndex(which);
87
90
  }
@@ -94,11 +97,10 @@
94
97
  AblePlayer.prototype.addWindowMenu = function(which, $window, windowName) {
95
98
 
96
99
 
97
- var thisObj, $windowAlert, $newButton, $buttonIcon, buttonImgSrc, $buttonImg,
100
+ var thisObj, $windowAlert, menuId, $newButton, $buttonIcon, buttonImgSrc, $buttonImg,
98
101
  $buttonLabel, tooltipId, $tooltip, $popup,
99
102
  label, position, buttonHeight, buttonWidth, tooltipY, tooltipX, tooltipStyle, tooltip,
100
- $optionList, radioName, options, i, $optionItem, option,
101
- radioId, $radioButton, $radioLabel;
103
+ $optionList, menuBaseId, options, i, $optionItem, option, menuId;
102
104
 
103
105
  thisObj = this;
104
106
 
@@ -114,6 +116,7 @@
114
116
  // create an alert div and add it to window
115
117
  $windowAlert = $('<div role="alert"></div>');
116
118
  $windowAlert.addClass('able-alert');
119
+ $windowAlert.hide();
117
120
  $windowAlert.appendTo(this.$activeWindow);
118
121
  $windowAlert.css({
119
122
  top: $window.offset().top
@@ -121,10 +124,13 @@
121
124
 
122
125
  // add button to draggable window which triggers a popup menu
123
126
  // for now, re-use preferences icon for this purpose
127
+ menuId = this.mediaId + '-' + windowName + '-menu';
124
128
  $newButton = $('<button>',{
125
129
  'type': 'button',
126
130
  'tabindex': '0',
127
131
  'aria-label': this.tt.windowButtonLabel,
132
+ 'aria-haspopup': 'true',
133
+ 'aria-controls': menuId,
128
134
  'class': 'able-button-handler-preferences'
129
135
  });
130
136
  if (this.iconType === 'font') {
@@ -156,8 +162,8 @@
156
162
  $tooltip = $('<div>',{
157
163
  'class' : 'able-tooltip',
158
164
  'id' : tooltipId
159
- });
160
- $newButton.on('mouseenter focus',function(event) {
165
+ }).hide();
166
+ $newButton.on('mouseenter focus',function(e) {
161
167
  var label = $(this).attr('aria-label');
162
168
  // get position of this button
163
169
  var position = $(this).position();
@@ -170,83 +176,15 @@
170
176
  right: tooltipX + 'px',
171
177
  top: tooltipY + 'px'
172
178
  };
173
- var tooltip = $('#' + tooltipId).text(label).css(tooltipStyle);
179
+ var tooltip = AblePlayer.localGetElementById($newButton[0], tooltipId).text(label).css(tooltipStyle);
174
180
  thisObj.showTooltip(tooltip);
175
181
  $(this).on('mouseleave blur',function() {
176
- $('#' + tooltipId).text('').hide();
177
- });
178
- });
179
-
180
- // add a popup menu
181
- $popup = this.createPopup(windowName);
182
- $optionList = $('<ul></ul>');
183
- radioName = this.mediaId + '-' + windowName + '-choice';
184
-
185
- options = [];
186
- options.push({
187
- 'name': 'move',
188
- 'label': this.tt.windowMove
189
- });
190
- options.push({
191
- 'name': 'resize',
192
- 'label': this.tt.windowResize
193
- });
194
- for (i = 0; i < options.length; i++) {
195
- $optionItem = $('<li></li>');
196
- option = options[i];
197
- radioId = radioName + '-' + i;
198
- $radioButton = $('<input>',{
199
- 'type': 'radio',
200
- 'val': option.name,
201
- 'name': radioName,
202
- 'id': radioId
203
- });
204
- $radioLabel = $('<label>',{
205
- 'for': radioId
182
+ AblePlayer.localGetElementById($newButton[0], tooltipId).text('').hide();
206
183
  });
207
- $radioLabel.text(option.label);
208
- $radioButton.on('focus',function(e) {
209
- $(this).parents('ul').children('li').removeClass('able-focus');
210
- $(this).parent('li').addClass('able-focus');
211
- });
212
- $radioButton.on('click',function(e) {
213
- e.stopPropagation();
214
- if (!thisObj.windowMenuClickRegistered && !thisObj.finishingDrag) {
215
- thisObj.windowMenuClickRegistered = true;
216
- thisObj.handleMenuChoice( which, $(this).val(), e.type);
217
- }
218
- });
219
- // due to an apparent bug (in jquery?) clicking the label
220
- // does not result in a click event on the associated radio button
221
- // Observed this in Firefox 45.0.2 and Chrome 50
222
- // It works fine on a simple test page so this could be an Able Player bug
223
- // Added the following as a workaround rather than mess with isolating the bug
224
- $radioLabel.on('click mousedown', function() {
225
- var clickedId = $(this).attr('for');
226
- $('#' + clickedId).click();
227
- })
228
- $optionItem.append($radioButton,$radioLabel);
229
- $optionList.append($optionItem);
230
- }
231
- $popup.append($optionList);
232
- $newButton.on('click mousedown keydown',function(e) {
233
- e.stopPropagation();
234
- if (!thisObj.windowMenuClickRegistered && !thisObj.finishingDrag) {
235
- // don't set windowMenuClickRegistered yet; that happens in handler function
236
- thisObj.handleWindowButtonClick(which, e);
237
- }
238
- thisObj.finishingDrag = false;
239
- });
240
-
241
- $popup.on('keydown', function(event) {
242
- // Escape key
243
- if (event.which === 27) {
244
- // Close Window Options Menu
245
- $newButton.focus();
246
- $popup.hide();
247
- }
248
184
  });
249
185
 
186
+ // setup popup menu
187
+ $popup = this.setupPopups(windowName); // 'transcript-window' or 'sign-window'
250
188
  // define vars and assemble all the parts
251
189
  if (which === 'transcript') {
252
190
  this.$transcriptAlert = $windowAlert;
@@ -261,6 +199,16 @@
261
199
  this.$signToolbar.append($windowAlert,$newButton,$tooltip,$popup);
262
200
  }
263
201
 
202
+ // handle button click
203
+ $newButton.on('click mousedown keydown',function(e) {
204
+ e.stopPropagation();
205
+ if (!thisObj.windowMenuClickRegistered && !thisObj.finishingDrag) {
206
+ // don't set windowMenuClickRegistered yet; that happens in handler function
207
+ thisObj.handleWindowButtonClick(which, e);
208
+ }
209
+ thisObj.finishingDrag = false;
210
+ });
211
+
264
212
  this.addResizeDialog(which, $window);
265
213
  };
266
214
 
@@ -380,12 +328,34 @@
380
328
 
381
329
  thisObj = this;
382
330
 
331
+ if (which === 'transcript') {
332
+ $windowPopup = this.$transcriptPopup;
333
+ $windowButton = this.$transcriptPopupButton;
334
+ $toolbar = this.$transcriptToolbar;
335
+ }
336
+ else if (which === 'sign') {
337
+ $windowPopup = this.$signPopup;
338
+ $windowButton = this.$signPopupButton;
339
+ $toolbar = this.$signToolbar;
340
+ }
341
+
383
342
  if (e.type === 'keydown') {
384
343
  // user pressed a key
385
- if (e.which === 32 || e.which === 13 || e.which === 27) {
386
- // this was Enter, space, or escape
344
+ if (e.which === 32 || e.which === 13) {
345
+ // this was Enter or space
387
346
  this.windowMenuClickRegistered = true;
388
347
  }
348
+ else if (e.which === 27) { // escape
349
+ // hide the popup menu
350
+ $windowPopup.hide('fast', function() {
351
+ // also reset the Boolean
352
+ thisObj.windowMenuClickRegistered = false;
353
+ // also restore menu items to their original state
354
+ $windowPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
355
+ // also return focus to window options button
356
+ $windowButton.focus();
357
+ });
358
+ }
389
359
  else {
390
360
  return false;
391
361
  }
@@ -394,23 +364,13 @@
394
364
  // this was a mouse event
395
365
  this.windowMenuClickRegistered = true;
396
366
  }
397
- if (which === 'transcript') {
398
- $windowPopup = this.$transcriptPopup;
399
- $windowButton = this.$transcriptPopupButton;
400
- $toolbar = this.$transcriptToolbar;
401
- }
402
- else if (which === 'sign') {
403
- $windowPopup = this.$signPopup;
404
- $windowButton = this.$signPopupButton;
405
- $toolbar = this.$signToolbar;
406
- }
407
367
 
408
368
  if ($windowPopup.is(':visible')) {
409
369
  $windowPopup.hide(200,'',function() {
410
370
  thisObj.windowMenuClickRegistered = false; // reset
411
371
  });
412
372
  $windowPopup.find('li').removeClass('able-focus');
413
- $windowButton.focus();
373
+ $windowButton.attr('aria-expanded','false').focus();
414
374
  }
415
375
  else {
416
376
  // first, be sure window is on top
@@ -418,13 +378,14 @@
418
378
  popupTop = $windowButton.position().top + $windowButton.outerHeight();
419
379
  $windowPopup.css('top', popupTop);
420
380
  $windowPopup.show(200,'',function() {
421
- $(this).find('input').first().focus().parent().addClass('able-focus');
381
+ $windowButton.attr('aria-expanded','true');
382
+ $(this).find('li').first().focus().addClass('able-focus');
422
383
  thisObj.windowMenuClickRegistered = false; // reset
423
384
  });
424
385
  }
425
386
  };
426
387
 
427
- AblePlayer.prototype.handleMenuChoice = function (which, choice, eventType) {
388
+ AblePlayer.prototype.handleMenuChoice = function (which, choice, e) {
428
389
 
429
390
  var thisObj, $window, $windowPopup, $windowButton, resizeDialog, $thisRadio;
430
391
 
@@ -443,11 +404,35 @@
443
404
  resizeDialog = this.signResizeDialog;
444
405
  }
445
406
 
446
- // hide the popup menu, and reset the Boolean
407
+ if (e.type === 'keydown') {
408
+ if (e.which === 27) { // escape
409
+ // hide the popup menu
410
+ $windowPopup.hide('fast', function() {
411
+ // also reset the Boolean
412
+ thisObj.windowMenuClickRegistered = false;
413
+ // also restore menu items to their original state
414
+ $windowPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
415
+ // also return focus to window options button
416
+ $windowButton.focus();
417
+ });
418
+ return false;
419
+ }
420
+ else {
421
+ // all other keys will be handled by upstream functions
422
+ return false;
423
+ }
424
+ }
425
+
426
+ // hide the popup menu
447
427
  $windowPopup.hide('fast', function() {
448
- thisObj.windowMenuClickRegistered = false; // reset
428
+ // also reset the boolean
429
+ thisObj.windowMenuClickRegistered = false;
430
+ // also restore menu items to their original state
431
+ $windowPopup.find('li').removeClass('able-focus').attr('tabindex','-1');
449
432
  });
450
- $windowButton.focus();
433
+ if (choice !== 'close') {
434
+ $windowButton.focus();
435
+ }
451
436
 
452
437
  if (choice === 'move') {
453
438
  if (!this.showedAlert(which)) {
@@ -459,7 +444,7 @@
459
444
  this.showedSignAlert = true;
460
445
  }
461
446
  }
462
- if (eventType === 'keydown') {
447
+ if (e.type === 'keydown') {
463
448
  this.dragDevice = 'keyboard';
464
449
  }
465
450
  else {
@@ -472,10 +457,18 @@
472
457
  // resize through the menu uses a form, not drag
473
458
  resizeDialog.show();
474
459
  }
460
+ else if (choice == 'close') {
461
+ // close window, place focus on corresponding button on controller bar
462
+ if (which === 'transcript') {
463
+ this.handleTranscriptToggle();
464
+ }
465
+ else if (which === 'sign') {
466
+ this.handleSignToggle();
467
+ }
468
+ }
475
469
  };
476
470
 
477
471
  AblePlayer.prototype.startDrag = function(which, $element) {
478
-
479
472
  var thisObj, $windowPopup, zIndex, startPos, newX, newY;
480
473
  thisObj = this;
481
474
 
@@ -29,7 +29,14 @@
29
29
  };
30
30
 
31
31
  AblePlayer.prototype.onMediaPause = function () {
32
- // do something
32
+ if (this.controlsHidden) {
33
+ this.fadeControls('in');
34
+ this.controlsHidden = false;
35
+ }
36
+ if (this.hidingControls) { // a timeout is actively counting
37
+ window.clearTimeout(this.hideControlsTimeout);
38
+ this.hidingControls = false;
39
+ }
33
40
  };
34
41
 
35
42
  AblePlayer.prototype.onMediaComplete = function () {
@@ -138,10 +145,10 @@
138
145
  var thisObj = this;
139
146
 
140
147
  // Handle seek bar events.
141
- this.seekBar.bodyDiv.on('startTracking', function (event) {
148
+ this.seekBar.bodyDiv.on('startTracking', function (e) {
142
149
  thisObj.pausedBeforeTracking = thisObj.isPaused();
143
150
  thisObj.pauseMedia();
144
- }).on('tracking', function (event, position) {
151
+ }).on('tracking', function (e, position) {
145
152
  // Scrub transcript, captions, and metadata.
146
153
  thisObj.highlightTranscript(position);
147
154
  thisObj.updateCaption(position);
@@ -149,7 +156,7 @@
149
156
  thisObj.updateChapter(thisObj.convertChapterTimeToVideoTime(position));
150
157
  thisObj.updateMeta(position);
151
158
  thisObj.refreshControls();
152
- }).on('stopTracking', function (event, position) {
159
+ }).on('stopTracking', function (e, position) {
153
160
  if (thisObj.useChapterTimes) {
154
161
  thisObj.seekTo(thisObj.convertChapterTimeToVideoTime(position));
155
162
  }
@@ -171,12 +178,15 @@
171
178
  this.handlePlay();
172
179
  }
173
180
  else if (whichButton === 'restart') {
181
+ this.seekTrigger = 'restart';
174
182
  this.handleRestart();
175
183
  }
176
184
  else if (whichButton === 'rewind') {
185
+ this.seekTrigger = 'rewind';
177
186
  this.handleRewind();
178
187
  }
179
188
  else if (whichButton === 'forward') {
189
+ this.seekTrigger = 'forward';
180
190
  this.handleFastForward();
181
191
  }
182
192
  else if (whichButton === 'mute') {
@@ -221,23 +231,30 @@
221
231
 
222
232
  // returns true unless user's focus is on a UI element
223
233
  // that is likely to need supported keystrokes, including space
224
- var activeElement = $(document.activeElement).prop('tagName');
225
- if (activeElement === 'INPUT') {
234
+
235
+ var activeElement = AblePlayer.getActiveDOMElement();
236
+
237
+ if ($(activeElement).prop('tagName') === 'INPUT') {
226
238
  return false;
227
239
  }
228
240
  else {
229
241
  return true;
230
242
  }
231
- }
243
+ };
232
244
 
233
245
  AblePlayer.prototype.onPlayerKeyPress = function (e) {
246
+
234
247
  // handle keystrokes (using DHTML Style Guide recommended key combinations)
235
- // http://dev.aol.com/dhtml_style_guide/#mediaplayer
248
+ // https://web.archive.org/web/20130127004544/http://dev.aol.com/dhtml_style_guide/#mediaplayer
236
249
  // Modifier keys Alt + Ctrl are on by default, but can be changed within Preferences
237
250
  // NOTE #1: Style guide only supports Play/Pause, Stop, Mute, Captions, & Volume Up & Down
238
251
  // The rest are reasonable best choices
239
252
  // NOTE #2: If there are multiple players on a single page, keystroke handlers
240
253
  // are only bound to the FIRST player
254
+ // NOTE #3: The DHTML Style Guide is now the W3C WAI-ARIA Authoring Guide and has undergone many revisions
255
+ // including removal of the "media player" design pattern. There's an issue about that:
256
+ // https://github.com/w3c/aria-practices/issues/27
257
+
241
258
  if (!this.okToHandleKeyPress()) {
242
259
  return false;
243
260
  }
@@ -247,75 +264,88 @@
247
264
  if (which >= 65 && which <= 90) {
248
265
  which += 32;
249
266
  }
250
- if (which === 27) {
251
- this.closePopups();
252
- }
253
- else if (which === 32) { // spacebar = play/pause
254
- if (!($('.able-controller button').is(':focus'))) {
255
- // only toggle play if a button does not have focus
256
- // if a button has focus, space should activate that button
257
- this.handlePlay();
267
+
268
+ // Only use keypress to control player if focus is NOT on a form field or contenteditable element
269
+ if (!(
270
+ $(':focus').is('[contenteditable]') ||
271
+ $(':focus').is('input') ||
272
+ $(':focus').is('textarea') ||
273
+ $(':focus').is('select') ||
274
+ e.target.hasAttribute('contenteditable') ||
275
+ e.target.tagName === 'INPUT' ||
276
+ e.target.tagName === 'TEXTAREA' ||
277
+ e.target.tagName === 'SELECT'
278
+ )){
279
+ if (which === 27) { // escape
280
+ this.closePopups();
258
281
  }
259
- }
260
- else if (which === 112) { // p = play/pause
261
- if (this.usingModifierKeys(e)) {
262
- this.handlePlay();
282
+ else if (which === 32) { // spacebar = play/pause
283
+ if (this.$ableWrapper.find('.able-controller button:focus').length === 0) {
284
+ // only toggle play if a button does not have focus
285
+ // if a button has focus, space should activate that button
286
+ this.handlePlay();
287
+ }
263
288
  }
264
- }
265
- else if (which === 115) { // s = stop (now restart)
266
- if (this.usingModifierKeys(e)) {
267
- this.handleRestart();
289
+ else if (which === 112) { // p = play/pause
290
+ if (this.usingModifierKeys(e)) {
291
+ this.handlePlay();
292
+ }
268
293
  }
269
- }
270
- else if (which === 109) { // m = mute
271
- if (this.usingModifierKeys(e)) {
272
- this.handleMute();
294
+ else if (which === 115) { // s = stop (now restart)
295
+ if (this.usingModifierKeys(e)) {
296
+ this.handleRestart();
297
+ }
273
298
  }
274
- }
275
- else if (which === 118) { // v = volume
276
- if (this.usingModifierKeys(e)) {
277
- this.handleVolume();
299
+ else if (which === 109) { // m = mute
300
+ if (this.usingModifierKeys(e)) {
301
+ this.handleMute();
302
+ }
278
303
  }
279
- }
280
- else if (which >= 49 && which <= 57) { // set volume 1-9
281
- if (this.usingModifierKeys(e)) {
282
- this.handleVolume(which);
304
+ else if (which === 118) { // v = volume
305
+ if (this.usingModifierKeys(e)) {
306
+ this.handleVolume();
307
+ }
283
308
  }
284
- }
285
- else if (which === 99) { // c = caption toggle
286
- if (this.usingModifierKeys(e)) {
287
- this.handleCaptionToggle();
309
+ else if (which >= 49 && which <= 57) { // set volume 1-9
310
+ if (this.usingModifierKeys(e)) {
311
+ this.handleVolume(which);
312
+ }
288
313
  }
289
- }
290
- else if (which === 100) { // d = description
291
- if (this.usingModifierKeys(e)) {
292
- this.handleDescriptionToggle();
314
+ else if (which === 99) { // c = caption toggle
315
+ if (this.usingModifierKeys(e)) {
316
+ this.handleCaptionToggle();
317
+ }
293
318
  }
294
- }
295
- else if (which === 102) { // f = forward
296
- if (this.usingModifierKeys(e)) {
297
- this.handleFastForward();
319
+ else if (which === 100) { // d = description
320
+ if (this.usingModifierKeys(e)) {
321
+ this.handleDescriptionToggle();
322
+ }
298
323
  }
299
- }
300
- else if (which === 114) { // r = rewind
301
- if (this.usingModifierKeys(e)) {
302
- this.handleRewind();
324
+ else if (which === 102) { // f = forward
325
+ if (this.usingModifierKeys(e)) {
326
+ this.handleFastForward();
327
+ }
303
328
  }
304
- }
305
- else if (which === 101) { // e = preferences
306
- if (this.usingModifierKeys(e)) {
307
- this.handlePrefsClick();
329
+ else if (which === 114) { // r = rewind
330
+ if (this.usingModifierKeys(e)) {
331
+ this.handleRewind();
332
+ }
308
333
  }
309
- }
310
- else if (which === 13) { // Enter
311
- var thisElement = $(document.activeElement);
312
- if (thisElement.prop('tagName') === 'SPAN') {
313
- // register a click on this SPAN
314
- // if it's a transcript span the transcript span click handler will take over
315
- thisElement.click();
334
+ else if (which === 101) { // e = preferences
335
+ if (this.usingModifierKeys(e)) {
336
+ this.handlePrefsClick();
337
+ }
316
338
  }
317
- else if (thisElement.prop('tagName') === 'LI') {
318
- thisElement.click();
339
+ else if (which === 13) { // Enter
340
+ var thisElement = $(document.activeElement);
341
+ if (thisElement.prop('tagName') === 'SPAN') {
342
+ // register a click on this SPAN
343
+ // if it's a transcript span the transcript span click handler will take over
344
+ thisElement.click();
345
+ }
346
+ else if (thisElement.prop('tagName') === 'LI') {
347
+ thisElement.click();
348
+ }
319
349
  }
320
350
  }
321
351
  };
@@ -324,8 +354,10 @@
324
354
 
325
355
  var thisObj = this;
326
356
 
327
- // NOTE: iOS does not support autoplay,
357
+ // NOTE: iOS and some browsers do not support autoplay
328
358
  // and no events are triggered until media begins to play
359
+ // Able Player gets around this by automatically loading media in some circumstances
360
+ // (see initialize.js > initPlayer() for details)
329
361
  this.$media
330
362
  .on('emptied',function() {
331
363
  // do something
@@ -339,7 +371,13 @@
339
371
  // so we know player can seek ahead to anything
340
372
  })
341
373
  .on('canplaythrough',function() {
342
- if (!thisObj.startedPlaying) {
374
+ if (thisObj.seekTrigger == 'restart' || thisObj.seekTrigger == 'chapter' || thisObj.seekTrigger == 'transcript') {
375
+ // by clicking on any of these elements, user is likely intending to play
376
+ // Not included: elements where user might click multiple times in succession
377
+ // (i.e., 'rewind', 'forward', or seekbar); for these, video remains paused until user initiates play
378
+ thisObj.playMedia();
379
+ }
380
+ else if (!thisObj.startedPlaying) {
343
381
  if (thisObj.startTime) {
344
382
  if (thisObj.seeking) {
345
383
  // a seek has already been initiated
@@ -364,6 +402,12 @@
364
402
  }
365
403
  }
366
404
  }
405
+ else if (thisObj.hasPlaylist) {
406
+ if ((thisObj.playlistIndex !== (thisObj.$playlist.length - 1)) || thisObj.loop) {
407
+ // this is not the last track in the playlist (OR playlist is looping so it doesn't matter)
408
+ thisObj.playMedia();
409
+ }
410
+ }
367
411
  else {
368
412
  // already started playing
369
413
  }
@@ -373,6 +417,7 @@
373
417
  thisObj.refreshControls();
374
418
  })
375
419
  .on('ended',function() {
420
+ thisObj.playing = false;
376
421
  thisObj.onMediaComplete();
377
422
  })
378
423
  .on('progress', function() {
@@ -394,6 +439,21 @@
394
439
  }
395
440
  })
396
441
  .on('pause',function() {
442
+ if (!thisObj.clickedPlay) {
443
+ // 'pause' was triggered automatically, not initiated by user
444
+ // this happens between tracks in a playlist
445
+ if (thisObj.hasPlaylist) {
446
+ // do NOT set playing to false.
447
+ // doing so prevents continual playback after new track is loaded
448
+ }
449
+ else {
450
+ thisObj.playing = false;
451
+ }
452
+ }
453
+ else {
454
+ thisObj.playing = false;
455
+ }
456
+ thisObj.clickedPlay = false; // done with this variable
397
457
  thisObj.onMediaPause();
398
458
  })
399
459
  .on('ratechange',function() {
@@ -455,11 +515,11 @@
455
515
  }
456
516
  thisObj.refreshControls();
457
517
  })
458
- .onSeek(function(event) {
518
+ .onSeek(function(e) {
459
519
  // this is called when user scrubs ahead or back,
460
520
  // after the target offset is reached
461
521
  if (thisObj.debug) {
462
- console.log('Seeking to ' + event.position + '; target: ' + event.offset);
522
+ console.log('Seeking to ' + e.position + '; target: ' + e.offset);
463
523
  }
464
524
 
465
525
  if (thisObj.jwSeekPause) {
@@ -524,29 +584,72 @@
524
584
 
525
585
  // Refresh player if it changes from hidden to visible
526
586
  // There is no event triggered by a change in visibility
527
- // but MutationObserver works in most browsers:
587
+ // but MutationObserver works in most browsers (but NOT in IE 10 or earlier)
528
588
  // http://caniuse.com/#feat=mutationobserver
529
- var target = this.$ableDiv[0];
530
- var observer = new MutationObserver(function(mutations) {
531
- mutations.forEach(function(mutation) {
532
- if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
533
- // the player's style attribute has changed. Check to see if it's visible
534
- if (thisObj.$ableDiv.is(':visible')) {
535
- thisObj.refreshControls();
589
+ if (window.MutationObserver) {
590
+ var target = this.$ableDiv[0];
591
+ var observer = new MutationObserver(function(mutations) {
592
+ mutations.forEach(function(mutation) {
593
+ if (mutation.type === 'attributes' && mutation.attributeName === 'style') {
594
+ // the player's style attribute has changed. Check to see if it's visible
595
+ if (thisObj.$ableDiv.is(':visible')) {
596
+ thisObj.refreshControls();
597
+ }
536
598
  }
537
- }
599
+ });
538
600
  });
539
- });
540
- var config = { attributes: true, childList: true, characterData: true };
541
- observer.observe(target, config);
601
+ var config = { attributes: true, childList: true, characterData: true };
602
+ observer.observe(target, config);
603
+ }
604
+ else {
605
+ // browser doesn't support MutationObserver
606
+ // TODO: Figure out an alternative solution for this rare use case in older browsers
607
+ // See example in buildplayer.js > useSvg()
608
+ }
542
609
 
543
610
  this.addSeekbarListeners();
544
-
545
611
  // handle clicks on player buttons
546
- this.$controllerDiv.find('button').on('click',function(){
612
+ this.$controllerDiv.find('button').on('click',function(e){
613
+ e.stopPropagation();
547
614
  thisObj.onClickPlayerButton(this);
548
615
  });
549
616
 
617
+ // handle clicks (left only) anywhere on the page. If any popups are open, close them.
618
+ $(document).on('click',function(e) {
619
+ if (e.button !== 0) { // not a left click
620
+ return false;
621
+ }
622
+ if ($('.able-popup:visible').length || $('.able-volume-popup:visible')) {
623
+ // at least one popup is visible
624
+ thisObj.closePopups();
625
+ }
626
+ });
627
+
628
+ // handle mouse movement over player; make controls visible again if hidden
629
+ this.$ableDiv.on('mousemove',function() {
630
+ if (thisObj.controlsHidden) {
631
+ thisObj.fadeControls('in');
632
+ thisObj.controlsHidden = false;
633
+ // after showing controls, wait another few seconds, then hide them again if video continues to play
634
+ thisObj.hidingControls = true;
635
+ thisObj.hideControlsTimeout = window.setTimeout(function() {
636
+ if (typeof thisObj.playing !== 'undefined' && thisObj.playing === true) {
637
+ thisObj.fadeControls('out');
638
+ thisObj.controlsHidden = true;
639
+ thisObj.hidingControls = false;
640
+ }
641
+ },3000);
642
+ };
643
+ });
644
+
645
+ // if user presses a key from anywhere on the page, show player controls
646
+ $(document).keydown(function() {
647
+ if (thisObj.controlsHidden) {
648
+ thisObj.fadeControls('in');
649
+ thisObj.controlsHidden = false;
650
+ }
651
+ });
652
+
550
653
  // handle local keydown events if this isn't the only player on the page;
551
654
  // otherwise these are dispatched by global handler (see ableplayer-base,js)
552
655
  this.$ableDiv.keydown(function (e) {