Linux moodl-moodle-3s8bw1nuh5yqd9-5b875fdd66-8hs4m 4.4.0-186-generic #216-Ubuntu SMP Wed Jul 1 05:34:05 UTC 2020 x86_64
Apache/2.4.41 (Ubuntu)
: 10.39.0.36 | : 10.36.0.0
Cant Read [ /etc/named.conf ]
7.4.3
www-data
www.github.com/MadExploits
Terminal
AUTO ROOT
Adminer
Backdoor Destroyer
Linux Exploit
Lock Shell
Lock File
Create User
CREATE RDP
PHP Mailer
BACKCONNECT
UNLOCK SHELL
HASH IDENTIFIER
CPANEL RESET
CREATE WP USER
README
+ Create Folder
+ Create File
/
var /
moodledata /
filedir /
03 /
f8 /
[ HOME SHELL ]
Name
Size
Permission
Action
03f8beb15ab97bb506a8263e118a36...
68.34
KB
-rw-rw-rw-
Delete
Unzip
Zip
${this.title}
Close
Code Editor : 03f8beb15ab97bb506a8263e118a3626405b3438
/*global H5P,ns*/ var H5PEditor = H5PEditor || {}; /** * Create a field for the form. * * @param {mixed} parent * @param {Object} field * @param {mixed} params * @param {function} setValue * @returns {H5PEditor.Text} */ H5PEditor.CoursePresentation = function (parent, field, params, setValue) { var that = this; H5P.DragNBar.FormManager.call(this, parent, { doneButtonLabel: H5PEditor.t('H5PEditor.CoursePresentation', 'done'), deleteButtonLabel: H5PEditor.t('H5PEditor.CoursePresentation', 'remove'), expandBreadcrumbButtonLabel: H5PEditor.t('H5PEditor.CoursePresentation', 'expandBreadcrumbButtonLabel'), collapseBreadcrumbButtonLabel: H5PEditor.t('H5PEditor.CoursePresentation', 'collapseBreadcrumbButtonLabel') }, 'coursepresentation'); if (params === undefined) { params = { slides: [{ elements: [], keywords: [] }] }; setValue(field, params); } this.parent = parent; this.field = field; this.params = params; // Elements holds a mix of forms and params, not element instances this.elements = []; this.slideRatio = 1.9753; this.passReadies = true; parent.ready(function () { that.passReadies = false; // Active surface mode var activeSurfaceCheckbox = H5PEditor.findField('override/activeSurface', parent); activeSurfaceCheckbox.on('checked', function () { // Make note of current height var oldHeight = parseFloat(window.getComputedStyle(that.cp.$current[0]).height); // Enable adjustments that.cp.$container.addClass('h5p-active-surface'); // Remove navigation that.cp.$progressbar.remove(); // Find change in % var newHeight = parseFloat(window.getComputedStyle(that.cp.$current[0]).height); var change = (newHeight - oldHeight) / newHeight; // Account for the progress bar that was removed that.slideRatio = H5PEditor.CoursePresentation.RATIO_SURFACE; // Update elements that.updateElementSizes(1 - change); }); }); if (H5PEditor.InteractiveVideo !== undefined) { // Disable IV's guided tour within CP H5PEditor.InteractiveVideo.disableGuidedTour(); } // Update paste button H5P.externalDispatcher.on('datainclipboard', function (event) { if (!that.libraries) { return; } var canPaste = !event.data.reset; if (canPaste) { // Check if content type is supported here canPaste = that.canPaste(H5P.getClipboard()); } that.dnb.setCanPaste(canPaste); }); }; H5PEditor.CoursePresentation.prototype = Object.create(H5P.DragNBar.FormManager.prototype); H5PEditor.CoursePresentation.prototype.constructor = H5PEditor.CoursePresentation; /** * Must be changed if the semantics for the elements changes. * @type {string} */ H5PEditor.CoursePresentation.clipboardKey = 'H5PEditor.CoursePresentation'; /** * Will change the size of all elements using the given ratio. * * @param {number} heightRatio */ H5PEditor.CoursePresentation.prototype.updateElementSizes = function (heightRatio) { var $slides = this.cp.$slidesWrapper.children(); // Go through all slides for (var i = 0; i < this.params.slides.length; i++) { var slide = this.params.slides[i]; var $slideElements = $slides.eq(i).children(); for (var j = 0; j < slide.elements.length; j++) { var element = slide.elements[j]; // Update params element.height *= heightRatio; element.y *= heightRatio; // Update visuals if possible $slideElements.eq(j).css({ height: element.height + '%', top: element.y + '%' }); } } }; /** * Add an element to the current slide and params. * * @param {string|object} library Content type or parameters * @param {object} [options] Override the default options * @returns {object} */ H5PEditor.CoursePresentation.prototype.addElement = function (library, options) { options = options || {}; var elementParams; if (!(library instanceof String || typeof library === 'string')) { elementParams = library; } if (!elementParams) { // Create default start parameters elementParams = { x: 30, y: 30, width: 40, height: 40 }; if (library === 'GoToSlide') { elementParams.goToSlide = 1; } else { elementParams.action = (options.action ? options.action : { library: library, params: {} }); elementParams.action.subContentId = H5P.createUUID(); var libraryName = library.split(' ')[0]; switch (libraryName) { case 'H5P.Audio': elementParams.width = 2.577632696; elementParams.height = 5.091753604; elementParams.action.params.fitToWrapper = true; break; case 'H5P.DragQuestion': elementParams.width = 50; elementParams.height = 50; break; case 'H5P.Video': elementParams.width = 50; elementParams.height = 50.5553; break; case 'H5P.InteractiveVideo': elementParams.width = 50; elementParams.height = 64.5536; break; case 'H5P.AudioRecorder': elementParams.y = 11; elementParams.width = 41.89; elementParams.height = 78; elementParams.backgroundOpacity = 100; break; } } if (options.width && options.height && !options.displayAsButton) { // Use specified size elementParams.width = options.width; elementParams.height = options.height * this.slideRatio; } if (options.displayAsButton) { elementParams.displayAsButton = true; } } if (options.pasted) { elementParams.pasted = true; } var slideIndex = this.cp.$current.index(); var slideParams = this.params.slides[slideIndex]; if (slideParams.elements === undefined) { // No previous elements slideParams.elements = [elementParams]; } else { var containerStyle = window.getComputedStyle(this.dnb.$container[0]); var containerWidth = parseFloat(containerStyle.width); var containerHeight = parseFloat(containerStyle.height); // Make sure we don't overlap another element var pToPx = containerWidth / 100; var pos = { x: elementParams.x * pToPx, y: (elementParams.y * pToPx) / this.slideRatio }; this.dnb.avoidOverlapping(pos, { width: (elementParams.width / 100) * containerWidth, height: (elementParams.height / 100) * containerHeight, }); elementParams.x = pos.x / pToPx; elementParams.y = (pos.y / pToPx) * this.slideRatio; // Add as last element slideParams.elements.push(elementParams); } this.cp.$boxWrapper.add(this.cp.$boxWrapper.find('.h5p-presentation-wrapper:first')).css('overflow', 'visible'); const element = this.cp.children[slideIndex].addChild(elementParams); return this.cp.attachElement(elementParams, element.instance, this.cp.$current, slideIndex); }; /** * Append field to wrapper. * * @param {type} $wrapper * @returns {undefined} */ H5PEditor.CoursePresentation.prototype.appendTo = function ($wrapper) { var that = this; this.$item = H5PEditor.$(this.createHtml()).appendTo($wrapper); this.$editor = this.$item.children('.editor'); this.$errors = this.$item.children('.h5p-errors'); // Create new presentation. var presentationParams = (this.parent instanceof ns.Library ? this.parent.params.params : this.parent.params); if (presentationParams && presentationParams.override && presentationParams.override.activeSurface === true) { this.slideRatio = H5PEditor.CoursePresentation.RATIO_SURFACE; } this.cp = new H5P.CoursePresentation(presentationParams, H5PEditor.contentId, {cpEditor: this}); this.cp.attach(this.$editor); if (this.cp.$wrapper.is(':visible')) { this.cp.trigger('resize'); } var $settingsWrapper = H5PEditor.$('<div>', { 'class': 'h5p-settings-wrapper hidden', appendTo: that.cp.$boxWrapper.children('.h5p-presentation-wrapper') }); // Add drag and drop menu bar. that.initializeDNB(); // Find BG selector fields and init slide selector var globalBackgroundField = H5PEditor.CoursePresentation.findField('globalBackgroundSelector', this.field.fields); var slideFields = H5PEditor.CoursePresentation.findField('slides', this.field.fields); this.backgroundSelector = new H5PEditor.CoursePresentation.SlideSelector(that, that.cp.$slidesWrapper, globalBackgroundField, slideFields, that.params) .appendTo($settingsWrapper); // Add and bind slide controls. var slideControls = { $add: H5PEditor.$('<a href="#" aria-label="' + H5PEditor.t('H5PEditor.CoursePresentation', 'newSlide') + '" class="h5p-slidecontrols-button h5p-slidecontrols-button-add"></a>'), $clone: H5PEditor.$('<a href="#" aria-label="' + H5PEditor.t('H5PEditor.CoursePresentation', 'cloneSlide') + '" class="h5p-clone-slide h5p-slidecontrols-button h5p-slidecontrols-button-clone"></a>'), $background: H5PEditor.$('<a href="#" aria-label="' + H5PEditor.t('H5PEditor.CoursePresentation', 'backgroundSlide') + '" class="h5p-slidecontrols-button h5p-slidecontrols-button-background"></a>'), $sortLeft: H5PEditor.$('<a href="#" aria-label="' + H5PEditor.t('H5PEditor.CoursePresentation', 'sortSlide', {':dir': 'left'}) + '" class="h5p-slidecontrols-button h5p-slidecontrols-button-sort-left"></a>'), $sortRight: H5PEditor.$('<a href="#" aria-label="' + H5PEditor.t('H5PEditor.CoursePresentation', 'sortSlide', {':dir': 'right'}) + '" class="h5p-slidecontrols-button h5p-slidecontrols-button-sort-right"></a>'), $delete: H5PEditor.$('<a href="#" aria-label="' + H5PEditor.t('H5PEditor.CoursePresentation', 'removeSlide') + '" class="h5p-slidecontrols-button h5p-slidecontrols-button-delete"></a>') }; this.slideControls = slideControls; H5PEditor.$('<div class="h5p-slidecontrols">').append([ slideControls.$add, slideControls.$clone, slideControls.$background, slideControls.$sortLeft, slideControls.$sortRight, slideControls.$delete ]).appendTo(this.cp.$wrapper) .children('a:first') .click(function () { that.addSlide(); that.updateSlidesSidebar(); return false; }) .next() .click(function () { var newSlide = H5P.cloneObject(that.params.slides[that.cp.$current.index()], true); // Set new subContentId for cloned contents that.resetSubContentId(newSlide.elements); newSlide.keywords = []; that.addSlide(newSlide); H5P.ContinuousText.Engine.run(that); that.updateSlidesSidebar(); return false; }) .next() .click(function () { that.backgroundSelector.toggleOpen(); H5PEditor.$(this).toggleClass('active'); return false; }) .next() .click(function () { that.sortSlide(that.cp.$current.prev(), -1); return false; }) .next() .click(function () { that.sortSlide(that.cp.$current.next(), 1); return false; }) .next() .click(function () { that.removeSlide(); return false; }); if (this.cp.activeSurface) { // Enable adjustments this.cp.$container.addClass('h5p-active-surface'); // Remove navigation this.cp.$progressbar.remove(); } // Relay window resize to CP view H5P.$window.on('resize', function () { that.cp.trigger('resize'); }); this.updateSlidesSidebar(); }; /** * Recursively reset all subContentIds. * * @param {object} params Parameters to parse. */ H5PEditor.CoursePresentation.prototype.resetSubContentId = function (params) { const that = this; if (Array.isArray(params)) { params.forEach(function (param) { that.resetSubContentId(param); }); } else if (typeof params === 'object') { if (params.library && params.subContentId) { params.subContentId = H5P.createUUID(); } for (param in params) { that.resetSubContentId(params[param]); } } }; /** * Add Drag and Drop button group. * * @param {H5P.Library} library Library for which a button will be added. * @param {object} options Options. */ H5PEditor.CoursePresentation.prototype.addDNBButton = function (library, options) { var that = this; options = options || {}; var id = library.name.split('.')[1].toLowerCase(); return { id: options.id || id, title: (options.title === undefined) ? library.title : options.title, createElement: function () { // Mind the functions's context return that.addElement(library.uberName, H5P.jQuery.extend(true, {}, options)); } }; }; /** * Add Drag and Drop button group. * * @param {H5P.Library} library Library for which a button will be added. * @param {object} groupData Data for the group. * @return {object} Button group. */ H5PEditor.CoursePresentation.prototype.addDNBButtonGroup = function (library, groupData) { var that = this; var id = library.name.split('.')[1].toLowerCase(); const buttonGroup = { id: id, title: groupData.dropdown.title || library.title, titleGroup: groupData.dropdown.titleGroup, type: 'group', buttons: [] }; // Add buttons to button group groupData.buttons.forEach(function (button) { const options = { id: button.id, title: button.title, width: button.width, height: button.height, action: { library: library.uberName, params: button.params || {} } }; buttonGroup.buttons.push(that.addDNBButton(library, options)); }); return buttonGroup; }; H5PEditor.CoursePresentation.prototype.setContainerEm = function (containerEm) { this.containerEm = containerEm; if (this.dnb !== undefined && this.dnb.dnr !== undefined) { this.dnb.dnr.setContainerEm(this.containerEm); } }; /** * Initialize the drag and drop menu bar. * * @returns {undefined} */ H5PEditor.CoursePresentation.prototype.initializeDNB = function () { var that = this; this.$bar = H5PEditor.$('<div class="h5p-dragnbar">' + H5PEditor.t('H5PEditor.CoursePresentation', 'loading') + '</div>').insertBefore(this.cp.$boxWrapper); var slides = H5PEditor.CoursePresentation.findField('slides', this.field.fields); var elementFields = H5PEditor.CoursePresentation.findField('elements', slides.field.fields).field.fields; var action = H5PEditor.CoursePresentation.findField('action', elementFields); const shapeButtonBase = { title: '', width: 14.09, // 100 units height: 14.09 }; const shapeButtonBase1D = { params: { line: { borderWidth: 1, borderStyle: 'solid', borderColor: '#000' } } }; const shapeButtonBase2D = { params: { shape: { fillColor: '#fff', borderWidth: 0, borderStyle: 'solid', borderColor: '#000', } } }; // Ideally, this would not be built here const dropdownMenus = []; dropdownMenus['shape'] = { dropdown: { id: 'shape' }, buttons: [ H5P.jQuery.extend(true, {}, shapeButtonBase, shapeButtonBase2D, { id: 'shape-rectangle', params: { type: 'rectangle', shape: { borderRadius: 0 } } }), H5P.jQuery.extend(true, {}, shapeButtonBase, shapeButtonBase2D, { id: 'shape-circle', params: { type: 'circle' } }), H5P.jQuery.extend(true, {}, shapeButtonBase, shapeButtonBase1D, { id: 'shape-horizontal-line', params: { type: 'horizontal-line' } }), H5P.jQuery.extend(true, {}, shapeButtonBase, shapeButtonBase1D, { id: 'shape-vertical-line', params: { type: 'vertical-line' } }) ] }; H5PEditor.LibraryListCache.getLibraries(action.options, function (libraries) { that.libraries = libraries; var buttons = []; for (var i = 0; i < libraries.length; i++) { if (libraries[i].restricted !== true) { // Insert button or buttongroup const libraryId = libraries[i].name.split('.')[1].toLowerCase(); if (dropdownMenus[libraryId] === undefined) { buttons.push(that.addDNBButton(libraries[i])); } else { buttons.push(that.addDNBButtonGroup(libraries[i], dropdownMenus[libraryId])); } } } // Add go to slide button var goToSlide = H5PEditor.CoursePresentation.findField('goToSlide', elementFields); if (goToSlide) { buttons.splice(5, 0, { id: 'gotoslide', title: H5PEditor.t('H5PEditor.CoursePresentation', 'goToSlide'), createElement: function () { return that.addElement('GoToSlide'); } }); } that.dnb = new H5P.DragNBar(buttons, that.cp.$current, that.$editor, {$blurHandlers: that.cp.$boxWrapper, libraries: libraries}); that.$dnbContainer = that.cp.$current; that.dnb.dnr.snap = 10; that.dnb.dnr.setContainerEm(that.containerEm); // Register all attached elements with dnb that.elements.forEach(function (slide, slideIndex) { slide.forEach(function (element, elementIndex) { var elementParams = that.params.slides[slideIndex].elements[elementIndex]; that.addToDragNBar(element, elementParams); }); }); var reflowLoop; var reflowInterval = 250; var reflow = function () { H5P.ContinuousText.Engine.run(that); reflowLoop = setTimeout(reflow, reflowInterval); }; // Resizing listener that.dnb.dnr.on('startResizing', function () { var elementParams = that.params.slides[that.cp.$current.index()].elements[that.dnb.$element.index()]; // Check for continuous text if (elementParams.action && elementParams.action.library.split(' ')[0] === 'H5P.ContinuousText') { reflowLoop = setTimeout(reflow, reflowInterval); } }); // Resizing has stopped that.dnb.dnr.on('stoppedResizing', function () { var elementParams = that.params.slides[that.cp.$current.index()].elements[that.dnb.$element.index()]; // Store new element position elementParams.width = that.dnb.$element.width() / (that.cp.$current.innerWidth() / 100); elementParams.height = that.dnb.$element.height() / (that.cp.$current.innerHeight() / 100); elementParams.y = ((parseFloat(that.dnb.$element.css('top')) / that.cp.$current.innerHeight()) * 100); elementParams.x = ((parseFloat(that.dnb.$element.css('left')) / that.cp.$current.innerWidth()) * 100); // Stop reflow loop and run one last reflow if (elementParams.action && elementParams.action.library.split(' ')[0] === 'H5P.ContinuousText') { clearTimeout(reflowLoop); H5P.ContinuousText.Engine.run(that); } // Trigger element resize var elementInstance = that.cp.elementInstances[that.cp.$current.index()][that.dnb.$element.index()]; H5P.trigger(elementInstance, 'resize'); }); // Update params when the element is dropped. that.dnb.stopMovingCallback = function (x, y) { var params = that.params.slides[that.cp.$current.index()].elements[that.dnb.$element.index()]; params.x = x; params.y = y; }; // Update params when the element is moved instead, to prevent timing issues. that.dnb.dnd.moveCallback = function (x, y) { var params = that.params.slides[that.cp.$current.index()].elements[that.dnb.$element.index()]; params.x = x; params.y = y; that.dnb.updateCoordinates(); }; // Edit element when it is dropped. that.dnb.dnd.releaseCallback = function () { var params = that.params.slides[that.cp.$current.index()].elements[that.dnb.$element.index()]; var element = that.elements[that.cp.$current.index()][that.dnb.$element.index()]; if (that.dnb.newElement) { that.cp.$boxWrapper.add(that.cp.$boxWrapper.find('.h5p-presentation-wrapper:first')).css('overflow', ''); if (params.action !== undefined && H5P.libraryFromString(params.action.library).machineName === 'H5P.ContinuousText') { H5P.ContinuousText.Engine.run(that); if (!that.params.ct) { // No CT text but there could be elements var CTs = that.getCTs(false, true); if (CTs.length === 1) { // First element, open form that.showElementForm(element, that.dnb.$element, params); } } } else { that.showElementForm(element, that.dnb.$element, params); } } }; /** * @private * @param {string} lib uber name * @returns {boolean} */ that.supported = function (lib) { for (var i = 0; i < libraries.length; i++) { if (libraries[i].restricted !== true && libraries[i].uberName === lib) { return true; // Library is supported and allowed } } return false; }; that.dnb.on('paste', function (event) { var pasted = event.data; var options = { width: pasted.width, height: pasted.height, pasted: true }; if (pasted.from === H5PEditor.CoursePresentation.clipboardKey) { // Pasted content comes from the same version of CP if (!pasted.generic) { // Non generic part, must be content like gotoslide or similar that.dnb.focus(that.addElement(pasted.specific, options)); } else if (that.supported(pasted.generic.library)) { // Special case for ETA - can't copy the index, then export won't include // the original, since they will have the same index. if (pasted.generic.library.split(' ')[0] === 'H5P.ExportableTextArea') { delete pasted.generic.params.index; } // Has generic part and the generic libray is supported that.dnb.focus(that.addElement(pasted.specific, options)); } else { that.showConfirmationDialog({ headerText: H5PEditor.t('core', 'pasteError'), dialogText: H5PEditor.t('H5P.DragNBar', 'unableToPaste'), confirmText: H5PEditor.t('H5PEditor.CoursePresentation', 'ok') }); } } else if (pasted.generic) { if (that.supported(pasted.generic.library)) { // Supported library from another content type) if (pasted.specific.displayType === 'button') { // Make sure buttons from IV still are buttons. options.displayAsButton = true; } options.action = pasted.generic; that.dnb.focus(that.addElement(pasted.generic.library, options)); } else { that.showConfirmationDialog({ headerText: H5PEditor.t('core', 'pasteError'), dialogText: H5PEditor.t('H5P.DragNBar', 'unableToPaste'), confirmText: H5PEditor.t('H5PEditor.CoursePresentation', 'ok') }); } } }); that.dnb.attach(that.$bar); // Set paste button that.dnb.setCanPaste(that.canPaste(H5P.getClipboard())); // Bind keyword interactions. that.initKeywordInteractions(); // Trigger event that.trigger('librariesReady'); }); }; /** * Check if the clipboard can be pasted into CP. * * @param {Object} [clipboard] Clipboard data. * @return {boolean} True, if clipboard can be pasted. */ H5PEditor.CoursePresentation.prototype.canPaste = function (clipboard) { if (clipboard) { if (clipboard.from === H5PEditor.CoursePresentation.clipboardKey && (!clipboard.generic || this.supported(clipboard.generic.library))) { // Content comes from the same version of CP // Non generic part = must be content like gotoslide or similar return true; } else if (clipboard.generic && this.supported(clipboard.generic.library)) { // Supported library from another content type return true; } } return false; }; /** * Create HTML for the field. */ H5PEditor.CoursePresentation.prototype.createHtml = function () { return H5PEditor.createFieldMarkup(this.field, '<div class="editor"></div>'); }; /** * Validate the current field. */ H5PEditor.CoursePresentation.prototype.validate = function () { // Validate all form elements var valid = true; var firstCT = true; for (var i = 0; i < this.elements.length; i++) { if (!this.elements[i]) { continue; } for (var j = 0; j < this.elements[i].length; j++) { // We must make sure form values are stored if the dialog was never closed var elementParams = this.params.slides[i].elements[j]; var isCT = (elementParams.action !== undefined && elementParams.action.library.split(' ')[0] === 'H5P.ContinuousText'); if (isCT && !firstCT) { continue; // Only need to process the first CT } // Validate element form for (var k = 0; k < this.elements[i][j].children.length; k++) { if (this.elements[i][j].children[k].validate() === false && valid) { valid = false; } } if (isCT) { if (!this.params.ct) { // Store complete text in CT param this.params.ct = elementParams.action.params.text; } firstCT = false; } } } valid &= this.backgroundSelector.validate(); // Distribute CT text across elements H5P.ContinuousText.Engine.run(this); this.trigger('validate'); return valid; }; /** * Remove this item. */ H5PEditor.CoursePresentation.prototype.remove = function () { this.trigger('remove'); if (this.dnb !== undefined) { this.dnb.remove(); } this.$item.remove(); this.elements.forEach(function (slides) { slides.forEach(function (interaction) { H5PEditor.removeChildren(interaction.children); }); }); }; /** * Initialize keyword interactions. * * @returns {undefined} Nothing */ H5PEditor.CoursePresentation.prototype.initKeywordInteractions = function () { var that = this; // Add our own menu to the drag and drop menu bar. that.$keywordsDNB = H5PEditor.$( '<ul class="h5p-dragnbar-ul h5p-dragnbar-left">' + '<li class="h5p-slides-menu">' + '<div title="' + H5PEditor.t('H5PEditor.CoursePresentation', 'slides') + '" class="h5p-dragnbar-keywords" role="button" tabindex="0">' + '<span>' + H5PEditor.t('H5PEditor.CoursePresentation', 'slides') + '</span>' + '</div>' + '<div class="h5p-keywords-dropdown">' + '<label class="h5p-keywords-enable">' + '<input type="checkbox"/>' + H5PEditor.t('H5PEditor.CoursePresentation', 'showTitles') + '</label>' + '<label class="h5p-keywords-always"><input type="checkbox"/>' + H5PEditor.t('H5PEditor.CoursePresentation', 'alwaysShow') + '</label>' + '<label class="h5p-keywords-hide"><input type="checkbox"/>' + H5PEditor.t('H5PEditor.CoursePresentation', 'autoHide') + '</label>' + '<label class="h5p-keywords-opacity"><input type="text"/> % ' + H5PEditor.t('H5PEditor.CoursePresentation', 'opacity') + '</label>' + '<div class="h5peditor-button h5peditor-button-textual importance-low" role="button" tabindex="0" aria-disabled="false">' + H5PEditor.t('H5PEditor.CoursePresentation', 'ok') + '</div>' + '</div>' + '</li>' + '</ul>').prependTo(this.$bar); that.initKeywordMenu(); // Make keywords drop down menu come alive var $slidesMenu = this.$bar.find('.h5p-dragnbar-keywords'); var $dropdown = this.$bar.find('.h5p-keywords-dropdown'); var preventClose = false; var closeDropdown = function () { if (preventClose) { preventClose = false; } else { $slidesMenu.removeClass('h5p-open'); $dropdown.removeClass('h5p-open'); that.cp.$container.off('click', closeDropdown); } }; $dropdown.find('.h5peditor-button').click(closeDropdown); // Make sure keywords settings and button is hidden on load if disabled if (!this.params.keywordListEnabled) { $dropdown.children().first().siblings().hide().last().show(); that.cp.$keywordsButton.hide(); } // Open dropdown when clicking the dropdown button $slidesMenu.click(function () { if (!$dropdown.hasClass('h5p-open')) { that.cp.$container.on('click', closeDropdown); $slidesMenu.addClass('h5p-open'); $dropdown.addClass('h5p-open'); preventClose = true; } }); // Prevent closing when clicking on the dropdown dialog it self $dropdown.click(function () { preventClose = true; }); // Enable keywords list var $enableKeywords = this.$bar.find('.h5p-keywords-enable input').change(function () { that.params.keywordListEnabled = $enableKeywords.is(':checked'); if (that.params.keywordListEnabled) { if (that.params.keywordListAlwaysShow) { that.cp.$keywordsWrapper.show().add(that.cp.$keywordsButton).addClass('h5p-open'); that.cp.$keywordsButton.hide(); } else { that.cp.$keywordsWrapper.add(that.cp.$keywordsButton).show(); } ns.$(this).parent().siblings().show(); } else { that.cp.$keywordsWrapper.add(that.cp.$keywordsButton).hide(); ns.$(this).parent().siblings().hide().last().show(); } }); // Always show keywords list var $alwaysKeywords = this.$bar.find('.h5p-keywords-always input').change(function () { var checked = $alwaysKeywords.is(':checked'); that.params.keywordListAlwaysShow = checked; if (checked) { // Disable auto hide that.params.keywordListAutoHide = false; that.$bar.find('.h5p-keywords-hide input') .attr('checked', false) .attr("disabled", true) .parent().addClass('h5p-disabled'); } else { that.$bar.find('.h5p-keywords-hide input') .attr("disabled", false) .parent().removeClass('h5p-disabled'); } if (!that.params.keywordListEnabled) { that.cp.hideKeywords(); that.cp.$keywordsButton.hide(); return; } else if (!that.params.keywordListAlwaysShow) { that.cp.$keywordsButton.show(); } if (that.params.keywordListAlwaysShow) { that.cp.$keywordsButton.hide(); that.cp.showKeywords(); } else if (that.params.keywordListEnabled) { that.cp.$keywordsButton.show(); that.cp.showKeywords(); } }); // Auto hide keywords list var $hideKeywords = this.$bar.find('.h5p-keywords-hide input').change(function () { that.params.keywordListAutoHide = $hideKeywords.is(':checked'); }); // Opacity for keywords list var $opacityKeywords = this.$bar.find('.h5p-keywords-opacity input').change(function () { var opacity = parseInt($opacityKeywords.val()); if (isNaN(opacity)) { opacity = 90; } if (opacity > 100) { opacity = 100; } if (opacity < 0) { opacity = 0; } that.params.keywordListOpacity = opacity; that.cp.setKeywordsOpacity(opacity); }); /** * Help set default values if undefined. * * @private * @param {String} option * @param {*} defaultValue */ var checkDefault = function (option, defaultValue) { if (that.params[option] === undefined) { that.params[option] = defaultValue; } }; // Set defaults if undefined checkDefault('keywordListEnabled', true); checkDefault('keywordListAlwaysShow', false); checkDefault('keywordListAutoHide', false); checkDefault('keywordListOpacity', 90); // Update HTML $enableKeywords.attr('checked', that.params.keywordListEnabled); $alwaysKeywords.attr('checked', that.params.keywordListAlwaysShow); $hideKeywords.attr('checked', that.params.keywordListAutoHide); $opacityKeywords.val(that.params.keywordListOpacity); }; /** * Initiates the keyword menu */ H5PEditor.CoursePresentation.prototype.initKeywordMenu = function () { var that = this; // Keyword events var keywordClick = function (event) { // Convert keywords into text areas when clicking. if (that.editKeyword(H5PEditor.$(this)) !== false) { event.stopPropagation(); H5PEditor.$(event.target).parent().addClass('h5p-editing'); } }; // Make existing keywords editable this.cp.$keywords.find('.h5p-keyword-title').click(keywordClick); }; /** * Adds slide after current slide. * * @param {object} slideParams * @returns {undefined} Nothing */ H5PEditor.CoursePresentation.prototype.addSlide = function (slideParams) { var that = this; if (slideParams === undefined) { // Set new slide params slideParams = { elements: [], keywords: [] }; } var index = this.cp.$current.index() + 1; this.params.slides.splice(index, 0, slideParams); this.elements.splice(index, 0, []); this.cp.elementInstances.splice(index, 0, []); this.cp.elementsAttached.splice(index, 0, []); const slide = this.cp.addChild(slideParams, index); // Add slide with elements slide.getElement().insertAfter(this.cp.$current); that.trigger('addedSlide', index); slide.appendElements(); this.cp.updateKeywordMenuFromSlides(); this.initKeywordMenu(); // Update progressbar this.updateNavigationLine(index); // Switch to the new slide. this.cp.nextSlide(); }; H5PEditor.CoursePresentation.prototype.updateNavigationLine = function (index) { var that = this; // Update slides with solutions. var hasSolutionArray = []; this.cp.slides.forEach(function (instanceArray, slideNumber) { var isTaskWithSolution = false; if (that.cp.elementInstances[slideNumber] !== undefined && that.cp.elementInstances[slideNumber].length) { that.cp.elementInstances[slideNumber].forEach(function (elementInstance) { if (that.cp.checkForSolutions(elementInstance)) { isTaskWithSolution = true; } }); } if (isTaskWithSolution) { hasSolutionArray.push([[isTaskWithSolution]]); } else { hasSolutionArray.push([]); } }); // Update progressbar and footer this.cp.navigationLine.initProgressbar(hasSolutionArray); this.cp.navigationLine.updateProgressBar(index); this.cp.navigationLine.updateFooter(index); }; /** * Remove the current slide * * @returns {Boolean} Indicates success */ H5PEditor.CoursePresentation.prototype.removeSlide = function () { var index = this.cp.$current.index(); var $remove = this.cp.$current.add(this.cp.$currentKeyword); var isRemovingDnbContainer = this.cp.$current.index() === this.$dnbContainer.index(); const confirmationDialog = this.showConfirmationDialog({ headerText: H5PEditor.t('H5PEditor.CoursePresentation', 'confirmDeleteSlide'), cancelText: H5PEditor.t('H5PEditor.CoursePresentation', 'cancel'), confirmText: H5PEditor.t('H5PEditor.CoursePresentation', 'ok') }); confirmationDialog.on('canceled', () => { return; }); confirmationDialog.on('confirmed', () => { // Remove elements from slide var slideKids = this.elements[index]; if (slideKids !== undefined) { for (var i = 0; i < slideKids.length; i++) { this.removeElement(slideKids[i], slideKids[i].$wrapper, this.cp.elementInstances[index][i].libraryInfo && this.cp.elementInstances[index][i].libraryInfo.machineName === 'H5P.ContinuousText'); } } this.elements.splice(index, 1); // Change slide var move = this.cp.previousSlide() ? -1 : (this.cp.nextSlide(true) ? 0 : undefined); // Replace existing DnB container used for calculating dimensions of elements if (isRemovingDnbContainer) { // Set new dnb container this.$dnbContainer = this.cp.$current; this.dnb.setContainer(this.$dnbContainer); } if (move === undefined) { return false; // No next or previous slide } // ExportableTextArea needs to know about the deletion: H5P.ExportableTextArea.CPInterface.onDeleteSlide(index); // Update presentation params. this.params.slides.splice(index, 1); // Update the list of element instances this.cp.elementInstances.splice(index, 1); this.cp.elementsAttached.splice(index, 1); this.cp.removeChild(index); this.cp.updateKeywordMenuFromSlides(); this.initKeywordMenu(); this.updateNavigationLine(index + move); // Remove visuals. $remove.remove(); H5P.ContinuousText.Engine.run(this); this.trigger('removeSlide', index); this.updateSlidesSidebar(); }); }; /** * Animate navigation line slide icons when the slides are sorted * * @param {number} direction 1 for next, -1 for prev. */ H5PEditor.CoursePresentation.prototype.animateNavigationLine = function (direction) { var that = this; var $selectedProgressPart = that.cp.$progressbar.find('.h5p-progressbar-part-selected'); $selectedProgressPart.css('transform', 'translateX(' + (-100 * direction) + '%)'); var $selectedNext = (direction == 1 ? $selectedProgressPart.prev() : $selectedProgressPart.next()); $selectedNext.css('transform', 'translateX(' + (100 * direction) + '%)'); setTimeout(function () { // Next tick triggers animation $selectedProgressPart.add($selectedNext).css('transform', ''); }, 0); }; /** * Update the slides sidebar */ H5PEditor.CoursePresentation.prototype.updateSlidesSidebar = function () { var self = this; var $keywords = this.cp.$keywords.children(); // Update the sub titles $keywords.each(function (index) { var $keyword = H5PEditor.$(this); $keyword.find('.h5p-keyword-subtitle').html(self.cp.l10n.slide + ' ' + (index + 1)); $keyword.find('.joubel-icon-edit').remove(); var $editIcon = H5PEditor.$( '<a href="#" class="joubel-icon-edit h5p-hidden" title="' + H5PEditor.t('H5PEditor.CoursePresentation', 'edit') + '" tabindex="0">' + '<span class="h5p-icon-circle"></span>' + '<span class="h5p-icon-pencil"></span>' + '</a>' ).click(function () { // If clicked is not already active, do a double click if (!H5PEditor.$(this).parents('[role="menuitem"]').hasClass('h5p-current')) { H5PEditor.$(this).siblings('span').click().click(); } else { H5PEditor.$(this).siblings('span').click(); } $editIcon.siblings('textarea').select(); return false; }).keydown(function (event) { if ([13,32].indexOf(event.which) !== -1) { H5PEditor.$(this).click(); return false; } // Ignore arrow keys for now to avoid JS-error if (event.which >= 37 && event.which <= 40) { return false; } }).blur(function () { $editIcon.addClass('h5p-hidden'); }).appendTo($keywords.eq(index)); H5PEditor.$(this).focus(function () { $editIcon.removeClass('h5p-hidden'); }).hover(function () { if (!H5PEditor.$(this).hasClass('h5p-editing')) { $editIcon.removeClass('h5p-hidden'); } }).mouseleave(function () { $editIcon.addClass('h5p-hidden'); }).blur(function (e) { if (e.relatedTarget && e.relatedTarget.className !== 'joubel-icon-edit' || !e.relatedTarget) { $editIcon.addClass('h5p-hidden'); } }); }); }; /** * Sort current slide in the given direction. * * @param {H5PEditor.$} $element The next/prev slide. * @param {int} direction 1 for next, -1 for prev. * @returns {Boolean} Indicates success. */ H5PEditor.CoursePresentation.prototype.sortSlide = function ($element, direction) { if (!$element.length) { return false; } var index = this.cp.$current.index(); var keywordsEnabled = this.cp.$currentKeyword !== undefined; // Move slides and keywords. if (direction === -1) { this.cp.$current.insertBefore($element.removeClass('h5p-previous')); if (keywordsEnabled) { var $prev = this.cp.$currentKeyword.prev(); this.cp.$currentKeyword.insertBefore($prev); this.swapIndexes(this.cp.$currentKeyword, $prev); } } else { this.cp.$current.insertAfter($element.addClass('h5p-previous')); if (keywordsEnabled) { var $next = this.cp.$currentKeyword.next(); this.cp.$currentKeyword.insertAfter($next); this.swapIndexes(this.cp.$currentKeyword, $next); } } if (keywordsEnabled) { this.cp.keywordMenu.scrollToKeywords(); } // Jump to sorted slide number var newIndex = index + direction; this.cp.jumpToSlide(newIndex); // Need to inform exportable text area about the change: H5P.ExportableTextArea.CPInterface.changeSlideIndex(direction > 0 ? index : index-1, direction > 0 ? index+1 : index); // Update params. this.swapCollectionIndex(this.params.slides, index, newIndex); this.swapCollectionIndex(this.elements, index, newIndex); this.swapCollectionIndex(this.cp.elementInstances, index, newIndex); this.swapCollectionIndex(this.cp.elementsAttached, index, newIndex); this.cp.moveChild(index, newIndex); this.updateNavigationLine(newIndex); H5P.ContinuousText.Engine.run(this); this.updateSlidesSidebar(); this.animateNavigationLine(direction); this.trigger('sortSlide', direction); return true; }; /** * Swap indexes in array, useful when sorting * * @param {Array} collection The collection we'll swap indexes in * @param {number} firstIndex First index that will be swapped * @param {number} secondIndex Second index that will be swapped */ H5PEditor.CoursePresentation.prototype.swapCollectionIndex = function (collection, firstIndex, secondIndex) { var temp = collection[firstIndex]; collection[firstIndex] = collection[secondIndex]; collection[secondIndex] = temp; }; /** * Swaps the [data-index] values of two elements * * @param {jQuery} $current * @param {jQuery} $other */ H5PEditor.CoursePresentation.prototype.swapIndexes = function ($current, $other) { var currentIndex = $current.attr('data-index'); var otherIndex = $other.attr('data-index'); $current.attr('data-index', otherIndex); $other.attr('data-index', currentIndex); }; /** * Edit keyword. * * @param {H5PEditor.$} $span Keyword wrapper. * @returns {unresolved} Nothing */ H5PEditor.CoursePresentation.prototype.editKeyword = function ($span) { var that = this; var $li = $span.parent(); if (!$li.hasClass('h5p-current')) { return false; // Can only edit title for the current slide } var oldTitle = $span.text(); // Used for reset / cancel var slideIndex = that.cp.$current.index(); if (!that.params.slides[slideIndex].keywords || !that.params.slides[slideIndex].keywords.length) { oldTitle = ''; // Prevent editing 'No title' string } var $delete = H5PEditor.$( '<a href="#" class="joubel-icon-cancel" title="' + H5PEditor.t('H5PEditor.CoursePresentation', 'cancel') + '">' + '<span class="h5p-icon-circle"></span>' + '<span class="h5p-icon-cross"></span>' + '</a>'); var $textarea = H5PEditor.$('<textarea></textarea>') .val(oldTitle) .insertBefore($span.hide()) .keydown(function (event) { if (event.keyCode === 13) { $textarea.blur(); $li.focus(); return false; } // don't propagate key events from textarea event.stopPropagation(); }).keyup(function () { $textarea.css('height', $textarea[0].scrollHeight); }).blur(function (event) { if (event.relatedTarget && event.relatedTarget.className !== 'joubel-icon-cancel' || !event.relatedTarget) { var keyword = $textarea.val(); // Text not HTML that.updateKeyword(keyword, slideIndex, $span.html()); // Remove textarea $li.removeClass('h5p-editing'); $span.css({'display': 'inline-block'}); $textarea.add($delete).remove(); } }).focus(); $textarea.keyup(); $delete.insertAfter($textarea).click(function (e) { e.preventDefault(); $textarea.val(oldTitle).blur(); H5PEditor.$('[role="menuitem"].h5p-current').focus(); }).keydown(function (e) { if ([32,13].indexOf(e.which) !== -1) { H5PEditor.$(this).click(); return false; } // Ignore arrow keys for now to avoid JS-error if (e.which >= 37 && e.which <= 40) { return false; } }).blur(function (e) { if (e.relatedTarget && e.relatedTarget.tagName !== 'TEXTAREA' || !e.relatedTarget) { $textarea.blur(); } }); }; /** * Updates the configs with the new keyword * * @param {string} keyword * @param {number} slideIndex * @param {string} oldTitle */ H5PEditor.CoursePresentation.prototype.updateKeyword = function (keyword, slideIndex, oldTitle) { var that = this; var hasTitle = true; if (H5P.trim(keyword) === '') { // Title is blank, use placeholder text keyword = that.cp.l10n.noTitle; hasTitle = false; } // Update navigation bar display? that.cp.progressbarParts[slideIndex].data('keyword', oldTitle); // Update keywords button H5PEditor.$('.current-slide-title').html(oldTitle); // Update params if (hasTitle) { that.params.slides[slideIndex].keywords = [{ main: keyword }]; } else { delete that.params.slides[slideIndex].keywords; } // Update keyword list item H5PEditor.$('[role="menuitem"].h5p-current .h5p-keyword-title').text(keyword); }; /** * Generate element form. * * @param {Object} elementParams * @param {String} type * @returns {Object} */ H5PEditor.CoursePresentation.prototype.generateForm = function (elementParams, type) { var self = this; if (type === 'H5P.ContinuousText' && self.ct) { // Continuous Text shares a single form across all elements return { '$form': self.ct.element.$form, children: self.ct.element.children }; } // Get semantics for the elements field var slides = H5PEditor.CoursePresentation.findField('slides', this.field.fields); var elementFields = H5PEditor.$.extend(true, [], H5PEditor.CoursePresentation.findField('elements', slides.field.fields).field.fields); // Manipulate semantics into only using a given set of fields if (type === 'goToSlide') { // Hide all others self.showFields(elementFields, ['title', 'goToSlide', 'goToSlideType', 'invisible']); } else { var hideFields = ['title', 'goToSlide', 'goToSlideType', 'invisible']; if (type === 'H5P.ContinuousText' || type === 'H5P.Audio') { // Continuous Text or Go To Slide cannot be displayed as a button hideFields.push('displayAsButton'); hideFields.push('buttonSize'); } else if (type === "H5P.Shape") { hideFields.push('solution'); hideFields.push('alwaysDisplayComments'); hideFields.push('backgroundOpacity'); hideFields.push('displayAsButton'); hideFields.push('buttonSize'); } // Only display goToSlide field for goToSlide elements self.hideFields(elementFields, hideFields); } var element = { '$form': H5P.jQuery('<div/>') }; // Render element fields H5PEditor.processSemanticsChunk(elementFields, elementParams, element.$form, self); element.children = self.children; // Remove library selector and copy button and paste button var pos = elementFields.map(function (field) { return field.type; }).indexOf('library'); if (pos !== -1 && element.children[pos].hide) { element.children[pos].hide(); element.$form.css('padding-top', '0'); } // Show or hide button size dropdown depending on display as button checkbox element.$form.find('.field-name-displayAsButton').each(function () { // TODO: Use showWhen in semantics.json instead… var buttonSizeField = ns.$(this).parent().find('.field-name-buttonSize'); if (!ns.$(this).find("input")[0].checked) { buttonSizeField.addClass("h5p-hidden2"); } ns.$(this).find("input").change(function (e) { if (e.target.checked) { buttonSizeField.removeClass("h5p-hidden2"); } else { buttonSizeField.addClass("h5p-hidden2"); } }); }); // Set correct aspect ratio on new images. // TODO: Do not use/rely on magic numbers! var library = element.children[4]; if (!(library instanceof H5PEditor.None)) { var libraryChange = function () { if (library.children[0].field.type === 'image') { library.children[0].changes.push(function (params) { self.setImageSize(element, elementParams, params); }); } else if (library.children[0].field.type === 'video') { library.children[0].changes.push(function (params) { self.setVideoSize(elementParams, params); }); } // Determine library options for this subcontent library var libraryOptions = H5PEditor.CoursePresentation.findField('action', elementFields).options; if (libraryOptions.length > 0 && typeof libraryOptions[0] === 'object') { libraryOptions = libraryOptions.filter(function (option) { return option.name.split(' ')[0] === type; }); libraryOptions = (libraryOptions.length > 0) ? libraryOptions[0] : {}; } else { libraryOptions = {}; } }; if (library.children === undefined) { library.changes.push(libraryChange); } else { libraryChange(); } } return element; }; /** * Help set size for new images and keep aspect ratio. * * @param {object} element * @param {object} elementParams * @param {object} fileParams */ H5PEditor.CoursePresentation.prototype.setImageSize = function (element, elementParams, fileParams) { if (fileParams === undefined || fileParams.width === undefined || fileParams.height === undefined) { return; } // Avoid to small images var minSize = parseInt(element.$wrapper.css('font-size')) + element.$wrapper.outerHeight() - element.$wrapper.innerHeight(); // Use minSize if (fileParams.width < minSize) { fileParams.width = minSize; } if (fileParams.height < minSize) { fileParams.height = minSize; } // Reduce height for tiny images, stretched pixels looks horrible var suggestedHeight = fileParams.height / (this.cp.$current.innerHeight() / 100); if (suggestedHeight < elementParams.height) { elementParams.height = suggestedHeight; } // Calculate new width elementParams.width = (elementParams.height * (fileParams.width / fileParams.height)) / this.slideRatio; }; /** * Help set size for new videos and keep aspect ratio. * * @param {object} element * @param {object} elementParams * @param {object} fileParams */ H5PEditor.CoursePresentation.prototype.setVideoSize = function (elementParams, fileParams) { if (!fileParams){ return; } if (!fileParams.aspectRatio) { fileParams.aspectRatio = '16:9'; } const ratioParts = String(fileParams.aspectRatio).split(':'); elementParams.height = (elementParams.width * (ratioParts.length === 1 ? fileParams.aspectRatio : (ratioParts[1] / ratioParts[0]))) * this.slideRatio; }; /** * Hide all fields in the given list. All others are shown. * * @param {Object[]} elementFields * @param {String[]} fields */ H5PEditor.CoursePresentation.prototype.hideFields = function (elementFields, fields) { // Find and hide fields in list for (var i = 0; i < fields.length; i++) { var field = H5PEditor.CoursePresentation.findField(fields[i], elementFields); if (field) { field.widget = 'none'; } } }; /** * Show all fields in the given list. All others are hidden. * * @param {Object[]} elementFields * @param {String[]} fields */ H5PEditor.CoursePresentation.prototype.showFields = function (elementFields, fields) { // Find and hide all fields not in list for (var i = 0; i < elementFields.length; i++) { var field = elementFields[i]; var found = false; for (var j = 0; j < fields.length; j++) { if (field.name === fields[j]) { found = true; break; } } if (!found) { field.widget = 'none'; } } }; /** * Find the title for the given library. * * @param {String} type Library name * @param {Function} next Called when we've found the title */ H5PEditor.CoursePresentation.prototype.findLibraryTitle = function (library, next) { var self = this; /** @private */ var find = function () { for (var i = 0; i < self.libraries.length; i++) { if (self.libraries[i].name === library) { next(self.libraries[i].title); return; } } }; if (self.libraries === undefined) { // Must wait until library titles are loaded self.once('librariesReady', find); } else { find(); } }; /** * Callback used by CP when a new element is added. * * @param {Object} elementParams * @param {jQuery} $wrapper * @param {Number} slideIndex * @param {Object} elementInstance * @returns {undefined} */ H5PEditor.CoursePresentation.prototype.processElement = function (elementParams, $wrapper, slideIndex, elementInstance) { var that = this; // Detect type var type; if (elementParams.action !== undefined) { type = elementParams.action.library.split(' ')[0]; } else { type = 'goToSlide'; } // Find element identifier var elementIndex = $wrapper.index(); // Generate element form if (this.elements[slideIndex] === undefined) { this.elements[slideIndex] = []; } if (this.elements[slideIndex][elementIndex] === undefined) { this.elements[slideIndex][elementIndex] = this.generateForm(elementParams, type); } // Get element var element = this.elements[slideIndex][elementIndex]; element.$wrapper = $wrapper; H5P.jQuery('<div/>', { 'class': 'h5p-element-overlay' }).appendTo($wrapper); if (that.dnb) { that.addToDragNBar(element, elementParams); } // Open form dialog when double clicking element $wrapper.dblclick(function () { that.showElementForm(element, $wrapper, elementParams); }); if (type === 'H5P.ContinuousText' && that.ct === undefined) { // Keep track of first CT element! that.ct = { element: element, params: elementParams }; } if (elementParams.pasted) { if (type === 'H5P.ContinuousText') { H5P.ContinuousText.Engine.run(this); } delete elementParams.pasted; } if (elementInstance.onAdd) { // Some sort of callback event thing elementInstance.onAdd(elementParams, slideIndex); } }; /** * Make sure element can be moved and stop moving while resizing. * * @param {Object} element * @param {Object} elementParams * @returns {H5P.DragNBarElement} */ H5PEditor.CoursePresentation.prototype.addToDragNBar = function (element, elementParams) { var self = this; var type = (elementParams.action ? elementParams.action.library.split(' ')[0] : null); const options = { disableResize: elementParams.displayAsButton, lock: (type === 'H5P.Chart' && elementParams.action.params.graphMode === 'pieChart'), cornerLock: (type === 'H5P.Image' || type === 'H5P.Shape') }; if (type === 'H5P.Shape') { options.minSize = 3; if (elementParams.action.params.type == 'vertical-line') { options.directionLock = "vertical"; } else if (elementParams.action.params.type == 'horizontal-line') { options.directionLock = "horizontal"; } } var clipboardData = H5P.DragNBar.clipboardify(H5PEditor.CoursePresentation.clipboardKey, elementParams, 'action'); var dnbElement = self.dnb.add(element.$wrapper, clipboardData, options); dnbElement.contextMenu.on('contextMenuEdit', function () { self.showElementForm(element, element.$wrapper, elementParams); }); element.$wrapper.find('*').attr('tabindex', '-1'); dnbElement.contextMenu.on('contextMenuRemove', function () { const confirmationDialog = self.showConfirmationDialog({ headerText: H5PEditor.t('H5PEditor.CoursePresentation', 'confirmRemoveElement'), cancelText: H5PEditor.t('H5PEditor.CoursePresentation', 'cancel'), confirmText: H5PEditor.t('H5PEditor.CoursePresentation', 'ok'), }); confirmationDialog.on('canceled', () => { return; }); confirmationDialog.on('confirmed', () => { if (H5PEditor.Html) { H5PEditor.Html.removeWysiwyg(); } self.removeElement(element, element.$wrapper, (elementParams.action !== undefined && H5P.libraryFromString(elementParams.action.library).machineName === 'H5P.ContinuousText')); self.dnb.blurAll(); }); }); dnbElement.contextMenu.on('contextMenuBringToFront', function () { // Old index var oldZ = element.$wrapper.index(); // Current slide index var slideIndex = self.cp.$current.index(); // Update visuals element.$wrapper.appendTo(self.cp.$current); // Find slide params var slide = self.params.slides[slideIndex].elements; // Remove from old pos slide.splice(oldZ, 1); // Add to top slide.push(elementParams); // Re-order elements in the same fashion self.elements[slideIndex].splice(oldZ, 1); self.elements[slideIndex].push(element); self.cp.children[slideIndex].moveChild(oldZ, self.cp.children[slideIndex].children.length - 1); }); dnbElement.contextMenu.on('contextMenuSendToBack', function () { // Old index var oldZ = element.$wrapper.index(); // Current slide index var slideIndex = self.cp.$current.index(); // Update visuals element.$wrapper.prependTo(self.cp.$current); // Find slide params var slide = self.params.slides[slideIndex].elements; // Remove from old pos slide.splice(oldZ, 1); // Add to top slide.unshift(elementParams); // Re-order elements in the same fashion self.elements[slideIndex].splice(oldZ, 1); self.elements[slideIndex].unshift(element); self.cp.children[slideIndex].moveChild(oldZ, 0); }); return dnbElement; }; /** * Removes element from slide. * * @param {Object} element * @param {jQuery} $wrapper * @param {Boolean} isContinuousText * @returns {undefined} */ H5PEditor.CoursePresentation.prototype.removeElement = function (element, $wrapper, isContinuousText) { var slideIndex = this.cp.$current.index(); var elementIndex = $wrapper.index(); var elementInstance = this.cp.elementInstances[slideIndex][elementIndex]; var removeForm = (element.children.length ? true : false); if (isContinuousText) { var CTs = this.getCTs(false, true); if (CTs.length === 2) { // Prevent removing form while there are still some CT elements left removeForm = false; if (element === CTs[0].element && CTs.length === 2) { CTs[1].params.action.params = CTs[0].params.action.params; } } else { delete this.params.ct; delete this.ct; } } if (removeForm) { H5PEditor.removeChildren(element.children); } // Completely remove element from CP if (elementInstance.onDelete) { elementInstance.onDelete(this.params, slideIndex, elementIndex); } this.elements[slideIndex].splice(elementIndex, 1); this.cp.elementInstances[slideIndex].splice(elementIndex, 1); this.params.slides[slideIndex].elements.splice(elementIndex, 1); this.cp.children[slideIndex].removeChild(elementIndex); $wrapper.remove(); if (isContinuousText) { H5P.ContinuousText.Engine.run(this); } }; /** * Displays the given form in a popup. * * @param {jQuery} $form * @param {jQuery} $wrapper * @param {object} element Params * @returns {undefined} */ H5PEditor.CoursePresentation.prototype.showElementForm = function (element, $wrapper, elementParams) { var that = this; // Determine element type var machineName; if (elementParams.action !== undefined) { machineName = H5P.libraryFromString(elementParams.action.library).machineName; } // Special case for Continuous Text var isContinuousText = (machineName === 'H5P.ContinuousText'); if (isContinuousText && that.ct) { // Get CT text from storage that.ct.element.$form.find('.text .ckeditor').first().html(that.params.ct); that.ct.params.action.params.text = that.params.ct; } // Disable guided tour for IV if (machineName === 'H5P.InteractiveVideo') { // Recreate IV form, workaround for Youtube API not firing // onStateChange when IV is reopened. element = that.generateForm(elementParams, 'H5P.InteractiveVideo'); } /** * The user has clicked delete, remove the element. * @private */ const handleFormremove = function (e) { const confirmationDialog = this.showConfirmationDialog({ headerText: H5PEditor.t('H5PEditor.CoursePresentation', 'confirmRemoveElement'), cancelText: H5PEditor.t('H5PEditor.CoursePresentation', 'cancel'), confirmText: H5PEditor.t('H5PEditor.CoursePresentation', 'ok'), }); e.preventRemove = true; confirmationDialog.on('canceled', () => { return; }); confirmationDialog.on('confirmed', () => { that.currentlyDeletingElement = true; that.getFormManager().closeFormUntil(0); that.removeElement(element, $wrapper, isContinuousText); that.dnb.blurAll(); that.dnb.preventPaste = false; that.currentlyDeletingElement = false; }); }; that.on('formremove', handleFormremove); /** * The user is done editing, save and update the display. * @private */ const handleFormdone = function () { // Validate / save children for (var i = 0; i < element.children.length; i++) { element.children[i].validate(); } if (isContinuousText) { // Store complete CT on slide 0 that.params.ct = that.ct.params.action.params.text; // Split up text and place into CT elements H5P.ContinuousText.Engine.run(that); setTimeout(function () { // Put focus back on ct element that.dnb.focus($wrapper); }, 1); } else if (!that.currentlyDeletingElement) { that.redrawElement($wrapper, element, elementParams); } that.dnb.preventPaste = false; } that.on('formdone', handleFormdone); /** * The form pane is fully displayed. * @private */ const handleFormopened = function () { if (isLoaded) { focusFirstField(); } } that.on('formopened', handleFormopened); /** * Remove event listeners on form close * @private */ const handleFormclose = function () { that.off('formremove', handleFormremove); that.off('formdone', handleFormdone); that.off('formclose', handleFormclose); that.off('formopened', handleFormopened); }; that.on('formclose', handleFormclose); const libraryField = H5PEditor.findField('action', element); /** * Focus the first field of the form. * Should be triggered when library is loaded + form is opened. * * @private */ var focusFirstField = function () { // Find the first ckeditor or texteditor field that is not hidden. // h5p-editor dialog is copyright dialog // h5p-dialog-box is IVs video choose dialog H5P.jQuery('.ckeditor, .h5peditor-text', libraryField.$myField) .not('.h5p-editor-dialog .ckeditor, ' + '.h5p-editor-dialog .h5peditor-text, ' + '.h5p-dialog-box .ckeditor, ' + '.h5p-dialog-box .h5peditor-text', libraryField.$myField) .eq(0) .focus(); // GotoSlide is not library therefore require separate focus if (libraryField.$myField === undefined) { H5P.jQuery('.h5p-coursepresentation-editor .form-manager-slidein .h5peditor-text') .eq(0) .focus(); } }; // Determine if library is already loaded let isLoaded = false; if (libraryField.currentLibrary === undefined && libraryField.change !== undefined) { libraryField.change(function () { isLoaded = true; if (that.isFormOpen()) { focusFirstField(); } }); } else { isLoaded = true; } let customTitle, customIconId; if (elementParams.action === undefined) { customTitle = H5PEditor.t('H5PEditor.CoursePresentation', 'goToSlide'); customIconId = 'gotoslide'; } // Open a new form pane with the element form that.openForm(libraryField, element.$form[0], null, customTitle, customIconId); // Deselect any elements if (that.dnb !== undefined) { that.dnb.preventPaste = true; setTimeout(function () { that.dnb.blurAll(); }, 0); } }; /** * Redraw element. * * @param {jQuery} $wrapper Element container to be redrawn. * @param {object} element Element data. * @param {object} elementParams Element parameters. * @param {number} [repeat] Counter for redrawing if necessary. */ H5PEditor.CoursePresentation.prototype.redrawElement = function ($wrapper, element, elementParams, repeat) { var elementIndex = $wrapper.index(); var slideIndex = this.cp.$current.index(); var elementsParams = this.params.slides[slideIndex].elements; var elements = this.elements[slideIndex]; var elementInstances = this.cp.elementInstances[slideIndex]; // Determine how many elements still need redrawal after this one repeat = (typeof repeat === 'undefined') ? elements.length - 1 - elementIndex : repeat; if (elementParams.action && elementParams.action.library.split(' ')[0] === 'H5P.Chart' && elementParams.action.params.graphMode === 'pieChart') { elementParams.width = elementParams.height / this.slideRatio; } // Remove Element instance from Slide this.cp.children[slideIndex].removeChild(elementIndex); // Remove instance of lib: elementInstances.splice(elementIndex, 1); // Update params elementsParams.splice(elementIndex, 1); elementsParams.push(elementParams); // Update elements elements.splice(elementIndex, 1); elements.push(element); // Update visuals $wrapper.remove(); var instance = this.cp.children[slideIndex].addChild(elementParams).instance; var $element = this.cp.attachElement(elementParams, instance, this.cp.$current, slideIndex); // Make sure we're inside the container this.fitElement($element, elementParams); // Resize element. instance = elementInstances[elementInstances.length - 1]; if ((instance.preventResize === undefined || instance.preventResize === false) && instance.$ !== undefined && !elementParams.displayAsButton) { H5P.trigger(instance, 'resize'); } var that = this; if (repeat === elements.length - 1 - elementIndex) { setTimeout(function () { // Put focus back on element that.dnb.focus($element); }, 1); } /* * Reset to previous element order, otherwise the initially redrawn element * would be put on top instead of remaining at the original z position. */ if (repeat > 0) { repeat--; this.redrawElement(elements[elementIndex].$wrapper, elements[elementIndex], elementsParams[elementIndex], repeat); } }; /** * Applies the updated position and size properties to the given element. * * All properties are converted to percentage. * * @param {H5P.jQuery} $element * @param {Object} elementParams */ H5PEditor.CoursePresentation.prototype.fitElement = function ($element, elementParams) { var self = this; var sizeNPosition = self.dnb.getElementSizeNPosition($element); var updated = H5P.DragNBar.fitElementInside(sizeNPosition); var pW = (sizeNPosition.containerWidth / 100); var pH = (sizeNPosition.containerHeight / 100); // Set the updated properties var style = {}; if (updated.width !== undefined) { elementParams.width = updated.width / pW; style.width = elementParams.width + '%'; } if (updated.left !== undefined) { elementParams.x = updated.left / pW; style.left = elementParams.x + '%'; } if (updated.height !== undefined) { elementParams.height = updated.height / pH; style.height = elementParams.height + '%'; } if (updated.top !== undefined) { elementParams.y = updated.top / pH; style.top = elementParams.y + '%'; } // Apply style $element.css(style); }; /** * Find ContinuousText elements. * * @param {Boolean} [firstOnly] Return first element only * @param {Boolean} [maxTwo] Return after two elements have been found * @returns {{Object[]|Object}} */ H5PEditor.CoursePresentation.prototype.getCTs = function (firstOnly, maxTwo) { var self = this; var CTs = []; for (var i = 0; i < self.elements.length; i++) { var slideElements = self.elements[i]; if (!self.params.slides[i] || !self.params.slides[i].elements) { continue; } for (var j = 0; slideElements !== undefined && j < slideElements.length; j++) { var element = slideElements[j]; var params = self.params.slides[i].elements[j]; if (params.action !== undefined && params.action.library.split(' ')[0] === 'H5P.ContinuousText') { CTs.push({ element: element, params: params }); if (firstOnly) { return CTs[0]; } if (maxTwo && CTs.length === 2) { return CTs; } } } } return firstOnly ? null : CTs; }; /** * Collect functions to execute once the tree is complete. * * @param {function} ready * @returns {undefined} */ H5PEditor.CoursePresentation.prototype.ready = function (ready) { if (this.passReadies) { this.parent.ready(ready); } else { this.readies.push(ready); } }; /** * Look for field with the given name in the given collection. * * @param {String} name of field * @param {Array} fields collection to look in * @returns {Object} field object */ H5PEditor.CoursePresentation.findField = function (name, fields) { for (var i = 0; i < fields.length; i++) { if (fields[i].name === name) { return fields[i]; } } }; /** * Add confirmation dialog * @param {object} dialogOptions Dialog options. * @returns {HTMLElement} confirmationDialog */ H5PEditor.CoursePresentation.prototype.showConfirmationDialog = function (dialogOptions) { const confirmationDialog = new H5P.ConfirmationDialog(dialogOptions) .appendTo(document.body); confirmationDialog.show(this.$item.offset().top); return confirmationDialog; }; /** @constant {Number} */ H5PEditor.CoursePresentation.RATIO_SURFACE = 16 / 9; // Tell the editor what widget we are. H5PEditor.widgets.coursepresentation = H5PEditor.CoursePresentation;
Close