/** * CourseVideoSettingsView shows a sidebar containing course wide video settings which we show on Video Uploads page. */ define([ 'jquery', 'backbone', 'underscore', 'gettext', 'moment', 'common/js/components/utils/view_utils', 'edx-ui-toolkit/js/utils/html-utils', 'edx-ui-toolkit/js/utils/string-utils', 'text!templates/course-video-settings.underscore', 'text!templates/course-video-transcript-preferences.underscore', 'text!templates/course-video-transcript-provider-empty.underscore', 'text!templates/course-video-transcript-provider-selected.underscore', 'text!templates/transcript-organization-credentials.underscore', 'text!templates/course-video-settings-update-settings-footer.underscore', 'text!templates/course-video-settings-update-org-credentials-footer.underscore' ], function($, Backbone, _, gettext, moment, ViewUtils, HtmlUtils, StringUtils, TranscriptSettingsTemplate, TranscriptPreferencesTemplate, TranscriptProviderEmptyStateTemplate, TranscriptProviderSelectedStateTemplate, OrganizationCredentialsTemplate, UpdateSettingsFooterTemplate, OrganizationCredentialsFooterTemplate) { 'use strict'; var CourseVideoSettingsView, CIELO24 = 'Cielo24', THREE_PLAY_MEDIA = '3PlayMedia', INTERNAL_SERVER_ERROR_MESSAGE = gettext('An error has occurred. Wait a few minutes, and then try again.'); CourseVideoSettingsView = Backbone.View.extend({ el: 'div.video-transcript-settings-wrapper', events: { 'change .transcript-provider-group input': 'providerSelected', 'change #transcript-turnaround': 'turnaroundSelected', 'change #transcript-fidelity': 'fidelitySelected', 'change #video-source-language': 'videoSourceLanguageSelected', 'click .action-add-language': 'languageSelected', 'click .action-remove-language': 'languageRemoved', 'click .action-change-provider': 'renderOrganizationCredentials', 'click .action-update-org-credentials': 'updateOrganizationCredentials', 'click .action-update-course-video-settings': 'updateCourseVideoSettings', 'click .action-cancel-course-video-settings': 'discardChanges', 'click .action-close-course-video-settings': 'closeCourseVideoSettings' }, initialize: function(options) { var videoTranscriptSettings = options.videoTranscriptSettings; this.activeTranscriptionPlan = options.activeTranscriptPreferences; this.transcriptOrganizationCredentials = _.extend({}, options.transcriptOrganizationCredentials); this.availableTranscriptionPlans = videoTranscriptSettings.transcription_plans; this.transcriptHandlerUrl = videoTranscriptSettings.transcript_preferences_handler_url; this.transcriptCredentialsHandlerUrl = videoTranscriptSettings.transcript_credentials_handler_url; this.template = HtmlUtils.template(TranscriptSettingsTemplate); this.transcriptPreferencesTemplate = HtmlUtils.template(TranscriptPreferencesTemplate); this.organizationCredentialsTemplate = HtmlUtils.template(OrganizationCredentialsTemplate); this.organizationCredentialsFooterTemplate = HtmlUtils.template(OrganizationCredentialsFooterTemplate); this.updateSettingsFooterTemplate = HtmlUtils.template(UpdateSettingsFooterTemplate); this.transcriptProviderEmptyStateTemplate = HtmlUtils.template(TranscriptProviderEmptyStateTemplate); this.transcriptProviderSelectedStateTemplate = HtmlUtils.template(TranscriptProviderSelectedStateTemplate); this.setActiveTranscriptPlanData(); this.selectedLanguages = []; }, registerCloseClickHandler: function() { var self = this; // Preventing any parent handlers from being notified of the event. This is to stop from firing the document // level click handler to execute on course video settings pane click. self.$el.click(function(event) { event.stopPropagation(); }); // Click anywhere outside the course video settings pane would close the pane. $(document).click(function(event) { // If the target of the click isn't the container nor a descendant of the contain if (!self.$el.is(event.target) && self.$el.has(event.target).length === 0) { self.closeCourseVideoSettings(); } }); }, resetPlanData: function() { this.selectedProvider = ''; this.selectedTurnaroundPlan = ''; this.selectedFidelityPlan = ''; this.activeLanguages = []; this.selectedVideoSourceLanguage = ''; this.selectedLanguages = []; }, setActiveTranscriptPlanData: function() { if (this.activeTranscriptionPlan) { this.selectedProvider = this.activeTranscriptionPlan.provider; this.selectedFidelityPlan = this.activeTranscriptionPlan.cielo24_fidelity; this.selectedTurnaroundPlan = this.selectedProvider === CIELO24 ? this.activeTranscriptionPlan.cielo24_turnaround : this.activeTranscriptionPlan.three_play_turnaround; this.activeLanguages = this.activeTranscriptionPlan.preferred_languages; this.selectedVideoSourceLanguage = this.activeTranscriptionPlan.video_source_language; } else { this.resetPlanData(); } }, getTurnaroundPlan: function() { var turnaroundPlan = null; if (this.selectedProvider) { turnaroundPlan = this.availableTranscriptionPlans[this.selectedProvider].turnaround; } return turnaroundPlan; }, getFidelityPlan: function() { var fidelityPlan = null; if (this.selectedProvider === CIELO24) { fidelityPlan = this.availableTranscriptionPlans[this.selectedProvider].fidelity; } return fidelityPlan; }, getTargetLanguages: function() { var availableLanguages, selectedPlan = this.selectedProvider ? this.availableTranscriptionPlans[this.selectedProvider] : null; if (selectedPlan) { if (this.selectedProvider === CIELO24 && this.selectedFidelityPlan) { availableLanguages = selectedPlan.fidelity[this.selectedFidelityPlan].languages; // If fidelity is mechanical then target language would be same as source language. if (this.selectedFidelityPlan === 'MECHANICAL' && this.selectedVideoSourceLanguage) { availableLanguages = _.pick( availableLanguages, this.selectedVideoSourceLanguage ); } } else if (this.selectedProvider === THREE_PLAY_MEDIA) { availableLanguages = selectedPlan.languages; } } return availableLanguages; }, getSourceLanguages: function() { var sourceLanguages = []; if (this.selectedProvider === THREE_PLAY_MEDIA) { sourceLanguages = this.availableTranscriptionPlans[this.selectedProvider].translations; } else { sourceLanguages = this.getTargetLanguages(); } return sourceLanguages; }, fidelitySelected: function(event) { var $fidelityContainer = this.$el.find('.transcript-fidelity-wrapper'); this.selectedFidelityPlan = event.target.value; // Remove any error if present already. this.clearPreferenceErrorState($fidelityContainer); // Clear active and selected languages. this.selectedLanguages = this.activeLanguages = []; // Also clear selected language. this.selectedVideoSourceLanguage = ''; this.renderSourceLanguages(); this.renderTargetLanguages(); }, videoSourceLanguageSelected: function(event) { var $videoSourceLanguageContainer = this.$el.find('.video-source-language-wrapper'); this.selectedVideoSourceLanguage = event.target.value; // Remove any error if present already. this.clearPreferenceErrorState($videoSourceLanguageContainer); // Clear active and selected languages. this.selectedLanguages = this.activeLanguages = []; this.renderTargetLanguages(); }, turnaroundSelected: function(event) { var $turnaroundContainer = this.$el.find('.transcript-turnaround-wrapper'); this.selectedTurnaroundPlan = event.target.value; // Remove any error if present already. this.clearPreferenceErrorState($turnaroundContainer); }, providerSelected: function(event) { this.resetPlanData(); this.selectedProvider = event.target.value; // Re-render view this.reRenderView(); }, reRenderView: function() { var $courseVideoSettingsContentEl = this.$el.find('.course-video-settings-content'), dateModified = this.activeTranscriptionPlan ? moment.utc(this.activeTranscriptionPlan.modified).format('ll') : ''; if (!this.selectedProvider) { // Hide organization credentials and transcript preferences views $courseVideoSettingsContentEl.hide(); // Render footer HtmlUtils.setHtml( this.$el.find('.course-video-settings-footer'), this.updateSettingsFooterTemplate({ dateModified: dateModified }) ); return; } $courseVideoSettingsContentEl.show(); // If org provider specific credentials are present if (this.transcriptOrganizationCredentials[this.selectedProvider]) { this.renderTranscriptPreferences(); } else { this.renderOrganizationCredentials(); } }, languageSelected: function(event) { var $parentEl = $(event.target.parentElement).parent(), $languagesEl = this.$el.find('.transcript-languages-wrapper'), selectedLanguage = $parentEl.find('select').val(); // Remove any error if present already. this.clearPreferenceErrorState($languagesEl); // Only add if not in the list already. if (selectedLanguage && _.indexOf(this.selectedLanguages, selectedLanguage) === -1) { this.selectedLanguages.push(selectedLanguage); this.addLanguage(selectedLanguage); // Populate language menu with latest data. this.populateLanguageMenu(); } else { this.addErrorState($languagesEl); } }, languageRemoved: function(event) { var selectedLanguage = $(event.target).data('language-code'); $(event.target.parentElement).parent().remove(); // Remove language from selected languages. this.selectedLanguages = this.activeLanguages = _.without(this.selectedLanguages, selectedLanguage); // Populate menu again to reflect latest changes. this.populateLanguageMenu(); }, renderProviders: function(state) { var $transcriptProviderWrapperEl = this.$el.find('.transcript-provider-wrapper'); if (!state) { state = this.selectedProvider ? 'selected' : 'empty'; // eslint-disable-line no-param-reassign } // If no transcription plans are sentm return. if (!this.availableTranscriptionPlans) { return; } if (state === 'empty') { HtmlUtils.setHtml( $transcriptProviderWrapperEl, this.transcriptProviderEmptyStateTemplate({ providers: [ { key: 'none', value: '', name: gettext('None'), checked: this.selectedProvider === '' ? 'checked' : '' }, { key: CIELO24, value: CIELO24, name: this.availableTranscriptionPlans[CIELO24].display_name, checked: this.selectedProvider === CIELO24 ? 'checked' : '' }, { key: THREE_PLAY_MEDIA, value: THREE_PLAY_MEDIA, name: this.availableTranscriptionPlans[THREE_PLAY_MEDIA].display_name, checked: this.selectedProvider === THREE_PLAY_MEDIA ? 'checked' : '' } ] }) ); } else { HtmlUtils.setHtml( $transcriptProviderWrapperEl, this.transcriptProviderSelectedStateTemplate({ selectedProvider: this.availableTranscriptionPlans[this.selectedProvider].display_name }) ); this.renderTranscriptPreferences(); } }, renderTurnaround: function() { var self = this, turnaroundPlan = self.getTurnaroundPlan(), $turnaroundContainer = self.$el.find('.transcript-turnaround-wrapper'), $turnaround = self.$el.find('#transcript-turnaround'); // Clear error state if present any. this.clearPreferenceErrorState($turnaroundContainer); if (turnaroundPlan) { HtmlUtils.setHtml( $turnaround, HtmlUtils.HTML(new Option(gettext('Select turnaround'), '')) ); _.each(turnaroundPlan, function(value, key) { var option = new Option(value, key); if (self.selectedTurnaroundPlan === key) { option.selected = true; } HtmlUtils.append($turnaround, HtmlUtils.HTML(option)); }); $turnaroundContainer.show(); } else { $turnaroundContainer.hide(); } }, renderFidelity: function() { var self = this, fidelityPlan = self.getFidelityPlan(), $fidelityContainer = self.$el.find('.transcript-fidelity-wrapper'), $fidelity = self.$el.find('#transcript-fidelity'); // Clear error state if present any. this.clearPreferenceErrorState($fidelityContainer); // Fidelity dropdown if (fidelityPlan) { HtmlUtils.setHtml( $fidelity, HtmlUtils.HTML(new Option(gettext('Select fidelity'), '')) ); _.each(fidelityPlan, function(fidelityObject, key) { var option = new Option(fidelityObject.display_name, key); if (self.selectedFidelityPlan === key) { option.selected = true; } HtmlUtils.append($fidelity, HtmlUtils.HTML(option)); }); $fidelityContainer.show(); } else { $fidelityContainer.hide(); } }, renderTargetLanguages: function() { var self = this, // Merge active and selected languages, this handles the case when active languages are present and // user also has selected some languages but not saved, user changes organization credentials, // both active and selected languages should be rendered. selectedLanguages = _.union(self.activeLanguages, self.selectedLanguages), $languagesPreferenceContainer = self.$el.find('.transcript-languages-wrapper'), $languagesContainer = self.$el.find('.languages-container'); // Clear error state if present any. self.clearPreferenceErrorState($languagesPreferenceContainer); $languagesContainer.empty(); // Show language container if source language is selected. if (self.selectedVideoSourceLanguage) { _.each(selectedLanguages, function(language) { // Only add if not in the list already. if (_.indexOf(self.selectedLanguages, language) === -1) { self.selectedLanguages.push(language); } // Show active/ add language language container self.addLanguage(language); }); $languagesPreferenceContainer.show(); self.populateLanguageMenu(); } else { $languagesPreferenceContainer.hide(); } }, renderSourceLanguages: function() { var self = this, availableLanguages = self.getTargetLanguages(), availableTranslations = self.getSourceLanguages(), $videoSourceLanguageContainer = self.$el.find('.video-source-language-wrapper'), $languageMenuEl = self.$el.find('.video-source-language'), selectOptionEl = new Option(gettext('Select language'), ''); // Clear error state if present any. self.clearPreferenceErrorState($videoSourceLanguageContainer); if (!_.isEmpty(availableTranslations)) { $videoSourceLanguageContainer.show(); // We need to set id due to a11y aria-labelledby selectOptionEl.id = 'video-source-language-none'; HtmlUtils.setHtml( $languageMenuEl, HtmlUtils.HTML(selectOptionEl) ); _.each(availableTranslations, function(translatableLanguage, key) { var option = new Option(availableLanguages[key], key); if (self.selectedVideoSourceLanguage === key) { option.selected = true; } HtmlUtils.append( $languageMenuEl, HtmlUtils.HTML(option) ); }); } else { $videoSourceLanguageContainer.hide(); } }, populateLanguageMenu: function() { var availableLanguages = this.getTargetLanguages(), availableTranslations = this.availableTranscriptionPlans[THREE_PLAY_MEDIA].translations, $languageMenuEl = this.$el.find('.transcript-language-menu'), $languageMenuContainerEl = this.$el.find('.transcript-language-menu-container'), selectOptionEl = new Option(gettext('Select language'), ''); if (this.selectedProvider === THREE_PLAY_MEDIA) { // Pick out only those languages from plan laguages that also come from video source language. availableLanguages = _.pick( availableLanguages, availableTranslations[this.selectedVideoSourceLanguage] ); } // Omit out selected languages from selecting again. availableLanguages = _.omit(availableLanguages, this.selectedLanguages); // If no available language is left, then don't even show add language box. if (_.keys(availableLanguages).length) { $languageMenuContainerEl.show(); // We need to set id due to a11y aria-labelledby selectOptionEl.id = 'transcript-language-none'; HtmlUtils.setHtml( $languageMenuEl, HtmlUtils.HTML(selectOptionEl) ); _.each(availableLanguages, function(value, key) { HtmlUtils.append( $languageMenuEl, HtmlUtils.HTML(new Option(value, key)) ); }); } else { $languageMenuContainerEl.hide(); } }, addLanguage: function(language) { var $languagesContainer = this.$el.find('.languages-container'); HtmlUtils.append( $languagesContainer, HtmlUtils.joinHtml( HtmlUtils.HTML('