/* global Cookies */ import $ from 'jquery'; import AccessibleDialog from './dialog'; function addPreferenceFunctions(AblePlayer) { AblePlayer.prototype.setPrefs = function(preferences) { if ( typeof Cookies !== 'undefined' ) { Cookies.set('Able-Player', JSON.stringify(preferences), { expires: 90, sameSite: 'strict' }); } else { localStorage.setItem( 'Able-Player', JSON.stringify( preferences ) ); } }; AblePlayer.prototype.getPref = function() { var defaultPrefs = { preferences: {}, sign: {}, transcript: {}, voices: [] }; var preferences; try { if ( typeof Cookies !== 'undefined' ) { preferences = JSON.parse( Cookies.get('Able-Player') ); } else { preferences = JSON.parse( localStorage.getItem('Able-Player') ); } } catch (err) { // Original preferences can't be parsed; update to default this.setPrefs( defaultPrefs ); preferences = defaultPrefs; } return (preferences) ? preferences : defaultPrefs; }; AblePlayer.prototype.updatePreferences = function( setting ) { // useful for settings updated independently of Preferences dialog // e.g., prefAutoScrollTranscript, which is updated in control.js > handleTranscriptLockToggle() // setting is any supported preference name (e.g., "prefCaptions") // OR 'transcript' or 'sign' (not user-defined preferences, used to save position of draggable windows) var preferences, $window, windowPos, available, i, prefName, voiceLangFound, newVoice; preferences = this.getPref(); if (setting === 'transcript' || setting === 'sign') { if (setting === 'transcript') { $window = this.$transcriptArea; windowPos = $window.position(); if (typeof preferences.transcript === 'undefined') { preferences.transcript = {}; } preferences.transcript['position'] = $window.css('position'); // either 'relative' or 'absolute' preferences.transcript['zindex'] = $window.css('z-index'); preferences.transcript['top'] = windowPos.top; preferences.transcript['left'] = windowPos.left; preferences.transcript['width'] = $window.width(); preferences.transcript['height'] = $window.height(); } else if (setting === 'sign') { $window = this.$signWindow; windowPos = $window.position(); if (typeof preferences.sign === 'undefined') { preferences.sign = {}; } preferences.sign['position'] = $window.css('position'); // either 'relative' or 'absolute' preferences.sign['zindex'] = $window.css('z-index'); preferences.sign['top'] = windowPos.top; preferences.sign['left'] = windowPos.left; preferences.sign['width'] = $window.width(); preferences.sign['height'] = $window.height(); } } else if (setting === 'voice') { if (typeof preferences.voices === 'undefined') { preferences.voices = []; } // replace preferred voice for this lang in preferences.voices array, if one exists // otherwise, add it to the array voiceLangFound = false; for (var v=0; v < preferences.voices.length; v++) { if (preferences.voices[v].lang === this.prefDescVoiceLang) { voiceLangFound = true; preferences.voices[v].name = this.prefDescVoice; } } if (!voiceLangFound) { // no voice has been saved yet for this language. Add it to array. newVoice = {'name':this.prefDescVoice, 'lang':this.prefDescVoiceLang}; preferences.voices.push(newVoice); } } else { available = this.getAvailablePreferences(); // Rebuild preferences with current preferences values, // replacing the one value that's been changed for (i = 0; i < available.length; i++) { prefName = available[i]['name']; if (prefName == setting) { // this is the one that requires an update preferences.preferences[prefName] = this[prefName]; } } } // Save updated preferences this.setPrefs(preferences); }; AblePlayer.prototype.getPreferencesGroups = function() { // return array of groups in the order in which they will appear // in the Preferences popup menu // Human-readable label for each group is defined in translation table if (this.usingYouTubeCaptions) { // no transcript is possible return ['captions','descriptions','keyboard']; } else if (this.usingVimeoCaptions) { // users cannot control caption appearance // and no transcript is possible return ['descriptions','keyboard']; } else { return ['captions','descriptions','keyboard','transcript']; } } AblePlayer.prototype.getAvailablePreferences = function() { // Return the list of currently available preferences. // Preferences with no 'label' are set within player, not shown in Prefs dialog var prefs = []; // Modifier keys preferences prefs.push({ 'name': 'prefAltKey', // use alt key with shortcuts 'label': this.translate( 'prefAltKey', 'Alt' ), 'group': 'keyboard', 'default': 1 }); prefs.push({ 'name': 'prefCtrlKey', // use ctrl key with shortcuts 'label': this.translate( 'prefCtrlKey', 'Control' ), 'group': 'keyboard', 'default': 1 }); prefs.push({ 'name': 'prefShiftKey', 'label': this.translate( 'prefShiftKey', 'Shift' ), 'group': 'keyboard', 'default': 0 }); prefs.push({ 'name': 'prefNoKeyShortcuts', 'label': this.translate( 'prefNoKeyShortcuts', 'Disable Keyboard Shortcuts' ), 'group': 'keyboard', 'default': 0 }); // Transcript preferences prefs.push({ 'name': 'prefTranscript', // transcript default state 'label': null, 'group': 'transcript', 'default': 0 // off because turning it on has a certain WOW factor }); prefs.push({ 'name': 'prefHighlight', // highlight transcript as media plays 'label': this.translate( 'prefHighlight', 'Highlight transcript as media plays' ), 'group': 'transcript', 'default': 1 // on because many users can benefit }); prefs.push({ 'name': 'prefAutoScrollTranscript', 'label': null, 'group': 'transcript', 'default': 1 }); prefs.push({ 'name': 'prefTabbable', // tab-enable transcript 'label': this.translate( 'prefTabbable', 'Keyboard-enable transcript' ), 'group': 'transcript', 'default': 0 // off because if users don't need it, it impedes tabbing elsewhere on the page }); // Caption preferences prefs.push({ 'name': 'prefCaptions', // closed captions default state 'label': null, 'group': 'captions', 'default': this.defaultStateCaptions }); if (!this.usingYouTubeCaptions) { /* // not supported yet prefs.push({ 'name': 'prefCaptionsStyle', 'label': this.translate( 'prefCaptionsStyle', 'Style' ), 'group': 'captions', 'default': this.translate( 'captionsStylePopOn', 'Pop-on' ) }); */ // captions are always positioned above the player for audio if (this.mediaType === 'video') { prefs.push({ 'name': 'prefCaptionsPosition', 'label': this.translate( 'prefCaptionsPosition', 'Position' ), 'group': 'captions', 'default': this.defaultCaptionsPosition }); } prefs.push({ 'name': 'prefCaptionsFont', 'label': this.translate( 'prefCaptionsFont', 'Font' ), 'group': 'captions', 'default': 'sans-serif' }); } // This is the one option that is supported by YouTube IFrame API prefs.push({ 'name': 'prefCaptionsSize', 'label': this.translate( 'prefCaptionsSize', 'Font size' ), 'group': 'captions', 'default': '100%' }); if (!this.usingYouTubeCaptions) { prefs.push({ 'name': 'prefCaptionsColor', 'label': this.translate( 'prefCaptionsColor', 'Text Color' ), 'group': 'captions', 'default': 'white' }); prefs.push({ 'name': 'prefCaptionsBGColor', 'label': this.translate( 'prefCaptionsBGColor', 'Background' ), 'group': 'captions', 'default': 'black' }); prefs.push({ 'name': 'prefCaptionsOpacity', 'label': this.translate( 'prefCaptionsOpacity', 'Opacity' ), 'group': 'captions', 'default': '100%' }); prefs.push({ 'name': 'prefCaptionsSpeak', 'label': this.translate( 'prefVoicedCaptions', 'Spoken Captions' ), 'group': 'captions', 'default': 0 }); prefs.push({ 'name': 'prefCaptionsVoice', 'label': this.translate( 'prefDescVoice', 'Voice' ), 'group': 'captions', 'default': null // will be set later, in injectPrefsForm() }); prefs.push({ 'name': 'prefCaptionsPitch', 'label': this.translate( 'prefDescPitch', 'Pitch' ), 'group': 'captions', 'default': 1 // 0 to 2 }); prefs.push({ 'name': 'prefCaptionsRate', 'label': this.translate( 'prefCaptionRate', 'Spoken Caption Rate' ), 'group': 'captions', 'default': 1.2 // 0.1 to 10 (1 is normal speech; 2 is fast but decipherable; >2 is super fast) }); prefs.push({ 'name': 'prefCaptionsVolume', 'label': this.translate( 'volume', 'Volume' ), 'group': 'captions', 'default': 1 // 0 to 1 }); } if (this.mediaType === 'video') { // Description preferences prefs.push({ 'name': 'prefDesc', // audio description default state 'label': null, 'group': 'descriptions', 'default': this.defaultStateDescriptions }); prefs.push({ 'name': 'prefDescMethod', // audio description default format (if both 'video' and 'text' are available) 'label': null, 'group': 'descriptions', 'default': 'video' // video (an alternative described version) always wins }); prefs.push({ 'name': 'prefDescVoice', 'label': this.translate( 'prefDescVoice', 'Voice' ), 'group': 'descriptions', 'default': null // will be set later, in injectPrefsForm() }); prefs.push({ 'name': 'prefDescPitch', 'label': this.translate( 'prefDescPitch', 'Pitch' ), 'group': 'descriptions', 'default': 1 // 0 to 2 }); prefs.push({ 'name': 'prefDescRate', 'label': this.translate( 'prefDescRate', 'Spoken Description Rate' ), 'group': 'descriptions', 'default': 1 // 0.1 to 10 (1 is normal speech; 2 is fast but decipherable; >2 is super fast) }); prefs.push({ 'name': 'prefDescVolume', 'label': this.translate( 'volume', 'Volume' ), 'group': 'descriptions', 'default': 1 // 0 to 1 }); // Don't enable pause option if video described files in use. if ( this.descMethod !== 'video' ) { prefs.push({ 'name': 'prefDescPause', // automatically pause when closed description starts 'label': this.translate( 'prefDescPause', 'Automatically pause video when description starts' ), 'group': 'descriptions', 'default': this.defaultDescPause }); } prefs.push({ 'name': 'prefDescVisible', // visibly show closed description (if avilable and used) 'label': this.translate( 'prefDescVisible', 'Make description visible' ), 'group': 'descriptions', 'default': 0 // off as of 4.3.16, to avoid overloading the player with visible features }); } // Preferences without a category (not shown in Preferences dialogs) prefs.push({ 'name': 'prefSign', // open sign language window by default if avilable 'label': null, 'group': null, 'default': 0 // off because clicking an icon to see the sign window has a powerful impact }); return prefs; }; AblePlayer.prototype.loadCurrentPreferences = function () { // Load current/default preferences into the AblePlayer object. var available = this.getAvailablePreferences(); var preferences = this.getPref(); // Copy current preferences values into this object, and fill in any default values. for (var ii = 0; ii < available.length; ii++) { var prefName = available[ii]['name']; var defaultValue = available[ii]['default']; if (preferences.preferences[prefName] !== undefined) { this[prefName] = preferences.preferences[prefName]; } else { preferences.preferences[prefName] = defaultValue; this[prefName] = defaultValue; } } // Also load array of preferred voices from preferences if (typeof preferences.voices !== 'undefined') { this.prefVoices = preferences.voices; } this.setPrefs(preferences); }; AblePlayer.prototype.injectPrefsForm = function (form) { // Creates a preferences form and injects it. // form is one of the supported forms (groups) defined in getPreferencesGroups() var thisObj, available, $prefsDiv, formTitle, introText, $prefsIntro,$prefsIntroP2,p3Text,$prefsIntroP3,i, j, $fieldset, fieldsetClass, fieldsetId, $legend, legendId, thisPref, $thisDiv, thisClass, thisId, $thisLabel, $thisField, captionsOptions,options,$thisOption,optionValue,optionLang,optionText, changedPref,changedSpan,changedText, currentDescState, prefDescVoice, prefCaptionVoice, $kbHeading,$kbList, kbLabels,keys,kbListText,$kbListItem, dialog,$saveButton,$cancelButton,$buttonContainer; thisObj = this; available = this.getAvailablePreferences(); // outer container, will be assigned role="dialog" $prefsDiv = $('
',{ 'class': 'able-prefs-form ' }); var customClass = 'able-prefs-form-' + form; $prefsDiv.addClass(customClass); // add titles and intros if (form == 'captions') { formTitle = this.translate( 'prefTitleCaptions', 'Captions Preferences' ); } else if (form == 'descriptions') { formTitle = this.translate( 'prefTitleDescriptions', 'Audio Description Preferences' ); $prefsIntro = $('

',{ text: this.translate( 'prefIntroDescription1', 'This media player supports audio description in two ways: ' ) }); var $prefsIntroUL = $('