wai-website-theme 1.6 → 1.7

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml 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);