From 39efe0c3ecb499f53fc97c11ba06926a5e7438a4 Mon Sep 17 00:00:00 2001 From: Anshul Sharma Date: Thu, 7 May 2020 17:34:07 +0200 Subject: [PATCH 1/4] media interaction for review mode --- package-lock.json | 2 +- package.json | 2 +- src/reviewRenderer/renderers/config.js | 2 +- .../interactions/MediaInteraction.js | 138 ++++++++++++++++++ 4 files changed, 141 insertions(+), 3 deletions(-) create mode 100644 src/reviewRenderer/renderers/interactions/MediaInteraction.js diff --git a/package-lock.json b/package-lock.json index 6a539517..19d17e4d 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "@oat-sa/tao-item-runner-qti", - "version": "0.6.0", + "version": "0.7.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index 90274e73..388dbe81 100644 --- a/package.json +++ b/package.json @@ -1,6 +1,6 @@ { "name": "@oat-sa/tao-item-runner-qti", - "version": "0.6.0", + "version": "0.7.0", "displayName": "TAO Item Runner QTI", "description": "TAO QTI Item Runner modules", "files": [ diff --git a/src/reviewRenderer/renderers/config.js b/src/reviewRenderer/renderers/config.js index 5aa2bf5c..3862b349 100644 --- a/src/reviewRenderer/renderers/config.js +++ b/src/reviewRenderer/renderers/config.js @@ -98,6 +98,7 @@ const locations = { graphicGapMatchInteraction: 'taoQtiItem/reviewRenderer/renderers/interactions/GraphicGapMatchInteraction', uploadInteraction: 'taoQtiItem/reviewRenderer/renderers/interactions/UploadInteraction', customInteraction: 'taoQtiItem/reviewRenderer/renderers/interactions/PortableCustomInteraction', + mediaInteraction: 'taoQtiItem/reviewRenderer/renderers/interactions/MediaInteraction', // Interactions/Choices inherited from qtiCommonRenderer @@ -108,7 +109,6 @@ const locations = { 'taoQtiItem/qtiCommonRenderer/renderers/choices/SimpleAssociableChoice.MatchInteraction', 'simpleAssociableChoice.associateInteraction': 'taoQtiItem/qtiCommonRenderer/renderers/choices/SimpleAssociableChoice.AssociateInteraction', - mediaInteraction: 'taoQtiItem/qtiCommonRenderer/renderers/interactions/MediaInteraction', gapImg: 'taoQtiItem/qtiCommonRenderer/renderers/choices/GapImg', endAttemptInteraction: 'taoQtiItem/qtiCommonRenderer/renderers/interactions/EndAttemptInteraction', }; diff --git a/src/reviewRenderer/renderers/interactions/MediaInteraction.js b/src/reviewRenderer/renderers/interactions/MediaInteraction.js new file mode 100644 index 00000000..430506bc --- /dev/null +++ b/src/reviewRenderer/renderers/interactions/MediaInteraction.js @@ -0,0 +1,138 @@ +/* + * This program is free software; you can redistribute it and/or + * modify it under the terms of the GNU General Public License + * as published by the Free Software Foundation; under version 2 + * of the License (non-upgradable). + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + * GNU General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program; if not, write to the Free Software + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. + * + * Copyright (c) 2020 (original work) Open Assessment Technologies SA (under the project TAO-PRODUCT); + * + */ + +/** + * @author Ansul Sharma + */ +import $ from 'jquery'; +import _ from 'lodash'; +import containerHelper from 'taoQtiItem/qtiCommonRenderer/helpers/container'; +import mediaInteraction from 'taoQtiItem/qtiCommonRenderer/renderers/interactions/MediaInteraction'; +import mediaplayer from 'ui/mediaplayer'; + +var defaults = { + type: 'video/mp4', + video: { + height: 270 + } +}; + +/** + * Init rendering, called after template injected into the DOM + * All options are listed in the QTI v2.1 information model: + * http://www.imsglobal.org/question/qtiv2p1/imsqti_infov2p1.html#element10391 + * + * @param {object} interaction + * @returns {Promise} + */ +var render = function render(interaction) { + return new Promise((resolve) => { + var $container = containerHelper.get(interaction); + var media = interaction.object; + var $item = $container.parents('.qti-item'); + var url = media.attr('data') || ''; + + /** + * Resize video player elements to fit container size + * @param {Object} mediaElement - player instance + * @param {jQueryElement} $container - container element to adapt + */ + var resize = _.debounce(function resize() { + var width, height; + if (interaction.mediaElement) { + height = $container.find('.media-container').height(); + width = $container.find('.media-container').width(); + + interaction.mediaElement.resize(width, height); + } + }, 200); + + //intialize the player if not yet done + const initMediaPlayer = () => { + if (!interaction.mediaElement) { + interaction.mediaElement = mediaplayer({ + url: url && this.resolveUrl(url), + type: media.attr('type') || defaults.type, + canPause: true, + canSeek: true, + width: media.attr('width'), + height: media.attr('height'), + autoStart: false, + loop: false, + renderTo: $('.media-container', $container) + }) + .on('render', function() { + resize(); + + $(window) + .off('resize.mediaInteraction') + .on('resize.mediaInteraction', resize); + + $item.off('resize.gridEdit').on('resize.gridEdit', resize); + + /** + * @event playerrendered + */ + $container.trigger('playerrendered'); + }) + .on('ready', function() { + /** + * @event playerready + */ + $container.trigger('playerready'); + + // declare the item ready when player is ready to play. + resolve(); + }) + .on( + 'update', + _.throttle(function() { + containerHelper.triggerResponseChangeEvent(interaction); + }, 1000) + ) + .on('ended', function() { + containerHelper.triggerResponseChangeEvent(interaction); + }); + } + }; + + if (_.size(media.attributes) === 0) { + //TODO move to afterCreate + media.attr('type', defaults.type); + media.attr('width', $container.innerWidth()); + + media.attr('height', defaults.video.height); + media.attr('data', ''); + } + + //initialize the component + $container.on('responseSet', function() { + initMediaPlayer(); + }); + + //gives a small chance to the responseSet event before initializing the player + initMediaPlayer(); + }); +}; + +/** + * Expose the common renderer for the match interaction + * @exports reviewRenderer/renderers/interactions/MatchInteraction + */ +export default Object.assign({}, mediaInteraction, {render}); \ No newline at end of file From 006b326136e62db6d848d5146196bb76d1b0a069 Mon Sep 17 00:00:00 2001 From: Anshul Sharma Date: Thu, 7 May 2020 17:53:32 +0200 Subject: [PATCH 2/4] fix comment --- src/reviewRenderer/renderers/interactions/MediaInteraction.js | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/reviewRenderer/renderers/interactions/MediaInteraction.js b/src/reviewRenderer/renderers/interactions/MediaInteraction.js index 430506bc..5c54a1a0 100644 --- a/src/reviewRenderer/renderers/interactions/MediaInteraction.js +++ b/src/reviewRenderer/renderers/interactions/MediaInteraction.js @@ -132,7 +132,7 @@ var render = function render(interaction) { }; /** - * Expose the common renderer for the match interaction - * @exports reviewRenderer/renderers/interactions/MatchInteraction + * Expose the common renderer for the media interaction + * @exports reviewRenderer/renderers/interactions/mediaInteraction */ export default Object.assign({}, mediaInteraction, {render}); \ No newline at end of file From 889df71a5b196317a5a845e2c554ef084c9dd075 Mon Sep 17 00:00:00 2001 From: Anshul Sharma Date: Thu, 7 May 2020 18:01:53 +0200 Subject: [PATCH 3/4] convert to es6 --- .../interactions/MediaInteraction.js | 26 +++++++++---------- 1 file changed, 13 insertions(+), 13 deletions(-) diff --git a/src/reviewRenderer/renderers/interactions/MediaInteraction.js b/src/reviewRenderer/renderers/interactions/MediaInteraction.js index 5c54a1a0..217e080c 100644 --- a/src/reviewRenderer/renderers/interactions/MediaInteraction.js +++ b/src/reviewRenderer/renderers/interactions/MediaInteraction.js @@ -26,7 +26,7 @@ import containerHelper from 'taoQtiItem/qtiCommonRenderer/helpers/container'; import mediaInteraction from 'taoQtiItem/qtiCommonRenderer/renderers/interactions/MediaInteraction'; import mediaplayer from 'ui/mediaplayer'; -var defaults = { +const defaults = { type: 'video/mp4', video: { height: 270 @@ -41,20 +41,20 @@ var defaults = { * @param {object} interaction * @returns {Promise} */ -var render = function render(interaction) { +const render = function render(interaction) { return new Promise((resolve) => { - var $container = containerHelper.get(interaction); - var media = interaction.object; - var $item = $container.parents('.qti-item'); - var url = media.attr('data') || ''; + const $container = containerHelper.get(interaction); + const media = interaction.object; + const $item = $container.parents('.qti-item'); + const url = media.attr('data') || ''; /** * Resize video player elements to fit container size * @param {Object} mediaElement - player instance * @param {jQueryElement} $container - container element to adapt */ - var resize = _.debounce(function resize() { - var width, height; + const resize = _.debounce(() => { + let width, height; if (interaction.mediaElement) { height = $container.find('.media-container').height(); width = $container.find('.media-container').width(); @@ -77,7 +77,7 @@ var render = function render(interaction) { loop: false, renderTo: $('.media-container', $container) }) - .on('render', function() { + .on('render', () => { resize(); $(window) @@ -91,7 +91,7 @@ var render = function render(interaction) { */ $container.trigger('playerrendered'); }) - .on('ready', function() { + .on('ready', () => { /** * @event playerready */ @@ -102,11 +102,11 @@ var render = function render(interaction) { }) .on( 'update', - _.throttle(function() { + _.throttle(() => { containerHelper.triggerResponseChangeEvent(interaction); }, 1000) ) - .on('ended', function() { + .on('ended', () => { containerHelper.triggerResponseChangeEvent(interaction); }); } @@ -122,7 +122,7 @@ var render = function render(interaction) { } //initialize the component - $container.on('responseSet', function() { + $container.on('responseSet', () => { initMediaPlayer(); }); From ccbf6610549436f671a2ba1f8e246941a92ace35 Mon Sep 17 00:00:00 2001 From: Anshul Sharma Date: Fri, 8 May 2020 10:03:57 +0200 Subject: [PATCH 4/4] use const --- .../renderers/interactions/MediaInteraction.js | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/reviewRenderer/renderers/interactions/MediaInteraction.js b/src/reviewRenderer/renderers/interactions/MediaInteraction.js index 217e080c..129e6a6c 100644 --- a/src/reviewRenderer/renderers/interactions/MediaInteraction.js +++ b/src/reviewRenderer/renderers/interactions/MediaInteraction.js @@ -51,13 +51,11 @@ const render = function render(interaction) { /** * Resize video player elements to fit container size * @param {Object} mediaElement - player instance - * @param {jQueryElement} $container - container element to adapt */ const resize = _.debounce(() => { - let width, height; if (interaction.mediaElement) { - height = $container.find('.media-container').height(); - width = $container.find('.media-container').width(); + const height = $container.find('.media-container').height(); + const width = $container.find('.media-container').width(); interaction.mediaElement.resize(width, height); }