diff --git a/lib.php b/lib.php index 36ea85a..65a3d37 100644 --- a/lib.php +++ b/lib.php @@ -719,7 +719,7 @@ function interactivevideo_displayinline(cm_info $cm) { $OUTPUT->get_generated_image_for_id($cm->id) : $interactivevideo->posterimage; // Fallback to default image. $duration = $interactivevideo->endtime - $interactivevideo->starttime; // Convert to hh:mm:ss format. - $duration = gmdate($duration > 3600 * 60 ? 'H:i:s' : 'i:s', (int) $duration); + $duration = gmdate($duration > 3600 ? 'H:i:s' : 'i:s', (int) $duration); // Format the intro: keep text only and truncate it. $datafortemplate = [ diff --git a/locallib.php b/locallib.php index 23836b8..3e8eb5e 100644 --- a/locallib.php +++ b/locallib.php @@ -304,18 +304,15 @@ public static function get_report_data_by_group($interactivevideo, $group, $cont // Get fields for userpicture. $fields = \core_user\fields::get_picture_fields(); $fields = 'u.' . implode(', u.', $fields); + // Graded roles. + $roles = get_config('core', 'gradebookroles'); if ($group == 0) { // Get all enrolled users (student only). $sql = "SELECT " . $fields . ", ac.timecompleted, ac.timecreated, ac.completionpercentage, ac.completeditems, ac.xp, ac.completiondetails, ac.id as completionid FROM {user} u LEFT JOIN {interactivevideo_completion} ac ON ac.userid = u.id AND ac.cmid = :cmid - WHERE u.id IN ( - SELECT ra.userid - FROM {role_assignments} ra - JOIN {role} r ON ra.roleid = r.id - WHERE ra.contextid = :contextid AND r.archetype = 'student' - ) + WHERE u.id IN (SELECT userid FROM {role_assignments} WHERE contextid = :contextid AND roleid IN (" . $roles . ")) ORDER BY u.lastname, u.firstname"; $records = $DB->get_records_sql($sql, ['cmid' => $interactivevideo, 'contextid' => $contextid]); } else { @@ -325,8 +322,9 @@ public static function get_report_data_by_group($interactivevideo, $group, $cont FROM {user} u LEFT JOIN {interactivevideo_completion} ac ON ac.userid = u.id AND ac.cmid = :cmid WHERE u.id IN (SELECT userid FROM {groups_members} WHERE groupid = :groupid) + AND u.id IN (SELECT userid FROM {role_assignments} WHERE contextid = :contextid AND roleid IN (" . $roles . ")) ORDER BY u.lastname, u.firstname"; - $records = $DB->get_records_sql($sql, ['cmid' => $interactivevideo, 'groupid' => $group]); + $records = $DB->get_records_sql($sql, ['cmid' => $interactivevideo, 'groupid' => $group, 'contextid' => $contextid]); } // Render the photo of the user. @@ -558,7 +556,7 @@ public static function encode_text($text) { * @param string $field The field associated with the text. * @param int $id The ID related to the text processing. * - * @return void + * @return string The processed text. */ public static function process_text($text, $contextid, $field, $id) { if (!$text) { @@ -650,7 +648,7 @@ public static function get_taught_courses($userid) { if (!$userid) { $userid = $USER->id; } - $PAGE->set_context(context_system::instance()); + $PAGE->set_context(\context_system::instance()); // Get all courses where the user is a teacher. $sql = "SELECT c.id, c.fullname, c.shortname FROM {course} c JOIN {context} ctx ON c.id = ctx.instanceid AND ctx.contextlevel = 50 @@ -677,7 +675,7 @@ public static function get_taught_courses($userid) { * Retrieves the course module by course ID. * * @param int $courseid The ID of the course. - * @return object|null The course module object if found, null otherwise. + * @return array The course modules. */ public static function get_cm_by_courseid($courseid) { global $DB, $PAGE; @@ -751,7 +749,7 @@ public static function import_annotations($fromcourse, $tocourse, $module, $from * @param int $userid The user ID. * @param int $courseid The course ID. * @param int $contextid The context ID. - * @return string The completion information. + * @return array The completion information. */ public static function get_cm_completion($cmid, $userid, $courseid, $contextid) { global $OUTPUT, $CFG, $PAGE, $USER; diff --git a/plugins/chapter/amd/build/main.min.js.map b/plugins/chapter/amd/build/main.min.js.map index 63aca5a..2295093 100644 --- a/plugins/chapter/amd/build/main.min.js.map +++ b/plugins/chapter/amd/build/main.min.js.map @@ -1 +1 @@ -{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Interactive video chapter type script\n *\n * @module ivplugin_chapter/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Base from 'mod_interactivevideo/type/base';\n\nexport default class Chapter extends Base {\n /**\n * Initialize the interaction type\n * @returns {void}\n */\n init() {\n if (this.isEditMode()) {\n return;\n }\n let self = this;\n let chapters = this.annotations.filter((annotation) => annotation.type == 'chapter');\n\n $(\"#chaptertoggle\").removeClass('d-none');\n // Order the chapters by timestamp.\n chapters.sort((a, b) => a.timestamp - b.timestamp);\n // If the first chapter doesn't start at the beginning, add a chapter at the beginning.\n if (chapters[0].timestamp > this.start) {\n chapters.unshift({\n id: 0,\n title: M.util.get_string('startchapter', 'ivplugin_chapter'),\n formattedtitle: M.util.get_string('startchapter', 'ivplugin_chapter'),\n timestamp: this.start\n });\n }\n // Calculate start and end time of each chapter.\n chapters.forEach((chapter, index) => {\n chapter.start = chapter.timestamp;\n if (index < chapters.length - 1) {\n chapter.end = chapters[index + 1].timestamp;\n } else {\n chapter.end = this.end;\n }\n });\n\n const convertSecondsToHMS = (seconds) => {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor(seconds % 3600 / 60);\n const s = Math.floor(seconds % 3600 % 60);\n return (h > 0 ? h + ':' : '') + (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s;\n };\n\n // Render the chapters.\n const $chapterlists = $('[data-region=chapterlists]');\n $chapterlists.empty();\n chapters.forEach((chapter) => {\n $chapterlists.append(`
  • \n
    \n \n ${chapter.formattedtitle}\n ${convertSecondsToHMS(chapter.start - this.start)}
    \n
    • `);\n });\n\n $(document).on('timeupdate', async (e) => {\n const currenttime = e.originalEvent.detail.time;\n const currentchapter = chapters.find((chapter) => currenttime >= chapter.start && currenttime < chapter.end);\n if (currentchapter) {\n $chapterlists.find('.chapter').removeClass('active-chapter');\n $chapterlists.find(`.chapter[data-id=${currentchapter.id}]`).addClass('active-chapter');\n if (currentchapter.id != 0) {\n $('#controller #chaptertitle').text(currentchapter.formattedtitle);\n } else {\n $('#controller #chaptertitle').text('');\n }\n }\n });\n\n $chapterlists.on('click', '.chapter .chapter-title', function(e) {\n e.preventDefault();\n // Hide the start, end screen.\n $('#start-screen').fadeOut(300);\n $('#end-screen').fadeOut(300);\n\n let starttime = $(this).closest('li').data('start');\n self.player.seek(starttime);\n\n self.player.play();\n\n // Replace the progress bar.\n const percentage = (starttime - self.start) / self.totaltime * 100;\n $('#video-nav #progress').replaceWith(`
      100 ? 100 : percentage}%;\">
      `);\n // If the .chapter is in the left container, hide it.\n if ($(this).closest('#chapter-container-left').length > 0) {\n $('#chaptertoggle .btn').trigger('click');\n }\n });\n\n // Chapter toggle.\n $(document).on('click', '#chaptertoggle .btn', function(e) {\n e.preventDefault();\n $('#interactivevideo-container').toggleClass('chapter-open');\n $(this).find('i').toggleClass('bi-collection bi-collection-fill');\n });\n\n $(document).on('click', '#closechapter', function(e) {\n e.preventDefault();\n $('#chaptertoggle .btn').trigger('click');\n });\n\n // Collapse/Expand the chapters on click of the chevron.\n $(document).on('click', '.chapter i.toggle.bi', function(e) {\n e.preventDefault();\n $(this).closest('.chapter').find('.annolistinchapter').slideToggle(300);\n $(this).toggleClass('bi-chevron-down bi-chevron-right');\n });\n }\n\n /**\n * Renders the edit item for the annotations list.\n *\n * @param {Array} annotations - The list of annotations.\n * @param {jQuery} listItem - The jQuery object representing the list item.\n * @param {Object} item - The item to be rendered.\n * @param {number} item.timestamp - The timestamp of the item.\n * @returns {jQuery} The modified list item.\n */\n renderEditItem(annotations, listItem, item) {\n listItem = super.renderEditItem(annotations, listItem, item);\n listItem.find('.type-name').addClass('justify-content-center');\n listItem.find('.type-icon i').remove();\n if (Number(item.timestamp) > this.end || Number(item.timestamp) < this.start || this.isSkipped(item.timestamp)) {\n listItem.find('.title').addClass('text-muted');\n }\n return listItem;\n }\n\n /**\n * Render the annotation on the video navigation\n * @param {object} annotation The annotation object\n * @returns {void}\n */\n renderItemOnVideoNavigation(annotation) {\n if (annotation.hide) {\n return;\n }\n if (annotation.timestamp < this.start || annotation.timestamp > this.end) {\n return;\n }\n const percentage = ((Number(annotation.timestamp) - this.start) / this.totaltime) * 100;\n if (this.isVisible(annotation)) {\n let classes = annotation.type + ' annotation li-draggable ';\n if (annotation.completed) {\n classes += 'completed ';\n }\n if (!this.isClickable(annotation)) {\n classes += 'no-pointer-events ';\n }\n if (this.isSkipped(annotation.timestamp)) {\n classes += 'skipped ';\n }\n $(\"#video-nav ul\").append(`
    • \n
    • `);\n }\n }\n\n /**\n * Run the interaction\n * @param {object} annotation The annotation object\n */\n async runInteraction(annotation) {\n if (annotation.char1 != '1') {\n this.player.play();\n // Show the tooltip for 2 seconds.\n $('#video-nav ul li[data-id=' + annotation.id + '] .item').tooltip('show');\n setTimeout(() => {\n $('#video-nav ul li[data-id=' + annotation.id + '] .item').tooltip('hide');\n }, 2000);\n return;\n }\n\n await this.player.pause();\n $('#controler').addClass('no-pointer-events');\n $('#video-wrapper').append(`

      ${annotation.formattedtitle}

      `);\n // Add a progress bar and load it for 3 seconds.\n $('#video-wrapper #message').append(`
      \n
      `);\n $('#message span').animate({\n 'top': '1em',\n }, 300, 'swing');\n $('#chapterprogress .progress-bar').animate({'width': '100%'}, 3000, 'linear', () => {\n if (!this.isEditMode()) {\n $('#message span').css('top', '0');\n this.player.play();\n $('#controler').removeClass('no-pointer-events');\n } else {\n $('h2#message').remove();\n }\n });\n }\n}"],"names":["Chapter","Base","init","this","isEditMode","self","chapters","annotations","filter","annotation","type","removeClass","sort","a","b","timestamp","start","unshift","id","title","M","util","get_string","formattedtitle","forEach","chapter","index","length","end","$chapterlists","empty","append","seconds","h","Math","floor","m","s","convertSecondsToHMS","document","on","async","currenttime","e","originalEvent","detail","time","currentchapter","find","addClass","text","preventDefault","fadeOut","starttime","closest","data","player","seek","play","percentage","totaltime","replaceWith","trigger","toggleClass","slideToggle","renderEditItem","listItem","item","super","remove","Number","isSkipped","renderItemOnVideoNavigation","hide","isVisible","classes","completed","isClickable","prop","icon","char1","tooltip","setTimeout","pause","animate","css"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Interactive video chapter type script\n *\n * @module ivplugin_chapter/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Base from 'mod_interactivevideo/type/base';\n\nexport default class Chapter extends Base {\n /**\n * Initialize the interaction type\n * @returns {void}\n */\n init() {\n if (this.isEditMode()) {\n return;\n }\n let self = this;\n let chapters = this.annotations.filter((annotation) => annotation.type == 'chapter');\n\n $(\"#chaptertoggle\").removeClass('d-none');\n // Order the chapters by timestamp.\n chapters.sort((a, b) => a.timestamp - b.timestamp);\n // If the first chapter doesn't start at the beginning, add a chapter at the beginning.\n if (chapters[0].timestamp > this.start) {\n chapters.unshift({\n id: 0,\n title: M.util.get_string('startchapter', 'ivplugin_chapter'),\n formattedtitle: M.util.get_string('startchapter', 'ivplugin_chapter'),\n timestamp: this.start\n });\n }\n // Calculate start and end time of each chapter.\n chapters.forEach((chapter, index) => {\n chapter.start = chapter.timestamp;\n if (index < chapters.length - 1) {\n chapter.end = chapters[index + 1].timestamp;\n } else {\n chapter.end = this.end;\n }\n });\n\n const convertSecondsToHMS = (seconds) => {\n const h = Math.floor(seconds / 3600);\n const m = Math.floor(seconds % 3600 / 60);\n const s = Math.floor(seconds % 3600 % 60);\n return (h > 0 ? h + ':' : '') + (m < 10 ? '0' : '') + m + ':' + (s < 10 ? '0' : '') + s;\n };\n\n // Render the chapters.\n const $chapterlists = $('[data-region=chapterlists]');\n $chapterlists.empty();\n chapters.forEach((chapter) => {\n $chapterlists.append(`
    • \n
      \n \n ${chapter.formattedtitle}\n ${convertSecondsToHMS(chapter.start - this.start)}
      \n
      • `);\n });\n\n $(document).on('timeupdate', async(e) => {\n const currenttime = e.originalEvent.detail.time;\n const currentchapter = chapters.find((chapter) => currenttime >= chapter.start && currenttime < chapter.end);\n if (currentchapter) {\n $chapterlists.find('.chapter').removeClass('active-chapter');\n $chapterlists.find(`.chapter[data-id=${currentchapter.id}]`).addClass('active-chapter');\n if (currentchapter.id != 0) {\n $('#controller #chaptertitle').text(currentchapter.formattedtitle);\n } else {\n $('#controller #chaptertitle').text('');\n }\n }\n });\n\n $chapterlists.on('click', '.chapter .chapter-title', function(e) {\n e.preventDefault();\n // Hide the start, end screen.\n $('#start-screen').fadeOut(300);\n $('#end-screen').fadeOut(300);\n\n let starttime = $(this).closest('li').data('start');\n self.player.seek(starttime);\n\n self.player.play();\n\n // Replace the progress bar.\n const percentage = (starttime - self.start) / self.totaltime * 100;\n $('#video-nav #progress').replaceWith(`
        100 ? 100 : percentage}%;\">
        `);\n // If the .chapter is in the left container, hide it.\n if ($(this).closest('#chapter-container-left').length > 0) {\n $('#chaptertoggle .btn').trigger('click');\n }\n });\n\n // Chapter toggle.\n $(document).on('click', '#chaptertoggle .btn', function(e) {\n e.preventDefault();\n $('#interactivevideo-container').toggleClass('chapter-open');\n $(this).find('i').toggleClass('bi-collection bi-collection-fill');\n });\n\n $(document).on('click', '#closechapter', function(e) {\n e.preventDefault();\n $('#chaptertoggle .btn').trigger('click');\n });\n\n // Collapse/Expand the chapters on click of the chevron.\n $(document).on('click', '.chapter i.toggle.bi', function(e) {\n e.preventDefault();\n $(this).closest('.chapter').find('.annolistinchapter').slideToggle(300);\n $(this).toggleClass('bi-chevron-down bi-chevron-right');\n });\n }\n\n /**\n * Renders the edit item for the annotations list.\n *\n * @param {Array} annotations - The list of annotations.\n * @param {jQuery} listItem - The jQuery object representing the list item.\n * @param {Object} item - The item to be rendered.\n * @param {number} item.timestamp - The timestamp of the item.\n * @returns {jQuery} The modified list item.\n */\n renderEditItem(annotations, listItem, item) {\n listItem = super.renderEditItem(annotations, listItem, item);\n listItem.find('.type-name').addClass('justify-content-center');\n listItem.find('.type-icon i').remove();\n if (Number(item.timestamp) > this.end || Number(item.timestamp) < this.start || this.isSkipped(item.timestamp)) {\n listItem.find('.title').addClass('text-muted');\n }\n return listItem;\n }\n\n /**\n * Render the annotation on the video navigation\n * @param {object} annotation The annotation object\n * @returns {void}\n */\n renderItemOnVideoNavigation(annotation) {\n if (annotation.hide) {\n return;\n }\n if (annotation.timestamp < this.start || annotation.timestamp > this.end) {\n return;\n }\n const percentage = ((Number(annotation.timestamp) - this.start) / this.totaltime) * 100;\n if (this.isVisible(annotation)) {\n let classes = annotation.type + ' annotation li-draggable ';\n if (annotation.completed) {\n classes += 'completed ';\n }\n if (!this.isClickable(annotation)) {\n classes += 'no-pointer-events ';\n }\n if (this.isSkipped(annotation.timestamp)) {\n classes += 'skipped ';\n }\n $(\"#video-nav ul\").append(`
      • \n
      • `);\n }\n }\n\n /**\n * Run the interaction\n * @param {object} annotation The annotation object\n */\n async runInteraction(annotation) {\n if (annotation.char1 != '1') {\n this.player.play();\n // Show the tooltip for 2 seconds.\n $('#video-nav ul li[data-id=' + annotation.id + '] .item').tooltip('show');\n setTimeout(() => {\n $('#video-nav ul li[data-id=' + annotation.id + '] .item').tooltip('hide');\n }, 2000);\n return;\n }\n\n await this.player.pause();\n $('#controler').addClass('no-pointer-events');\n $('#video-wrapper').append(`

        ${annotation.formattedtitle}

        `);\n // Add a progress bar and load it for 3 seconds.\n $('#video-wrapper #message').append(`
        \n
        `);\n $('#message span').animate({\n 'top': '1em',\n }, 300, 'swing');\n $('#chapterprogress .progress-bar').animate({'width': '100%'}, 3000, 'linear', () => {\n if (!this.isEditMode()) {\n $('#message span').css('top', '0');\n this.player.play();\n $('#controler').removeClass('no-pointer-events');\n } else {\n $('h2#message').remove();\n }\n });\n }\n}"],"names":["Chapter","Base","init","this","isEditMode","self","chapters","annotations","filter","annotation","type","removeClass","sort","a","b","timestamp","start","unshift","id","title","M","util","get_string","formattedtitle","forEach","chapter","index","length","end","$chapterlists","empty","append","seconds","h","Math","floor","m","s","convertSecondsToHMS","document","on","async","currenttime","e","originalEvent","detail","time","currentchapter","find","addClass","text","preventDefault","fadeOut","starttime","closest","data","player","seek","play","percentage","totaltime","replaceWith","trigger","toggleClass","slideToggle","renderEditItem","listItem","item","super","remove","Number","isSkipped","renderItemOnVideoNavigation","hide","isVisible","classes","completed","isClickable","prop","icon","char1","tooltip","setTimeout","pause","animate","css"],"mappings":"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"} \ No newline at end of file diff --git a/plugins/chapter/amd/src/main.js b/plugins/chapter/amd/src/main.js index 48eb09d..bcf405b 100644 --- a/plugins/chapter/amd/src/main.js +++ b/plugins/chapter/amd/src/main.js @@ -78,7 +78,7 @@ export default class Chapter extends Base {
          `); }); - $(document).on('timeupdate', async (e) => { + $(document).on('timeupdate', async(e) => { const currenttime = e.originalEvent.detail.time; const currentchapter = chapters.find((chapter) => currenttime >= chapter.start && currenttime < chapter.end); if (currentchapter) { diff --git a/plugins/contentbank/amd/build/main.min.js b/plugins/contentbank/amd/build/main.min.js index 30ed790..4d2dbe4 100644 --- a/plugins/contentbank/amd/build/main.min.js +++ b/plugins/contentbank/amd/build/main.min.js @@ -5,6 +5,6 @@ define("ivplugin_contentbank/main",["exports","jquery","ivplugin_contentbank/uti * @module ivplugin_contentbank/main * @copyright 2024 Sokunthearith Makara * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_util=_interopRequireDefault(_util),_modalform=_interopRequireDefault(_modalform),_base=_interopRequireDefault(_base);class ContentBank extends _base.default{onEditFormLoaded(form,event){let self=this,body=form.modal.modal.find(".modal-body");return _util.default.init(M.cfg.courseContextId),body.off("click","#refreshcontentbank").on("click","#refreshcontentbank",(function(e){e.preventDefault(),(0,_jquery.default)(this).find("i").addClass("fa-spin");const currentid=(0,_jquery.default)("[name=contentid]").val();(0,_jquery.default)(".contentbank-container").html('
          \n Loading...
          '),_util.default.refreshContentBank(currentid,M.cfg.courseContextId,(0,_jquery.default)(this).data("editable"),(function(){(0,_jquery.default)("#refreshcontentbank i").removeClass("fa-spin")}))})),body.off("click","#uploadcontentbank").on("click","#uploadcontentbank",(function(e){e.preventDefault();const uploadForm=new _modalform.default({formClass:"core_contentbank\\form\\upload_files",args:{contextid:M.cfg.courseContextId},modalConfig:{title:M.util.get_string("uploadcontent","ivplugin_contentbank")}});uploadForm.addEventListener(uploadForm.events.FORM_SUBMITTED,(e=>{self.addNotification(M.util.get_string("contentuploaded","ivplugin_contentbank"),"success");const contentid=e.detail.returnurl.match(/id=(\d+)/)[1];(0,_jquery.default)("[name=contentid]").val(contentid),setTimeout((function(){(0,_jquery.default)("#refreshcontentbank").trigger("click")}),1e3)})),uploadForm.addEventListener(uploadForm.events.ERROR,(()=>{self.addNotification(M.util.get_string("contentuploaderror","ivplugin_contentbank"))})),uploadForm.show()})),{form:form,event:event}}postContentRender(annotation,callback){return(0,_jquery.default)("#message[data-id='".concat(annotation.id,"']")).addClass("hascontentbank"),!(1==annotation.hascompletion&&"manual"!=annotation.completiontracking&&!annotation.completed)||callback}renderContainer(annotation){super.renderContainer(annotation);let $message=(0,_jquery.default)("#message[data-id='".concat(annotation.id,"']"));$message.find(".modal-body").addClass("p-0");let $completiontoggle=$message.find("#completiontoggle");switch($message.find("#title .info").remove(),annotation.completiontracking){case"complete":$completiontoggle.before(''));break;case"completepass":$completiontoggle.before(''));break;case"completefull":$completiontoggle.before(''))}return $message.find('[data-toggle="tooltip"]').tooltip(),$message}resizeIframe(annotation){const modalbody=document.querySelector("#message[data-id='".concat(annotation.id,"'] .modal-body"));new ResizeObserver((()=>{const iframe=modalbody.querySelector("iframe.h5p-player");if(iframe){const height=iframe.scrollHeight;modalbody.style.height="".concat(height+2e3,"px")}})).observe(modalbody)}async runInteraction(annotation){await this.player.pause();const annoid=annotation.id;let $message,self=this;await this.renderViewer(annotation),$message=this.renderContainer(annotation),async function(annotation){const data=await self.render(annotation);$message.find(".modal-body").html(data).attr("id","content").fadeIn(300),1!=annotation.hascompletion||self.isEditMode()||(annotation.completed||"view"!=annotation.completiontracking||self.toggleCompletion(annoid,"mark-done","automatic"),function(annotation){let listenToEvents=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const detectH5P=()=>{let H5P;try{H5P=document.querySelector("#message[data-id='".concat(annoid,"'] iframe")).contentWindow.H5P}catch(e){H5P=null}if(null!=H5P){if(!listenToEvents)return;self.isEditMode()&&($message.find("#title .btns .xapi").remove(),$message.find("#title .btns").prepend('
          '.concat(M.util.get_string("xapicheck","ivplugin_contentbank"),"
          ")));let statements=[];try{H5P.externalDispatcher.on("xAPI",(function(event){if("http://adlnet.gov/expapi/verbs/completed"!=event.data.statement.verb.id&&"http://adlnet.gov/expapi/verbs/answered"!=event.data.statement.verb.id||statements.push(event.data.statement),("http://adlnet.gov/expapi/verbs/completed"==event.data.statement.verb.id||"http://adlnet.gov/expapi/verbs/answered"==event.data.statement.verb.id)&&event.data.statement.object.id.indexOf("subContentId")<0){if(self.isEditMode())return(0,_jquery.default)("#message[data-id='".concat(annotation.id,"'] #title .btns .xapi")).remove(),(0,_jquery.default)("#message[data-id='".concat(annotation.id,"'] #title .btns")).prepend('
          \n \n '.concat(M.util.get_string("xapieventdetected","ivplugin_h5pupload"),"\n
          ")),void new Audio(M.cfg.wwwroot+"/mod/interactivevideo/sounds/pop.mp3").play();let complete=!1,textclass="";if(("completepass"==annotation.completiontracking&&event.data.statement.result&&event.data.statement.result.score.scaled>=.5||"completefull"==annotation.completiontracking&&event.data.statement.result&&1==event.data.statement.result.score.scaled||"complete"==annotation.completiontracking)&&(complete=!0),textclass=event.data.statement.result.score.scaled<.5?"fa fa-check text-danger":event.data.statement.result.score.scaled<1?"fa fa-check text-success":"bi bi-check2-all text-success",complete&&!annotation.completed){let details={};const completeTime=new Date;details.xp=annotation.xp,"1"==annotation.char1&&(details.xp=(event.data.statement.result.score.scaled*annotation.xp).toFixed(2)),details.duration=completeTime.getTime()-(0,_jquery.default)("#video-wrapper").data("timestamp"),details.timecompleted=completeTime.getTime();const completiontime=completeTime.toLocaleString();let duration=self.formatTime(details.duration/1e3);details.reportView='\n '.concat(completiontime,'').concat(duration,'\n \n ').concat(event.data.statement.result.score.raw,"/").concat(event.data.statement.result.score.max,"'>\n
          ').concat(Number(details.xp),""),details.details=statements,self.toggleCompletion(annoid,"mark-done","automatic",details)}}}))}catch(e){requestAnimationFrame(detectH5P)}}else requestAnimationFrame(detectH5P)};requestAnimationFrame(detectH5P)}(annotation,!annotation.completed&&"manual"!=annotation.completiontracking))}(annotation),this.enableManualCompletion(annotation),this.resizeIframe(annotation),"popup"==annotation.displayoptions&&(0,_jquery.default)("#annotation-modal").on("shown.bs.modal",(function(){self.setModalDraggable("#annotation-modal .modal-dialog")}))}}return _exports.default=ContentBank,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_util=_interopRequireDefault(_util),_modalform=_interopRequireDefault(_modalform),_base=_interopRequireDefault(_base);class ContentBank extends _base.default{onEditFormLoaded(form,event){let self=this,body=form.modal.modal.find(".modal-body");return _util.default.init(M.cfg.courseContextId),body.off("click","#refreshcontentbank").on("click","#refreshcontentbank",(function(e){e.preventDefault(),(0,_jquery.default)(this).find("i").addClass("fa-spin");const currentid=(0,_jquery.default)("[name=contentid]").val();(0,_jquery.default)(".contentbank-container").html('
          \n Loading...
          '),_util.default.refreshContentBank(currentid,M.cfg.courseContextId,(0,_jquery.default)(this).data("editable"),(function(){(0,_jquery.default)("#refreshcontentbank i").removeClass("fa-spin")}))})),body.off("click","#uploadcontentbank").on("click","#uploadcontentbank",(function(e){e.preventDefault();const uploadForm=new _modalform.default({formClass:"core_contentbank\\form\\upload_files",args:{contextid:M.cfg.courseContextId},modalConfig:{title:M.util.get_string("uploadcontent","ivplugin_contentbank")}});uploadForm.addEventListener(uploadForm.events.FORM_SUBMITTED,(e=>{self.addNotification(M.util.get_string("contentuploaded","ivplugin_contentbank"),"success");const contentid=e.detail.returnurl.match(/id=(\d+)/)[1];(0,_jquery.default)("[name=contentid]").val(contentid),setTimeout((function(){(0,_jquery.default)("#refreshcontentbank").trigger("click")}),1e3)})),uploadForm.addEventListener(uploadForm.events.ERROR,(()=>{self.addNotification(M.util.get_string("contentuploaderror","ivplugin_contentbank"))})),uploadForm.show()})),{form:form,event:event}}postContentRender(annotation,callback){return(0,_jquery.default)("#message[data-id='".concat(annotation.id,"']")).addClass("hascontentbank"),!(1==annotation.hascompletion&&"manual"!=annotation.completiontracking&&!annotation.completed)||callback}renderContainer(annotation){super.renderContainer(annotation);let $message=(0,_jquery.default)("#message[data-id='".concat(annotation.id,"']"));$message.find(".modal-body").addClass("p-0");let $completiontoggle=$message.find("#completiontoggle");switch($message.find("#title .info").remove(),annotation.completiontracking){case"complete":$completiontoggle.before(''));break;case"completepass":$completiontoggle.before(''));break;case"completefull":$completiontoggle.before(''))}return $message.find('[data-toggle="tooltip"]').tooltip(),$message}resizeIframe(annotation){const modalbody=document.querySelector("#message[data-id='".concat(annotation.id,"'] .modal-body"));new ResizeObserver((()=>{const iframe=modalbody.querySelector("iframe.h5p-player");if(iframe){const height=iframe.scrollHeight;modalbody.style.height="".concat(height+2e3,"px")}})).observe(modalbody)}async runInteraction(annotation){await this.player.pause();const annoid=annotation.id;let $message,self=this;await this.renderViewer(annotation),$message=this.renderContainer(annotation),async function(annotation){const data=await self.render(annotation);$message.find(".modal-body").html(data).attr("id","content").fadeIn(300),1!=annotation.hascompletion||self.isEditMode()||(annotation.completed||"view"!=annotation.completiontracking||self.toggleCompletion(annoid,"mark-done","automatic"),function(annotation){let listenToEvents=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];const detectH5P=()=>{let H5P;try{H5P=document.querySelector("#message[data-id='".concat(annoid,"'] iframe")).contentWindow.H5P}catch(e){H5P=null}if(null!=H5P){if(void 0===H5P.externalDispatcher)return void requestAnimationFrame(detectH5P);if(!listenToEvents)return;self.isEditMode()&&($message.find("#title .btns .xapi").remove(),$message.find("#title .btns").prepend('
          '.concat(M.util.get_string("xapicheck","ivplugin_contentbank"),"
          ")));let statements=[];try{H5P.externalDispatcher.on("xAPI",(function(event){if("http://adlnet.gov/expapi/verbs/completed"!=event.data.statement.verb.id&&"http://adlnet.gov/expapi/verbs/answered"!=event.data.statement.verb.id||statements.push(event.data.statement),("http://adlnet.gov/expapi/verbs/completed"==event.data.statement.verb.id||"http://adlnet.gov/expapi/verbs/answered"==event.data.statement.verb.id)&&event.data.statement.object.id.indexOf("subContentId")<0){if(self.isEditMode())return(0,_jquery.default)("#message[data-id='".concat(annotation.id,"'] #title .btns .xapi")).remove(),(0,_jquery.default)("#message[data-id='".concat(annotation.id,"'] #title .btns")).prepend('
          \n \n '.concat(M.util.get_string("xapieventdetected","ivplugin_h5pupload"),"\n
          ")),void new Audio(M.cfg.wwwroot+"/mod/interactivevideo/sounds/pop.mp3").play();let complete=!1,textclass="";if(("completepass"==annotation.completiontracking&&event.data.statement.result&&event.data.statement.result.score.scaled>=.5||"completefull"==annotation.completiontracking&&event.data.statement.result&&1==event.data.statement.result.score.scaled||"complete"==annotation.completiontracking)&&(complete=!0),textclass=event.data.statement.result.score.scaled<.5?"fa fa-check text-danger":event.data.statement.result.score.scaled<1?"fa fa-check text-success":"bi bi-check2-all text-success",complete&&!annotation.completed){let details={};const completeTime=new Date;details.xp=annotation.xp,"1"==annotation.char1&&(details.xp=(event.data.statement.result.score.scaled*annotation.xp).toFixed(2)),details.duration=completeTime.getTime()-(0,_jquery.default)("#video-wrapper").data("timestamp"),details.timecompleted=completeTime.getTime();const completiontime=completeTime.toLocaleString();let duration=self.formatTime(details.duration/1e3);details.reportView='\n '.concat(completiontime,'').concat(duration,'\n \n ').concat(event.data.statement.result.score.raw,"/").concat(event.data.statement.result.score.max,"'>\n
          ').concat(Number(details.xp),""),details.details=statements,self.toggleCompletion(annoid,"mark-done","automatic",details)}}}))}catch(e){requestAnimationFrame(detectH5P)}}else requestAnimationFrame(detectH5P)};requestAnimationFrame(detectH5P)}(annotation,!annotation.completed&&"manual"!=annotation.completiontracking))}(annotation),this.enableManualCompletion(annotation),this.resizeIframe(annotation),"popup"==annotation.displayoptions&&(0,_jquery.default)("#annotation-modal").on("shown.bs.modal",(function(){self.setModalDraggable("#annotation-modal .modal-dialog")}))}}return _exports.default=ContentBank,_exports.default})); //# sourceMappingURL=main.min.js.map \ No newline at end of file diff --git a/plugins/contentbank/amd/build/main.min.js.map b/plugins/contentbank/amd/build/main.min.js.map index 9f67b5b..fc13f4e 100644 --- a/plugins/contentbank/amd/build/main.min.js.map +++ b/plugins/contentbank/amd/build/main.min.js.map @@ -1 +1 @@ -{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Main class for content bank\n *\n * @module ivplugin_contentbank/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport contentbankutil from 'ivplugin_contentbank/util';\nimport ModalForm from 'core_form/modalform';\nimport Base from 'mod_interactivevideo/type/base';\n\nexport default class ContentBank extends Base {\n /**\n * Called when the edit form is loaded.\n * @param {Object} form The form object\n * @param {Event} event The event object\n * @return {void}\n */\n onEditFormLoaded(form, event) {\n let self = this;\n let body = form.modal.modal.find('.modal-body');\n contentbankutil.init(M.cfg.courseContextId);\n // Refresh the content from the content bank.\n body.off('click', '#refreshcontentbank').on('click', '#refreshcontentbank', function(e) {\n e.preventDefault();\n $(this).find('i').addClass('fa-spin');\n const currentid = $('[name=contentid]').val();\n $('.contentbank-container').html(`
          \n Loading...
          `);\n contentbankutil.refreshContentBank(currentid, M.cfg.courseContextId, $(this).data('editable'), function() {\n $('#refreshcontentbank i').removeClass('fa-spin');\n });\n });\n\n // Upload a new content.\n body.off('click', '#uploadcontentbank').on('click', '#uploadcontentbank', function(e) {\n e.preventDefault();\n const uploadForm = new ModalForm({\n formClass: \"core_contentbank\\\\form\\\\upload_files\",\n args: {\n contextid: M.cfg.courseContextId,\n },\n modalConfig: {\n title: M.util.get_string('uploadcontent', 'ivplugin_contentbank')\n }\n });\n\n uploadForm.addEventListener(uploadForm.events.FORM_SUBMITTED, (e) => {\n self.addNotification(M.util.get_string('contentuploaded', 'ivplugin_contentbank'), 'success');\n const returnurl = e.detail.returnurl;\n const contentid = returnurl.match(/id=(\\d+)/)[1];\n $('[name=contentid]').val(contentid);\n setTimeout(function() {\n $('#refreshcontentbank').trigger('click');\n }, 1000);\n });\n\n uploadForm.addEventListener(uploadForm.events.ERROR, () => {\n self.addNotification(M.util.get_string('contentuploaderror', 'ivplugin_contentbank'));\n });\n\n uploadForm.show();\n });\n return {form, event};\n }\n\n /**\n * Handles the rendering of content annotations and applies specific classes and conditions.\n *\n * @param {Object} annotation - The annotation object containing details about the content.\n * @param {Function} callback - The callback function to be executed if certain conditions are met.\n * @returns {boolean|Function} - Returns true if the annotation does not meet the conditions for completion tracking,\n * otherwise returns the callback function.\n */\n postContentRender(annotation, callback) {\n $(`#message[data-id='${annotation.id}']`).addClass('hascontentbank');\n if (annotation.hascompletion == 1\n && annotation.completiontracking != 'manual' && !annotation.completed) {\n return callback;\n }\n return true;\n }\n\n /**\n * Initialize the container to display the annotation\n * @param {Object} annotation The annotation object\n * @returns {void}\n */\n renderContainer(annotation) {\n super.renderContainer(annotation);\n let $message = $(`#message[data-id='${annotation.id}']`);\n $message.find('.modal-body').addClass('p-0');\n let $completiontoggle = $message.find('#completiontoggle');\n $message.find('#title .info').remove();\n switch (annotation.completiontracking) {\n case 'complete':\n $completiontoggle.before(``);\n break;\n case 'completepass':\n $completiontoggle.before(``);\n break;\n case 'completefull':\n $completiontoggle.before(``);\n break;\n }\n $message.find('[data-toggle=\"tooltip\"]').tooltip();\n return $message;\n }\n\n /**\n * Resizes the iframe within a modal body based on the height of the iframe content.\n *\n * @param {Object} annotation - The annotation object containing the id.\n */\n resizeIframe(annotation) {\n const modalbody = document.querySelector(`#message[data-id='${annotation.id}'] .modal-body`);\n const resizeObserver = new ResizeObserver(() => {\n const iframe = modalbody.querySelector('iframe.h5p-player');\n if (iframe) {\n const height = iframe.scrollHeight;\n modalbody.style.height = `${height + 2000}px`;\n }\n });\n\n resizeObserver.observe(modalbody);\n }\n\n /**\n * Run the interaction\n * @param {Object} annotation The annotation object\n * @returns {void}\n */\n async runInteraction(annotation) {\n await this.player.pause();\n const annoid = annotation.id;\n let self = this;\n let $message;\n\n const xAPICheck = (annotation, listenToEvents = true) => {\n const detectH5P = () => {\n let H5P;\n try { // Try to get the H5P object.\n H5P = document.querySelector(`#message[data-id='${annoid}'] iframe`).contentWindow.H5P;\n } catch (e) {\n H5P = null;\n }\n if (typeof H5P !== 'undefined' && H5P !== null) {\n\n if (!listenToEvents) {\n return;\n }\n if (self.isEditMode()) {\n $message.find(`#title .btns .xapi`).remove();\n $message.find(`#title .btns`)\n .prepend(`
          ${M.util.get_string('xapicheck', 'ivplugin_contentbank')}
          `);\n }\n let statements = [];\n try {\n H5P.externalDispatcher.on('xAPI', function(event) {\n if (event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/completed'\n || event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/answered') {\n statements.push(event.data.statement);\n }\n if ((event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/completed'\n || event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/answered')\n && event.data.statement.object.id.indexOf('subContentId') < 0) {\n if (self.isEditMode()) {\n $(`#message[data-id='${annotation.id}'] #title .btns .xapi`).remove();\n $(`#message[data-id='${annotation.id}'] #title .btns`)\n .prepend(`
          \n \n ${M.util.get_string('xapieventdetected', 'ivplugin_h5pupload')}\n
          `);\n const audio = new Audio(M.cfg.wwwroot + '/mod/interactivevideo/sounds/pop.mp3');\n audio.play();\n return;\n }\n let complete = false;\n let textclass = '';\n if (annotation.completiontracking == 'completepass'\n && event.data.statement.result && event.data.statement.result.score.scaled >= 0.5) {\n complete = true;\n } else if (annotation.completiontracking == 'completefull'\n && event.data.statement.result && event.data.statement.result.score.scaled == 1) {\n complete = true;\n } else if (annotation.completiontracking == 'complete') {\n complete = true;\n }\n if (event.data.statement.result.score.scaled < 0.5) {\n textclass = 'fa fa-check text-danger';\n } else if (event.data.statement.result.score.scaled < 1) {\n textclass = 'fa fa-check text-success';\n } else {\n textclass = 'bi bi-check2-all text-success';\n }\n if (complete && !annotation.completed) {\n let details = {};\n const completeTime = new Date();\n details.xp = annotation.xp;\n if (annotation.char1 == '1') { // Partial points.\n details.xp = (event.data.statement.result.score.scaled * annotation.xp).toFixed(2);\n }\n details.duration = completeTime.getTime() - $('#video-wrapper').data('timestamp');\n details.timecompleted = completeTime.getTime();\n const completiontime = completeTime.toLocaleString();\n let duration = self.formatTime(details.duration / 1000);\n details.reportView = `\n
          ${Number(details.xp)}
          `;\n details.details = statements;\n self.toggleCompletion(annoid, 'mark-done', 'automatic', details);\n }\n }\n });\n } catch (e) {\n requestAnimationFrame(detectH5P);\n }\n } else {\n requestAnimationFrame(detectH5P);\n }\n };\n requestAnimationFrame(detectH5P);\n };\n\n const applyContent = async function(annotation) {\n const data = await self.render(annotation);\n $message.find(`.modal-body`).html(data).attr('id', 'content').fadeIn(300);\n if (annotation.hascompletion != 1 || self.isEditMode()) {\n return;\n }\n if (!annotation.completed && annotation.completiontracking == 'view') {\n self.toggleCompletion(annoid, 'mark-done', 'automatic');\n }\n xAPICheck(annotation, !annotation.completed && annotation.completiontracking != 'manual');\n };\n\n\n await this.renderViewer(annotation);\n $message = this.renderContainer(annotation);\n applyContent(annotation);\n\n this.enableManualCompletion(annotation);\n\n this.resizeIframe(annotation);\n\n if (annotation.displayoptions == 'popup') {\n $('#annotation-modal').on('shown.bs.modal', function() {\n self.setModalDraggable('#annotation-modal .modal-dialog');\n });\n }\n }\n}"],"names":["ContentBank","Base","onEditFormLoaded","form","event","self","this","body","modal","find","init","M","cfg","courseContextId","off","on","e","preventDefault","addClass","currentid","val","html","refreshContentBank","data","removeClass","uploadForm","ModalForm","formClass","args","contextid","modalConfig","title","util","get_string","addEventListener","events","FORM_SUBMITTED","addNotification","contentid","detail","returnurl","match","setTimeout","trigger","ERROR","show","postContentRender","annotation","callback","id","hascompletion","completiontracking","completed","renderContainer","$message","$completiontoggle","remove","before","tooltip","resizeIframe","modalbody","document","querySelector","ResizeObserver","iframe","height","scrollHeight","style","observe","player","pause","annoid","renderViewer","async","render","attr","fadeIn","isEditMode","toggleCompletion","listenToEvents","detectH5P","H5P","contentWindow","prepend","statements","externalDispatcher","statement","verb","push","object","indexOf","Audio","wwwroot","play","complete","textclass","result","score","scaled","details","completeTime","Date","xp","char1","toFixed","duration","getTime","timecompleted","completiontime","toLocaleString","formatTime","reportView","raw","max","Number","requestAnimationFrame","xAPICheck","applyContent","enableManualCompletion","displayoptions","setModalDraggable"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Main class for content bank\n *\n * @module ivplugin_contentbank/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport contentbankutil from 'ivplugin_contentbank/util';\nimport ModalForm from 'core_form/modalform';\nimport Base from 'mod_interactivevideo/type/base';\n\nexport default class ContentBank extends Base {\n /**\n * Called when the edit form is loaded.\n * @param {Object} form The form object\n * @param {Event} event The event object\n * @return {void}\n */\n onEditFormLoaded(form, event) {\n let self = this;\n let body = form.modal.modal.find('.modal-body');\n contentbankutil.init(M.cfg.courseContextId);\n // Refresh the content from the content bank.\n body.off('click', '#refreshcontentbank').on('click', '#refreshcontentbank', function(e) {\n e.preventDefault();\n $(this).find('i').addClass('fa-spin');\n const currentid = $('[name=contentid]').val();\n $('.contentbank-container').html(`
          \n Loading...
          `);\n contentbankutil.refreshContentBank(currentid, M.cfg.courseContextId, $(this).data('editable'), function() {\n $('#refreshcontentbank i').removeClass('fa-spin');\n });\n });\n\n // Upload a new content.\n body.off('click', '#uploadcontentbank').on('click', '#uploadcontentbank', function(e) {\n e.preventDefault();\n const uploadForm = new ModalForm({\n formClass: \"core_contentbank\\\\form\\\\upload_files\",\n args: {\n contextid: M.cfg.courseContextId,\n },\n modalConfig: {\n title: M.util.get_string('uploadcontent', 'ivplugin_contentbank')\n }\n });\n\n uploadForm.addEventListener(uploadForm.events.FORM_SUBMITTED, (e) => {\n self.addNotification(M.util.get_string('contentuploaded', 'ivplugin_contentbank'), 'success');\n const returnurl = e.detail.returnurl;\n const contentid = returnurl.match(/id=(\\d+)/)[1];\n $('[name=contentid]').val(contentid);\n setTimeout(function() {\n $('#refreshcontentbank').trigger('click');\n }, 1000);\n });\n\n uploadForm.addEventListener(uploadForm.events.ERROR, () => {\n self.addNotification(M.util.get_string('contentuploaderror', 'ivplugin_contentbank'));\n });\n\n uploadForm.show();\n });\n return {form, event};\n }\n\n /**\n * Handles the rendering of content annotations and applies specific classes and conditions.\n *\n * @param {Object} annotation - The annotation object containing details about the content.\n * @param {Function} callback - The callback function to be executed if certain conditions are met.\n * @returns {boolean|Function} - Returns true if the annotation does not meet the conditions for completion tracking,\n * otherwise returns the callback function.\n */\n postContentRender(annotation, callback) {\n $(`#message[data-id='${annotation.id}']`).addClass('hascontentbank');\n if (annotation.hascompletion == 1\n && annotation.completiontracking != 'manual' && !annotation.completed) {\n return callback;\n }\n return true;\n }\n\n /**\n * Initialize the container to display the annotation\n * @param {Object} annotation The annotation object\n * @returns {void}\n */\n renderContainer(annotation) {\n super.renderContainer(annotation);\n let $message = $(`#message[data-id='${annotation.id}']`);\n $message.find('.modal-body').addClass('p-0');\n let $completiontoggle = $message.find('#completiontoggle');\n $message.find('#title .info').remove();\n switch (annotation.completiontracking) {\n case 'complete':\n $completiontoggle.before(``);\n break;\n case 'completepass':\n $completiontoggle.before(``);\n break;\n case 'completefull':\n $completiontoggle.before(``);\n break;\n }\n $message.find('[data-toggle=\"tooltip\"]').tooltip();\n return $message;\n }\n\n /**\n * Resizes the iframe within a modal body based on the height of the iframe content.\n *\n * @param {Object} annotation - The annotation object containing the id.\n */\n resizeIframe(annotation) {\n const modalbody = document.querySelector(`#message[data-id='${annotation.id}'] .modal-body`);\n const resizeObserver = new ResizeObserver(() => {\n const iframe = modalbody.querySelector('iframe.h5p-player');\n if (iframe) {\n const height = iframe.scrollHeight;\n modalbody.style.height = `${height + 2000}px`;\n }\n });\n\n resizeObserver.observe(modalbody);\n }\n\n /**\n * Run the interaction\n * @param {Object} annotation The annotation object\n * @returns {void}\n */\n async runInteraction(annotation) {\n await this.player.pause();\n const annoid = annotation.id;\n let self = this;\n let $message;\n\n const xAPICheck = (annotation, listenToEvents = true) => {\n const detectH5P = () => {\n let H5P;\n try { // Try to get the H5P object.\n H5P = document.querySelector(`#message[data-id='${annoid}'] iframe`).contentWindow.H5P;\n } catch (e) {\n H5P = null;\n }\n if (typeof H5P !== 'undefined' && H5P !== null) {\n if (H5P.externalDispatcher === undefined) {\n requestAnimationFrame(detectH5P);\n return;\n }\n if (!listenToEvents) {\n return;\n }\n if (self.isEditMode()) {\n $message.find(`#title .btns .xapi`).remove();\n $message.find(`#title .btns`)\n .prepend(`
          ${M.util.get_string('xapicheck', 'ivplugin_contentbank')}
          `);\n }\n let statements = [];\n try {\n H5P.externalDispatcher.on('xAPI', function(event) {\n if (event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/completed'\n || event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/answered') {\n statements.push(event.data.statement);\n }\n if ((event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/completed'\n || event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/answered')\n && event.data.statement.object.id.indexOf('subContentId') < 0) {\n if (self.isEditMode()) {\n $(`#message[data-id='${annotation.id}'] #title .btns .xapi`).remove();\n $(`#message[data-id='${annotation.id}'] #title .btns`)\n .prepend(`
          \n \n ${M.util.get_string('xapieventdetected', 'ivplugin_h5pupload')}\n
          `);\n const audio = new Audio(M.cfg.wwwroot + '/mod/interactivevideo/sounds/pop.mp3');\n audio.play();\n return;\n }\n let complete = false;\n let textclass = '';\n if (annotation.completiontracking == 'completepass'\n && event.data.statement.result && event.data.statement.result.score.scaled >= 0.5) {\n complete = true;\n } else if (annotation.completiontracking == 'completefull'\n && event.data.statement.result && event.data.statement.result.score.scaled == 1) {\n complete = true;\n } else if (annotation.completiontracking == 'complete') {\n complete = true;\n }\n if (event.data.statement.result.score.scaled < 0.5) {\n textclass = 'fa fa-check text-danger';\n } else if (event.data.statement.result.score.scaled < 1) {\n textclass = 'fa fa-check text-success';\n } else {\n textclass = 'bi bi-check2-all text-success';\n }\n if (complete && !annotation.completed) {\n let details = {};\n const completeTime = new Date();\n details.xp = annotation.xp;\n if (annotation.char1 == '1') { // Partial points.\n details.xp = (event.data.statement.result.score.scaled * annotation.xp).toFixed(2);\n }\n details.duration = completeTime.getTime() - $('#video-wrapper').data('timestamp');\n details.timecompleted = completeTime.getTime();\n const completiontime = completeTime.toLocaleString();\n let duration = self.formatTime(details.duration / 1000);\n details.reportView = `\n
          ${Number(details.xp)}
          `;\n details.details = statements;\n self.toggleCompletion(annoid, 'mark-done', 'automatic', details);\n }\n }\n });\n } catch (e) {\n requestAnimationFrame(detectH5P);\n }\n } else {\n requestAnimationFrame(detectH5P);\n }\n };\n requestAnimationFrame(detectH5P);\n };\n\n const applyContent = async function(annotation) {\n const data = await self.render(annotation);\n $message.find(`.modal-body`).html(data).attr('id', 'content').fadeIn(300);\n if (annotation.hascompletion != 1 || self.isEditMode()) {\n return;\n }\n if (!annotation.completed && annotation.completiontracking == 'view') {\n self.toggleCompletion(annoid, 'mark-done', 'automatic');\n }\n xAPICheck(annotation, !annotation.completed && annotation.completiontracking != 'manual');\n };\n\n\n await this.renderViewer(annotation);\n $message = this.renderContainer(annotation);\n applyContent(annotation);\n\n this.enableManualCompletion(annotation);\n\n this.resizeIframe(annotation);\n\n if (annotation.displayoptions == 'popup') {\n $('#annotation-modal').on('shown.bs.modal', function() {\n self.setModalDraggable('#annotation-modal .modal-dialog');\n });\n }\n }\n}"],"names":["ContentBank","Base","onEditFormLoaded","form","event","self","this","body","modal","find","init","M","cfg","courseContextId","off","on","e","preventDefault","addClass","currentid","val","html","refreshContentBank","data","removeClass","uploadForm","ModalForm","formClass","args","contextid","modalConfig","title","util","get_string","addEventListener","events","FORM_SUBMITTED","addNotification","contentid","detail","returnurl","match","setTimeout","trigger","ERROR","show","postContentRender","annotation","callback","id","hascompletion","completiontracking","completed","renderContainer","$message","$completiontoggle","remove","before","tooltip","resizeIframe","modalbody","document","querySelector","ResizeObserver","iframe","height","scrollHeight","style","observe","player","pause","annoid","renderViewer","async","render","attr","fadeIn","isEditMode","toggleCompletion","listenToEvents","detectH5P","H5P","contentWindow","undefined","externalDispatcher","requestAnimationFrame","prepend","statements","statement","verb","push","object","indexOf","Audio","wwwroot","play","complete","textclass","result","score","scaled","details","completeTime","Date","xp","char1","toFixed","duration","getTime","timecompleted","completiontime","toLocaleString","formatTime","reportView","raw","max","Number","xAPICheck","applyContent","enableManualCompletion","displayoptions","setModalDraggable"],"mappings":"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"} \ No newline at end of file diff --git a/plugins/contentbank/amd/build/util.min.js b/plugins/contentbank/amd/build/util.min.js index e69f44b..ef43ce0 100644 --- a/plugins/contentbank/amd/build/util.min.js +++ b/plugins/contentbank/amd/build/util.min.js @@ -5,6 +5,6 @@ define("ivplugin_contentbank/util",["exports","jquery","core/ajax"],(function(_e * @module ivplugin_contentbank/util * @copyright 2024 Sokunthearith Makara * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_ajax=_interopRequireDefault(_ajax);const getcontent=(id,contextid,target)=>{_ajax.default.call([{args:{id:id,contextid:contextid},contextid:contextid,methodname:"ivplugin_contentbank_getitem"}])[0].then((response=>target?(0,_jquery.default)(target).html(response.item):response)).catch((()=>{}))};var _default={init:contextid=>{(0,_jquery.default)(document).off("click",".contentbank-container .contentbank-item .contentbank-item-details").on("click",".contentbank-container .contentbank-item .contentbank-item-details",(function(e){e.preventDefault(),(0,_jquery.default)(".contentbank-container .contentbank-item").removeClass("selected"),(0,_jquery.default)(this).closest(".contentbank-item").addClass("selected"),(0,_jquery.default)("#contentbank-preview").empty();const id=(0,_jquery.default)(this).closest(".contentbank-item").data("contentid");(0,_jquery.default)("[name=contentid]").val(id)})),(0,_jquery.default)(document).off("click",".contentbank-container .contentbank-item .contentbankview").on("click",".contentbank-container .contentbank-item .contentbankview",(function(e){e.preventDefault(),(0,_jquery.default)(".contentbank-container .contentbank-item").removeClass("selected");let targetContentbank=(0,_jquery.default)(this).closest(".contentbank-item");targetContentbank.addClass("selected");const id=targetContentbank.data("contentid");(0,_jquery.default)("#contentbank-preview").empty(),(0,_jquery.default)("#contentbank-preview").attr("data-contentid",id),(0,_jquery.default)("[name=contentid]").val(id),getcontent(id,contextid,"#contentbank-preview");const xapicheck=M.util.get_string("xapicheck","ivplugin_contentbank");let H5P;const checkH5P=()=>{try{H5P=document.querySelector("#contentbank-preview iframe.h5p-player").contentWindow.H5P}catch(e){H5P=null}null!=H5P?((0,_jquery.default)("#contentbank-preview .xapi").remove(),(0,_jquery.default)("#contentbank-preview[data-contentid=".concat(id,"]")).prepend('
          \n '.concat(xapicheck,"
          ")),H5P.externalDispatcher.on("xAPI",(function(event){if(("http://adlnet.gov/expapi/verbs/completed"==event.data.statement.verb.id||"http://adlnet.gov/expapi/verbs/answered"==event.data.statement.verb.id)&&event.data.statement.object.id.indexOf("subContentId")<0){(0,_jquery.default)("#contentbank-preview .xapi").remove(),(0,_jquery.default)("#contentbank-preview").prepend('
          \n '.concat(M.util.get_string("xapieventdetected","ivplugin_contentbank"),"
          "));new Audio(M.cfg.wwwroot+"/mod/interactivevideo/sounds/pop.mp3").play()}}))):requestAnimationFrame(checkH5P)};requestAnimationFrame(checkH5P)}))},getcontent:getcontent,refreshContentBank:async function(id,coursecontextid){let edit=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],callback=arguments.length>3?arguments[3]:void 0;(0,_jquery.default)("#contentbank-preview").empty();let contentbankitems=await _ajax.default.call([{args:{contextid:coursecontextid},contextid:coursecontextid,methodname:"ivplugin_contentbank_getitems"}])[0],contents=JSON.parse(contentbankitems.contents),contentbank=(0,_jquery.default)(".modal-body form .contentbank-container");contentbank.empty(),contents.forEach((function(content){const editurl=M.cfg.wwwroot+"/contentbank/edit.php?contextid="+coursecontextid+"&id="+content.id+"&plugin="+content.type;let html='
          ';content.icon?html+='':html+='
          ',html+='
          '+content.name+"
          ",html+='
          \n
          '),edit&&(html+='\n ')),html+="
          ",contentbank.append(html)})),callback&&callback()}};return _exports.default=_default,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_ajax=_interopRequireDefault(_ajax);const getcontent=(id,contextid,target)=>{_ajax.default.call([{args:{id:id,contextid:contextid},contextid:contextid,methodname:"ivplugin_contentbank_getitem"}])[0].then((response=>target?(0,_jquery.default)(target).html(response.item):response)).catch((()=>{}))};var _default={init:contextid=>{(0,_jquery.default)(document).off("click",".contentbank-container .contentbank-item .contentbank-item-details").on("click",".contentbank-container .contentbank-item .contentbank-item-details",(function(e){e.preventDefault(),(0,_jquery.default)(".contentbank-container .contentbank-item").removeClass("selected"),(0,_jquery.default)(this).closest(".contentbank-item").addClass("selected"),(0,_jquery.default)("#contentbank-preview").empty();const id=(0,_jquery.default)(this).closest(".contentbank-item").data("contentid");(0,_jquery.default)("[name=contentid]").val(id)})),(0,_jquery.default)(document).off("click",".contentbank-container .contentbank-item .contentbankview").on("click",".contentbank-container .contentbank-item .contentbankview",(function(e){e.preventDefault(),(0,_jquery.default)(".contentbank-container .contentbank-item").removeClass("selected");let targetContentbank=(0,_jquery.default)(this).closest(".contentbank-item");targetContentbank.addClass("selected");const id=targetContentbank.data("contentid");(0,_jquery.default)("#contentbank-preview").empty(),(0,_jquery.default)("#contentbank-preview").attr("data-contentid",id),(0,_jquery.default)("[name=contentid]").val(id),getcontent(id,contextid,"#contentbank-preview");const xapicheck=M.util.get_string("xapicheck","ivplugin_contentbank");let H5P;const checkH5P=()=>{try{H5P=document.querySelector("#contentbank-preview iframe.h5p-player").contentWindow.H5P}catch(e){H5P=null}if(null!=H5P){if(void 0===H5P.externalDispatcher)return void requestAnimationFrame(checkH5P);(0,_jquery.default)("#contentbank-preview .xapi").remove(),(0,_jquery.default)("#contentbank-preview[data-contentid=".concat(id,"]")).prepend('
          \n '.concat(xapicheck,"
          ")),H5P.externalDispatcher.on("xAPI",(function(event){if(("http://adlnet.gov/expapi/verbs/completed"==event.data.statement.verb.id||"http://adlnet.gov/expapi/verbs/answered"==event.data.statement.verb.id)&&event.data.statement.object.id.indexOf("subContentId")<0){(0,_jquery.default)("#contentbank-preview .xapi").remove(),(0,_jquery.default)("#contentbank-preview").prepend('
          \n '.concat(M.util.get_string("xapieventdetected","ivplugin_contentbank"),"
          "));new Audio(M.cfg.wwwroot+"/mod/interactivevideo/sounds/pop.mp3").play()}}))}else requestAnimationFrame(checkH5P)};requestAnimationFrame(checkH5P)}))},getcontent:getcontent,refreshContentBank:async function(id,coursecontextid){let edit=!(arguments.length>2&&void 0!==arguments[2])||arguments[2],callback=arguments.length>3?arguments[3]:void 0;(0,_jquery.default)("#contentbank-preview").empty();let contentbankitems=await _ajax.default.call([{args:{contextid:coursecontextid},contextid:coursecontextid,methodname:"ivplugin_contentbank_getitems"}])[0],contents=JSON.parse(contentbankitems.contents),contentbank=(0,_jquery.default)(".modal-body form .contentbank-container");contentbank.empty(),contents.forEach((function(content){const editurl=M.cfg.wwwroot+"/contentbank/edit.php?contextid="+coursecontextid+"&id="+content.id+"&plugin="+content.type;let html='
          ';content.icon?html+='':html+='
          ',html+='
          '+content.name+"
          ",html+='
          \n
          '),edit&&(html+='\n ')),html+="
          ",contentbank.append(html)})),callback&&callback()}};return _exports.default=_default,_exports.default})); //# sourceMappingURL=util.min.js.map \ No newline at end of file diff --git a/plugins/contentbank/amd/build/util.min.js.map b/plugins/contentbank/amd/build/util.min.js.map index f6bc673..1260cb4 100644 --- a/plugins/contentbank/amd/build/util.min.js.map +++ b/plugins/contentbank/amd/build/util.min.js.map @@ -1 +1 @@ -{"version":3,"file":"util.min.js","sources":["../src/util.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Content bank utility functions\n *\n * @module ivplugin_contentbank/util\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Ajax from 'core/ajax';\n/**\n * Fetches content from the content bank and updates the target element if provided.\n *\n * @param {number} id - The ID of the content item to fetch.\n * @param {number} contextid - The context ID where the content item resides.\n * @param {string} [target] - The optional target element selector to update with the fetched content.\n * @returns {Promise} A promise that resolves with the response or updates the target element with the fetched content.\n */\nconst getcontent = (id, contextid, target) => {\n Ajax.call([{\n args: {\n id: id,\n contextid: contextid,\n },\n contextid: contextid,\n methodname: 'ivplugin_contentbank_getitem',\n }])[0].then((response) => {\n if (target) {\n return $(target).html(response.item);\n } else {\n return response;\n }\n }).catch(() => {\n // Do nothing.\n });\n};\n\n/**\n * Initializes event listeners for content bank interactions.\n *\n * @param {number} contextid - The context ID for the content bank.\n *\n * This function sets up click event handlers for elements within the content bank container.\n * It handles the selection of content items, updates the preview area, and manages xAPI event detection.\n *\n * Event Listeners:\n * - Click on content item details: Selects the item and updates the hidden input with the content ID.\n * - Click on content item view: Selects the item, updates the preview area, and sets up xAPI event detection.\n *\n * xAPI Event Detection:\n * - Monitors for xAPI events (completed, answered) emitted by the content.\n * - Displays a notification if such events are detected.\n */\nconst init = (contextid) => {\n $(document).off('click', '.contentbank-container .contentbank-item .contentbank-item-details')\n .on('click', '.contentbank-container .contentbank-item .contentbank-item-details', function(e) {\n e.preventDefault();\n $('.contentbank-container .contentbank-item').removeClass('selected');\n $(this).closest('.contentbank-item').addClass('selected');\n $('#contentbank-preview').empty();\n const id = $(this).closest('.contentbank-item').data('contentid');\n $('[name=contentid]').val(id);\n });\n\n $(document).off('click', '.contentbank-container .contentbank-item .contentbankview')\n .on('click', '.contentbank-container .contentbank-item .contentbankview', function(e) {\n e.preventDefault();\n $('.contentbank-container .contentbank-item').removeClass('selected');\n let targetContentbank = $(this).closest('.contentbank-item');\n targetContentbank.addClass('selected');\n const id = targetContentbank.data('contentid');\n $('#contentbank-preview').empty();\n $('#contentbank-preview').attr('data-contentid', id);\n $('[name=contentid]').val(id);\n // Preview selected content.\n getcontent(id, contextid, '#contentbank-preview');\n\n // Handle xAPI event. We want user to be able to check if the content emits xAPI events (completed, answered)\n // because some content types may not emit these events. Then user can decide\n // if they want students to mark it complete manually or automatically.\n const xapicheck = M.util.get_string('xapicheck', 'ivplugin_contentbank');\n let H5P;\n\n const checkH5P = () => {\n try { // Try to get the H5P object.\n H5P = document.querySelector('#contentbank-preview iframe.h5p-player').contentWindow.H5P;\n } catch (e) {\n H5P = null;\n }\n\n if (typeof H5P !== 'undefined' && H5P !== null) {\n $(\"#contentbank-preview .xapi\").remove();\n $(`#contentbank-preview[data-contentid=${id}]`)\n .prepend(`
          \n ${xapicheck}
          `);\n H5P.externalDispatcher.on('xAPI', function(event) {\n if ((event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/completed'\n || event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/answered')\n && event.data.statement.object.id.indexOf('subContentId') < 0) {\n $(\"#contentbank-preview .xapi\").remove();\n $(\"#contentbank-preview\")\n .prepend(`
          \n ${M.util.get_string('xapieventdetected', 'ivplugin_contentbank')}
          `);\n const audio = new Audio(M.cfg.wwwroot + '/mod/interactivevideo/sounds/pop.mp3');\n audio.play();\n }\n });\n } else {\n requestAnimationFrame(checkH5P);\n }\n };\n\n requestAnimationFrame(checkH5P);\n });\n};\n\n/**\n * Refreshes the content bank by fetching and displaying content items.\n *\n * @param {number} id - The ID of the content to be highlighted.\n * @param {number} coursecontextid - The context ID of the course.\n * @param {boolean} [edit=true] - Whether to show edit options for the content items.\n * @param {Function} [callback] - Optional callback function to be executed after refreshing the content bank.\n * @returns {Promise} - A promise that resolves when the content bank is refreshed.\n */\nconst refreshContentBank = async (id, coursecontextid, edit = true, callback) => {\n $('#contentbank-preview').empty();\n let contentbankitems = await Ajax.call([{\n args: {\n contextid: coursecontextid\n },\n contextid: coursecontextid,\n methodname: 'ivplugin_contentbank_getitems',\n }])[0];\n\n let contents = JSON.parse(contentbankitems.contents);\n let contentbank = $('.modal-body form .contentbank-container');\n contentbank.empty();\n contents.forEach(function(content) {\n const editurl = M.cfg.wwwroot + '/contentbank/edit.php?contextid='\n + coursecontextid + '&id=' + content.id + '&plugin=' + content.type;\n let html = '
          ';\n if (content.icon) {\n html += '';\n } else {\n html += '
          ';\n }\n\n html += '
          ' + content.name + '
          ';\n html += `
          \n
          `;\n if (edit) {\n html += `\n `;\n }\n\n html += `
          `;\n\n\n contentbank.append(html);\n });\n\n if (callback) {\n callback();\n }\n};\n\nexport default {init, getcontent, refreshContentBank};"],"names":["getcontent","id","contextid","target","call","args","methodname","then","response","html","item","catch","init","document","off","on","e","preventDefault","removeClass","this","closest","addClass","empty","data","val","targetContentbank","attr","xapicheck","M","util","get_string","H5P","checkH5P","querySelector","contentWindow","remove","prepend","externalDispatcher","event","statement","verb","object","indexOf","Audio","cfg","wwwroot","play","requestAnimationFrame","refreshContentBank","async","coursecontextid","edit","callback","contentbankitems","Ajax","contents","JSON","parse","contentbank","forEach","content","editurl","type","icon","name","append"],"mappings":";;;;;;;uKAgCMA,WAAa,CAACC,GAAIC,UAAWC,wBAC1BC,KAAK,CAAC,CACPC,KAAM,CACFJ,GAAIA,GACJC,UAAWA,WAEfA,UAAWA,UACXI,WAAY,kCACZ,GAAGC,MAAMC,UACLL,QACO,mBAAEA,QAAQM,KAAKD,SAASE,MAExBF,WAEZG,OAAM,uBA4IE,CAACC,KAvHFV,gCACRW,UAAUC,IAAI,QAAS,sEACpBC,GAAG,QAAS,sEAAsE,SAASC,GACxFA,EAAEC,qCACA,4CAA4CC,YAAY,gCACxDC,MAAMC,QAAQ,qBAAqBC,SAAS,gCAC5C,wBAAwBC,cACpBrB,IAAK,mBAAEkB,MAAMC,QAAQ,qBAAqBG,KAAK,iCACnD,oBAAoBC,IAAIvB,2BAGhCY,UAAUC,IAAI,QAAS,6DACpBC,GAAG,QAAS,6DAA6D,SAASC,GAC/EA,EAAEC,qCACA,4CAA4CC,YAAY,gBACtDO,mBAAoB,mBAAEN,MAAMC,QAAQ,qBACxCK,kBAAkBJ,SAAS,kBACrBpB,GAAKwB,kBAAkBF,KAAK,iCAChC,wBAAwBD,4BACxB,wBAAwBI,KAAK,iBAAkBzB,wBAC/C,oBAAoBuB,IAAIvB,IAE1BD,WAAWC,GAAIC,UAAW,8BAKpByB,UAAYC,EAAEC,KAAKC,WAAW,YAAa,4BAC7CC,UAEEC,SAAW,SAETD,IAAMlB,SAASoB,cAAc,0CAA0CC,cAAcH,IACvF,MAAOf,GACLe,IAAM,KAGN,MAAOA,yBACL,8BAA8BI,2EACSlC,SACpCmC,8HACPT,qBACEI,IAAIM,mBAAmBtB,GAAG,QAAQ,SAASuB,WACF,4CAAhCA,MAAMf,KAAKgB,UAAUC,KAAKvC,IACQ,2CAAhCqC,MAAMf,KAAKgB,UAAUC,KAAKvC,KAC1BqC,MAAMf,KAAKgB,UAAUE,OAAOxC,GAAGyC,QAAQ,gBAAkB,EAAG,qBAC7D,8BAA8BP,6BAC9B,wBACGC,oKACyBR,EAAEC,KAAKC,WAAW,oBAAqB,mCACvD,IAAIa,MAAMf,EAAEgB,IAAIC,QAAU,wCAClCC,YAIdC,sBAAsBf,WAI9Be,sBAAsBf,cA4DZhC,WAAAA,WAAYgD,mBA/CPC,eAAOhD,GAAIiD,qBAAiBC,gEAAaC,oEAC9D,wBAAwB9B,YACtB+B,uBAAyBC,cAAKlD,KAAK,CAAC,CACpCC,KAAM,CACFH,UAAWgD,iBAEfhD,UAAWgD,gBACX5C,WAAY,mCACZ,GAEAiD,SAAWC,KAAKC,MAAMJ,iBAAiBE,UACvCG,aAAc,mBAAE,2CACpBA,YAAYpC,QACZiC,SAASI,SAAQ,SAASC,eAChBC,QAAUjC,EAAEgB,IAAIC,QAAU,mCAC1BK,gBAAkB,OAASU,QAAQ3D,GAAK,WAAa2D,QAAQE,SAC/DrD,KAAO,+DACJmD,QAAQ3D,IAAMA,GAAK,WAAa,IAAM,sBAAwB2D,QAAQ3D,GACvE,qEACF2D,QAAQG,KACRtD,MAAQ,gDAAkDmD,QAAQG,KAAO,MAEzEtD,MAAQ,iDAGZA,MAAQ,4CAA8CmD,QAAQI,KAAO,eACrEvD,mKAC4CmB,EAAEC,KAAKC,WAAW,UAAW,qFAErEqB,OACA1C,iEAA4DoD,mJAEpCjC,EAAEC,KAAKC,WAAW,OAAQ,6FAItDrB,eAGAiD,YAAYO,OAAOxD,SAGnB2C,UACAA"} \ No newline at end of file +{"version":3,"file":"util.min.js","sources":["../src/util.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Content bank utility functions\n *\n * @module ivplugin_contentbank/util\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Ajax from 'core/ajax';\n/**\n * Fetches content from the content bank and updates the target element if provided.\n *\n * @param {number} id - The ID of the content item to fetch.\n * @param {number} contextid - The context ID where the content item resides.\n * @param {string} [target] - The optional target element selector to update with the fetched content.\n * @returns {Promise} A promise that resolves with the response or updates the target element with the fetched content.\n */\nconst getcontent = (id, contextid, target) => {\n Ajax.call([{\n args: {\n id: id,\n contextid: contextid,\n },\n contextid: contextid,\n methodname: 'ivplugin_contentbank_getitem',\n }])[0].then((response) => {\n if (target) {\n return $(target).html(response.item);\n } else {\n return response;\n }\n }).catch(() => {\n // Do nothing.\n });\n};\n\n/**\n * Initializes event listeners for content bank interactions.\n *\n * @param {number} contextid - The context ID for the content bank.\n *\n * This function sets up click event handlers for elements within the content bank container.\n * It handles the selection of content items, updates the preview area, and manages xAPI event detection.\n *\n * Event Listeners:\n * - Click on content item details: Selects the item and updates the hidden input with the content ID.\n * - Click on content item view: Selects the item, updates the preview area, and sets up xAPI event detection.\n *\n * xAPI Event Detection:\n * - Monitors for xAPI events (completed, answered) emitted by the content.\n * - Displays a notification if such events are detected.\n */\nconst init = (contextid) => {\n $(document).off('click', '.contentbank-container .contentbank-item .contentbank-item-details')\n .on('click', '.contentbank-container .contentbank-item .contentbank-item-details', function(e) {\n e.preventDefault();\n $('.contentbank-container .contentbank-item').removeClass('selected');\n $(this).closest('.contentbank-item').addClass('selected');\n $('#contentbank-preview').empty();\n const id = $(this).closest('.contentbank-item').data('contentid');\n $('[name=contentid]').val(id);\n });\n\n $(document).off('click', '.contentbank-container .contentbank-item .contentbankview')\n .on('click', '.contentbank-container .contentbank-item .contentbankview', function(e) {\n e.preventDefault();\n $('.contentbank-container .contentbank-item').removeClass('selected');\n let targetContentbank = $(this).closest('.contentbank-item');\n targetContentbank.addClass('selected');\n const id = targetContentbank.data('contentid');\n $('#contentbank-preview').empty();\n $('#contentbank-preview').attr('data-contentid', id);\n $('[name=contentid]').val(id);\n // Preview selected content.\n getcontent(id, contextid, '#contentbank-preview');\n\n // Handle xAPI event. We want user to be able to check if the content emits xAPI events (completed, answered)\n // because some content types may not emit these events. Then user can decide\n // if they want students to mark it complete manually or automatically.\n const xapicheck = M.util.get_string('xapicheck', 'ivplugin_contentbank');\n let H5P;\n\n const checkH5P = () => {\n try { // Try to get the H5P object.\n H5P = document.querySelector('#contentbank-preview iframe.h5p-player').contentWindow.H5P;\n } catch (e) {\n H5P = null;\n }\n\n if (typeof H5P !== 'undefined' && H5P !== null) {\n if (H5P.externalDispatcher === undefined) {\n requestAnimationFrame(checkH5P);\n return;\n }\n $(\"#contentbank-preview .xapi\").remove();\n $(`#contentbank-preview[data-contentid=${id}]`)\n .prepend(`
          \n ${xapicheck}
          `);\n H5P.externalDispatcher.on('xAPI', function(event) {\n if ((event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/completed'\n || event.data.statement.verb.id == 'http://adlnet.gov/expapi/verbs/answered')\n && event.data.statement.object.id.indexOf('subContentId') < 0) {\n $(\"#contentbank-preview .xapi\").remove();\n $(\"#contentbank-preview\")\n .prepend(`
          \n ${M.util.get_string('xapieventdetected', 'ivplugin_contentbank')}
          `);\n const audio = new Audio(M.cfg.wwwroot + '/mod/interactivevideo/sounds/pop.mp3');\n audio.play();\n }\n });\n } else {\n requestAnimationFrame(checkH5P);\n }\n };\n\n requestAnimationFrame(checkH5P);\n });\n};\n\n/**\n * Refreshes the content bank by fetching and displaying content items.\n *\n * @param {number} id - The ID of the content to be highlighted.\n * @param {number} coursecontextid - The context ID of the course.\n * @param {boolean} [edit=true] - Whether to show edit options for the content items.\n * @param {Function} [callback] - Optional callback function to be executed after refreshing the content bank.\n * @returns {Promise} - A promise that resolves when the content bank is refreshed.\n */\nconst refreshContentBank = async(id, coursecontextid, edit = true, callback) => {\n $('#contentbank-preview').empty();\n let contentbankitems = await Ajax.call([{\n args: {\n contextid: coursecontextid\n },\n contextid: coursecontextid,\n methodname: 'ivplugin_contentbank_getitems',\n }])[0];\n\n let contents = JSON.parse(contentbankitems.contents);\n let contentbank = $('.modal-body form .contentbank-container');\n contentbank.empty();\n contents.forEach(function(content) {\n const editurl = M.cfg.wwwroot + '/contentbank/edit.php?contextid='\n + coursecontextid + '&id=' + content.id + '&plugin=' + content.type;\n let html = '
          ';\n if (content.icon) {\n html += '';\n } else {\n html += '
          ';\n }\n\n html += '
          ' + content.name + '
          ';\n html += `
          \n
          `;\n if (edit) {\n html += `\n `;\n }\n\n html += `
          `;\n\n\n contentbank.append(html);\n });\n\n if (callback) {\n callback();\n }\n};\n\nexport default {init, getcontent, refreshContentBank};"],"names":["getcontent","id","contextid","target","call","args","methodname","then","response","html","item","catch","init","document","off","on","e","preventDefault","removeClass","this","closest","addClass","empty","data","val","targetContentbank","attr","xapicheck","M","util","get_string","H5P","checkH5P","querySelector","contentWindow","undefined","externalDispatcher","requestAnimationFrame","remove","prepend","event","statement","verb","object","indexOf","Audio","cfg","wwwroot","play","refreshContentBank","async","coursecontextid","edit","callback","contentbankitems","Ajax","contents","JSON","parse","contentbank","forEach","content","editurl","type","icon","name","append"],"mappings":";;;;;;;uKAgCMA,WAAa,CAACC,GAAIC,UAAWC,wBAC1BC,KAAK,CAAC,CACPC,KAAM,CACFJ,GAAIA,GACJC,UAAWA,WAEfA,UAAWA,UACXI,WAAY,kCACZ,GAAGC,MAAMC,UACLL,QACO,mBAAEA,QAAQM,KAAKD,SAASE,MAExBF,WAEZG,OAAM,uBAgJE,CAACC,KA3HFV,gCACRW,UAAUC,IAAI,QAAS,sEACpBC,GAAG,QAAS,sEAAsE,SAASC,GACxFA,EAAEC,qCACA,4CAA4CC,YAAY,gCACxDC,MAAMC,QAAQ,qBAAqBC,SAAS,gCAC5C,wBAAwBC,cACpBrB,IAAK,mBAAEkB,MAAMC,QAAQ,qBAAqBG,KAAK,iCACnD,oBAAoBC,IAAIvB,2BAGhCY,UAAUC,IAAI,QAAS,6DACpBC,GAAG,QAAS,6DAA6D,SAASC,GAC/EA,EAAEC,qCACA,4CAA4CC,YAAY,gBACtDO,mBAAoB,mBAAEN,MAAMC,QAAQ,qBACxCK,kBAAkBJ,SAAS,kBACrBpB,GAAKwB,kBAAkBF,KAAK,iCAChC,wBAAwBD,4BACxB,wBAAwBI,KAAK,iBAAkBzB,wBAC/C,oBAAoBuB,IAAIvB,IAE1BD,WAAWC,GAAIC,UAAW,8BAKpByB,UAAYC,EAAEC,KAAKC,WAAW,YAAa,4BAC7CC,UAEEC,SAAW,SAETD,IAAMlB,SAASoB,cAAc,0CAA0CC,cAAcH,IACvF,MAAOf,GACLe,IAAM,QAGN,MAAOA,IAAqC,SACbI,IAA3BJ,IAAIK,+BACJC,sBAAsBL,8BAGxB,8BAA8BM,2EACSrC,SACpCsC,8HACPZ,qBACEI,IAAIK,mBAAmBrB,GAAG,QAAQ,SAASyB,WACF,4CAAhCA,MAAMjB,KAAKkB,UAAUC,KAAKzC,IACQ,2CAAhCuC,MAAMjB,KAAKkB,UAAUC,KAAKzC,KAC1BuC,MAAMjB,KAAKkB,UAAUE,OAAO1C,GAAG2C,QAAQ,gBAAkB,EAAG,qBAC7D,8BAA8BN,6BAC9B,wBACGC,oKACyBX,EAAEC,KAAKC,WAAW,oBAAqB,mCACvD,IAAIe,MAAMjB,EAAEkB,IAAIC,QAAU,wCAClCC,gBAIdX,sBAAsBL,WAI9BK,sBAAsBL,cA4DZhC,WAAAA,WAAYiD,mBA/CPC,eAAMjD,GAAIkD,qBAAiBC,gEAAaC,oEAC7D,wBAAwB/B,YACtBgC,uBAAyBC,cAAKnD,KAAK,CAAC,CACpCC,KAAM,CACFH,UAAWiD,iBAEfjD,UAAWiD,gBACX7C,WAAY,mCACZ,GAEAkD,SAAWC,KAAKC,MAAMJ,iBAAiBE,UACvCG,aAAc,mBAAE,2CACpBA,YAAYrC,QACZkC,SAASI,SAAQ,SAASC,eAChBC,QAAUlC,EAAEkB,IAAIC,QAAU,mCAC1BI,gBAAkB,OAASU,QAAQ5D,GAAK,WAAa4D,QAAQE,SAC/DtD,KAAO,+DACJoD,QAAQ5D,IAAMA,GAAK,WAAa,IAAM,sBAAwB4D,QAAQ5D,GACvE,qEACF4D,QAAQG,KACRvD,MAAQ,gDAAkDoD,QAAQG,KAAO,MAEzEvD,MAAQ,iDAGZA,MAAQ,4CAA8CoD,QAAQI,KAAO,eACrExD,mKAC4CmB,EAAEC,KAAKC,WAAW,UAAW,qFAErEsB,OACA3C,iEAA4DqD,mJAEpClC,EAAEC,KAAKC,WAAW,OAAQ,6FAItDrB,eAGAkD,YAAYO,OAAOzD,SAGnB4C,UACAA"} \ No newline at end of file diff --git a/plugins/contentbank/amd/src/util.js b/plugins/contentbank/amd/src/util.js index 606d1b4..4d789f4 100644 --- a/plugins/contentbank/amd/src/util.js +++ b/plugins/contentbank/amd/src/util.js @@ -141,7 +141,7 @@ const init = (contextid) => { * @param {Function} [callback] - Optional callback function to be executed after refreshing the content bank. * @returns {Promise} - A promise that resolves when the content bank is refreshed. */ -const refreshContentBank = async (id, coursecontextid, edit = true, callback) => { +const refreshContentBank = async(id, coursecontextid, edit = true, callback) => { $('#contentbank-preview').empty(); let contentbankitems = await Ajax.call([{ args: { diff --git a/plugins/iframe/amd/build/main.min.js.map b/plugins/iframe/amd/build/main.min.js.map index cbbdd8e..97ca5d1 100644 --- a/plugins/iframe/amd/build/main.min.js.map +++ b/plugins/iframe/amd/build/main.min.js.map @@ -1 +1 @@ -{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Main class for the iframe plugin\n *\n * @module ivplugin_iframe/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Base from 'mod_interactivevideo/type/base';\nexport default class Iframe extends Base {\n /**\n * Called when the edit form is loaded.\n * @param {Object} form The form object\n * @param {Event} event The event object\n * @return {void}\n */\n onEditFormLoaded(form, event) {\n const preview = (embed, ratio) => {\n $('.preview-iframe').html(embed);\n $('.preview-iframe').css('padding-bottom', ratio);\n };\n $(document).off('input', '[name=\"iframeurl\"]').on('input', '[name=\"iframeurl\"]', function(e) {\n e.preventDefault();\n e.stopPropagation();\n $('.preview-iframe').html('').css('padding-bottom', '0');\n $('[name=\"char1\"], [name=\"content\"]').val('');\n if ($('[name=\"iframeurl\"]').val() === '') {\n return;\n }\n const fallback = (url) => {\n $('[name=\"char1\"]').val('56.25%');\n $('[name=\"content\"]').val(``);\n preview(``, '56.25%');\n };\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/plugins/iframe/ajax.php',\n type: 'GET',\n data: {\n action: 'getproviders',\n sesskey: M.cfg.sesskey,\n contextid: M.cfg.contextid,\n },\n success: function(data) {\n const providers = data;\n let url = $('[name=\"iframeurl\"]').val();\n // Format the url to match the provider_url\n let providerUrl = url.split('/')[2];\n const domain = providerUrl.split('.');\n if (domain.length > 2) {\n providerUrl = domain[1] + '.' + domain[2];\n } else {\n providerUrl = domain[0] + '.' + domain[1];\n }\n const provider = providers.find(function(provider) {\n return provider.provider_url.includes(providerUrl);\n });\n if (!provider) {\n fallback(url);\n return;\n }\n if (provider) {\n // Reformat the url to match the endpoints scheme.\n let urlendpoint = provider.endpoints[0].url.replace('{format}', 'json');\n if (urlendpoint.includes('?')) {\n urlendpoint = urlendpoint + '&url=' + url;\n } else {\n urlendpoint = urlendpoint + '?url=' + url;\n }\n if (!urlendpoint.includes('format=json')) {\n urlendpoint = urlendpoint + '&format=json';\n }\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/plugins/iframe/ajax.php',\n data: {\n url: urlendpoint,\n sesskey: M.cfg.sesskey,\n action: 'getoembedinfo',\n contextid: M.cfg.contextid,\n },\n method: \"POST\",\n dataType: \"text\",\n success: function(res) {\n let data;\n try {\n data = JSON.parse(res);\n } catch (e) {\n fallback(url);\n return;\n }\n\n if (!data.html) {\n fallback(url);\n return;\n }\n\n let ratio = '56.25%';\n\n if (!data.width || data.width == 0 || data.width == '100%') {\n if (data.height && data.height !== 0) {\n ratio = data.height + 'px';\n }\n } else {\n ratio = (data.height / data.width * 100) + '%';\n }\n\n $('[name=\"char1\"]').val(ratio);\n // Remove any script tags from the html to avoid errors with requirejs from data.html using regex\n data.html = data.html.replace(/)<[^<]*)*<\\/script>/gi, '');\n let embed = $(data.html);\n $('[name=\"content\"]').val(data.html);\n preview(embed, ratio);\n },\n error: function() {\n fallback(url);\n }\n });\n }\n }\n });\n });\n\n $(document).off('input', '[name=content]').on('input', '[name=content]', function(e) {\n e.preventDefault();\n if ($(this).val() === '') {\n $('.preview-iframe').html('').css('padding-bottom', '0');\n return;\n }\n preview($(this).val(), '100%');\n });\n return {form, event};\n }\n\n /**\n * Override the renderContainer method\n * @param {Object} annotation The annotation object\n * @return {void}\n */\n renderContainer(annotation) {\n $(`#message[data-id='${annotation.id}']`).addClass('hasiframe');\n super.renderContainer(annotation);\n }\n\n /**\n * Override the postContentRender method\n * @param {Object} annotation The annotation object\n * @return {void}\n */\n postContentRender(annotation) {\n const checkIframe = () => {\n if ($(`#message[data-id='${annotation.id}'] iframe`).length > 0) {\n // Remove the loading background because some iframe has transparent content\n setTimeout(() => {\n $(`#message[data-id='${annotation.id}'] iframe`).css('background', 'none');\n }, 1000);\n } else {\n requestAnimationFrame(checkIframe);\n }\n };\n requestAnimationFrame(checkIframe);\n }\n\n /**\n * Override the displayReportView method\n * @param {Object} annotation The annotation object\n * @return {void}\n */\n async displayReportView(annotation) {\n const data = await this.render(annotation, 'html');\n let $message = $(`#message[data-id='${annotation.id}']`);\n $message.addClass('hasiframe');\n $message.find(`.modal-body`).html(data);\n $message.find(`.modal-body`).attr('id', 'content');\n this.postContentRender(annotation);\n }\n}"],"names":["Iframe","Base","onEditFormLoaded","form","event","preview","embed","ratio","html","css","document","off","on","e","preventDefault","stopPropagation","val","fallback","url","ajax","M","cfg","wwwroot","type","data","action","sesskey","contextid","success","providers","providerUrl","split","domain","length","provider","find","provider_url","includes","urlendpoint","endpoints","replace","method","dataType","res","JSON","parse","width","height","error","this","renderContainer","annotation","id","addClass","postContentRender","checkIframe","setTimeout","requestAnimationFrame","render","$message","attr"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Main class for the iframe plugin\n *\n * @module ivplugin_iframe/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Base from 'mod_interactivevideo/type/base';\nexport default class Iframe extends Base {\n /**\n * Called when the edit form is loaded.\n * @param {Object} form The form object\n * @param {Event} event The event object\n * @return {void}\n */\n onEditFormLoaded(form, event) {\n const preview = (embed, ratio) => {\n $('.preview-iframe').html(embed);\n $('.preview-iframe').css('padding-bottom', ratio);\n };\n $(document).off('input', '[name=\"iframeurl\"]').on('input', '[name=\"iframeurl\"]', function(e) {\n e.preventDefault();\n e.stopPropagation();\n $('.preview-iframe').html('').css('padding-bottom', '0');\n $('[name=\"char1\"], [name=\"content\"]').val('');\n if ($('[name=\"iframeurl\"]').val() === '') {\n return;\n }\n const fallback = (url) => {\n $('[name=\"char1\"]').val('56.25%');\n $('[name=\"content\"]').val(``);\n preview(``, '56.25%');\n };\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/plugins/iframe/ajax.php',\n type: 'GET',\n data: {\n action: 'getproviders',\n sesskey: M.cfg.sesskey,\n contextid: M.cfg.contextid,\n },\n success: function(data) {\n const providers = data;\n let url = $('[name=\"iframeurl\"]').val();\n // Format the url to match the provider_url.\n let providerUrl = url.split('/')[2];\n const domain = providerUrl.split('.');\n if (domain.length > 2) {\n providerUrl = domain[1] + '.' + domain[2];\n } else {\n providerUrl = domain[0] + '.' + domain[1];\n }\n const provider = providers.find(function(provider) {\n return provider.provider_url.includes(providerUrl);\n });\n if (!provider) {\n fallback(url);\n return;\n }\n if (provider) {\n // Reformat the url to match the endpoints scheme.\n let urlendpoint = provider.endpoints[0].url.replace('{format}', 'json');\n if (urlendpoint.includes('?')) {\n urlendpoint = urlendpoint + '&url=' + url;\n } else {\n urlendpoint = urlendpoint + '?url=' + url;\n }\n if (!urlendpoint.includes('format=json')) {\n urlendpoint = urlendpoint + '&format=json';\n }\n $.ajax({\n url: M.cfg.wwwroot + '/mod/interactivevideo/plugins/iframe/ajax.php',\n data: {\n url: urlendpoint,\n sesskey: M.cfg.sesskey,\n action: 'getoembedinfo',\n contextid: M.cfg.contextid,\n },\n method: \"POST\",\n dataType: \"text\",\n success: function(res) {\n let data;\n try {\n data = JSON.parse(res);\n } catch (e) {\n fallback(url);\n return;\n }\n\n if (!data.html) {\n fallback(url);\n return;\n }\n\n let ratio = '56.25%';\n\n if (!data.width || data.width == 0 || data.width == '100%') {\n if (data.height && data.height !== 0) {\n ratio = data.height + 'px';\n }\n } else {\n ratio = (data.height / data.width * 100) + '%';\n }\n\n $('[name=\"char1\"]').val(ratio);\n // Remove any script tags from the html to avoid errors with requirejs from data.html using regex.\n data.html = data.html.replace(/)<[^<]*)*<\\/script>/gi, '');\n let embed = $(data.html);\n $('[name=\"content\"]').val(data.html);\n preview(embed, ratio);\n },\n error: function() {\n fallback(url);\n }\n });\n }\n }\n });\n });\n\n $(document).off('input', '[name=content]').on('input', '[name=content]', function(e) {\n e.preventDefault();\n if ($(this).val() === '') {\n $('.preview-iframe').html('').css('padding-bottom', '0');\n return;\n }\n preview($(this).val(), '100%');\n });\n return {form, event};\n }\n\n /**\n * Override the renderContainer method\n * @param {Object} annotation The annotation object\n * @return {void}\n */\n renderContainer(annotation) {\n $(`#message[data-id='${annotation.id}']`).addClass('hasiframe');\n super.renderContainer(annotation);\n }\n\n /**\n * Override the postContentRender method\n * @param {Object} annotation The annotation object\n * @return {void}\n */\n postContentRender(annotation) {\n const checkIframe = () => {\n if ($(`#message[data-id='${annotation.id}'] iframe`).length > 0) {\n // Remove the loading background because some iframe has transparent content\n setTimeout(() => {\n $(`#message[data-id='${annotation.id}'] iframe`).css('background', 'none');\n }, 1000);\n } else {\n requestAnimationFrame(checkIframe);\n }\n };\n requestAnimationFrame(checkIframe);\n }\n\n /**\n * Override the displayReportView method\n * @param {Object} annotation The annotation object\n * @return {void}\n */\n async displayReportView(annotation) {\n const data = await this.render(annotation, 'html');\n let $message = $(`#message[data-id='${annotation.id}']`);\n $message.addClass('hasiframe');\n $message.find(`.modal-body`).html(data);\n $message.find(`.modal-body`).attr('id', 'content');\n this.postContentRender(annotation);\n }\n}"],"names":["Iframe","Base","onEditFormLoaded","form","event","preview","embed","ratio","html","css","document","off","on","e","preventDefault","stopPropagation","val","fallback","url","ajax","M","cfg","wwwroot","type","data","action","sesskey","contextid","success","providers","providerUrl","split","domain","length","provider","find","provider_url","includes","urlendpoint","endpoints","replace","method","dataType","res","JSON","parse","width","height","error","this","renderContainer","annotation","id","addClass","postContentRender","checkIframe","setTimeout","requestAnimationFrame","render","$message","attr"],"mappings":";;;;;;;uKAwBqBA,eAAeC,cAOhCC,iBAAiBC,KAAMC,aACbC,QAAU,CAACC,MAAOC,6BAClB,mBAAmBC,KAAKF,2BACxB,mBAAmBG,IAAI,iBAAkBF,kCAE7CG,UAAUC,IAAI,QAAS,sBAAsBC,GAAG,QAAS,sBAAsB,SAASC,MACtFA,EAAEC,iBACFD,EAAEE,sCACA,mBAAmBP,KAAK,IAAIC,IAAI,iBAAkB,yBAClD,oCAAoCO,IAAI,IACJ,MAAlC,mBAAE,sBAAsBA,mBAGtBC,SAAYC,0BACZ,kBAAkBF,IAAI,8BACtB,oBAAoBA,2BAAoBE,oDAC1Cb,+BAAwBa,mDAAkD,2BAE5EC,KAAK,CACHD,IAAKE,EAAEC,IAAIC,QAAU,gDACrBC,KAAM,MACNC,KAAM,CACFC,OAAQ,eACRC,QAASN,EAAEC,IAAIK,QACfC,UAAWP,EAAEC,IAAIM,WAErBC,QAAS,SAASJ,YACRK,UAAYL,SACdN,KAAM,mBAAE,sBAAsBF,MAE9Bc,YAAcZ,IAAIa,MAAM,KAAK,SAC3BC,OAASF,YAAYC,MAAM,KAE7BD,YADAE,OAAOC,OAAS,EACFD,OAAO,GAAK,IAAMA,OAAO,GAEzBA,OAAO,GAAK,IAAMA,OAAO,SAErCE,SAAWL,UAAUM,MAAK,SAASD,iBAC9BA,SAASE,aAAaC,SAASP,mBAErCI,aAIDA,SAAU,KAENI,YAAcJ,SAASK,UAAU,GAAGrB,IAAIsB,QAAQ,WAAY,QAE5DF,YADAA,YAAYD,SAAS,KACPC,YAAc,QAAUpB,IAExBoB,YAAc,QAAUpB,IAErCoB,YAAYD,SAAS,iBACtBC,aAA4B,gCAE9BnB,KAAK,CACHD,IAAKE,EAAEC,IAAIC,QAAU,gDACrBE,KAAM,CACFN,IAAKoB,YACLZ,QAASN,EAAEC,IAAIK,QACfD,OAAQ,gBACRE,UAAWP,EAAEC,IAAIM,WAErBc,OAAQ,OACRC,SAAU,OACVd,QAAS,SAASe,SACVnB,SAEAA,KAAOoB,KAAKC,MAAMF,KACpB,MAAO9B,eACLI,SAASC,SAIRM,KAAKhB,iBACNS,SAASC,SAITX,MAAQ,SAEPiB,KAAKsB,OAAuB,GAAdtB,KAAKsB,OAA4B,QAAdtB,KAAKsB,MAKvCvC,MAASiB,KAAKuB,OAASvB,KAAKsB,MAAQ,IAAO,IAJvCtB,KAAKuB,QAA0B,IAAhBvB,KAAKuB,SACpBxC,MAAQiB,KAAKuB,OAAS,0BAM5B,kBAAkB/B,IAAIT,OAExBiB,KAAKhB,KAAOgB,KAAKhB,KAAKgC,QAAQ,sDAAuD,QACjFlC,OAAQ,mBAAEkB,KAAKhB,0BACjB,oBAAoBQ,IAAIQ,KAAKhB,MAC/BH,QAAQC,MAAOC,QAEnByC,MAAO,WACH/B,SAASC,cAxDjBD,SAASC,+BAgEvBR,UAAUC,IAAI,QAAS,kBAAkBC,GAAG,QAAS,kBAAkB,SAASC,GAC9EA,EAAEC,iBACoB,MAAlB,mBAAEmC,MAAMjC,MAIZX,SAAQ,mBAAE4C,MAAMjC,MAAO,4BAHjB,mBAAmBR,KAAK,IAAIC,IAAI,iBAAkB,QAKrD,CAACN,KAAAA,KAAMC,MAAAA,OAQlB8C,gBAAgBC,4DACWA,WAAWC,UAAQC,SAAS,mBAC7CH,gBAAgBC,YAQ1BG,kBAAkBH,kBACRI,YAAc,MACZ,+CAAuBJ,WAAWC,iBAAenB,OAAS,EAE9DuB,YAAW,qDACgBL,WAAWC,iBAAe3C,IAAI,aAAc,UACpE,KAEHgD,sBAAsBF,cAG1BE,sBAAsBF,qCAQFJ,kBACd3B,WAAayB,KAAKS,OAAOP,WAAY,YACvCQ,UAAW,+CAAuBR,WAAWC,UACjDO,SAASN,SAAS,aAClBM,SAASxB,oBAAoB3B,KAAKgB,MAClCmC,SAASxB,oBAAoByB,KAAK,KAAM,gBACnCN,kBAAkBH"} \ No newline at end of file diff --git a/plugins/skipsegment/amd/build/main.min.js b/plugins/skipsegment/amd/build/main.min.js index 5f0217a..a041e07 100644 --- a/plugins/skipsegment/amd/build/main.min.js +++ b/plugins/skipsegment/amd/build/main.min.js @@ -5,6 +5,6 @@ define("ivplugin_skipsegment/main",["exports","jquery","mod_interactivevideo/typ * @module ivplugin_skipsegment/main * @copyright 2024 Sokunthearith Makara * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_base=_interopRequireDefault(_base),_ajax=_interopRequireDefault(_ajax);class SkipSegment extends _base.default{init(){if(!this.isEditMode()){let self=this;const skipsegment=this.annotations.filter((annotation=>"skipsegment"==annotation.type));(0,_jquery.default)(document).on("timeupdate",(async function(e){const t=e.originalEvent.detail.time;for(let annotation of skipsegment)if(annotation.timestampt){await self.player.seek(Number(annotation.title)),await self.runInteraction(annotation);break}}))}}renderEditItem(annotations,listItem,item){return(listItem=super.renderEditItem(annotations,listItem,item)).find("[data-editable]").removeAttr("data-editable"),listItem.find(".btn.copy").remove(),listItem.find(".title").replaceWith('').concat(this.convertSecondsToHMS(item.title,this.totaltime<3600,!0),"")),this.isSkipped(item.timestamp)&&listItem.find(".skipend").after('\n '.concat(M.util.get_string("skipped","ivplugin_skipsegment"),"")),listItem}async addAnnotation(annotations,timestamp,coursemodule){let data={title:timestamp+5>this.end?this.end:timestamp+5,timestamp:timestamp,contextid:M.cfg.contextid,type:this.prop.name,courseid:this.course,cmid:coursemodule,annotationid:this.interaction,hascompletion:this.prop.hascompletion?1:0,advanced:JSON.stringify({visiblebeforecompleted:"1",visibleaftercompleted:null,clickablebeforecompleted:"1",clickableaftercompleted:null,replaybehavior:"1"})},ajax=await _ajax.default.call([{methodname:"ivplugin_skipsegment_add_skip",args:{skipdata:JSON.stringify(data)},contextid:M.cfg.contextid}])[0],newAnnotation=JSON.parse(ajax.data);(0,_event_dispatcher.dispatchEvent)("annotationupdated",{annotation:newAnnotation,action:"add"}),(0,_jquery.default)("#contentmodal").modal("hide")}onEditFormLoaded(form,event){let self=this;return super.onEditFormLoaded(form,event).on("change","[name=titleassist]",(function(e){e.preventDefault();const originalValue=(0,_jquery.default)(this).data("initial-value");if(!self.validateTimestampFormat((0,_jquery.default)(this).val()))return self.addNotification(M.util.get_string("invalidtimestampformat","ivplugin_skipsegment")),void(0,_jquery.default)(this).val(originalValue);const parts=(0,_jquery.default)(this).val().split(":"),timestamp=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]);if(!self.isBetweenStartAndEnd(timestamp)){const message=M.util.get_string("interactioncanonlybeaddedbetweenstartandendtime","mod_interactivevideo",{start:self.convertSecondsToHMS(self.start),end:self.convertSecondsToHMS(self.end)});return self.addNotification(message),void(0,_jquery.default)(this).val(originalValue)}if(timestamp<=Number((0,_jquery.default)("[name=timestamp]").val()))return self.addNotification(M.util.get_string("segmentendmustbegreaterthantimestamp","mod_interactivevideo")),void(0,_jquery.default)(this).val(originalValue);(0,_jquery.default)("[name=title]").val(timestamp)})),{form:form,event:event}}renderItemOnVideoNavigation(annotation){if(annotation.timestampthis.end)return;const percentage=(Number(annotation.timestamp)-this.start)/this.totaltime*100,length=(Number(annotation.title)-Number(annotation.timestamp))/this.totaltime*100;this.isVisible(annotation)&&!this.isEditMode()&&(0,_jquery.default)("#video-nav ul").append('
        • '>
        • ")),this.isEditMode()&&(0,_jquery.default)("#video-timeline-wrapper").append('
          \n
          \n
          '))}async runInteraction(annotation){(0,_jquery.default)(".video-block").append('
          \n
          ')),(0,_jquery.default)("#skipsegment").fadeIn(300),await this.player.seek(Number(annotation.title)+.01),this.player.play(),setTimeout((()=>{(0,_jquery.default)("#skipsegment").fadeOut(300),setTimeout((()=>{(0,_jquery.default)("#skipsegment").remove()}),300)}),1e3)}}return _exports.default=SkipSegment,_exports.default})); + */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_jquery=_interopRequireDefault(_jquery),_base=_interopRequireDefault(_base),_ajax=_interopRequireDefault(_ajax);class SkipSegment extends _base.default{init(){if(!this.isEditMode()){let self=this;const skipsegment=this.annotations.filter((annotation=>"skipsegment"==annotation.type));(0,_jquery.default)(document).on("timeupdate",(async function(e){const t=e.originalEvent.detail.time;for(let annotation of skipsegment)if(annotation.timestampt){await self.player.seek(Number(annotation.title)),await self.runInteraction(annotation);break}}))}}renderEditItem(annotations,listItem,item){return(listItem=super.renderEditItem(annotations,listItem,item)).find("[data-editable]").removeAttr("data-editable"),listItem.find(".btn.copy").remove(),listItem.find(".title").replaceWith('').concat(this.convertSecondsToHMS(item.title,this.totaltime<3600,!0),"")),this.isSkipped(item.timestamp)&&listItem.find(".skipend").after('\n '.concat(M.util.get_string("skipped","ivplugin_skipsegment"),"")),listItem}async addAnnotation(annotations,timestamp,coursemodule){let data={title:timestamp+5>this.end?this.end:timestamp+5,timestamp:timestamp,contextid:M.cfg.contextid,type:this.prop.name,courseid:this.course,cmid:coursemodule,annotationid:this.interaction,hascompletion:this.prop.hascompletion?1:0,advanced:JSON.stringify({visiblebeforecompleted:"1",visibleaftercompleted:null,clickablebeforecompleted:"1",clickableaftercompleted:null,replaybehavior:"1"})},ajax=await _ajax.default.call([{methodname:"ivplugin_skipsegment_add_skip",args:{skipdata:JSON.stringify(data),contextid:M.cfg.contextid},contextid:M.cfg.contextid}])[0],newAnnotation=JSON.parse(ajax.data);(0,_event_dispatcher.dispatchEvent)("annotationupdated",{annotation:newAnnotation,action:"add"}),(0,_jquery.default)("#contentmodal").modal("hide")}onEditFormLoaded(form,event){let self=this;return super.onEditFormLoaded(form,event).on("change","[name=titleassist]",(function(e){e.preventDefault();const originalValue=(0,_jquery.default)(this).data("initial-value");if(!self.validateTimestampFormat((0,_jquery.default)(this).val()))return self.addNotification(M.util.get_string("invalidtimestampformat","ivplugin_skipsegment")),void(0,_jquery.default)(this).val(originalValue);const parts=(0,_jquery.default)(this).val().split(":"),timestamp=3600*Number(parts[0])+60*Number(parts[1])+Number(parts[2]);if(!self.isBetweenStartAndEnd(timestamp)){const message=M.util.get_string("interactioncanonlybeaddedbetweenstartandendtime","mod_interactivevideo",{start:self.convertSecondsToHMS(self.start),end:self.convertSecondsToHMS(self.end)});return self.addNotification(message),void(0,_jquery.default)(this).val(originalValue)}if(timestamp<=Number((0,_jquery.default)("[name=timestamp]").val()))return self.addNotification(M.util.get_string("segmentendmustbegreaterthantimestamp","mod_interactivevideo")),void(0,_jquery.default)(this).val(originalValue);(0,_jquery.default)("[name=title]").val(timestamp)})),{form:form,event:event}}renderItemOnVideoNavigation(annotation){if(annotation.timestampthis.end)return;const percentage=(Number(annotation.timestamp)-this.start)/this.totaltime*100,length=(Number(annotation.title)-Number(annotation.timestamp))/this.totaltime*100;this.isVisible(annotation)&&!this.isEditMode()&&(0,_jquery.default)("#video-nav ul").append('
        • '>
        • ")),this.isEditMode()&&(0,_jquery.default)("#video-timeline-wrapper").append('
          \n
          \n
          '))}async runInteraction(annotation){(0,_jquery.default)(".video-block").append('
          \n
          ')),(0,_jquery.default)("#skipsegment").fadeIn(300),await this.player.seek(Number(annotation.title)+.01),this.player.play(),setTimeout((()=>{(0,_jquery.default)("#skipsegment").fadeOut(300),setTimeout((()=>{(0,_jquery.default)("#skipsegment").remove()}),300)}),1e3)}}return _exports.default=SkipSegment,_exports.default})); //# sourceMappingURL=main.min.js.map \ No newline at end of file diff --git a/plugins/skipsegment/amd/build/main.min.js.map b/plugins/skipsegment/amd/build/main.min.js.map index db1fc72..09f10d1 100644 --- a/plugins/skipsegment/amd/build/main.min.js.map +++ b/plugins/skipsegment/amd/build/main.min.js.map @@ -1 +1 @@ -{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Main class for skip segment\n *\n * @module ivplugin_skipsegment/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Base from 'mod_interactivevideo/type/base';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Ajax from 'core/ajax';\nexport default class SkipSegment extends Base {\n /**\n * Initializes the skip segment plugin.\n * If not in edit mode, sets up an event listener for the 'timeupdate' event.\n * Filters annotations to find those of type 'skipsegment' and runs the interaction\n * when the current time falls within the annotation's timestamp range.\n *\n * @method init\n */\n init() {\n if (!this.isEditMode()) {\n let self = this;\n const skipsegment = this.annotations.filter((annotation) => annotation.type == 'skipsegment');\n $(document).on('timeupdate', async function(e) {\n const t = e.originalEvent.detail.time;\n for (let annotation of skipsegment) {\n if (annotation.timestamp < t && annotation.title > t) {\n await self.player.seek(Number(annotation.title));\n await self.runInteraction(annotation);\n break;\n }\n }\n });\n }\n }\n /**\n * Renders the edit item for the skip segment plugin.\n *\n * @param {Object} annotations - The annotations object.\n * @param {jQuery} listItem - The jQuery object representing the list item.\n * @param {Object} item - The item object containing details of the segment.\n * @returns {jQuery} The modified list item with the rendered edit item.\n */\n renderEditItem(annotations, listItem, item) {\n listItem = super.renderEditItem(annotations, listItem, item);\n listItem.find('[data-editable]').removeAttr('data-editable');\n listItem.find('.btn.copy').remove();\n listItem.find('.title').replaceWith(`${this.convertSecondsToHMS(item.title, this.totaltime < 3600, true)}`);\n if (this.isSkipped(item.timestamp)) {\n listItem.find('.skipend').after(`\n ${M.util.get_string('skipped', 'ivplugin_skipsegment')}`);\n }\n return listItem;\n }\n\n /**\n * Adds an annotation to the interactive video.\n *\n * @param {Array} annotations - The list of current annotations.\n * @param {number} timestamp - The timestamp at which to add the annotation.\n * @param {number} coursemodule - The course module ID.\n * @returns {Promise} A promise that resolves when the annotation is added.\n */\n async addAnnotation(annotations, timestamp, coursemodule) {\n let self = this;\n let data = {\n title: timestamp + 5 > this.end ? this.end : timestamp + 5,\n timestamp: timestamp,\n contextid: M.cfg.contextid,\n type: self.prop.name,\n courseid: self.course,\n cmid: coursemodule,\n annotationid: self.interaction,\n hascompletion: self.prop.hascompletion ? 1 : 0,\n advanced: JSON.stringify({\n \"visiblebeforecompleted\": \"1\",\n \"visibleaftercompleted\": null,\n \"clickablebeforecompleted\": \"1\",\n \"clickableaftercompleted\": null,\n \"replaybehavior\": \"1\",\n }),\n };\n let ajax = await Ajax.call([{\n methodname: 'ivplugin_skipsegment_add_skip',\n args: {\n skipdata: JSON.stringify(data),\n },\n contextid: M.cfg.contextid,\n }])[0];\n\n let newAnnotation = JSON.parse(ajax.data);\n dispatchEvent('annotationupdated', {\n annotation: newAnnotation,\n action: 'add'\n });\n\n $('#contentmodal').modal('hide');\n\n }\n\n /**\n * Handles the event when the edit form is loaded.\n *\n * @param {Object} form - The form object that is being edited.\n * @param {Event} event - The event object associated with the form loading.\n * @returns {Object} - An object containing the form and event.\n *\n */\n onEditFormLoaded(form, event) {\n let self = this;\n const body = super.onEditFormLoaded(form, event);\n body.on('change', '[name=titleassist]', function(e) {\n e.preventDefault();\n const originalValue = $(this).data('initial-value');\n // Make sure the timestamp format is hh:mm:ss.\n if (!self.validateTimestampFormat($(this).val())) {\n self.addNotification(M.util.get_string('invalidtimestampformat', 'ivplugin_skipsegment'));\n $(this).val(originalValue);\n return;\n }\n\n // Convert the timestamp to seconds.\n const parts = $(this).val().split(':');\n const timestamp = Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);\n if (!self.isBetweenStartAndEnd(timestamp)) {\n const message = M.util.get_string('interactioncanonlybeaddedbetweenstartandendtime', 'mod_interactivevideo', {\n \"start\": self.convertSecondsToHMS(self.start),\n \"end\": self.convertSecondsToHMS(self.end),\n });\n self.addNotification(message);\n $(this).val(originalValue);\n return;\n }\n\n // Make sure the title assist is not the same as the timestamp assist or less than the timestamp assist\n if (timestamp <= Number($('[name=timestamp]').val())) {\n self.addNotification(M.util.get_string('segmentendmustbegreaterthantimestamp', 'mod_interactivevideo'));\n $(this).val(originalValue);\n return;\n }\n\n $('[name=title]').val(timestamp);\n });\n return {form, event};\n }\n\n /**\n * Renders an annotation on the video navigation timeline.\n *\n * @param {Object} annotation - The annotation object to be rendered.\n * @param {number} annotation.timestamp - The timestamp of the annotation.\n * @param {string} annotation.title - The title of the annotation.\n * @param {string} annotation.type - The type of the annotation.\n * @param {string} annotation.id - The unique identifier of the annotation.\n */\n renderItemOnVideoNavigation(annotation) {\n if (annotation.timestamp < this.start || annotation.timestamp > this.end) {\n return;\n }\n const percentage = ((Number(annotation.timestamp) - this.start) / this.totaltime) * 100;\n const length = (Number(annotation.title) - Number(annotation.timestamp)) / this.totaltime * 100;\n if (this.isVisible(annotation) && !this.isEditMode()) {\n $(\"#video-nav ul\").append(`
        • `);\n }\n if (this.isEditMode()) {\n $(\"#video-timeline-wrapper\").append(`
          \n
          \n
          `);\n }\n }\n /**\n * Executes the interaction for skipping a segment in the video.\n *\n * This function appends a skip segment icon to the video block, seeks the video player to the specified annotation time,\n * updates the progress bar, and then plays the video.\n * The skip segment icon is displayed for a short duration before being removed.\n *\n * @param {Object} annotation - The annotation object containing the title which represents the time to seek to.\n * @returns {Promise} A promise that resolves when the interaction is complete.\n */\n async runInteraction(annotation) {\n $('.video-block').append(`
          \n
          `);\n $('#skipsegment').fadeIn(300);\n await this.player.seek(Number(annotation.title) + 0.01);\n this.player.play();\n setTimeout(() => {\n $('#skipsegment').fadeOut(300);\n setTimeout(() => {\n $('#skipsegment').remove();\n }, 300);\n }, 1000);\n\n }\n}"],"names":["SkipSegment","Base","init","this","isEditMode","self","skipsegment","annotations","filter","annotation","type","document","on","async","e","t","originalEvent","detail","time","timestamp","title","player","seek","Number","runInteraction","renderEditItem","listItem","item","super","find","removeAttr","remove","replaceWith","convertSecondsToHMS","totaltime","isSkipped","after","M","util","get_string","coursemodule","data","end","contextid","cfg","prop","name","courseid","course","cmid","annotationid","interaction","hascompletion","advanced","JSON","stringify","ajax","Ajax","call","methodname","args","skipdata","newAnnotation","parse","action","modal","onEditFormLoaded","form","event","preventDefault","originalValue","validateTimestampFormat","val","addNotification","parts","split","isBetweenStartAndEnd","message","start","renderItemOnVideoNavigation","percentage","length","isVisible","append","isClickable","id","icon","fadeIn","play","setTimeout","fadeOut"],"mappings":"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"} \ No newline at end of file +{"version":3,"file":"main.min.js","sources":["../src/main.js"],"sourcesContent":["// This file is part of Moodle - http://moodle.org/\n//\n// Moodle is free software: you can redistribute it and/or modify\n// it under the terms of the GNU General Public License as published by\n// the Free Software Foundation, either version 3 of the License, or\n// (at your option) any later version.\n//\n// Moodle is distributed in the hope that it will be useful,\n// but WITHOUT ANY WARRANTY; without even the implied warranty of\n// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the\n// GNU General Public License for more details.\n//\n// You should have received a copy of the GNU General Public License\n// along with Moodle. If not, see .\n\n/**\n * Main class for skip segment\n *\n * @module ivplugin_skipsegment/main\n * @copyright 2024 Sokunthearith Makara \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport $ from 'jquery';\nimport Base from 'mod_interactivevideo/type/base';\nimport {dispatchEvent} from 'core/event_dispatcher';\nimport Ajax from 'core/ajax';\nexport default class SkipSegment extends Base {\n /**\n * Initializes the skip segment plugin.\n * If not in edit mode, sets up an event listener for the 'timeupdate' event.\n * Filters annotations to find those of type 'skipsegment' and runs the interaction\n * when the current time falls within the annotation's timestamp range.\n *\n * @method init\n */\n init() {\n if (!this.isEditMode()) {\n let self = this;\n const skipsegment = this.annotations.filter((annotation) => annotation.type == 'skipsegment');\n $(document).on('timeupdate', async function(e) {\n const t = e.originalEvent.detail.time;\n for (let annotation of skipsegment) {\n if (annotation.timestamp < t && annotation.title > t) {\n await self.player.seek(Number(annotation.title));\n await self.runInteraction(annotation);\n break;\n }\n }\n });\n }\n }\n /**\n * Renders the edit item for the skip segment plugin.\n *\n * @param {Object} annotations - The annotations object.\n * @param {jQuery} listItem - The jQuery object representing the list item.\n * @param {Object} item - The item object containing details of the segment.\n * @returns {jQuery} The modified list item with the rendered edit item.\n */\n renderEditItem(annotations, listItem, item) {\n listItem = super.renderEditItem(annotations, listItem, item);\n listItem.find('[data-editable]').removeAttr('data-editable');\n listItem.find('.btn.copy').remove();\n listItem.find('.title').replaceWith(`${this.convertSecondsToHMS(item.title, this.totaltime < 3600, true)}`);\n if (this.isSkipped(item.timestamp)) {\n listItem.find('.skipend').after(`\n ${M.util.get_string('skipped', 'ivplugin_skipsegment')}`);\n }\n return listItem;\n }\n\n /**\n * Adds an annotation to the interactive video.\n *\n * @param {Array} annotations - The list of current annotations.\n * @param {number} timestamp - The timestamp at which to add the annotation.\n * @param {number} coursemodule - The course module ID.\n * @returns {Promise} A promise that resolves when the annotation is added.\n */\n async addAnnotation(annotations, timestamp, coursemodule) {\n let self = this;\n let data = {\n title: timestamp + 5 > this.end ? this.end : timestamp + 5,\n timestamp: timestamp,\n contextid: M.cfg.contextid,\n type: self.prop.name,\n courseid: self.course,\n cmid: coursemodule,\n annotationid: self.interaction,\n hascompletion: self.prop.hascompletion ? 1 : 0,\n advanced: JSON.stringify({\n \"visiblebeforecompleted\": \"1\",\n \"visibleaftercompleted\": null,\n \"clickablebeforecompleted\": \"1\",\n \"clickableaftercompleted\": null,\n \"replaybehavior\": \"1\",\n }),\n };\n let ajax = await Ajax.call([{\n methodname: 'ivplugin_skipsegment_add_skip',\n args: {\n skipdata: JSON.stringify(data),\n contextid: M.cfg.contextid,\n },\n contextid: M.cfg.contextid,\n }])[0];\n\n let newAnnotation = JSON.parse(ajax.data);\n dispatchEvent('annotationupdated', {\n annotation: newAnnotation,\n action: 'add'\n });\n\n $('#contentmodal').modal('hide');\n }\n\n /**\n * Handles the event when the edit form is loaded.\n *\n * @param {Object} form - The form object that is being edited.\n * @param {Event} event - The event object associated with the form loading.\n * @returns {Object} - An object containing the form and event.\n *\n */\n onEditFormLoaded(form, event) {\n let self = this;\n const body = super.onEditFormLoaded(form, event);\n body.on('change', '[name=titleassist]', function(e) {\n e.preventDefault();\n const originalValue = $(this).data('initial-value');\n // Make sure the timestamp format is hh:mm:ss.\n if (!self.validateTimestampFormat($(this).val())) {\n self.addNotification(M.util.get_string('invalidtimestampformat', 'ivplugin_skipsegment'));\n $(this).val(originalValue);\n return;\n }\n\n // Convert the timestamp to seconds.\n const parts = $(this).val().split(':');\n const timestamp = Number(parts[0]) * 3600 + Number(parts[1]) * 60 + Number(parts[2]);\n if (!self.isBetweenStartAndEnd(timestamp)) {\n const message = M.util.get_string('interactioncanonlybeaddedbetweenstartandendtime', 'mod_interactivevideo', {\n \"start\": self.convertSecondsToHMS(self.start),\n \"end\": self.convertSecondsToHMS(self.end),\n });\n self.addNotification(message);\n $(this).val(originalValue);\n return;\n }\n\n // Make sure the title assist is not the same as the timestamp assist or less than the timestamp assist\n if (timestamp <= Number($('[name=timestamp]').val())) {\n self.addNotification(M.util.get_string('segmentendmustbegreaterthantimestamp', 'mod_interactivevideo'));\n $(this).val(originalValue);\n return;\n }\n\n $('[name=title]').val(timestamp);\n });\n return {form, event};\n }\n\n /**\n * Renders an annotation on the video navigation timeline.\n *\n * @param {Object} annotation - The annotation object to be rendered.\n * @param {number} annotation.timestamp - The timestamp of the annotation.\n * @param {string} annotation.title - The title of the annotation.\n * @param {string} annotation.type - The type of the annotation.\n * @param {string} annotation.id - The unique identifier of the annotation.\n */\n renderItemOnVideoNavigation(annotation) {\n if (annotation.timestamp < this.start || annotation.timestamp > this.end) {\n return;\n }\n const percentage = ((Number(annotation.timestamp) - this.start) / this.totaltime) * 100;\n const length = (Number(annotation.title) - Number(annotation.timestamp)) / this.totaltime * 100;\n if (this.isVisible(annotation) && !this.isEditMode()) {\n $(\"#video-nav ul\").append(`
        • `);\n }\n if (this.isEditMode()) {\n $(\"#video-timeline-wrapper\").append(`
          \n
          \n
          `);\n }\n }\n /**\n * Executes the interaction for skipping a segment in the video.\n *\n * This function appends a skip segment icon to the video block, seeks the video player to the specified annotation time,\n * updates the progress bar, and then plays the video.\n * The skip segment icon is displayed for a short duration before being removed.\n *\n * @param {Object} annotation - The annotation object containing the title which represents the time to seek to.\n * @returns {Promise} A promise that resolves when the interaction is complete.\n */\n async runInteraction(annotation) {\n $('.video-block').append(`
          \n
          `);\n $('#skipsegment').fadeIn(300);\n await this.player.seek(Number(annotation.title) + 0.01);\n this.player.play();\n setTimeout(() => {\n $('#skipsegment').fadeOut(300);\n setTimeout(() => {\n $('#skipsegment').remove();\n }, 300);\n }, 1000);\n\n }\n}"],"names":["SkipSegment","Base","init","this","isEditMode","self","skipsegment","annotations","filter","annotation","type","document","on","async","e","t","originalEvent","detail","time","timestamp","title","player","seek","Number","runInteraction","renderEditItem","listItem","item","super","find","removeAttr","remove","replaceWith","convertSecondsToHMS","totaltime","isSkipped","after","M","util","get_string","coursemodule","data","end","contextid","cfg","prop","name","courseid","course","cmid","annotationid","interaction","hascompletion","advanced","JSON","stringify","ajax","Ajax","call","methodname","args","skipdata","newAnnotation","parse","action","modal","onEditFormLoaded","form","event","preventDefault","originalValue","validateTimestampFormat","val","addNotification","parts","split","isBetweenStartAndEnd","message","start","renderItemOnVideoNavigation","percentage","length","isVisible","append","isClickable","id","icon","fadeIn","play","setTimeout","fadeOut"],"mappings":";;;;;;;2MA0BqBA,oBAAoBC,cASrCC,WACSC,KAAKC,aAAc,KAChBC,KAAOF,WACLG,YAAcH,KAAKI,YAAYC,QAAQC,YAAkC,eAAnBA,WAAWC,2BACrEC,UAAUC,GAAG,cAAcC,eAAeC,SAClCC,EAAID,EAAEE,cAAcC,OAAOC,SAC5B,IAAIT,cAAcH,eACfG,WAAWU,UAAYJ,GAAKN,WAAWW,MAAQL,EAAG,OAC5CV,KAAKgB,OAAOC,KAAKC,OAAOd,WAAWW,cACnCf,KAAKmB,eAAef,uBAe9CgB,eAAelB,YAAamB,SAAUC,aAClCD,SAAWE,MAAMH,eAAelB,YAAamB,SAAUC,OAC9CE,KAAK,mBAAmBC,WAAW,iBAC5CJ,SAASG,KAAK,aAAaE,SAC3BL,SAASG,KAAK,UAAUG,4IAEFL,KAAKP,mBAAUjB,KAAK8B,oBAAoBN,KAAKP,MAAOjB,KAAK+B,UAAY,MAAM,eAC7F/B,KAAKgC,UAAUR,KAAKR,YACpBO,SAASG,KAAK,YAAYO,qFACRC,EAAEC,KAAKC,WAAW,UAAW,oCAE5Cb,6BAWSnB,YAAaY,UAAWqB,kBAEpCC,KAAO,CACPrB,MAAOD,UAAY,EAAIhB,KAAKuC,IAAMvC,KAAKuC,IAAMvB,UAAY,EACzDA,UAAWA,UACXwB,UAAWN,EAAEO,IAAID,UACjBjC,KALOP,KAKI0C,KAAKC,KAChBC,SANO5C,KAMQ6C,OACfC,KAAMT,aACNU,aARO/C,KAQYgD,YACnBC,cATOjD,KASa0C,KAAKO,cAAgB,EAAI,EAC7CC,SAAUC,KAAKC,UAAU,wBACK,0BACD,8BACG,4BACD,oBACT,OAGtBC,WAAaC,cAAKC,KAAK,CAAC,CACxBC,WAAY,gCACZC,KAAM,CACFC,SAAUP,KAAKC,UAAUd,MACzBE,UAAWN,EAAEO,IAAID,WAErBA,UAAWN,EAAEO,IAAID,aACjB,GAEAmB,cAAgBR,KAAKS,MAAMP,KAAKf,0CACtB,oBAAqB,CAC/BhC,WAAYqD,cACZE,OAAQ,4BAGV,iBAAiBC,MAAM,QAW7BC,iBAAiBC,KAAMC,WACf/D,KAAOF,YACEyB,MAAMsC,iBAAiBC,KAAMC,OACrCxD,GAAG,SAAU,sBAAsB,SAASE,GAC7CA,EAAEuD,uBACIC,eAAgB,mBAAEnE,MAAMsC,KAAK,qBAE9BpC,KAAKkE,yBAAwB,mBAAEpE,MAAMqE,cACtCnE,KAAKoE,gBAAgBpC,EAAEC,KAAKC,WAAW,yBAA0B,iDAC/DpC,MAAMqE,IAAIF,qBAKVI,OAAQ,mBAAEvE,MAAMqE,MAAMG,MAAM,KAC5BxD,UAA+B,KAAnBI,OAAOmD,MAAM,IAAgC,GAAnBnD,OAAOmD,MAAM,IAAWnD,OAAOmD,MAAM,QAC5ErE,KAAKuE,qBAAqBzD,WAAY,OACjC0D,QAAUxC,EAAEC,KAAKC,WAAW,kDAAmD,uBAAwB,OAChGlC,KAAK4B,oBAAoB5B,KAAKyE,WAChCzE,KAAK4B,oBAAoB5B,KAAKqC,cAEzCrC,KAAKoE,gBAAgBI,iCACnB1E,MAAMqE,IAAIF,kBAKZnD,WAAaI,QAAO,mBAAE,oBAAoBiD,cAC1CnE,KAAKoE,gBAAgBpC,EAAEC,KAAKC,WAAW,uCAAwC,iDAC7EpC,MAAMqE,IAAIF,mCAId,gBAAgBE,IAAIrD,cAEnB,CAACgD,KAAAA,KAAMC,MAAAA,OAYlBW,4BAA4BtE,eACpBA,WAAWU,UAAYhB,KAAK2E,OAASrE,WAAWU,UAAYhB,KAAKuC,iBAG/DsC,YAAezD,OAAOd,WAAWU,WAAahB,KAAK2E,OAAS3E,KAAK+B,UAAa,IAC9E+C,QAAU1D,OAAOd,WAAWW,OAASG,OAAOd,WAAWU,YAAchB,KAAK+B,UAAY,IACxF/B,KAAK+E,UAAUzE,cAAgBN,KAAKC,kCAClC,iBAAiB+E,uCAAgC1E,WAAWC,+BAC3DP,KAAKiF,YAAY3E,YAAc,GAAK,4HACnBA,WAAWU,gCAAuBV,WAAW4E,6CAC/CL,gCAAuBC,sKAEM9E,KAAK0C,KAAKyC,wBAEzDnF,KAAKC,kCACH,2BAA2B+E,6GACN1E,WAAWU,gCAAuBV,WAAW4E,6DAClCL,gCAAuBC,6OAe5CxE,gCACf,gBAAgB0E,0GACLhF,KAAK0C,KAAKyC,0CACrB,gBAAgBC,OAAO,WACnBpF,KAAKkB,OAAOC,KAAKC,OAAOd,WAAWW,OAAS,UAC7CC,OAAOmE,OACZC,YAAW,yBACL,gBAAgBC,QAAQ,KAC1BD,YAAW,yBACL,gBAAgB1D,WACnB,OACJ"} \ No newline at end of file diff --git a/plugins/skipsegment/amd/src/main.js b/plugins/skipsegment/amd/src/main.js index c85dea1..a0ac44a 100644 --- a/plugins/skipsegment/amd/src/main.js +++ b/plugins/skipsegment/amd/src/main.js @@ -102,6 +102,7 @@ export default class SkipSegment extends Base { methodname: 'ivplugin_skipsegment_add_skip', args: { skipdata: JSON.stringify(data), + contextid: M.cfg.contextid, }, contextid: M.cfg.contextid, }])[0]; @@ -113,7 +114,6 @@ export default class SkipSegment extends Base { }); $('#contentmodal').modal('hide'); - } /** diff --git a/plugins/skipsegment/classes/external/add_skip.php b/plugins/skipsegment/classes/external/add_skip.php index 9fda0a4..25ccedf 100644 --- a/plugins/skipsegment/classes/external/add_skip.php +++ b/plugins/skipsegment/classes/external/add_skip.php @@ -47,18 +47,21 @@ public static function execute_parameters(): external_function_parameters { /** * Implementation of web service ivplugin_skipsegment_add_skip * @param string $skipdata The data of the skip segment + * @param int $contextid The context id of the skip segment * @return array */ - public static function execute($skipdata) { + public static function execute($skipdata, $contextid) { global $DB; // Parameter validation. $params = self::validate_parameters(self::execute_parameters(), [ 'skipdata' => $skipdata, + 'contextid' => $contextid, ]); require_login(); - $data = json_decode($skipdata, true); + require_capability('mod/interactivevideo:edit', context_module::instance($contextid)); + $data = json_decode($skipdata, true); $data['timecreated'] = time(); $data['timemodified'] = time(); $id = $DB->insert_record('interactivevideo_items', (object)$data); diff --git a/report.php b/report.php index dc47d67..c971edb 100644 --- a/report.php +++ b/report.php @@ -29,7 +29,7 @@ $id = required_param('id', PARAM_INT); // Course_module ID. -$cm = get_coursemodule_from_id('interactivevideo', $id, 0, false, MUST_EXIST); +$cm = get_coursemodule_from_id('interactivevideo', $id, 0, false, MUST_EXIST); $moduleinstance = $DB->get_record('interactivevideo', ['id' => $cm->instance], '*', MUST_EXIST); $group = optional_param('group', 0, PARAM_INT); $course = $DB->get_record('course', ['id' => $cm->course], '*', MUST_EXIST); @@ -214,6 +214,8 @@ echo $OUTPUT->render_from_template('mod_interactivevideo/reporttable', $reporttabledata); +$url = ''; + if ($moduleinstance->source == 'url') { $url = $moduleinstance->videourl; } else { @@ -226,9 +228,7 @@ 'filesize DESC', ); $file = reset($files); - if (!$file) { - $url = ''; - } else { + if ($file) { $url = moodle_url::make_pluginfile_url( $file->get_contextid(), $file->get_component(), diff --git a/styles.css b/styles.css index ca01937..5c19542 100644 --- a/styles.css +++ b/styles.css @@ -303,7 +303,7 @@ html:has(.path-mod-interactivevideo) { z-index: 1032; height: 100dvh; width: 0; - margin-right: 0px; + margin-right: 0; bottom: 0; top: 0; align-content: center; diff --git a/version.php b/version.php index 51d64b0..ce5cb0d 100644 --- a/version.php +++ b/version.php @@ -26,7 +26,7 @@ $plugin->component = 'mod_interactivevideo'; $plugin->release = 'RC0.2'; -$plugin->version = 2024092224; +$plugin->version = 2024092225; $plugin->requires = 2022112800; $plugin->supported = [401, 405]; $plugin->maturity = MATURITY_RC;