AutorÃa | Ultima modificación | Ver Log |
/* global ns *//*** Construct a form from library semantics.*/ns.Form = function (library, startLanguages, defaultLanguage) {var self = this;this.params = {};this.passReadies = false;this.commonFields = {};this.$form = ns.$('' +'<div class="h5peditor-form">' +'<div class="tree"></div>' +'<div class="common collapsed hidden">' +'<div class="fields">' +'<p class="desc">' +ns.t('core', 'commonFieldsDescription') +'</p>' +'<div class="h5peditor-language-switcher">' +'<label class="language-label" for="h5peditor-language-switcher">' + ns.t('core', 'language') + ':</label>' +'<select id="h5peditor-language-switcher">' +'<option value="-">' + ns.t('core', 'noLanguagesSupported') + '</option>' +'</select>' +'</div>' +'<div class="h5peditor-language-notice">' +'<div class="first"></div>' +'<div class="last"></div>' +'</div>' +'</div>' +'</div>' +'</div>');this.$common = this.$form.find('.common > .fields');if (ns.FullscreenBar !== undefined) {// Exception from rulesif (library.indexOf('H5P.CoursePresentation') === -1 &&library.indexOf('H5P.BranchingScenario') === -1 &&library.indexOf('H5P.InteractiveVideo') === -1) {ns.FullscreenBar(this.$form, library);}}// Add title expand/collapse buttonself.$commonButton = ns.$('<div/>', {'class': 'h5peditor-label','aria-expanded': 'false',title: ns.t('core', 'expandCollapse'),role: 'button',tabIndex: 0,html: '<span class="icon"></span>' + ns.t('core', 'commonFields'),on: {click: function () {toggleCommonFields();},keypress: function (event) {if ((event.charCode || event.keyCode) === 32) {toggleCommonFields();event.preventDefault();}}},prependTo: this.$common.parent()});// Alternate background colorsthis.zebra = "odd";// Locate the language switcher DOM elementconst $switcher = this.$form.find('.h5peditor-language-switcher select');const $notice = this.$form.find('.h5peditor-language-notice');const loadedLibs = [];const languages = {};ns.defaultLanguage = ns.contentLanguage;if (defaultLanguage) {ns.defaultLanguage = defaultLanguage;}/*** Toggle common fields group visibility*/const toggleCommonFields = function () {const expandedValue = self.$common.parent().hasClass('collapsed')? 'true' : 'false';self.$commonButton.attr('aria-expanded', expandedValue);self.$common.parent().toggleClass('collapsed');};/*** Create options DOM elements** @private* @return {string}*/const createOptions = function () {let options = '';for (let code in languages) {let label = ns.supportedLanguages[code] ? ns.supportedLanguages[code] : code.toLocaleUpperCase();options += '<option value="' + code + '"' + (code === ns.defaultLanguage ? ' selected' : '') + '>' + label + '</option>';}return options;};/*** Figure out if all loaded libraries supports the chosen language code** @private* @param {string} code* @return {boolean}*/const isSupportedByAll = function (code) {return (languages[code].length === loadedLibs.length);};/*** This function does something different than the other functions.** @private* @param {string} lang Global value not used to avoid it changing while loading*/const updateCommonFields = function (lang) {const libs = languages[lang];for (let lib in ns.libraryCache) {// Update common fieldsif (ns.renderableCommonFields[lib] && ns.renderableCommonFields[lib].fields) {for (let j = 0; j < ns.renderableCommonFields[lib].fields.length; j++) {const field = ns.renderableCommonFields[lib].fields[j];// Determine translation to useconst translation = ns.libraryCache[lib].translation[lang];if (field.instance === undefined || translation === undefined) {continue; // Skip}// Find the correct translation for the fieldconst fieldTranslation = findFieldDefaultTranslation(field.field, ns.libraryCache[lib].semantics, translation);// Extract the default values from the translationconst defaultValue = getDefaultValue(fieldTranslation, field.field);// Update the widgetfield.instance.forceValue(defaultValue);}}if (ns.libraryCache[lib].translation[lang] !== undefined) {// Update semantics, so that the next time something is inserted it will get the same languagens.updateCommonFieldsDefault(ns.libraryCache[lib].semantics, ns.libraryCache[lib].translation[lang]);}}};/*** Recursivly search for the field's translations** @private* @param {Object} field The field we're looking for* @param {Array} semantics The fields tree to search amongst* @param {Array} translation The translation tree to search and return from* @return {Object} The translation if found*/const findFieldDefaultTranslation = function (field, semantics, translation) {for (let i = 0; i < semantics.length; i++) {if (semantics[i] === field) {return translation[i];}if (semantics[i].fields !== undefined && semantics[i].fields.length &&translation[i].fields !== undefined && translation[i].fields.length) {const found1 = findFieldDefaultTranslation(field, semantics[i].fields, translation[i].fields);if (found1 !== undefined) {return found1;}}if (semantics[i].field !== undefined && translation[i].field !== undefined) {const found2 = findFieldDefaultTranslation(field, [semantics[i].field], [translation[i].field]);if (found2 !== undefined) {return found2;}}}};/*** Recursivly format a default value for a field.** @private* @param {Object} translation The translation field to extract the default values from* @param {Object} field Needed for field naming* @return {Object} The default value*/const getDefaultValue = function (translation, field) {if (translation.default !== undefined) {return translation.default;}if (translation.fields !== undefined && translation.fields.length) {if (translation.fields.length === 1) {return getDefaultValue(translation.fields[0], field.fields[0]);}const values = {};for (let i = 0; i < translation.fields.length; i++) {values[field.fields[i].name] = getDefaultValue(translation.fields[i], field.fields[i]);}return values;}if (translation.field !== undefined) {return getDefaultValue(translation.field, field.field);}};/*** Prepares and loads all the missing translations from the server.** @param {string} lang Global value not used to avoid it changing while loading* @param {function} done Callback*/const loadTranslations = function (lang, done) {// Figure out what we actually need to loadconst loadLibs = [];for (let li in ns.libraryCache) {if (ns.libraryCache[li] === 0 || ns.libraryCache[li].translation[lang] === undefined) {loadLibs.push(li);}}if (loadLibs.length) {ns.$.post(ns.getAjaxUrl('translations', { language: lang }),{ libraries: loadLibs },function (res) {for (let lib in res.data) {ns.libraryCache[lib].translation[lang] = JSON.parse(res.data[lib]).semantics;}done();});}else {done(); // Continue without loading anything}}/*** Add new languages for content type.** @param {string} lib uberName* @param {Array} langs*/self.addLanguages = function (lib, langs) {// Update language countersfor (let i = 0; i < langs.length; i++) {const code = langs[i];if (languages[code] === undefined) {languages[code] = [lib];}else {languages[code].push(lib);}}loadedLibs.push(lib);// Update$switcher.html(createOptions());};/*** Remove languages for content type.** @param {string} lib uberName* @param {Array} langs*/self.removeLanguages = function (lib, langs) {// Update language countersfor (let i = 0; i < langs.length; i++) {const code = langs[i];if (languages[code] !== undefined) {if (languages[code].length === 1) {delete languages[code];}else {languages[code].splice(languages[code].indexOf(lib), 1);}}}loadedLibs.splice(loadedLibs.indexOf(lib), 1);// Update$switcher.html(createOptions());};// Handle switching language and loading new translations$switcher.change(function (e) {// Create confirmation dialogconst confirmDialog = new H5P.ConfirmationDialog({headerText: ns.t('core', 'changeLanguage', {':language': (ns.supportedLanguages[this.value] ? ns.supportedLanguages[this.value] : this.value.toLocaleUpperCase())}),dialogText: ns.t('core', 'thisWillPotentially'),}).appendTo(document.body);confirmDialog.on('confirmed', function () {const lang = ns.defaultLanguage = $switcher.val();const humanLang = (ns.supportedLanguages[lang] ? ns.supportedLanguages[lang] : lang.toLocaleUpperCase());// Update chosen default language for main content and sub-contentself.metadata.defaultLanguage = lang;self.params = self.setSubContentDefaultLanguage(self.params, lang);// Figure out if all libraries were supportedif (!isSupportedByAll(lang)) {// Show a warning message$notice.children('.first').html(ns.t('core', 'notAllTextsChanged', {':language': humanLang}));$notice.children('.last').html(ns.t('core', 'contributeTranslations', {':language': humanLang, ':url': 'https://h5p.org/contributing#translating'}));$notice.addClass('show');}else {// Hide a warning message$notice.removeClass('show');}$switcher.prop('disabled', 'disabled');loadTranslations(lang, function () {// Do the actualy update of the field valuesupdateCommonFields(lang);$switcher.prop('disabled', false);});});confirmDialog.on('canceled', function () {$switcher.val(ns.defaultLanguage);});// ShowconfirmDialog.show($switcher.offset().top);});// Add initial langauges for content typeself.addLanguages(library, startLanguages);};/*** Recursively traverse params and sets default language for each sub-content** @param {Object|Array} params Parameters* @param {string} lang Default language that will be set** @return {Object|Array} Parameters with default language set for sub-content*/ns.Form.prototype.setSubContentDefaultLanguage = function (params, lang) {if (!params) {return params;}const self = this;if (Array.isArray(params)) {for (let i; i < params.length; i++) {params[i] = self.setSubContentDefaultLanguage(params[i], lang);}}else if (typeof params === 'object') {if (params.metadata) {params.metadata.defaultLanguage = lang;}for (let parameter in params) {if (!params.hasOwnProperty(parameter)) {continue;}params[parameter] = this.setSubContentDefaultLanguage(params[parameter],lang);}}return params;};/*** Replace the given element with our form.** @param {jQuery} $element* @returns {undefined}*/ns.Form.prototype.replace = function ($element) {$element.replaceWith(this.$form);this.offset = this.$form.offset();// Prevent inputs and selects in an h5peditor form from submitting the main// framework form.this.$form.on('keydown', 'input,select', function (event) {if (event.keyCode === 13) {// Prevent enter key from submitting form.return false;}});};/*** Remove the current form.*/ns.Form.prototype.remove = function () {ns.removeChildren(this.metadataForm.children);ns.removeChildren(this.children);ns.renderableCommonFields = {}; // Reset all common fieldsthis.$form.remove();};/*** Wrapper for processing the semantics.** @param {Array} semantics* @param {Object} defaultParams* @returns {undefined}*/ns.Form.prototype.processSemantics = function (semantics, defaultParams, metadata) {this.metadata = (metadata ? metadata : defaultParams.metadata || {});// Set language initially usedif (!this.metadata.defaultLanguage) {this.metadata.defaultLanguage = ns.defaultLanguage;}if (ns.enableMetadata(this.currentLibrary)) {this.metadataForm = new ns.MetadataForm(this, this.metadata, this.$form.children('.tree'), true);}else {this.metadataForm = H5PEditor.MetadataForm.createLegacyForm(this.metadata, this.$form.children('.tree'));// This fixes CSS overrides done by some old custom editorsswitch (this.currentLibrary.split(' ')[0]) {case 'H5P.InteractiveVideo':case 'H5P.DragQuestion':case 'H5P.ImageHotspotQuestion':this.metadataForm.getExtraTitleField().$item.css('padding', '20px 20px 0 20px');break;case 'H5P.CoursePresentation':this.metadataForm.getExtraTitleField().$item.css('padding-bottom', '1em');break;}}// Overriding this.params with {} will lead to old content not being editable for nowthis.params = (defaultParams.params ? defaultParams.params : defaultParams);// Create real childrenns.processSemanticsChunk(semantics, this.params, this.$form.children('.tree'), this);};/*** Collect functions to execute once the tree is complete.** @param {function} ready* @returns {undefined}*/ns.Form.prototype.ready = function (ready) {this.readies.push(ready);};