@@ -174,18 +174,34 @@ var AblePlayerInstances = [];
174174 this . useDescriptionsButton = true ;
175175 }
176176
177- // Silence audio description
178- // set to "false" if the sole purposes of the WebVTT descriptions file
179- // is to display description text visibly and to integrate it into the transcript
177+ // Control whether text descriptions are read aloud
178+ // set to "false" if the sole purpose of the WebVTT descriptions file
179+ // is to integrate text description into the transcript
180+ // set to "true" to write description text to a div
181+ // This variable does *not* control the method by which description is read.
182+ // For that, see below (this.descMethod)
180183 if ( $ ( media ) . data ( 'descriptions-audible' ) !== undefined && $ ( media ) . data ( 'descriptions-audible' ) === false ) {
181- this . exposeTextDescriptions = false ;
184+ this . readDescriptionsAloud = false ;
182185 }
183186 else if ( $ ( media ) . data ( 'description-audible' ) !== undefined && $ ( media ) . data ( 'description-audible' ) === false ) {
184187 // support both singular and plural spelling of attribute
185- this . exposeTextDescriptions = false ;
188+ this . readDescriptionsAloud = false ;
186189 }
187190 else {
188- this . exposeTextDescriptions = true ;
191+ this . readDescriptionsAloud = true ;
192+ }
193+
194+ // Method by which text descriptions are read
195+ // valid values of data-desc-reader are:
196+ // 'brower' (default) - text-based audio description is handled by the browser, if supported
197+ // 'screenreader' - text-based audio description is always handled by screen readers
198+ // The latter may be preferable by owners of websites in languages that are not well supported
199+ // by the Web Speech API
200+ if ( $ ( media ) . data ( 'desc-reader' ) == 'screenreader' ) {
201+ this . descReader = 'screenreader' ;
202+ }
203+ else {
204+ this . descReader = 'browser' ;
189205 }
190206
191207 // Headings
@@ -1709,7 +1725,7 @@ var AblePlayerInstances = [];
17091725 'default' : 0 // off because users who don't need it might find it distracting
17101726 } ) ;
17111727 prefs . push ( {
1712- 'name' : 'prefDescFormat ' , // audio description default format (if both 'video' and 'text' are available)
1728+ 'name' : 'prefDescMethod ' , // audio description default format (if both 'video' and 'text' are available)
17131729 'label' : null ,
17141730 'group' : 'descriptions' ,
17151731 'default' : 'video' // video (an alternative described version) always wins
@@ -2420,12 +2436,12 @@ var AblePlayerInstances = [];
24202436 if ( available [ i ] [ 'label' ] ) {
24212437 prefName = available [ i ] [ 'name' ] ;
24222438 prefId = this . mediaId + '_' + prefName ;
2423- if ( prefName == 'prefDescFormat ' ) {
2424- // As of v4.0.10, prefDescFormat is no longer a choice
2425- // this.prefDescFormat = $('input[name="' + prefName + '"]:checked').val();
2426- this . prefDescFormat = 'video' ;
2427- if ( this . prefDescFormat !== cookie . preferences [ 'prefDescFormat ' ] ) { // user's preference has changed
2428- cookie . preferences [ 'prefDescFormat ' ] = this . prefDescFormat ;
2439+ if ( prefName == 'prefDescMethod ' ) {
2440+ // As of v4.0.10, prefDescMethod is no longer a choice
2441+ // this.prefDescMethod = $('input[name="' + prefName + '"]:checked').val();
2442+ this . prefDescMethod = 'video' ;
2443+ if ( this . prefDescMethod !== cookie . preferences [ 'prefDescMethod ' ] ) { // user's preference has changed
2444+ cookie . preferences [ 'prefDescMethod ' ] = this . prefDescMethod ;
24292445 numChanges ++ ;
24302446 }
24312447 }
@@ -3391,9 +3407,6 @@ var AblePlayerInstances = [];
33913407
33923408 this . injectPlayerControlArea ( ) ; // this may need to be injected after captions???
33933409 this . $captionsContainer = this . $mediaContainer . wrap ( captionsContainer ) . parent ( ) ;
3394- if ( this . mediaType === 'video' ) {
3395- this . injectTextDescriptionArea ( ) ;
3396- }
33973410 this . injectAlert ( ) ;
33983411 this . injectPlaylist ( ) ;
33993412 } ;
@@ -3503,17 +3516,18 @@ var AblePlayerInstances = [];
35033516
35043517 AblePlayer . prototype . injectTextDescriptionArea = function ( ) {
35053518
3506- // create a div for exposing description
3507- // description will be exposed via role="alert" & announced by screen readers
3519+ // create a div for writing description text
35083520 this . $descDiv = $ ( '<div>' , {
35093521 'class' : 'able-descriptions'
35103522 } ) ;
3511- if ( this . exposeTextDescriptions ) {
3512- this . $descDiv . attr ( {
3513- 'aria-live' : 'assertive' ,
3514- 'aria-atomic' : 'true'
3515- } ) ;
3516- }
3523+ // Add ARIA so description will be announced by screen readers
3524+ // Later (in description.js > showDescription()),
3525+ // if browser supports Web Speech API and this.descMethod === 'browser'
3526+ // these attributes will be removed
3527+ this . $descDiv . attr ( {
3528+ 'aria-live' : 'assertive' ,
3529+ 'aria-atomic' : 'true'
3530+ } ) ;
35173531 // Start off with description hidden.
35183532 // It will be exposed conditionally within description.js > initDescription()
35193533 this . $descDiv . hide ( ) ;
@@ -7189,21 +7203,18 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
71897203
71907204 // The following variables are applicable to delivery of description:
71917205 // prefDesc == 1 if user wants description (i.e., Description button is on); else 0
7192- // prefDescFormat == either 'video' or 'text' (as of v4.0.10, prefDescFormat is always 'video')
7193- // useDescFormat is the format actually used ('video' or 'text'), regardless of user preference
7194- // prevDescFormat is the value of useDescFormat before user toggled off description
71957206 // prefDescPause == 1 to pause video when description starts; else 0
71967207 // prefDescVisible == 1 to visibly show text-based description area; else 0
7208+ // prefDescMethod == either 'video' or 'text' (as of v4.0.10, prefDescMethod is always 'video')
7209+ // descMethod is the format actually used ('video' or 'text'), regardless of user preference
71977210 // hasOpenDesc == true if a described version of video is available via data-desc-src attribute
71987211 // hasClosedDesc == true if a description text track is available
71997212 // descOn == true if description of either type is on
7200- // exposeTextDescriptions == true if text description is to be announced audibly; otherwise false
7213+ // readDescriptionsAloud == true if text description is to be announced audibly; otherwise false
7214+ // descReader == either 'browser' or 'screenreader'
72017215
72027216 var thisObj = this ;
7203- if ( this . refreshingDesc ) {
7204- this . prevDescFormat = this . useDescFormat ;
7205- }
7206- else {
7217+ if ( ! this . refreshingDesc ) {
72077218 // this is the initial build
72087219 // first, check to see if there's an open-described version of this video
72097220 // checks only the first source since if a described version is provided,
@@ -7224,35 +7235,47 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
72247235 }
72257236 }
72267237
7227- // Set this.useDescFormat based on media availability & user preferences
7228- if ( this . prefDesc ) {
7229- if ( this . hasOpenDesc && this . hasClosedDesc ) {
7230- // both formats are available. User gets their preference.
7231- this . useDescFormat = this . prefDescFormat ;
7232- this . descOn = true ;
7238+ // Set this.descMethod based on media availability & user preferences
7239+ if ( this . hasOpenDesc && this . hasClosedDesc ) {
7240+ // both formats are available. User gets their preference.
7241+ if ( this . prefDescMethod ) {
7242+ this . descMethod = this . prefDescMethod ;
72337243 }
7234- else if ( this . hasOpenDesc ) {
7235- this . useDescFormat = 'video' ;
7236- this . descOn = true ;
7237- }
7238- else if ( this . hasClosedDesc ) {
7239- this . useDescFormat = 'text' ;
7240- this . descOn = true ;
7244+ else {
7245+ // user has no preference. Video is default.
7246+ this . descMethod = 'video' ;
72417247 }
72427248 }
7249+ else if ( this . hasOpenDesc ) {
7250+ this . descMethod = 'video' ;
7251+ }
7252+ else if ( this . hasClosedDesc ) {
7253+ this . descMethod = 'text' ;
7254+ }
72437255 else {
7244- // prefDesc is not set for this user
7245- this . useDescFormat = null ;
7256+ // no description is available for this video
7257+ this . descMethod = null ;
7258+ }
7259+
7260+ // Set the default state of descriptions
7261+ if ( this . descMethod ) {
7262+ if ( this . prefDesc ) {
7263+ this . descOn = true ;
7264+ }
7265+ else {
7266+ this . descOn = false ;
7267+ }
7268+ }
7269+ else {
72467270 this . descOn = false ;
72477271 }
72487272
7249- if ( this . useDescFormat === 'video' ) {
7250- // If text descriptions are also available, silence them
7251- this . exposeTextDescriptions = false ;
7273+ if ( typeof this . $descDiv === 'undefined' && this . hasClosedDesc && this . descMethod === 'text' ) {
7274+ this . injectTextDescriptionArea ( ) ;
72527275 }
72537276
72547277 if ( this . descOn ) {
7255- if ( this . useDescFormat === 'video' ) {
7278+ if ( this . descMethod === 'video' ) {
72567279 if ( ! this . usingDescribedVersion ( ) ) {
72577280 // switched from non-described to described version
72587281 this . swapDescription ( ) ;
@@ -7261,30 +7284,35 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
72617284 if ( this . hasClosedDesc ) {
72627285 if ( this . prefDescVisible ) {
72637286 // make description text visible
7264- // New in v4.0.10: Do this regardless of useDescFormat
7265- this . $descDiv . show ( ) ;
7266- this . $descDiv . removeClass ( 'able-clipped' ) ;
7287+ if ( typeof this . $descDiv !== 'undefined' ) {
7288+ this . $descDiv . show ( ) ;
7289+ this . $descDiv . removeClass ( 'able-clipped' ) ;
7290+ }
72677291 }
72687292 else {
72697293 // keep it visible to screen readers, but hide it visibly
7270- this . $descDiv . addClass ( 'able-clipped' ) ;
7294+ if ( typeof this . $descDiv !== 'undefined' ) {
7295+ this . $descDiv . addClass ( 'able-clipped' ) ;
7296+ }
72717297 }
72727298 if ( ! this . swappingSrc ) {
72737299 this . showDescription ( this . elapsed ) ;
72747300 }
72757301 }
72767302 }
72777303 else { // description is off.
7278- if ( this . prevDescFormat === 'video' ) { // user has turned off described version of video
7304+ if ( this . descMethod === 'video' ) { // user has turned off described version of video
72797305 if ( this . usingDescribedVersion ( ) ) {
72807306 // user was using the described verion. Swap for non-described version
72817307 this . swapDescription ( ) ;
72827308 }
72837309 }
7284- else if ( this . prevDescFormat === 'text' ) { // user has turned off text description
7310+ else if ( this . descMethod === 'text' ) { // user has turned off text description
72857311 // hide description div from everyone, including screen reader users
7286- this . $descDiv . hide ( ) ;
7287- this . $descDiv . removeClass ( 'able-clipped' ) ;
7312+ if ( typeof this . $descDiv !== 'undefined' ) {
7313+ this . $descDiv . hide ( ) ;
7314+ this . $descDiv . removeClass ( 'able-clipped' ) ;
7315+ }
72887316 }
72897317 }
72907318 this . refreshingDesc = false ;
@@ -7515,10 +7543,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
75157543
75167544 AblePlayer . prototype . showDescription = function ( now ) {
75177545
7518- // there's a lot of redundancy between this function and showCaptions
7519- // Trying to combine them ended up in a mess though. Keeping as is for now.
7520-
7521- if ( ! this . exposeTextDescriptions || this . swappingSrc || ! this . descOn ) {
7546+ if ( ! this . hasClosedDesc || this . swappingSrc || ! this . descOn || this . descMethod === 'video' ) {
75227547 return ;
75237548 }
75247549
@@ -7558,7 +7583,11 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
75587583 // temporarily remove aria-live from $status in order to prevent description from being interrupted
75597584 this . $status . removeAttr ( 'aria-live' ) ;
75607585 descText = flattenComponentForDescription ( cues [ thisDescription ] . components ) ;
7561- if ( window . speechSynthesis ) {
7586+ if ( this . descReader === 'screenreader' ) {
7587+ // load the new description into the container div for screen readers to read
7588+ this . $descDiv . html ( descText ) ;
7589+ }
7590+ else if ( window . speechSynthesis ) {
75627591 // browser supports speech synthsis
75637592 this . announceDescriptionText ( 'description' , descText ) ;
75647593 if ( this . prefDescVisible ) {
@@ -7572,7 +7601,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
75727601 // load the new description into the container div for screen readers to read
75737602 this . $descDiv . html ( descText ) ;
75747603 }
7575- if ( this . prefDescPause && this . exposeTextDescriptions ) {
7604+ if ( this . prefDescPause && this . descMethod === 'text' ) {
75767605 this . pauseMedia ( ) ;
75777606 this . pausedForDescription = true ;
75787607 }
@@ -7690,7 +7719,7 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
76907719
76917720 if ( context === 'description' ) {
76927721 if ( thisObj . prefDescPause ) {
7693- if ( thisObj . pausedForDescription && thisObj . exposeTextDescriptions ) {
7722+ if ( thisObj . pausedForDescription ) {
76947723 thisObj . playMedia ( ) ;
76957724 this . pausedForDescription = false ;
76967725 }
@@ -8047,7 +8076,8 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
80478076 }
80488077 }
80498078 else if ( this . player === 'youtube' ) {
8050- // Youtube supports varying playback rates per video. Only expose controls if more than one playback rate is available.
8079+ // Youtube supports varying playback rates per video.
8080+ // Only expose controls if more than one playback rate is available.
80518081 if ( this . youTubePlayer . getAvailablePlaybackRates ( ) . length > 1 ) {
80528082 return true ;
80538083 }
@@ -8988,8 +9018,12 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
89889018 this . descOn = ! this . descOn ;
89899019 this . prefDesc = + this . descOn ; // convert boolean to integer
89909020 this . updateCookie ( 'prefDesc' ) ;
8991- if ( ! this . $descDiv . is ( ':hidden' ) ) {
8992- this . $descDiv . hide ( ) ;
9021+ if ( typeof this . $descDiv !== 'undefined' ) {
9022+ if ( ! this . $descDiv . is ( ':hidden' ) ) {
9023+ this . $descDiv . hide ( ) ;
9024+ }
9025+ // NOTE: now showing $descDiv here if previously hidden
9026+ // that's handled elsewhere, dependent on whether there's text to show
89939027 }
89949028 this . refreshingDesc = true ;
89959029 this . initDescription ( ) ;
@@ -9235,8 +9269,10 @@ if (thisObj.useTtml && (trackSrc.endsWith('.xml') || trackText.startsWith('<?xml
92359269 $el . width ( '100%' ) ;
92369270 }
92379271 var newHeight = $ ( window ) . height ( ) - this . $playerDiv . height ( ) ;
9238- if ( ! this . $descDiv . is ( ':hidden' ) ) {
9239- newHeight -= this . $descDiv . height ( ) ;
9272+ if ( typeof this . $descDiv !== 'undefined' ) {
9273+ if ( ! this . $descDiv . is ( ':hidden' ) ) {
9274+ newHeight -= this . $descDiv . height ( ) ;
9275+ }
92409276 }
92419277 }
92429278 else {
0 commit comments