wai-website-theme 1.6 → 1.7

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: d03da6d65cf78f515a401d2d86fda003371911070e5e06508a98ab728370108f
4
- data.tar.gz: d7a901e46b04b3c926df20c3b1f99be5ba4c5c30e082747bfaa027158bc671b2
3
+ metadata.gz: e5ff63f525b7bdbfaa526b5948a80d9d15cc614ef5d7755d86452872de8fba92
4
+ data.tar.gz: b8ab68ccfe3ca812cf2cf3129d59d812911d226a9087e9097985d5891e955d10
5
5
  SHA512:
6
- metadata.gz: de632cd8d264278e97d8422f591336b03db6c5fd90b13a2aee543f16572d31bacde0e9acc382c0f9696adf54ac10ab67f315fa5779e747d06f13d9ddbbddb8bd
7
- data.tar.gz: 824f11ade4ea25ae8ee9f1c62e6e47659d733f76ea6f4e46b10e477760f09f9949dc186d95021ecd81955a599cff60d8f629500122afff96e135c584386a21ee
6
+ metadata.gz: f963eeee23e3c8e16afb9568555345d7c29ce2067c25e1e0c22129d6bb436c05cf831a8fdcf62b1c5d2a24f47d77d9855d0f3c297c308da9b5c4e85757a46c54
7
+ data.tar.gz: a195c9358385c40ce63f4a0292ded5fa527a710f38beceb3ab601004601437dc8b1c82602b273bdbe6954a2a34dc872bb65e528d161263588b7a47afeecb3df9
@@ -0,0 +1 @@
1
+ ../_external/data/lang.json
@@ -0,0 +1,85 @@
1
+ - name:
2
+ en: 'Home'
3
+ pages:
4
+ - name: Overview
5
+ url: "/overview/"
6
+ - name:
7
+ en: 'Writing Documents'
8
+ pages:
9
+ - name: Overview
10
+ url: "/writing/"
11
+ - name: Markdown
12
+ url: "/writing/markdown/"
13
+ - name: Custom Markdown Extensions
14
+ url: "/writing/wai-markdown/"
15
+ - name:
16
+ en: 'Design Components'
17
+ pages:
18
+ - name: Overview
19
+ url: "/components/"
20
+ - name: Colors
21
+ url: "/components/colors/"
22
+ - name: Atoms
23
+ url: "/components/atoms/"
24
+ pages:
25
+ - name: Buttons
26
+ url: "/components/atoms/buttons/"
27
+ - name: Button Group
28
+ url: "/components/atoms/button-group/"
29
+ - name: "Show/Hide Buttons"
30
+ url: "/components/atoms/show-hide-buttons/"
31
+ - name: Input Elements
32
+ url: "/components/atoms/input/"
33
+ - name: "Mark Element"
34
+ url: "/components/atoms/mark/"
35
+ - name: "Paragraphs"
36
+ url: "/components/atoms/p/"
37
+ - name: Block Quotes
38
+ url: "/components/blockquotes/"
39
+ - name: Boxes
40
+ url: "/components/boxes/"
41
+ - name: Decision Tree
42
+ url: "/components/decision-tree/"
43
+ - name: "Document Notes"
44
+ url: "/components/document-notes/archived/"
45
+ pages:
46
+ - name: "Archived"
47
+ url: "/components/document-notes/archived/"
48
+ - name: "Draft"
49
+ url: "/components/document-notes/draft/"
50
+ - name: "Translation"
51
+ url: "/components/document-notes/translation/"
52
+ - name: "Deprecated"
53
+ url: "/components/document-notes/deprecated/"
54
+ - name: "Expand/<wbr>Collapse"
55
+ url: "/components/excol/single/"
56
+ pages:
57
+ - name: "Single"
58
+ url: "/components/excol/single/"
59
+ - name: "Multiple"
60
+ url: "/components/excol/multiple/"
61
+ - name: "Figure"
62
+ url: "/components/figure/"
63
+ - name: "Footer (Page & Site)"
64
+ url: "/components/footer/"
65
+ - name: "Form"
66
+ url: "/components/form/elements/"
67
+ pages:
68
+ - name: "Elements"
69
+ url: "/components/form/elements/"
70
+ - name: "Search"
71
+ url: "/components/form/search/"
72
+ - name: "Headings"
73
+ url: "/components/headings/"
74
+ - name: Images
75
+ url: "/components/images/"
76
+ - name: "Lists"
77
+ url: "/components/lists/"
78
+ - name: "Publication Warning"
79
+ url: "/components/publication-warning/"
80
+ - name: "Tables"
81
+ url: "/components/tables/"
82
+ - name: "Video Links"
83
+ url: "/components/video-links/"
84
+ - name: "Video Player"
85
+ url: "/components/video-player/"
@@ -0,0 +1 @@
1
+ ../_external/data/techniques.yml
@@ -0,0 +1 @@
1
+ ../_external/data/translations.yml
@@ -0,0 +1 @@
1
+ ../_external/data/wcag.yml
@@ -0,0 +1,6 @@
1
+ <blockquote{% if include.class %} class="{{ include.class }}"{% endif %}>
2
+ {{ include.content }}
3
+ {%- if include.source %}
4
+ <cite>{%if include.href %}<a href="{{ include.href }}">{%endif%}{{ include.source }}{%if include.href %}</a>{%endif%}</cite>
5
+ {% endif -%}
6
+ </blockquote>
@@ -1,12 +1,9 @@
1
- {% if include.type == 'start' %}
2
- {% assign classes = include.class | split: " " %}
1
+ {%- if include.type == 'start' -%}
2
+ {%- assign classes = include.class | split: " " -%}
3
3
  <aside class="box{% for class in classes %} box-{{ class }}{% endfor %}"{% if include.id %} id="{{include.id}}"{% endif %}>
4
4
  {%- if include.title -%}
5
5
  <header class="box-h {% for class in classes %} box-h-{{ class }}{% endfor %}{% if include.icon %} box-h-icon{% endif %}">{% if include.icon %} {% include_cached icon.html name=include.icon %}{% endif %}{% if include.h %} <h{{include.h}}>{% endif %} {{ include.title}}{% if include.h %} </h{{include.h}}>{% endif %}</header>
6
6
  {%- endif -%}
7
7
  <div class="box-i">
8
- {% endif %}
9
- {% if include.type == 'end' %}
10
- </div>
11
- </aside>
12
- {% endif %}
8
+ {%- endif -%}
9
+ {%- if include.type == 'end' -%}</div></aside>{%- endif -%}
@@ -0,0 +1,9 @@
1
+ {%- if include.type == "fake" -%}
2
+ <span class="button{% for class in include.class %} button-{{ class }}{% endfor %}">{{include.label}}</span>
3
+ {%- else -%}
4
+ {%- if include.type == "link" -%}
5
+ <a class="button{% for class in include.class %} button-{{ class }}{% endfor %}" href="{{ include.href }}"><span>{% if icon %}{% include_cached icon.html name=include.icon %} {% endif %}{{ include.label }}</span></a>
6
+ {%- else -%}
7
+ <button class="button{% for class in include.class %} button-{{ class }}{% endfor %}"{% if include.submit %} type="submit"{% else %} type="submit"{% endif %}{% if include.disabled %} disabled{% endif%}><span>{% if include.icon %}{% include_cached icon.html name=include.icon %} {% endif %}{{ include.label }}</span></button>
8
+ {%- endif -%}
9
+ {%- endif -%}
@@ -1,2 +1,2 @@
1
- {%- capture thelabel %}{{include.label | default: "Different View"}}{% endcapture -%}
2
- {% include_cached icon.html name="different-view" label=thelabel %}
1
+ {% comment%}{%- capture thelabel %}{{include.label | default: "Different View"}}{% endcapture -%}
2
+ {% include_cached icon.html name="different-view" label=thelabel %}{% endcomment%}
@@ -1,6 +1,6 @@
1
1
  {%- assign col = site.collections | where: "label", page.collection | first -%}
2
2
  {%- if page.changelog -%}
3
- {%- assign chagelogpage=site.documents | where:"ref", page.changelog | first -%}
3
+ {%- assign changelogpage=site.documents | where:"ref", page.changelog | first -%}
4
4
  {%- unless changelogpage.ref -%}
5
5
  {%- assign changelogpage=site.pages | where:"ref", page.changelog | first -%}
6
6
  {%- endunless -%}
@@ -8,7 +8,7 @@ The first section creates an array of page urls that are page urls of this colle
8
8
  </p>
9
9
  </section>
10
10
  {%- endunless -%}
11
- <nav aria-label="Skip Link, Customization, and Language Selector" id="controls" class="default-grid">
11
+ <nav aria-label="{% include t.html t="Skip Link and Language Selector" %}" id="controls" class="default-grid">
12
12
  {% if include.translations[0].ref %}
13
13
  {%- comment -%} Translations on this page {%- endcomment-%}
14
14
  <ul>
@@ -58,7 +58,7 @@ The first section creates an array of page urls that are page urls of this colle
58
58
  </div>
59
59
 
60
60
  <div class="navigations">
61
- <nav class="metanav" aria-label="Meta &amp; Search">
61
+ <nav class="metanav" aria-label="Meta &amp; Search" lang="en">
62
62
  <ul>
63
63
  <li><a href="{{ "/about/participating/" | relative_url }}">Get Involved</a></li>
64
64
  <li><a href="{{ "/about/" | relative_url }}">About W3C WAI</a></li>
@@ -157,13 +157,13 @@ The first section creates an array of page urls that are page urls of this colle
157
157
  {%- endfor -%}
158
158
  {%- endif -%}
159
159
 
160
- <nav class="mainnav" aria-label="Main" lang="en">
160
+ <nav class="mainnav" aria-label="{% include t.html t="Main" lang=page.lang %}" lang="{{ page.lang }}">
161
161
  {%- if page.url == "/" %}{% assign a_url = "" %}{% endif -%}
162
- {%- include_cached navlist.html data=site.data.navigation current=a_url -%}
162
+ {%- include_cached navlist.html data=site.data.navigation current=a_url lang=page.lang -%}
163
163
  </nav>
164
164
 
165
165
  {%- unless page.url == "/" -%}
166
- <nav{% comment %} style="grid-column: 2/8; display: flex;"{% endcomment %} class="default-grid breadcrumb" aria-label="Breadcrumb" lang="en">
166
+ <nav{% comment %} style="grid-column: 2/8; display: flex;"{% endcomment %} class="default-grid breadcrumb" aria-label="{% include t.html t="Breadcrumb" lang=page.lang %}" lang="en">
167
167
  <ul style="align-self: center;">
168
168
  <li><a href="{{ "/" | relative_url }}" lang="en">Home{%comment%}{% include_cached t.html t="Home" lang=pagelang %}{%endcomment%}</a></li>
169
169
  {%- unless a_name == "" -%}<li>{% if a_url == enpage.url%}{% include_cached link.html to=a_url text=a_name aria-current="page" lang=page.lang hidelangnotice=true %}{% else %}{% include_cached link.html text=a_name to=a_url lang=page.lang hidelangnotice=true %}{% endif %}</li>{%- endunless -%}
@@ -0,0 +1,14 @@
1
+ {%- assign repo = page.github.repository | replace: "w3c/", "" -%}
2
+ {%- assign path = "/" | prepend: repo | prepend: "/content-images/" | relative_url -%}
3
+ {%- if include.srcset %}
4
+ {%- assign srcset = "" -%}
5
+ {%- assign srcsetsrces = include.srcset | split: ", " -%}
6
+ {%- for src in srcsetsrces -%}
7
+ {%- unless forloop.last -%}
8
+ {%- assign srcset = srcset | append: path | append: src | append: ", " -%}
9
+ {%- else -%}
10
+ {%- assign srcset = srcset | append: path | append: src -%}
11
+ {%- endunless-%}
12
+ {%- endfor -%}
13
+ {%- endif -%}
14
+ <img src="{{path}}{{include.src}}"{% if include.srcset %} srcset="{{srcset}}"{% endif %} {% if include.sizes %} sizes="{{sizes}}"{% endif %} alt="{{include.alt}}"{% if include.class %} class="{% assign classes = include.class | split: " " %}{% for class in classes %} {{ class }}{% endfor %}"{% endif %}{% if include.style %} style="{{style}}"{% endif %}>
@@ -0,0 +1,2 @@
1
+ <label for="{{ include.label | slugify }}">{{ include.label }}:</label>
2
+ <input type="{{ include.type }}" name="{{ include.name }}"{% if include.disabled %} disabled{% endif%} id="{{ include.label | slugify }}">
@@ -89,4 +89,4 @@
89
89
  {%- endunless -%}
90
90
  {%- endif -%}
91
91
 
92
- <a lang="{{linklang}}" href="{{linkurl}}"{% if include.aria-current %} aria-current="{{include.aria-current}}"{% endif %}{% if include.class %} class="{{include.class}}"{% endif %}><span>{{ link_text }}{%- unless in_lang %}{% unless include.hidelangnotice %} <span class="lang" lang="{{pagelang}}" dir="auto" translate="no">({% include_cached t.html t="in English" lang=pagelang %})</span>{% endunless -%}{% endunless -%}{% if include.different %} {% include_cached different.html %}{% endif %}{% if include.external %} {% include_cached external.html %}{% endif %}</span></a>
92
+ <a lang="{{linklang}}" href="{{linkurl}}"{% if include.aria-current %} aria-current="{{include.aria-current}}"{% endif %}{% if include.class %} class="{{include.class}}"{% endif %}><span>{{ link_text }}{%- unless in_lang %}{% unless include.hidelangnotice %} <span class="lang" lang="{{pagelang}}" dir="auto" translate="no">({% include_cached t.html t="in English" lang=pagelang %})</span>{% endunless -%}{% endunless -%}{% if include.external %} {% include_cached external.html %}{% endif %}</span></a>
@@ -1,7 +1,7 @@
1
1
  {%- if include.current %}{% assign aria="page" %}{% else %}{% if include.insub%}{% assign aria="location" %}{% endif %}{% endif -%}
2
2
  {%- unless include.hide -%}
3
3
  <li>
4
- {% include_cached link.html to=include.url text=include.name aria-current=aria different=include.different external=include.external class="page-link" usenavtitle=true lang=page.lang hidelangnotice=true %}
4
+ {% include_cached link.html to=include.url text=include.name aria-current=aria external=include.external class="page-link" usenavtitle=true lang=page.lang hidelangnotice=true %}
5
5
  {%- unless include.submenu == "<ul></ul>" -%}
6
6
  {{include.submenu}}
7
7
  {%- endunless -%}
@@ -9,7 +9,7 @@
9
9
  {%- else -%}
10
10
  {%- if include.current == true -%}
11
11
  <li>
12
- {% include_cached link.html to=include.url text=include.name aria-current=aria different=include.different external=include.external class="page-link" usenavtitle=true lang=page.lang hidelangnotice=true %}
12
+ {% include_cached link.html to=include.url text=include.name aria-current=aria external=include.external class="page-link" usenavtitle=true lang=page.lang hidelangnotice=true %}
13
13
  {%- unless include.submenu == "<ul></ul>" -%}
14
14
  {{include.submenu}}
15
15
  {%- endunless -%}
@@ -3,7 +3,8 @@
3
3
  {%- assign the_url = item.pages.first.url -%}
4
4
  {%- unless item.mainnav == false -%}
5
5
  <li>
6
- <a href="{{ the_url | relative_url }}"{% if include.current == the_url %} aria-current="location"{% endif %}><span>{% include_cached inpl.html lang=page.lang a=item.name %}</span></a>
6
+ {%- capture navtitle %}{% include_cached inpl.html lang=include.lang a=item.name %}{% endcapture -%}
7
+ {%- include_cached link.html to=the_url text=navtitle hidelangnotice=true -%}
7
8
  </li>
8
9
  {%- endunless -%}
9
10
  {%- if forloop.last %}</ul>{% endif -%}
@@ -51,9 +51,9 @@
51
51
  {%- if k.url == searchurl -%}
52
52
  {% assign insub = true %}
53
53
  {% assign insubsub = true %}
54
- {% include menuitem.html name=k.name url=k.url different=k.different hide=k.hide insub=false current=true %}
54
+ {% include menuitem.html name=k.name url=k.url hide=k.hide insub=false current=true %}
55
55
  {%- else -%}
56
- {% include menuitem.html name=k.name url=k.url different=k.different hide=k.hide insub=false current=false %}
56
+ {% include menuitem.html name=k.name url=k.url hide=k.hide insub=false current=false %}
57
57
  {%- endif -%}
58
58
  {%- if forloop.last %}</ul>{% endif -%}
59
59
  {%- endfor -%}
@@ -61,17 +61,17 @@
61
61
  {%- if forloop.first %}<ul>{% endif -%}
62
62
  {%- if j.url == searchurl -%}
63
63
  {% assign insub = true %}
64
- {% include menuitem.html name=j.name url=j.url different=j.different hide=j.hide insub=insubsub current=true submenu=subsubmenu %}
64
+ {% include menuitem.html name=j.name url=j.url hide=j.hide insub=insubsub current=true submenu=subsubmenu %}
65
65
  {%- else -%}
66
- {% include menuitem.html name=j.name url=j.url different=j.different hide=j.hide insub=insubsub current=false submenu=subsubmenu %}
66
+ {% include menuitem.html name=j.name url=j.url hide=j.hide insub=insubsub current=false submenu=subsubmenu %}
67
67
  {%- endif -%}
68
68
  {%- if forloop.last %}</ul>{% endif -%}
69
69
  {%- endfor -%}
70
70
  {%- endcapture -%}
71
71
  {%- if i.url == searchurl -%}
72
- {% include menuitem.html name=i.name url=i.url different=i.different hide=i.hide insub=insub current=true submenu=submenu %}
72
+ {% include menuitem.html name=i.name url=i.url hide=i.hide insub=insub current=true submenu=submenu %}
73
73
  {%- else -%}
74
- {% include menuitem.html name=i.name url=i.url different=i.different hide=i.hide insub=insub current=false submenu=submenu %}
74
+ {% include menuitem.html name=i.name url=i.url hide=i.hide insub=insub current=false submenu=submenu %}
75
75
  {%- endif -%}
76
76
  {%- if forloop.last %}</ul></nav>{% endif -%}
77
77
  {%- endfor -%}
@@ -0,0 +1 @@
1
+ <button class="showhidebutton button-icon button-small" data-target="{{ include.target }}" aria-expanded="{% if include.default=="show" %}true{% else %}false{% endif %}" data-showtext="{{ include.showtext | xml_escape }}" data-hidetext="{{ include.hidetext | xml_escape }}">{% if include.default=="show" %}{{ include.hidetext }}{% else %}{{ include.showtext }}{% endif %}</button>
@@ -4,7 +4,21 @@
4
4
  <header id="tocheading" class="box-h box-h-simple{% for class in classes %} box-h-{{ class }}{% endfor %}{% if input.icon %} box-h-icon{% endif %}">{% if input.icon %} {% include_cached icon.html name=input.icon %}{% endif %} {{ include.title}}</header>
5
5
  <div class="box-i">
6
6
  {% endif %}
7
- {% if include.type == 'end' %}
7
+ {% if include.type == 'end' %}
8
8
  </div>
9
9
  </nav>
10
+ {% endif %}
11
+ {% if (include.type != 'start') and (include.type != 'end') %}
12
+ {::nomarkdown}
13
+ {% include toc.html type="start" title="Page Contents" %}
14
+ {:/}
15
+
16
+ {% if include.levels %}{::options toc_levels="{{include.levels}}" /}{% endif %}
17
+
18
+ - This will be replaced by an automatically generated TOC when using Markdown formatting.
19
+ {:toc}
20
+
21
+ {::nomarkdown}
22
+ {% include toc.html type="end" %}
23
+ {:/}
10
24
  {% endif %}
@@ -1,5 +1,5 @@
1
1
  {%- assign classes = include.class | split: " " -%}
2
- <a href="{{ include.href | relative_url }}" class="video-link{% for class in classes %} video-link-{{ class }}{% endfor %}{% if include.ratio == "4:3" %} ratio-43{%- endif -%}">
2
+ <a href="{{ include.href | relative_url }}" class="video-link{% for class in classes %} video-link-{{ class }}{% endfor %}">
3
3
  <img src="{{ include.src | relative_url }}" alt="">
4
4
  <span>{{ include.title }}</span>
5
5
  </a>
@@ -1,6 +1,6 @@
1
1
  {%- assign langs = site.data.lang -%}
2
2
  <div class="video-container" data-video-type="" dir="ltr">
3
- <video preload="metadata" data-youtube-id="{{include.yt-id}}" data-youtube-nocookie="true" width="450">
3
+ <video preload="metadata" data-youtube-id="{{include.yt-id}}" data-youtube-nocookie="true" data-description-audible="false" width="450">
4
4
  {%- assign captions = include.captions | split: ',' -%}
5
5
  {%- for caption in captions -%}
6
6
  {%- assign c = caption | split: '|' -%}
@@ -26,7 +26,7 @@
26
26
  </div>
27
27
  {% if include.yt-id-ad %}
28
28
  <div class="video-container" data-video-type="audio-described">
29
- <video preload="metadata" data-youtube-id="{{include.yt-id-ad}}" data-youtube-nocookie="true" width="450">
29
+ <video preload="metadata" data-youtube-id="{{include.yt-id-ad}}" data-youtube-nocookie="true" data-description-audible="false" width="450">
30
30
  {%- assign captions = include.captions-ad | split: ',' -%}
31
31
  {%- for caption in captions -%}
32
32
  {%- assign c = caption | split: '|' -%}
@@ -168,6 +168,20 @@
168
168
  this.useDescriptionsButton = true;
169
169
  }
170
170
 
171
+ // Silence audio description
172
+ // set to "false" if the sole purposes of the WebVTT descriptions file
173
+ // is to display description text visibly and to integrate it into the transcript
174
+ if ($(media).data('descriptions-audible') !== undefined && $(media).data('descriptions-audible') === false) {
175
+ this.exposeTextDescriptions = false;
176
+ }
177
+ else if ($(media).data('description-audible') !== undefined && $(media).data('description-audible') === false) {
178
+ // support both singular and plural spelling of attribute
179
+ this.exposeTextDescriptions = false;
180
+ }
181
+ else {
182
+ this.exposeTextDescriptions = true;
183
+ }
184
+
171
185
  // Headings
172
186
  // By default, an off-screen heading is automatically added to the top of the media player
173
187
  // It is intelligently assigned a heading level based on context, via misc.js > getNextHeadingLevel()
@@ -189,6 +203,19 @@
189
203
  // 3. "popup" - Automatically generated, written to a draggable, resizable popup window that can be toggled on/off with a button
190
204
  // If data-include-transcript="false", there is no "popup" transcript
191
205
 
206
+ if ($(media).data('transcript-div') !== undefined && $(media).data('transcript-div') !== "") {
207
+ this.transcriptDivLocation = $(media).data('transcript-div');
208
+ }
209
+ else {
210
+ this.transcriptDivLocation = null;
211
+ }
212
+ if ($(media).data('include-transcript') !== undefined && $(media).data('include-transcript') === false) {
213
+ this.hideTranscriptButton = true;
214
+ }
215
+ else {
216
+ this.hideTranscriptButton = null;
217
+ }
218
+
192
219
  this.transcriptType = null;
193
220
  if ($(media).data('transcript-src') !== undefined) {
194
221
  this.transcriptSrc = $(media).data('transcript-src');
@@ -198,15 +225,9 @@
198
225
  }
199
226
  else if ($(media).find('track[kind="captions"], track[kind="subtitles"]').length > 0) {
200
227
  // required tracks are present. COULD automatically generate a transcript
201
- if ($(media).data('transcript-div') !== undefined && $(media).data('transcript-div') !== "") {
202
- this.transcriptDivLocation = $(media).data('transcript-div');
228
+ if (this.transcriptDivLocation) {
203
229
  this.transcriptType = 'external';
204
230
  }
205
- else if ($(media).data('include-transcript') !== undefined) {
206
- if ($(media).data('include-transcript') !== false) {
207
- this.transcriptType = 'popup';
208
- }
209
- }
210
231
  else {
211
232
  this.transcriptType = 'popup';
212
233
  }
@@ -258,18 +279,6 @@
258
279
  this.defaultChapter = null;
259
280
  }
260
281
 
261
- // Previous/Next buttons
262
- // valid values of data-prevnext-unit are 'playlist' and 'chapter'; will also accept 'chapters'
263
- if ($(media).data('prevnext-unit') === 'chapter' || $(media).data('prevnext-unit') === 'chapters') {
264
- this.prevNextUnit = 'chapter';
265
- }
266
- else if ($(media).data('prevnext-unit') === 'playlist') {
267
- this.prevNextUnit = 'playlist';
268
- }
269
- else {
270
- this.prevNextUnit = false;
271
- }
272
-
273
282
  // Slower/Faster buttons
274
283
  // valid values of data-speed-icons are 'animals' (default) and 'arrows'
275
284
  // 'animals' uses turtle and rabbit; 'arrows' uses up/down arrows
@@ -757,7 +766,7 @@
757
766
  svg[1] = 'M7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714zM18.75 12.093v1.657h-1.657l-2.093-2.093-2.093 2.093h-1.657v-1.657l2.093-2.093-2.093-2.093v-1.657h1.657l2.093 2.093 2.093-2.093h1.657v1.657l-2.093 2.093z';
758
767
  break;
759
768
 
760
- case 'volume-mute':
769
+ case 'volume-soft':
761
770
  svg[0] = '0 0 20 20';
762
771
  svg[1] = 'M10.723 14.473c-0.24 0-0.48-0.092-0.663-0.275-0.366-0.366-0.366-0.96 0-1.326 1.584-1.584 1.584-4.161 0-5.745-0.366-0.366-0.366-0.96 0-1.326s0.96-0.366 1.326 0c2.315 2.315 2.315 6.082 0 8.397-0.183 0.183-0.423 0.275-0.663 0.275zM7.839 1.536c0.501-0.501 0.911-0.331 0.911 0.378v16.172c0 0.709-0.41 0.879-0.911 0.378l-4.714-4.713h-3.125v-7.5h3.125l4.714-4.714z';
763
772
  break;
@@ -984,7 +993,7 @@
984
993
  this.iconType = 'image';
985
994
  }
986
995
  if (this.debug) {
987
-
996
+
988
997
  }
989
998
  if (typeof $tempButton !== 'undefined') {
990
999
  $tempButton.remove();
@@ -1144,11 +1153,10 @@
1144
1153
  thisObj.showSearchResults();
1145
1154
 
1146
1155
  // Go ahead and load media, without user requesting it
1147
- // Normally, we wait until user clicks play, rather than unnecessarily consume their bandwidth
1148
- // Exceptions are if the video is intended to autostart or if running on iOS (a workaround for iOS issues)
1149
- // TODO: Confirm that this is still necessary with iOS (this would added early, & I don't remember what the issues were)
1150
- if (thisObj.player === 'html5' &&
1151
- (thisObj.isIOS() || thisObj.startTime > 0 || thisObj.autoplay || thisObj.okToPlay)) {
1156
+ // Ideally, we would wait until user clicks play, rather than unnecessarily consume their bandwidth
1157
+ // However, the media needs to load before the 'loadedmetadata' event is fired
1158
+ // and until that happens we can't get the media's duration
1159
+ if (thisObj.player === 'html5') {
1152
1160
  thisObj.$media[0].load();
1153
1161
  }
1154
1162
  // refreshControls is called twice building/initializing the player
@@ -1571,7 +1579,7 @@
1571
1579
  });
1572
1580
  prefs.push({
1573
1581
  'name': 'prefDescFormat', // audio description default state
1574
- 'label': this.tt.prefDescFormat,
1582
+ 'label': null,
1575
1583
  'group': 'descriptions',
1576
1584
  'default': 'video'
1577
1585
  });
@@ -1630,7 +1638,7 @@
1630
1638
  var available, thisObj, $prefsDiv, formTitle, introText,
1631
1639
  $prefsIntro,$prefsIntroP2,p3Text,$prefsIntroP3,i, j,
1632
1640
  $fieldset, fieldsetClass, fieldsetId,
1633
- $descFieldset1, $descLegend1, $descFieldset2, $descLegend2, $legend,
1641
+ $descFieldset, $descLegend, $legend,
1634
1642
  thisPref, $thisDiv, thisClass, thisId, $thisLabel, $thisField,
1635
1643
  $div1,id1,$radio1,$label1,
1636
1644
  $div2,id2,$radio2,$label2,
@@ -1728,35 +1736,17 @@
1728
1736
  $prefsDiv.append($prefsIntro);
1729
1737
  }
1730
1738
 
1731
- if (form === 'descriptions') {
1732
- // descriptions form has two field sets
1733
-
1734
- // Fieldset 1
1735
- $descFieldset1 = $('<fieldset>');
1736
- fieldsetClass = 'able-prefs-' + form + '1';
1737
- fieldsetId = this.mediaId + '-prefs-' + form + '1';
1738
- $descFieldset1.addClass(fieldsetClass).attr('id',fieldsetId);
1739
- $descLegend1 = $('<legend>' + this.tt.prefDescFormat + '</legend>');
1740
- $descFieldset1.append($descLegend1);
1741
-
1742
- // Fieldset 2
1743
- $descFieldset2 = $('<fieldset>');
1744
- fieldsetClass = 'able-prefs-' + form + '2';
1745
- fieldsetId = this.mediaId + '-prefs-' + form + '2';
1746
- $descFieldset2.addClass(fieldsetClass).attr('id',fieldsetId);
1747
- $descLegend2 = $('<legend>' + this.tt.prefHeadingTextDescription + '</legend>');
1748
- $descFieldset2.append($descLegend2);
1739
+ $fieldset = $('<fieldset>');
1740
+ fieldsetClass = 'able-prefs-' + form;
1741
+ fieldsetId = this.mediaId + '-prefs-' + form;
1742
+ $fieldset.addClass(fieldsetClass).attr('id',fieldsetId);
1743
+ if (form === 'keyboard') {
1744
+ $legend = $('<legend>' + this.tt.prefHeadingKeyboard1 + '</legend>');
1745
+ $fieldset.append($legend);
1749
1746
  }
1750
- else {
1751
- // all other forms just have one fieldset
1752
- $fieldset = $('<fieldset>');
1753
- fieldsetClass = 'able-prefs-' + form;
1754
- fieldsetId = this.mediaId + '-prefs-' + form;
1755
- $fieldset.addClass(fieldsetClass).attr('id',fieldsetId);
1756
- if (form === 'keyboard') {
1757
- $legend = $('<legend>' + this.tt.prefHeadingKeyboard1 + '</legend>');
1758
- $fieldset.append($legend);
1759
- }
1747
+ else if (form === 'descriptions') {
1748
+ $legend = $('<legend>' + this.tt.prefHeadingTextDescription + '</legend>');
1749
+ $fieldset.append($legend);
1760
1750
  }
1761
1751
  for (i=0; i<available.length; i++) {
1762
1752
 
@@ -1766,48 +1756,9 @@
1766
1756
  thisPref = available[i]['name'];
1767
1757
  thisClass = 'able-' + thisPref;
1768
1758
  thisId = this.mediaId + '_' + thisPref;
1769
- if (thisPref !== 'prefDescFormat') {
1770
- $thisDiv = $('<div>').addClass(thisClass);
1771
- }
1759
+ $thisDiv = $('<div>').addClass(thisClass);
1772
1760
 
1773
- // Audio Description preferred format radio buttons
1774
- if (thisPref == 'prefDescFormat') {
1775
-
1776
- // option 1 radio button
1777
- $div1 = $('<div>');
1778
- id1 = thisId + '_1';
1779
- $label1 = $('<label>')
1780
- .attr('for',id1)
1781
- .text(this.capitalizeFirstLetter(this.tt.prefDescFormatOption1))
1782
- $radio1 = $('<input>',{
1783
- type: 'radio',
1784
- name: thisPref,
1785
- id: id1,
1786
- value: 'video'
1787
- });
1788
- if (this.prefDescFormat === 'video') {
1789
- $radio1.prop('checked',true);
1790
- };
1791
- $div1.append($radio1,$label1);
1792
-
1793
- // option 2 radio button
1794
- $div2 = $('<div>');
1795
- id2 = thisId + '_2';
1796
- $label2 = $('<label>')
1797
- .attr('for',id2)
1798
- .text(this.capitalizeFirstLetter(this.tt.prefDescFormatOption2));
1799
- $radio2 = $('<input>',{
1800
- type: 'radio',
1801
- name: thisPref,
1802
- id: id2,
1803
- value: 'text'
1804
- });
1805
- if (this.prefDescFormat === 'text') {
1806
- $radio2.prop('checked',true);
1807
- };
1808
- $div2.append($radio2,$label2);
1809
- }
1810
- else if (form === 'captions') {
1761
+ if (form === 'captions') {
1811
1762
  $thisLabel = $('<label for="' + thisId + '"> ' + available[i]['label'] + '</label>');
1812
1763
  $thisField = $('<select>',{
1813
1764
  name: thisPref,
@@ -1899,25 +1850,11 @@
1899
1850
  }
1900
1851
  $thisDiv.append($thisField,$thisLabel);
1901
1852
  }
1902
- if (form === 'descriptions') {
1903
- if (thisPref === 'prefDescFormat') {
1904
- $descFieldset1.append($div1,$div2);
1905
- }
1906
- else {
1907
- $descFieldset2.append($thisDiv);
1908
- }
1909
- }
1910
- else {
1911
- $fieldset.append($thisDiv);
1912
- }
1853
+ $fieldset.append($thisDiv);
1913
1854
  }
1914
1855
  }
1915
- if (form === 'descriptions') {
1916
- $prefsDiv.append($descFieldset1,$descFieldset2);
1917
- }
1918
- else {
1919
- $prefsDiv.append($fieldset);
1920
- }
1856
+ $prefsDiv.append($fieldset);
1857
+
1921
1858
  if (form === 'captions') {
1922
1859
  // add a sample closed caption div to prefs dialog
1923
1860
  if (this.mediaType === 'video') {
@@ -1946,6 +1883,14 @@
1946
1883
  kbLabels.push(this.tt.restart);
1947
1884
  keys.push('s');
1948
1885
  }
1886
+ else if (this.controls[i] === 'previous') {
1887
+ kbLabels.push(this.tt.prevTrack);
1888
+ keys.push('b'); // b = back
1889
+ }
1890
+ else if (this.controls[i] === 'next') {
1891
+ kbLabels.push(this.tt.nextTrack);
1892
+ keys.push('n');
1893
+ }
1949
1894
  else if (this.controls[i] === 'rewind') {
1950
1895
  kbLabels.push(this.tt.rewind);
1951
1896
  keys.push('r');
@@ -2096,15 +2041,7 @@
2096
2041
  available = this.getAvailablePreferences();
2097
2042
  for (i=0; i<available.length; i++) {
2098
2043
  prefName = available[i]['name'];
2099
- if (prefName === 'prefDescFormat') {
2100
- if (this[prefName] === 'text') {
2101
- $('input[value="text"]').prop('checked',true);
2102
- }
2103
- else {
2104
- $('input[value="video"]').prop('checked',true);
2105
- }
2106
- }
2107
- else if ((prefName.indexOf('Captions') !== -1) && (prefName !== 'prefCaptions')) {
2044
+ if ((prefName.indexOf('Captions') !== -1) && (prefName !== 'prefCaptions')) {
2108
2045
  // this is a caption-related select box
2109
2046
  $('select[name="' + prefName + '"]').val(cookie.preferences[prefName]);
2110
2047
  }
@@ -2137,8 +2074,10 @@
2137
2074
  if (available[i]['label']) {
2138
2075
  var prefName = available[i]['name'];
2139
2076
  if (prefName == 'prefDescFormat') {
2140
- this.prefDescFormat = $('input[name="' + prefName + '"]:checked').val();
2141
- if (this.prefDescFormat !== cookie.preferences['prefDescFormat']) { // user changed setting
2077
+ // As of v4.0.10, prefDescFormat is no longer a choice
2078
+ // this.prefDescFormat = $('input[name="' + prefName + '"]:checked').val();
2079
+ this.prefDescFormat = 'video';
2080
+ if (this.prefDescFormat !== cookie.preferences['prefDescFormat']) { // user's preference has changed
2142
2081
  cookie.preferences['prefDescFormat'] = this.prefDescFormat;
2143
2082
  numChanges++;
2144
2083
  }
@@ -2292,10 +2231,10 @@
2292
2231
  errString += 'Column: ' + parserState.column + '\n';
2293
2232
  errString += err;
2294
2233
  if (console.warn) {
2295
-
2234
+
2296
2235
  }
2297
2236
  else if (console.log) {
2298
-
2237
+
2299
2238
  }
2300
2239
  }
2301
2240
  return parserState;
@@ -2481,10 +2420,10 @@
2481
2420
  errString += 'Column: ' + state.column + '\n';
2482
2421
  errString += 'Expected cue timing for cueId \''+cueId+'\' but found: ' + nextLine + '\n';
2483
2422
  if (console.warn) {
2484
-
2423
+
2485
2424
  }
2486
2425
  else if (console.log) {
2487
-
2426
+
2488
2427
  }
2489
2428
  return; // Return leaving line for parseCuesAndComments to handle
2490
2429
  }
@@ -3187,10 +3126,14 @@
3187
3126
  // create a div for exposing description
3188
3127
  // description will be exposed via role="alert" & announced by screen readers
3189
3128
  this.$descDiv = $('<div>',{
3190
- 'class': 'able-descriptions',
3191
- 'aria-live': 'assertive',
3192
- 'aria-atomic': 'true'
3129
+ 'class': 'able-descriptions'
3193
3130
  });
3131
+ if (this.exposeTextDescriptions) {
3132
+ this.$descDiv.attr({
3133
+ 'aria-live': 'assertive',
3134
+ 'aria-atomic': 'true'
3135
+ });
3136
+ }
3194
3137
  // Start off with description hidden.
3195
3138
  // It will be exposed conditionally within description.js > initDescription()
3196
3139
  this.$descDiv.hide();
@@ -3843,6 +3786,11 @@
3843
3786
  'br': []
3844
3787
  }
3845
3788
 
3789
+ if (this.hasPlaylist) {
3790
+ controlLayout['ur'].push('previous');
3791
+ controlLayout['ur'].push('next');
3792
+ }
3793
+
3846
3794
  // test for browser support for volume before displaying volume button
3847
3795
  if (this.browserSupportsVolume()) {
3848
3796
  // volume buttons are: 'mute','volume-soft','volume-medium','volume-loud'
@@ -3874,7 +3822,7 @@
3874
3822
  bll.push('descriptions'); //audio description
3875
3823
  }
3876
3824
  }
3877
- if (this.transcriptType === 'popup') {
3825
+ if (this.transcriptType === 'popup' && !(this.hideTranscriptButton)) {
3878
3826
  bll.push('transcript');
3879
3827
  }
3880
3828
 
@@ -3955,7 +3903,7 @@
3955
3903
  $controllerSpan.append($sliderDiv);
3956
3904
  if (typeof this.duration === 'undefined' || this.duration === 0) {
3957
3905
  // set arbitrary starting duration, and change it when duration is known
3958
- this.duration = 100;
3906
+ this.duration = 60;
3959
3907
  // also set elapsed to 0
3960
3908
  this.elapsed = 0;
3961
3909
  }
@@ -4720,6 +4668,12 @@
4720
4668
  else if (control === 'restart') {
4721
4669
  return this.tt.restart;
4722
4670
  }
4671
+ else if (control === 'previous') {
4672
+ return this.tt.prevTrack;
4673
+ }
4674
+ else if (control === 'next') {
4675
+ return this.tt.nextTrack;
4676
+ }
4723
4677
  else if (control === 'rewind') {
4724
4678
  return this.tt.rewind;
4725
4679
  }
@@ -4781,7 +4735,7 @@
4781
4735
  // return the name of the control with first letter in upper case
4782
4736
  // ultimately will need to get a translated label from this.tt
4783
4737
  if (this.debug) {
4784
-
4738
+
4785
4739
  }
4786
4740
  return control.charAt(0).toUpperCase() + control.slice(1);
4787
4741
  }
@@ -5159,7 +5113,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
5159
5113
  $tempDiv.load(src, function (trackText, status, req) {
5160
5114
  if (status === 'error') {
5161
5115
  if (thisObj.debug) {
5162
-
5116
+
5163
5117
  }
5164
5118
  deferred.fail();
5165
5119
  }
@@ -5531,7 +5485,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
5531
5485
  deferred.resolve();
5532
5486
  })
5533
5487
  .fail(function(){
5534
-
5488
+
5535
5489
  });
5536
5490
  }
5537
5491
  else {
@@ -5662,8 +5616,8 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
5662
5616
  // Should still proceed, but with captions disabled
5663
5617
  // The specific error, if needed: reason.result.error.message
5664
5618
  // If no captions, the error is: "The video identified by the <code>videoId</code> parameter could not be found."
5665
-
5666
-
5619
+
5620
+
5667
5621
  thisObj.hasCaptions = false;
5668
5622
  thisObj.usingYouTubeCaptions = false;
5669
5623
  deferred.resolve();
@@ -5766,7 +5720,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
5766
5720
  }
5767
5721
  },
5768
5722
  error: function(xhr, status) {
5769
-
5723
+
5770
5724
  deferred.resolve();
5771
5725
  }
5772
5726
  });
@@ -6505,7 +6459,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
6505
6459
 
6506
6460
  AblePlayer.prototype.refreshVolumeButton = function(volume) {
6507
6461
 
6508
- var volumeName, volumePct, volumeLabel, volumeIconClass, volumeImg;
6462
+ var volumeName, volumePct, volumeLabel, volumeIconClass, volumeImg, newSvgData;
6509
6463
 
6510
6464
  volumeName = this.getVolumeName(volume);
6511
6465
  volumePct = (volume/10) * 100;
@@ -6516,10 +6470,18 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
6516
6470
  this.$volumeButton.find('span').first().removeClass().addClass(volumeIconClass);
6517
6471
  this.$volumeButton.find('span.able-clipped').text(volumeLabel);
6518
6472
  }
6519
- else {
6473
+ else if (this.iconType === 'image') {
6520
6474
  volumeImg = this.imgPath + 'volume-' + volumeName + '.png';
6521
6475
  this.$volumeButton.find('img').attr('src',volumeImg);
6522
6476
  }
6477
+ else if (this.iconType === 'svg') {
6478
+ if (volumeName !== 'mute') {
6479
+ volumeName = 'volume-' + volumeName;
6480
+ }
6481
+ newSvgData = this.getSvgData(volumeName);
6482
+ this.$volumeButton.find('svg').attr('viewBox',newSvgData[0]);
6483
+ this.$volumeButton.find('path').attr('d',newSvgData[1]);
6484
+ }
6523
6485
  };
6524
6486
 
6525
6487
  AblePlayer.prototype.moveVolumeHead = function(y) {
@@ -6617,6 +6579,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
6617
6579
  };
6618
6580
 
6619
6581
  AblePlayer.prototype.handleMute = function() {
6582
+
6620
6583
  if (this.isMuted()) {
6621
6584
  this.setMute(false);
6622
6585
  }
@@ -7095,17 +7058,20 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7095
7058
 
7096
7059
  // The following variables are applicable to delivery of description:
7097
7060
  // prefDesc == 1 if user wants description (i.e., Description button is on); else 0
7098
- // prefDescFormat == either 'video' or 'text'
7061
+ // prefDescFormat == either 'video' or 'text' (as of v4.0.10, prefDescFormat is always 'video')
7099
7062
  // prefDescPause == 1 to pause video when description starts; else 0
7100
7063
  // prefVisibleDesc == 1 to visibly show text-based description area; else 0
7101
7064
  // hasOpenDesc == true if a described version of video is available via data-desc-src attribute
7102
7065
  // hasClosedDesc == true if a description text track is available
7103
7066
  // this.useDescFormat == either 'video' or 'text'; the format ultimately delivered
7104
7067
  // descOn == true if description of either type is on
7068
+ // exposeTextDescriptions == true if text description is to be announced audibly; otherwise false
7105
7069
 
7106
7070
  var thisObj = this;
7107
-
7108
- if (!this.refreshingDesc) {
7071
+ if (this.refreshingDesc) {
7072
+ this.prevDescFormat = this.useDescFormat;
7073
+ }
7074
+ else {
7109
7075
  // this is the initial build
7110
7076
  // first, check to see if there's an open-described version of this video
7111
7077
  // checks only the first source since if a described version is provided,
@@ -7125,12 +7091,15 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7125
7091
  }
7126
7092
  }
7127
7093
  }
7094
+
7128
7095
  // update this.useDescFormat based on media availability & user preferences
7129
7096
  if (this.prefDesc) {
7130
7097
  if (this.hasOpenDesc && this.hasClosedDesc) {
7131
- // both formats are available. Use whichever one user prefers
7098
+ // both formats are available. Always use 'video'
7132
7099
  this.useDescFormat = this.prefDescFormat;
7133
7100
  this.descOn = true;
7101
+ // Do not pause during descriptions when playing described video
7102
+ this.prefDescPause = false;
7134
7103
  }
7135
7104
  else if (this.hasOpenDesc) {
7136
7105
  this.useDescFormat = 'video';
@@ -7142,14 +7111,8 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7142
7111
  }
7143
7112
  }
7144
7113
  else { // description button is off
7145
- if (this.refreshingDesc) { // user just now toggled it off
7146
- this.prevDescFormat = this.useDescFormat;
7147
- this.useDescFormat = false;
7148
- this.descOn = false;
7149
- }
7150
- else { // desc has always been off
7151
- this.useDescFormat = false;
7152
- }
7114
+ this.useDescFormat = false;
7115
+ this.descOn = false;
7153
7116
  }
7154
7117
 
7155
7118
  if (this.useDescFormat === 'text') {
@@ -7181,22 +7144,21 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7181
7144
  }
7182
7145
  }
7183
7146
  if (this.descOn) {
7184
-
7185
7147
  if (this.useDescFormat === 'video') {
7186
7148
  if (!this.usingAudioDescription()) {
7187
7149
  // switched from non-described to described version
7188
7150
  this.swapDescription();
7189
7151
  }
7190
- // hide description div
7191
- this.$descDiv.hide();
7192
- this.$descDiv.removeClass('able-clipped');
7193
7152
  }
7194
- else if (this.useDescFormat === 'text') {
7195
- this.$descDiv.show();
7196
- if (this.prefVisibleDesc) { // make it visible to everyone
7153
+ if (this.hasClosedDesc) {
7154
+ if (this.prefVisibleDesc) {
7155
+ // make description text visible
7156
+ // New in v4.0.10: Do this regardless of useDescFormat
7157
+ this.$descDiv.show();
7197
7158
  this.$descDiv.removeClass('able-clipped');
7198
7159
  }
7199
- else { // keep it visible to screen readers, but hide from everyone else
7160
+ else {
7161
+ // keep it visible to screen readers, but hide it visibly
7200
7162
  this.$descDiv.addClass('able-clipped');
7201
7163
  }
7202
7164
  if (!this.swappingSrc) {
@@ -7205,7 +7167,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7205
7167
  }
7206
7168
  }
7207
7169
  else { // description is off.
7208
-
7209
7170
  if (this.prevDescFormat === 'video') { // user was previously using description via video
7210
7171
  if (this.usingAudioDescription()) {
7211
7172
  this.swapDescription();
@@ -7235,7 +7196,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7235
7196
  };
7236
7197
 
7237
7198
  AblePlayer.prototype.swapDescription = function() {
7238
-
7239
7199
  // swap described and non-described source media, depending on which is playing
7240
7200
  // this function is only called in two circumstances:
7241
7201
  // 1. Swapping to described version when initializing player (based on user prefs & availability)
@@ -7358,7 +7318,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7358
7318
  // there's a lot of redundancy between this function and showCaptions
7359
7319
  // Trying to combine them ended up in a mess though. Keeping as is for now.
7360
7320
 
7361
- if (this.swappingSrc) {
7321
+ if (this.swappingSrc || !this.descOn) {
7362
7322
  return;
7363
7323
  }
7364
7324
 
@@ -7398,7 +7358,10 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7398
7358
  // temporarily remove aria-live from $status in order to prevent description from being interrupted
7399
7359
  this.$status.removeAttr('aria-live');
7400
7360
  descText = flattenComponentForDescription(cues[thisDescription].components);
7401
- if (typeof this.synth !== 'undefined' && typeof this.descVoiceIndex !== 'undefined') {
7361
+ if (
7362
+ this.exposeTextDescriptions &&
7363
+ typeof this.synth !== 'undefined' &&
7364
+ typeof this.descVoiceIndex !== 'undefined') {
7402
7365
  // browser supports speech synthesis and a voice has been selected in initDescription()
7403
7366
  // use the web speech API
7404
7367
  msg = new SpeechSynthesisUtterance();
@@ -7414,7 +7377,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7414
7377
  if (thisObj.pausedForDescription) {
7415
7378
  thisObj.playMedia();
7416
7379
  }
7417
- };
7380
+ };
7418
7381
  this.synth.speak(msg);
7419
7382
  if (this.prefVisibleDesc) {
7420
7383
  // write description to the screen for sighted users
@@ -7427,7 +7390,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7427
7390
  // load the new description into the container div for screen readers to read
7428
7391
  this.$descDiv.html(descText);
7429
7392
  }
7430
- if (this.prefDescPause) {
7393
+ if (this.prefDescPause && this.exposeTextDescriptions) {
7431
7394
  this.pauseMedia();
7432
7395
  this.pausedForDescription = true;
7433
7396
  }
@@ -7520,11 +7483,11 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7520
7483
  this.userAgent.os = "Linux";
7521
7484
  }
7522
7485
  if (this.debug) {
7523
-
7524
-
7525
-
7526
-
7527
-
7486
+
7487
+
7488
+
7489
+
7490
+
7528
7491
  }
7529
7492
  };
7530
7493
 
@@ -7532,7 +7495,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
7532
7495
 
7533
7496
  var userAgent = navigator.userAgent.toLowerCase();
7534
7497
  if (this.debug) {
7535
-
7498
+
7536
7499
  }
7537
7500
  if (userAgent.indexOf(which.toLowerCase()) !== -1) {
7538
7501
  return true;
@@ -8163,30 +8126,27 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8163
8126
  if (typeof this.$elapsedTimeContainer !== 'undefined') {
8164
8127
  this.$elapsedTimeContainer.text(this.formatSecondsAsColonTime(displayElapsed));
8165
8128
  }
8166
-
8167
8129
  // Update seekbar width.
8168
8130
  // To do this, we need to calculate the width of all buttons surrounding it.
8169
8131
  if (this.seekBar) {
8170
8132
  widthUsed = 0;
8171
- seekbarSpacer = 40; // adjust for discrepancies in browsers' calculated button widths
8172
-
8173
8133
  leftControls = this.seekBar.wrapperDiv.parent().prev('div.able-left-controls');
8174
8134
  rightControls = leftControls.next('div.able-right-controls');
8175
8135
  leftControls.children().each(function () {
8176
8136
  if ($(this).prop('tagName')=='BUTTON') {
8177
- widthUsed += $(this).width();
8137
+ widthUsed += $(this).outerWidth(true); // true = include margin
8178
8138
  }
8179
8139
  });
8180
8140
  rightControls.children().each(function () {
8181
8141
  if ($(this).prop('tagName')=='BUTTON') {
8182
- widthUsed += $(this).width();
8142
+ widthUsed += $(this).outerWidth(true);
8183
8143
  }
8184
8144
  });
8185
8145
  if (this.fullscreen) {
8186
- seekbarWidth = $(window).width() - widthUsed - seekbarSpacer;
8146
+ seekbarWidth = $(window).width() - widthUsed;
8187
8147
  }
8188
8148
  else {
8189
- seekbarWidth = this.$ableWrapper.width() - widthUsed - seekbarSpacer;
8149
+ seekbarWidth = this.$ableWrapper.width() - widthUsed;
8190
8150
  }
8191
8151
  // Sometimes some minor fluctuations based on browser weirdness, so set a threshold.
8192
8152
  if (Math.abs(seekbarWidth - this.seekBar.getWidth()) > 5) {
@@ -8437,7 +8397,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8437
8397
 
8438
8398
  // Don't change play/pause button display while using the seek bar (or if YouTube stopped)
8439
8399
  if (!thisObj.seekBar.tracking && !thisObj.stoppingYouTube) {
8440
- if (currentState === 'paused' || currentState === 'stopped') {
8400
+ if (currentState === 'paused' || currentState === 'stopped' || currentState === 'ended') {
8441
8401
  thisObj.$playpauseButton.attr('aria-label',thisObj.tt.play);
8442
8402
 
8443
8403
  if (thisObj.iconType === 'font') {
@@ -8538,6 +8498,34 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8538
8498
  this.seekTo(0);
8539
8499
  };
8540
8500
 
8501
+ AblePlayer.prototype.handlePrevTrack = function() {
8502
+
8503
+ if (this.playlistIndex === 0) {
8504
+ // currently on the first track
8505
+ // wrap to bottom and play the last track
8506
+ this.playlistIndex = this.$playlist.length - 1;
8507
+ }
8508
+ else {
8509
+ this.playlistIndex--;
8510
+ }
8511
+ this.cueingPlaylistItem = true; // stopgap to prevent multiple firings
8512
+ this.cuePlaylistItem(this.playlistIndex);
8513
+ };
8514
+
8515
+ AblePlayer.prototype.handleNextTrack = function() {
8516
+
8517
+ if (this.playlistIndex === this.$playlist.length - 1) {
8518
+ // currently on the last track
8519
+ // wrap to top and play the forst track
8520
+ this.playlistIndex = 0;
8521
+ }
8522
+ else {
8523
+ this.playlistIndex++;
8524
+ }
8525
+ this.cueingPlaylistItem = true; // stopgap to prevent multiple firings
8526
+ this.cuePlaylistItem(this.playlistIndex);
8527
+ };
8528
+
8541
8529
  AblePlayer.prototype.handleRewind = function() {
8542
8530
 
8543
8531
  var targetTime;
@@ -8616,7 +8604,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8616
8604
  currentRate = this.getPlaybackRate();
8617
8605
  index = rates.indexOf(currentRate);
8618
8606
  if (index === -1) {
8619
-
8607
+
8620
8608
  }
8621
8609
  else {
8622
8610
  index += dir;
@@ -8763,6 +8751,9 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
8763
8751
  this.descOn = !this.descOn;
8764
8752
  this.prefDesc = + this.descOn; // convert boolean to integer
8765
8753
  this.updateCookie('prefDesc');
8754
+ if (!this.$descDiv.is(':hidden')) {
8755
+ this.$descDiv.hide();
8756
+ }
8766
8757
  this.refreshingDesc = true;
8767
8758
  this.initDescription();
8768
8759
  this.refreshControls('descriptions');
@@ -9511,15 +9502,15 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
9511
9502
  switch (error.name) {
9512
9503
  case 'InvalidTrackLanguageError':
9513
9504
  // no track was available with the specified language
9514
-
9505
+
9515
9506
  break;
9516
9507
  case 'InvalidTrackError':
9517
9508
  // no track was available with the specified language and kind
9518
-
9509
+
9519
9510
  break;
9520
9511
  default:
9521
9512
  // some other error occurred
9522
-
9513
+
9523
9514
  break;
9524
9515
  }
9525
9516
  });
@@ -10279,6 +10270,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
10279
10270
  if (!this.transcriptType) {
10280
10271
  // previously set transcriptType to null since there are no <track> elements
10281
10272
  // check again to see if captions have been collected from other sources (e.g., YouTube)
10273
+
10282
10274
  if (this.captions.length && (!(this.usingYouTubeCaptions || this.usingVimeoCaptions))) {
10283
10275
  // captions are possible! Use the default type (popup)
10284
10276
  // if other types ('external' and 'manual') were desired, transcriptType would not be null here
@@ -11136,19 +11128,14 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11136
11128
  (function ($) {
11137
11129
  // Media events
11138
11130
  AblePlayer.prototype.onMediaUpdateTime = function (duration, elapsed) {
11139
-
11140
11131
  // duration and elapsed are passed from callback functions of Vimeo API events
11141
11132
  // duration is expressed as sss.xxx
11142
11133
  // elapsed is expressed as sss.xxx
11143
11134
  var thisObj = this;
11144
11135
 
11145
11136
  this.getMediaTimes(duration,elapsed).then(function(mediaTimes) {
11146
- if (typeof duration === 'undefined') {
11147
- thisObj.duration = mediaTimes['duration'];
11148
- }
11149
- if (typeof elapsed === 'undefined') {
11150
- thisObj.elapsed = mediaTimes['elapsed'];
11151
- }
11137
+ thisObj.duration = mediaTimes['duration'];
11138
+ thisObj.elapsed = mediaTimes['elapsed'];
11152
11139
  if (thisObj.swappingSrc && (typeof thisObj.swapTime !== 'undefined')) {
11153
11140
  if (thisObj.swapTime === thisObj.elapsed) {
11154
11141
  // described version been swapped and media has scrubbed to time of previous version
@@ -11190,7 +11177,6 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11190
11177
  };
11191
11178
 
11192
11179
  AblePlayer.prototype.onMediaComplete = function () {
11193
-
11194
11180
  // if there's a playlist, advance to next item and start playing
11195
11181
  if (this.hasPlaylist && !this.cueingPlaylistItem) {
11196
11182
  if (this.playlistIndex === (this.$playlist.length - 1)) {
@@ -11200,6 +11186,10 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11200
11186
  this.cueingPlaylistItem = true; // stopgap to prevent multiple firings
11201
11187
  this.cuePlaylistItem(0);
11202
11188
  }
11189
+ else {
11190
+ this.playing = false;
11191
+ this.paused = true;
11192
+ }
11203
11193
  }
11204
11194
  else {
11205
11195
  // this is not the last track. Play the next one.
@@ -11231,9 +11221,9 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11231
11221
  this.playMedia();
11232
11222
  }
11233
11223
  this.swappingSrc = false; // swapping is finished
11234
- this.refreshControls('init');
11235
11224
  }
11236
11225
  }
11226
+ this.refreshControls('init');
11237
11227
  };
11238
11228
 
11239
11229
  // End Media events
@@ -11325,12 +11315,21 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11325
11315
  // 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?
11326
11316
  var whichButton = $(el).attr('class').split(' ')[0].substr(20);
11327
11317
  if (whichButton === 'play') {
11318
+ this.clickedPlay = true;
11328
11319
  this.handlePlay();
11329
11320
  }
11330
11321
  else if (whichButton === 'restart') {
11331
11322
  this.seekTrigger = 'restart';
11332
11323
  this.handleRestart();
11333
11324
  }
11325
+ else if (whichButton === 'previous') {
11326
+ this.seekTrigger = 'previous';
11327
+ this.handlePrevTrack();
11328
+ }
11329
+ else if (whichButton === 'next') {
11330
+ this.seekTrigger = 'next';
11331
+ this.handleNextTrack();
11332
+ }
11334
11333
  else if (whichButton === 'rewind') {
11335
11334
  this.seekTrigger = 'rewind';
11336
11335
  this.handleRewind();
@@ -11427,6 +11426,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11427
11426
  e.target.tagName === 'TEXTAREA' ||
11428
11427
  e.target.tagName === 'SELECT'
11429
11428
  )){
11429
+
11430
11430
  if (which === 27) { // escape
11431
11431
  this.closePopups();
11432
11432
  }
@@ -11482,6 +11482,16 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11482
11482
  this.handleRewind();
11483
11483
  }
11484
11484
  }
11485
+ else if (which === 98) { // b = back (previous track)
11486
+ if (this.usingModifierKeys(e)) {
11487
+ this.handlePrevTrack();
11488
+ }
11489
+ }
11490
+ else if (which === 110) { // n = next track
11491
+ if (this.usingModifierKeys(e)) {
11492
+ this.handleNextTrack();
11493
+ }
11494
+ }
11485
11495
  else if (which === 101) { // e = preferences
11486
11496
  if (this.usingModifierKeys(e)) {
11487
11497
  this.handlePrefsClick();
@@ -11514,6 +11524,8 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11514
11524
  // do something
11515
11525
  })
11516
11526
  .on('loadedmetadata',function() {
11527
+ // should be able to get duration now
11528
+ thisObj.duration = thisObj.media.duration;
11517
11529
  thisObj.onMediaNewSourceLoad();
11518
11530
  })
11519
11531
  .on('canplay',function() {
@@ -11642,23 +11654,23 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
11642
11654
  .on('volumechange',function() {
11643
11655
  thisObj.volume = thisObj.getVolume();
11644
11656
  if (thisObj.debug) {
11645
-
11657
+
11646
11658
  }
11647
11659
  })
11648
11660
  .on('error',function() {
11649
11661
  if (thisObj.debug) {
11650
11662
  switch (thisObj.media.error.code) {
11651
11663
  case 1:
11652
-
11664
+
11653
11665
  break;
11654
11666
  case 2:
11655
-
11667
+
11656
11668
  break;
11657
11669
  case 3:
11658
-
11670
+
11659
11671
  break;
11660
11672
  case 4:
11661
-
11673
+
11662
11674
  break;
11663
11675
  }
11664
11676
  }
@@ -12779,7 +12791,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
12779
12791
  this.signFile = this.$sources.first().attr('data-sign-src');
12780
12792
  if (this.signFile) {
12781
12793
  if (this.debug) {
12782
-
12794
+
12783
12795
  }
12784
12796
  this.hasSignLanguage = true;
12785
12797
  this.injectSignPlayerCode();
@@ -13891,7 +13903,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
13891
13903
  else {
13892
13904
  msg = lang + ' is not currently supported. Using default language (' + this.lang + ')';
13893
13905
  if (this.debug) {
13894
-
13906
+
13895
13907
  }
13896
13908
  }
13897
13909
  }
@@ -15507,7 +15519,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15507
15519
  deferred.resolve();
15508
15520
  })
15509
15521
  .fail(function(){
15510
-
15522
+
15511
15523
  });
15512
15524
  }
15513
15525
  else {
@@ -15669,4 +15681,4 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
15669
15681
  return false;
15670
15682
  };
15671
15683
 
15672
- })(jQuery);
15684
+ })(jQuery);