2d4a02ce95b223029b88bf0a86069f4922c35828
[moodle.git] / h5p / h5plib / v124 / joubel / editor / scripts / h5peditor-library-selector.js
1 /* global ns */
2 /**
3  * Construct a library selector.
4  *
5  * @param {Array} libraries
6  * @param {String} defaultLibrary
7  * @param {Object} defaultParams
8  * @returns {ns.LibrarySelector}
9  */
10 ns.LibrarySelector = function (libraries, defaultLibrary, defaultParams) {
11   var that = this;
13   this.libraries = libraries;
15   H5P.EventDispatcher.call(this);
17   try {
18     this.defaultParams = JSON.parse(defaultParams);
19     if (!(this.defaultParams instanceof Object)) {
20       throw true;
21     }
22   }
23   catch (event) {
24     // Content parameters are broken. Reset. (This allows for broken content to be reused without deleting it)
25     this.defaultParams = {};
26   }
28   this.defaultLibrary = this.currentLibrary = defaultLibrary;
29   this.defaultLibraryParameterized = defaultLibrary ? defaultLibrary.replace('.', '-').toLowerCase() : undefined;
31   //Add tutorial and example link:
32   this.$tutorialUrl = ns.$('<a class="h5p-tutorial-url" target="_blank">' + ns.t('core', 'tutorial') + '</a>').hide();
33   this.$exampleUrl = ns.$('<a class="h5p-example-url" target="_blank">' + ns.t('core', 'example') + '</a>').hide();
35   // Create confirm dialog
36   var changeLibraryDialog = new H5P.ConfirmationDialog({
37     headerText: H5PEditor.t('core', 'changeLibrary'),
38     dialogText: H5PEditor.t('core', 'confirmChangeLibrary')
39   }).appendTo(document.body);
41   if (H5PIntegration.hubIsEnabled) {
42     this.selector = new ns.SelectorHub(libraries, defaultLibrary, changeLibraryDialog);
43   }
44   else {
45     this.selector = new ns.SelectorLegacy(libraries, defaultLibrary, changeLibraryDialog);
46   }
48   this.$selector = ns.$(this.selector.getElement());
50   /**
51    * @private
52    * @param {object} library
53    */
54   var librarySelectHandler = function (library) {
55     that.currentLibrary = library.uberName;
56     that.loadSemantics(library.uberName, that.selector.getParams(), that.selector.getMetadata());
58     that.$tutorialUrl.attr('href', library.tutorialUrl ? library.tutorialUrl : '#').toggle(!!library.tutorialUrl);
59     that.$exampleUrl.attr('href', library.exampleUrl ? library.exampleUrl : '#').toggle(!!library.exampleUrl);
60   };
62   /**
63    * Event handler for loading a new library editor
64    * @private
65    */
66   var loadLibrary = function () {
67     that.trigger('editorload', that.selector.currentLibrary);
68     that.selector.getSelectedLibrary(librarySelectHandler);
69   };
71   /**
72    * Confirm replace if there is content selected
73    *
74    * @param {number} top Offset
75    * @param {function} next Next callback
76    */
77   this.confirmPasteError = function (message, top, next) {
78     // Confirm changing library
79     var confirmReplace = new H5P.ConfirmationDialog({
80       headerText: H5PEditor.t('core', 'pasteError'),
81       dialogText: message,
82       cancelText: ' ',
83       confirmText: H5PEditor.t('core', 'ok')
84     }).appendTo(document.body);
85     confirmReplace.on('confirmed', next);
86     confirmReplace.show(top);
87   };
89   // Change library on confirmation
90   changeLibraryDialog.on('confirmed', loadLibrary);
92   // Revert selector on cancel
93   changeLibraryDialog.on('canceled', function () {
94     that.selector.resetSelection(that.currentLibrary, that.defaultParams, that.form.metadata, true);
95   });
97   // First time a library is selected in the editor
98   this.selector.on('selected', loadLibrary);
100   this.selector.on('resize', function () {
101     that.trigger('resize');
102   });
104   this.on('select', loadLibrary);
105   H5P.externalDispatcher.on('datainclipboard', this.updateCopyPasteButtons.bind(this));
106   this.selector.on('paste', this.pasteContent.bind(this));
107 };
109 // Extends the event dispatcher
110 ns.LibrarySelector.prototype = Object.create(H5P.EventDispatcher.prototype);
111 ns.LibrarySelector.prototype.constructor = ns.LibrarySelector;
113 /**
114  * Sets the current library
115  *
116  * @param {string} library
117  */
118 ns.LibrarySelector.prototype.setLibrary = function (library) {
119   this.trigger('select');
120 };
122 /**
123  * Append the selector html to the given container.
124  *
125  * @param {jQuery} $element
126  * @returns {undefined}
127  */
128 ns.LibrarySelector.prototype.appendTo = function ($element) {
129   var self = this;
130   this.$parent = $element;
132   this.$selector.appendTo($element);
133   this.$tutorialUrl.appendTo($element);
134   this.$exampleUrl.appendTo($element);
136   if (window.localStorage) {
137     var $buttons = ns.$(ns.createCopyPasteButtons()).appendTo($element);
139     // Hide copy paste until library is selected:
140     $buttons.addClass('hidden');
141     self.on('editorloaded', function () {
142       $buttons.removeClass('hidden');
143     });
145     this.$copyButton = $buttons.find('.h5peditor-copy-button').click(function () {
146       H5P.clipboardify({
147         library: self.getCurrentLibrary(),
148         params: self.getParams(),
149         metadata: self.getMetadata()
150       });
151       ns.attachToastTo(
152         self.$copyButton.get(0),
153         H5PEditor.t('core', 'copiedToClipboard'), {
154           position: {
155             horizontal: 'center',
156             vertical: 'above',
157             noOverflowX: true
158           }
159         }
160       );
161     });
162     this.$pasteButton = $buttons.find('.h5peditor-paste-button')
163       .click(self.pasteContent.bind(this));
165     self.updateCopyPasteButtons();
166   }
167 };
169 /**
170  * Update state of copy and paste buttons dependent on what is currently in
171  * the clipboard
172  */
173 ns.LibrarySelector.prototype.updateCopyPasteButtons = function () {
174   if (!window.localStorage) {
175     return;
176   }
178   // Check if content type is supported here
179   const pasteCheck = ns.canPastePlus(H5P.getClipboard(), this.libraries);
180   const canPaste = pasteCheck.canPaste;
182   this.$copyButton
183     .prop('disabled', false)
184     .toggleClass('disabled', false);
186   this.$pasteButton
187     .text(ns.t('core', 'pasteAndReplaceButton'))
188     .attr('title', canPaste ? ns.t('core', 'pasteAndReplaceFromClipboard') : pasteCheck.description)
189     .toggleClass('disabled', !canPaste)
190     .prop('disabled', !canPaste);
192   this.selector.setCanPaste && this.selector.setCanPaste(canPaste, !canPaste ? pasteCheck.description : undefined);
193 };
195 /**
196  * Sets the current library
197  *
198  * @param {string} library
199  */
200 ns.LibrarySelector.prototype.pasteContent = function () {
201   var self = this;
202   var clipboard = H5P.getClipboard();
204   ns.confirmReplace(self.getCurrentLibrary(), self.$parent.offset().top, function () {
205     self.selector.resetSelection(clipboard.generic.library, clipboard.generic.params, clipboard.generic.metadata, false);
206     self.setLibrary();
207   });
208 };
210 /**
211  * Display loading message and load library semantics.
212  *
213  * @param {String} library
214  * @param {Object} params Pass in params to semantics
215  * @returns {unresolved}
216  */
217 ns.LibrarySelector.prototype.loadSemantics = function (library, params, metadata) {
218   var that = this;
220   if (this.form !== undefined) {
221     // Remove old form.
222     this.form.remove();
223   }
225   if (library === '-') {
226     // No library chosen.
227     this.$parent.attr('class', 'h5peditor');
228     return;
229   }
230   this.$parent.attr('class', 'h5peditor ' + library.split(' ')[0].toLowerCase().replace('.', '-') + '-editor');
232   // Display loading message
233   var $loading = ns.$('<div class="h5peditor-loading h5p-throbber">' + ns.t('core', 'loading') + '</div>').appendTo(this.$parent);
235   this.$selector.attr('disabled', true);
237   ns.resetLoadedLibraries();
238   ns.loadLibrary(library, function (semantics) {
239     if (!semantics) {
240       that.form = ns.$('<div/>', {
241         'class': 'h5p-errors',
242         text: H5PEditor.t('core', 'noSemantics'),
243         insertAfter: $loading
244       });
245     }
246     else {
247       var overrideParams = {};
248       if (params) {
249         overrideParams = params;
250         that.defaultParams = overrideParams;
251       }
252       else if (library === that.defaultLibrary || library === that.defaultLibraryParameterized) {
253         overrideParams = that.defaultParams;
254       }
256       if (!metadata) {
257         metadata = overrideParams.metadata;
258       }
259       const defaultLanguage = metadata && metadata.defaultLanguage
260         ? metadata.defaultLanguage
261         : null;
262       that.form = new ns.Form(
263         library,
264         ns.libraryCache[library].languages,
265         defaultLanguage
266       );
267       that.form.replace($loading);
268       that.form.currentLibrary = library;
269       that.form.processSemantics(semantics, overrideParams, metadata);
270       that.updateCopyPasteButtons();
271     }
273     that.$selector.attr('disabled', false);
274     $loading.remove();
275     that.trigger('editorloaded', library);
276   });
277 };
279 /**
280  * Returns currently selected library
281  *
282  * @returns {string} Currently selected library
283  */
284 ns.LibrarySelector.prototype.getCurrentLibrary = function () {
285   return this.currentLibrary;
286 };
288 /**
289  * Return params needed to start library.
290  */
291 ns.LibrarySelector.prototype.getParams = function () {
292   if (this.form === undefined) {
293     return;
294   }
296   // Only return if all fields has validated.
297   //var valid = true;
299   if (this.form.metadataForm.children !== undefined) {
300     for (var i = 0; i < this.form.metadataForm.children.length; i++) {
301       if (this.form.metadataForm.children[i].validate() === false) {
302         //valid = false;
303       }
304     }
305   }
307   if (this.form.children !== undefined) {
308     for (var i = 0; i < this.form.children.length; i++) {
309       if (this.form.children[i].validate() === false) {
310         //valid = false;
311       }
312     }
313   }
315   //return valid ? this.form.params : false;
316   return this.form.params; // TODO: Switch to the line above when we are able to tell the user where the validation fails
317 };
319 /**
320  * Get the metadata of the main form.
321  *
322  * @return {object} Metadata object.
323  */
324 ns.LibrarySelector.prototype.getMetadata = function () {
325   if (this.form === undefined) {
326     return;
327   }
329   return this.form.metadata;
330 };
332 /**
333  *
334  * @param content
335  * @param library
336  * @returns {H5PEditor.Presave} Result after processing library and content
337  */
338 ns.LibrarySelector.prototype.presave = function (content, library) {
339   return (new ns.Presave).process(library, content);
340 };