From 637e9eabeacb9c804476b1033cbb9e7ec0bb280e Mon Sep 17 00:00:00 2001 From: Markos Gogoulos Date: Tue, 3 Feb 2026 19:23:25 +0200 Subject: [PATCH] a --- .../mediacms-moodle/tiny/AUTOCONVERT.md | 206 --- lms-plugins/mediacms-moodle/tiny/README.md | 187 --- .../tiny/amd/build/autoconvert.min.js | 15 - .../tiny/amd/build/autoconvert.min.js.map | 1 - .../tiny/amd/build/commands.min.js | 10 - .../tiny/amd/build/commands.min.js.map | 1 - .../tiny/amd/build/common.min.js | 3 - .../tiny/amd/build/common.min.js.map | 1 - .../tiny/amd/build/configuration.min.js | 3 - .../tiny/amd/build/configuration.min.js.map | 1 - .../tiny/amd/build/embed.min.js | 3 - .../tiny/amd/build/embed.min.js.map | 1 - .../tiny/amd/build/embedmodal.min.js | 3 - .../tiny/amd/build/embedmodal.min.js.map | 1 - .../tiny/amd/build/iframeembed.min.js | 3 - .../tiny/amd/build/iframeembed.min.js.map | 1 - .../tiny/amd/build/iframemodal.min.js | 3 - .../tiny/amd/build/iframemodal.min.js.map | 1 - .../tiny/amd/build/image.min.js | 3 - .../tiny/amd/build/image.min.js.map | 1 - .../tiny/amd/build/imagedetails.min.js | 3 - .../tiny/amd/build/imagedetails.min.js.map | 1 - .../tiny/amd/build/imagehelpers.min.js | 10 - .../tiny/amd/build/imagehelpers.min.js.map | 1 - .../tiny/amd/build/imageinsert.min.js | 3 - .../tiny/amd/build/imageinsert.min.js.map | 1 - .../tiny/amd/build/imagemodal.min.js | 3 - .../tiny/amd/build/imagemodal.min.js.map | 1 - .../tiny/amd/build/manager.min.js | 3 - .../tiny/amd/build/manager.min.js.map | 1 - .../tiny/amd/build/options.min.js | 11 - .../tiny/amd/build/options.min.js.map | 1 - .../tiny/amd/build/plugin.min.js | 10 - .../tiny/amd/build/plugin.min.js.map | 1 - .../tiny/amd/build/selectors.min.js | 3 - .../tiny/amd/build/selectors.min.js.map | 1 - .../tiny/amd/build/usedfiles.min.js | 10 - .../tiny/amd/build/usedfiles.min.js.map | 1 - .../tiny/amd/src/autoconvert.js | 264 ---- .../mediacms-moodle/tiny/amd/src/commands.js | 282 ---- .../mediacms-moodle/tiny/amd/src/common.js | 30 - .../tiny/amd/src/configuration.js | 60 - .../mediacms-moodle/tiny/amd/src/embed.js | 467 ------- .../tiny/amd/src/embedmodal.js | 47 - .../tiny/amd/src/iframeembed.js | 1130 ----------------- .../tiny/amd/src/iframemodal.js | 47 - .../mediacms-moodle/tiny/amd/src/image.js | 273 ---- .../tiny/amd/src/imagedetails.js | 614 --------- .../tiny/amd/src/imagehelpers.js | 149 --- .../tiny/amd/src/imageinsert.js | 282 ---- .../tiny/amd/src/imagemodal.js | 49 - .../mediacms-moodle/tiny/amd/src/manager.js | 86 -- .../mediacms-moodle/tiny/amd/src/options.js | 117 -- .../mediacms-moodle/tiny/amd/src/plugin.js | 184 --- .../mediacms-moodle/tiny/amd/src/selectors.js | 162 --- .../mediacms-moodle/tiny/amd/src/usedfiles.js | 95 -- .../tiny/build_instructions.md | 33 - .../tiny/classes/form/manage_files_form.php | 118 -- .../tiny/classes/plugininfo.php | 226 ---- .../tiny/classes/privacy/provider.php | 30 - .../tiny/lang/en/deprecated.txt | 7 - .../tiny/lang/en/tiny_mediacms.php | 199 --- lms-plugins/mediacms-moodle/tiny/manage.php | 148 --- .../tiny/mediacms/AUTOCONVERT.md | 206 --- .../mediacms/amd/build/autoconvert.min.js | 15 - .../mediacms/amd/build/autoconvert.min.js.map | 1 - .../tiny/mediacms/amd/build/commands.min.js | 10 - .../mediacms/amd/build/commands.min.js.map | 1 - .../tiny/mediacms/amd/build/common.min.js | 3 - .../tiny/mediacms/amd/build/common.min.js.map | 1 - .../mediacms/amd/build/configuration.min.js | 3 - .../amd/build/configuration.min.js.map | 1 - .../tiny/mediacms/amd/build/embed.min.js | 3 - .../tiny/mediacms/amd/build/embed.min.js.map | 1 - .../tiny/mediacms/amd/build/embedmodal.min.js | 3 - .../mediacms/amd/build/embedmodal.min.js.map | 1 - .../mediacms/amd/build/iframeembed.min.js | 3 - .../mediacms/amd/build/iframeembed.min.js.map | 1 - .../mediacms/amd/build/iframemodal.min.js | 3 - .../mediacms/amd/build/iframemodal.min.js.map | 1 - .../tiny/mediacms/amd/build/image.min.js | 3 - .../tiny/mediacms/amd/build/image.min.js.map | 1 - .../mediacms/amd/build/imagedetails.min.js | 3 - .../amd/build/imagedetails.min.js.map | 1 - .../mediacms/amd/build/imagehelpers.min.js | 10 - .../amd/build/imagehelpers.min.js.map | 1 - .../mediacms/amd/build/imageinsert.min.js | 3 - .../mediacms/amd/build/imageinsert.min.js.map | 1 - .../tiny/mediacms/amd/build/imagemodal.min.js | 3 - .../mediacms/amd/build/imagemodal.min.js.map | 1 - .../tiny/mediacms/amd/build/manager.min.js | 3 - .../mediacms/amd/build/manager.min.js.map | 1 - .../tiny/mediacms/amd/build/options.min.js | 11 - .../mediacms/amd/build/options.min.js.map | 1 - .../tiny/mediacms/amd/build/plugin.min.js | 10 - .../tiny/mediacms/amd/build/plugin.min.js.map | 1 - .../tiny/mediacms/amd/build/selectors.min.js | 3 - .../mediacms/amd/build/selectors.min.js.map | 1 - .../tiny/mediacms/amd/build/usedfiles.min.js | 10 - .../mediacms/amd/build/usedfiles.min.js.map | 1 - .../tiny/mediacms/amd/src/autoconvert.js | 265 ---- .../tiny/mediacms/amd/src/commands.js | 282 ---- .../tiny/mediacms/amd/src/common.js | 30 - .../tiny/mediacms/amd/src/configuration.js | 60 - .../tiny/mediacms/amd/src/embed.js | 467 ------- .../tiny/mediacms/amd/src/embedmodal.js | 47 - .../tiny/mediacms/amd/src/iframeembed.js | 1090 ---------------- .../tiny/mediacms/amd/src/iframemodal.js | 47 - .../tiny/mediacms/amd/src/image.js | 273 ---- .../tiny/mediacms/amd/src/imagedetails.js | 614 --------- .../tiny/mediacms/amd/src/imagehelpers.js | 149 --- .../tiny/mediacms/amd/src/imageinsert.js | 282 ---- .../tiny/mediacms/amd/src/imagemodal.js | 49 - .../tiny/mediacms/amd/src/manager.js | 86 -- .../tiny/mediacms/amd/src/options.js | 117 -- .../tiny/mediacms/amd/src/plugin.js | 92 -- .../tiny/mediacms/amd/src/selectors.js | 162 --- .../tiny/mediacms/amd/src/usedfiles.js | 95 -- .../classes/form/manage_files_form.php | 118 -- .../tiny/mediacms/classes/plugininfo.php | 226 ---- .../mediacms/classes/privacy/provider.php | 30 - .../tiny/mediacms/lang/en/deprecated.txt | 7 - .../tiny/mediacms/lang/en/tiny_mediacms.php | 199 --- .../mediacms-moodle/tiny/mediacms/manage.php | 148 --- .../tiny/mediacms/pix/filemanager.svg | 4 - .../tiny/mediacms/pix/icon.svg | 4 - .../tiny/mediacms/settings.php | 97 -- .../mediacms-moodle/tiny/mediacms/styles.css | 79 -- .../templates/embed_media_audio.mustache | 41 - .../templates/embed_media_link.mustache | 33 - .../templates/embed_media_modal.mustache | 78 -- .../embed_media_modal_audio.mustache | 802 ------------ .../templates/embed_media_modal_link.mustache | 43 - .../embed_media_modal_video.mustache | 832 ------------ .../templates/embed_media_video.mustache | 50 - .../templates/iframe_embed_modal.mustache | 134 -- .../templates/iframe_embed_options.mustache | 127 -- .../templates/iframe_embed_output.mustache | 36 - .../templates/iframe_video_library.mustache | 91 -- .../iframe_video_library_item.mustache | 73 -- .../tiny/mediacms/templates/image.mustache | 34 - .../templates/insert_image_modal.mustache | 61 - .../insert_image_modal_details.mustache | 112 -- ...insert_image_modal_details_footer.mustache | 42 - .../insert_image_modal_insert.mustache | 53 - .../insert_image_modal_insert_footer.mustache | 56 - .../mediacms/templates/missingfiles.mustache | 34 - .../mediacms/templates/mm2_iframe.mustache | 28 - .../tiny/mediacms/tests/behat/image.feature | 74 -- .../tiny/mediacms/tests/behat/video.feature | 58 - .../mediacms-moodle/tiny/mediacms/version.php | 30 - .../mediacms-moodle/tiny/pix/filemanager.svg | 4 - lms-plugins/mediacms-moodle/tiny/pix/icon.svg | 4 - lms-plugins/mediacms-moodle/tiny/settings.php | 97 -- lms-plugins/mediacms-moodle/tiny/styles.css | 83 -- .../tiny/templates/embed_media_audio.mustache | 41 - .../tiny/templates/embed_media_link.mustache | 33 - .../tiny/templates/embed_media_modal.mustache | 78 -- .../embed_media_modal_audio.mustache | 802 ------------ .../templates/embed_media_modal_link.mustache | 43 - .../embed_media_modal_video.mustache | 832 ------------ .../tiny/templates/embed_media_video.mustache | 50 - .../templates/iframe_embed_modal.mustache | 134 -- .../templates/iframe_embed_options.mustache | 127 -- .../templates/iframe_embed_output.mustache | 36 - .../templates/iframe_video_library.mustache | 91 -- .../iframe_video_library_item.mustache | 73 -- .../tiny/templates/image.mustache | 34 - .../templates/insert_image_modal.mustache | 61 - .../insert_image_modal_details.mustache | 112 -- ...insert_image_modal_details_footer.mustache | 42 - .../insert_image_modal_insert.mustache | 53 - .../insert_image_modal_insert_footer.mustache | 56 - .../tiny/templates/missingfiles.mustache | 34 - .../tiny/templates/mm2_iframe.mustache | 28 - .../tiny/tests/behat/image.feature | 74 -- .../tiny/tests/behat/video.feature | 58 - lms-plugins/mediacms-moodle/tiny/version.php | 30 - 178 files changed, 17089 deletions(-) delete mode 100755 lms-plugins/mediacms-moodle/tiny/AUTOCONVERT.md delete mode 100644 lms-plugins/mediacms-moodle/tiny/README.md delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/autoconvert.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/commands.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/common.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/configuration.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/embed.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/embedmodal.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/iframeembed.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/iframemodal.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/image.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/imagedetails.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/imagehelpers.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/imageinsert.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/imagemodal.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/manager.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/options.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/plugin.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/selectors.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/amd/src/usedfiles.js delete mode 100644 lms-plugins/mediacms-moodle/tiny/build_instructions.md delete mode 100755 lms-plugins/mediacms-moodle/tiny/classes/form/manage_files_form.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/classes/plugininfo.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/classes/privacy/provider.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/lang/en/deprecated.txt delete mode 100755 lms-plugins/mediacms-moodle/tiny/lang/en/tiny_mediacms.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/manage.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/manage.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/pix/filemanager.svg delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/pix/icon.svg delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/settings.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/styles.css delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_audio.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_link.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_audio.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_link.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_video.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_video.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_modal.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_options.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_output.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library_item.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/image.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details_footer.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert_footer.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/missingfiles.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/templates/mm2_iframe.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/image.feature delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/video.feature delete mode 100755 lms-plugins/mediacms-moodle/tiny/mediacms/version.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/pix/filemanager.svg delete mode 100755 lms-plugins/mediacms-moodle/tiny/pix/icon.svg delete mode 100755 lms-plugins/mediacms-moodle/tiny/settings.php delete mode 100755 lms-plugins/mediacms-moodle/tiny/styles.css delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_audio.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_link.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_audio.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_link.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_video.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/embed_media_video.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_modal.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_options.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_output.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library_item.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/image.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details_footer.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert_footer.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/missingfiles.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/templates/mm2_iframe.mustache delete mode 100755 lms-plugins/mediacms-moodle/tiny/tests/behat/image.feature delete mode 100755 lms-plugins/mediacms-moodle/tiny/tests/behat/video.feature delete mode 100755 lms-plugins/mediacms-moodle/tiny/version.php diff --git a/lms-plugins/mediacms-moodle/tiny/AUTOCONVERT.md b/lms-plugins/mediacms-moodle/tiny/AUTOCONVERT.md deleted file mode 100755 index d1ac4526..00000000 --- a/lms-plugins/mediacms-moodle/tiny/AUTOCONVERT.md +++ /dev/null @@ -1,206 +0,0 @@ -# MediaCMS URL Auto-Convert Feature - -This feature automatically converts pasted MediaCMS video URLs into embedded video players within the TinyMCE editor. - -## Overview - -When a user pastes a MediaCMS video URL like: -``` -https://deic.mediacms.io/view?m=JpBd1Zvdl -``` - -It is automatically converted to an embedded video player: -```html -
- -
-``` - -## Supported URL Formats - -The auto-convert feature recognizes MediaCMS view URLs in this format: -- `https://[domain]/view?m=[VIDEO_ID]` - -Examples: -- `https://deic.mediacms.io/view?m=JpBd1Zvdl` -- `https://your-mediacms-instance.com/view?m=abc123` - -## Configuration - -### Accessing Settings - -1. Log in to Moodle as an administrator -2. Navigate to: **Site administration** → **Plugins** → **Text editors** → **TinyMCE editor** → **MediaCMS** -3. Scroll to the **Auto-convert MediaCMS URLs** section - -### Available Settings - -| Setting | Description | Default | -|---------|-------------|---------| -| **Enable auto-convert** | Turn the auto-convert feature on or off | Enabled | -| **MediaCMS base URL** | Restrict auto-conversion to a specific MediaCMS domain | Empty (allow all) | -| **Show video title** | Display the video title in the embedded player | Enabled | -| **Link video title** | Make the video title clickable, linking to the original video page | Enabled | -| **Show related videos** | Display related videos after the current video ends | Enabled | -| **Show user avatar** | Display the uploader's avatar in the embedded player | Enabled | - -### Settings Location in Moodle - -The settings are stored in the Moodle database under the `tiny_mediacms` plugin configuration: - -- `tiny_mediacms/autoconvertenabled` - Enable/disable auto-convert -- `tiny_mediacms/autoconvert_baseurl` - MediaCMS base URL (e.g., https://deic.mediacms.io) -- `tiny_mediacms/autoconvert_showtitle` - Show title option -- `tiny_mediacms/autoconvert_linktitle` - Link title option -- `tiny_mediacms/autoconvert_showrelated` - Show related option -- `tiny_mediacms/autoconvert_showuseravatar` - Show user avatar option - -### Base URL Configuration - -The **MediaCMS base URL** setting controls which MediaCMS instances are recognized for auto-conversion: - -- **Empty (default)**: Any MediaCMS URL will be auto-converted (e.g., URLs from any `*/view?m=*` pattern) -- **Specific URL**: Only URLs from the specified domain will be auto-converted - -Example configurations: -- `https://deic.mediacms.io` - Only convert URLs from deic.mediacms.io -- `https://media.myuniversity.edu` - Only convert URLs from your institution's MediaCMS - -## Technical Details - -### File Structure - -``` -amd/src/ -├── autoconvert.js # Main auto-convert module -├── plugin.js # Plugin initialization (imports autoconvert) -└── options.js # Configuration options definition - -classes/ -└── plugininfo.php # Passes PHP settings to JavaScript - -settings.php # Admin settings page definition - -lang/en/ -└── tiny_mediacms.php # Language strings for settings -``` - -### How It Works - -1. **Paste Detection**: The `autoconvert.js` module listens for `paste` events on the TinyMCE editor -2. **URL Validation**: When text is pasted, it checks if it matches the MediaCMS URL pattern -3. **HTML Generation**: If valid, it generates the responsive iframe HTML with configured options -4. **Content Insertion**: The original URL is replaced with the embedded video - -### JavaScript Configuration - -The settings are passed from PHP to JavaScript via the `plugininfo.php` class: - -```php -protected static function get_autoconvert_configuration(): array { - $baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl'); - - return [ - 'data' => [ - 'autoConvertEnabled' => (bool) get_config('tiny_mediacms', 'autoconvertenabled'), - 'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '', - 'autoConvertOptions' => [ - 'showTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_showtitle'), - 'linkTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_linktitle'), - 'showRelated' => (bool) get_config('tiny_mediacms', 'autoconvert_showrelated'), - 'showUserAvatar' => (bool) get_config('tiny_mediacms', 'autoconvert_showuseravatar'), - ], - ], - ]; -} -``` - -### Default Values (in options.js) - -If PHP settings are not configured, the JavaScript uses these defaults: - -```javascript -registerOption(dataName, { - processor: 'object', - "default": { - autoConvertEnabled: true, - autoConvertBaseUrl: '', // Empty = allow all MediaCMS domains - autoConvertOptions: { - showTitle: true, - linkTitle: true, - showRelated: true, - showUserAvatar: true, - }, - }, -}); -``` - -## Customization - -### Disabling Auto-Convert - -To disable the feature entirely: -1. Go to the plugin settings (see "Accessing Settings" above) -2. Uncheck **Enable auto-convert** -3. Save changes - -### Programmatic Configuration - -You can also set these values directly in the database using Moodle's `set_config()` function: - -```php -// Disable auto-convert -set_config('autoconvertenabled', 0, 'tiny_mediacms'); - -// Set the MediaCMS base URL (restrict to specific domain) -set_config('autoconvert_baseurl', 'https://deic.mediacms.io', 'tiny_mediacms'); - -// Customize embed options -set_config('autoconvert_showtitle', 1, 'tiny_mediacms'); -set_config('autoconvert_linktitle', 0, 'tiny_mediacms'); -set_config('autoconvert_showrelated', 0, 'tiny_mediacms'); -set_config('autoconvert_showuseravatar', 1, 'tiny_mediacms'); -``` - -### CLI Configuration - -Using Moodle CLI: - -```bash -# Enable auto-convert -php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvertenabled --set=1 - -# Set the MediaCMS base URL -php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_baseurl --set=https://deic.mediacms.io - -# Disable showing related videos -php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_showrelated --set=0 -``` - -## Troubleshooting - -### Auto-convert not working - -1. **Check if enabled**: Verify the setting is enabled in plugin settings -2. **Clear caches**: Purge all caches (Site administration → Development → Purge all caches) -3. **Check URL format**: Ensure the URL matches the pattern `https://[domain]/view?m=[VIDEO_ID]` -4. **Browser console**: Check for JavaScript errors in the browser developer console - -### Rebuilding JavaScript - -If you modify the source files, rebuild using: - -```bash -cd /path/to/moodle -npx grunt amd --root=public/lib/editor/tiny/plugins/mediacms -``` - -Note: Requires Node.js 22.x or compatible version as specified in Moodle's requirements. - -## Version History - -- **1.0.0** - Initial implementation of auto-convert feature diff --git a/lms-plugins/mediacms-moodle/tiny/README.md b/lms-plugins/mediacms-moodle/tiny/README.md deleted file mode 100644 index 99ef5639..00000000 --- a/lms-plugins/mediacms-moodle/tiny/README.md +++ /dev/null @@ -1,187 +0,0 @@ -# TinyMCE MediaCMS Plugin for Moodle - -A TinyMCE editor plugin for Moodle that provides media embedding capabilities with MediaCMS/LTI integration. - -## Plugin Information - -- **Component:** `tiny_mediacms` -- **Version:** See `version.php` -- **Requires:** Moodle 4.5+ (2024100100) - -## Directory Structure - -``` -mediacms/ -├── amd/ -│ ├── src/ # JavaScript source files (ES6 modules) -│ │ ├── plugin.js # Main plugin entry point -│ │ ├── commands.js # Editor commands -│ │ ├── configuration.js # Plugin configuration -│ │ ├── iframeembed.js # Iframe embedding logic -│ │ ├── iframemodal.js # Iframe modal UI -│ │ ├── autoconvert.js # URL auto-conversion -│ │ ├── embed.js # Media embedding -│ │ ├── embedmodal.js # Embed modal UI -│ │ ├── image.js # Image handling -│ │ ├── imagemodal.js # Image modal UI -│ │ ├── imageinsert.js # Image insertion -│ │ ├── imagedetails.js # Image details panel -│ │ ├── imagehelpers.js # Image utility functions -│ │ ├── manager.js # File manager -│ │ ├── options.js # Plugin options -│ │ ├── selectors.js # DOM selectors -│ │ ├── common.js # Shared utilities -│ │ └── usedfiles.js # Track used files -│ └── build/ # Compiled/minified files (generated) -├── classes/ # PHP classes -├── lang/ # Language strings -│ └── en/ -│ └── tiny_mediacms.php -├── templates/ # Mustache templates -├── styles.css # Plugin styles -├── settings.php # Admin settings -└── version.php # Plugin version -``` - -## Building JavaScript (AMD Modules) - -When you modify JavaScript files in `amd/src/`, you must rebuild the minified files in `amd/build/`. - -### Prerequisites - -Make sure you have Node.js installed and have run `npm install` in the Moodle root directory: - -```bash -cd /path/to/moodle/public -npm install -``` - -### Build Commands - -#### Build all AMD modules (entire Moodle): - -```bash -cd /path/to/moodle/public -npx grunt amd -``` - -#### Build only this plugin's AMD modules: - -```bash -cd /path/to/moodle/public -npx grunt amd --root=lib/editor/tiny/plugins/mediacms -``` - -#### Watch for changes (auto-rebuild): - -```bash -cd /path/to/moodle/public -npx grunt watch --root=lib/editor/tiny/plugins/mediacms -``` - -#### Force build (ignore warnings): - -```bash -cd /path/to/moodle/public -npx grunt amd --force --root=lib/editor/tiny/plugins/mediacms -``` - -### Build Output - -After running grunt, the following files are generated in `amd/build/`: - -- `*.min.js` - Minified JavaScript files -- `*.min.js.map` - Source maps for debugging - -## Development Mode (Skip Building) - -For faster development, you can skip building by enabling developer mode in Moodle's `config.php`: - -```php -// Add these lines to config.php -$CFG->debugdeveloper = true; -$CFG->cachejs = false; -``` - -This tells Moodle to load the unminified source files directly from `amd/src/` instead of `amd/build/`. - -**Note:** Always build before committing or deploying to production! - -## Purging Caches - -After making changes, you may need to purge Moodle caches: - -### Via CLI (Docker): - -```bash -docker compose exec moodle php /var/www/html/public/admin/cli/purge_caches.php -``` - -### Via CLI (Local): - -```bash -php admin/cli/purge_caches.php -``` - -### Via Web: - -Visit: `http://your-moodle-site/admin/purgecaches.php` - -## What Needs Cache Purging? - -| File Type | Cache Purge Needed? | -|-----------|---------------------| -| `amd/src/*.js` | No (if `$CFG->cachejs = false`) | -| `amd/build/*.min.js` | Yes | -| `lang/en/*.php` | Yes | -| `templates/*.mustache` | Yes | -| `styles.css` | Yes | -| `classes/*.php` | Usually no | -| `settings.php` | Yes | - -## Troubleshooting - -### Changes not appearing? - -1. **JavaScript changes:** - - Rebuild AMD modules: `npx grunt amd --root=lib/editor/tiny/plugins/mediacms` - - Hard refresh browser: `Cmd+Shift+R` (Mac) / `Ctrl+Shift+R` (Windows/Linux) - - Check browser console for errors - -2. **Language strings:** - - Purge Moodle caches - -3. **Templates:** - - Purge Moodle caches - -4. **Styles:** - - Purge Moodle caches - - Hard refresh browser - -### Grunt errors? - -```bash -# Make sure dependencies are installed -cd /path/to/moodle/public -npm install - -# Try with force flag -npx grunt amd --force --root=lib/editor/tiny/plugins/mediacms -``` - -### ESLint errors? - -Fix linting issues or use: - -```bash -npx grunt amd --force --root=lib/editor/tiny/plugins/mediacms -``` - -## Related Documentation - -- [AUTOCONVERT.md](./AUTOCONVERT.md) - URL auto-conversion feature documentation -- [LTI_INTEGRATION.md](./LTI_INTEGRATION.md) - LTI integration documentation - -## License - -GNU GPL v3 or later - http://www.gnu.org/copyleft/gpl.html diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js deleted file mode 100755 index 3cb705d4..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js +++ /dev/null @@ -1,15 +0,0 @@ -define("tiny_mediacms/autoconvert",["exports","./options"],(function(_exports,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupAutoConvert=_exports.isMediaCMSUrl=_exports.convertToEmbed=void 0; -/** - * Tiny MediaCMS Auto-convert module. - * - * This module automatically converts pasted MediaCMS URLs into embedded videos. - * When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl), - * it will be automatically converted to an iframe embed. - * - * @module tiny_mediacms/autoconvert - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -const MEDIACMS_VIEW_URL_PATTERN=/^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/,parseMediaCMSUrl=text=>{if(!text||"string"!=typeof text)return null;const trimmed=text.trim(),match=trimmed.match(MEDIACMS_VIEW_URL_PATTERN);return match?{baseUrl:match[1],videoId:match[2],originalUrl:trimmed}:null},isDomainAllowed=(parsed,config)=>{const configuredBaseUrl=config.autoConvertBaseUrl||config.mediacmsBaseUrl;if(!configuredBaseUrl)return!0;try{const configuredUrl=new URL(configuredBaseUrl),pastedUrl=new URL(parsed.baseUrl);return configuredUrl.host===pastedUrl.host}catch(e){return!0}},generateEmbedHtml=function(parsed){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const embedUrl=new URL("".concat(parsed.baseUrl,"/embed"));embedUrl.searchParams.set("m",parsed.videoId),embedUrl.searchParams.set("showTitle",!1!==options.showTitle?"1":"0"),embedUrl.searchParams.set("showRelated",!1!==options.showRelated?"1":"0"),embedUrl.searchParams.set("showUserAvatar",!1!==options.showUserAvatar?"1":"0"),embedUrl.searchParams.set("linkTitle",!1!==options.linkTitle?"1":"0");const html='';return html};_exports.setupAutoConvert=editor=>{const config=(0,_options.getData)(editor)||{};!1!==config.autoConvertEnabled&&(editor.on("paste",(e=>{handlePasteEvent(editor,e,config)})),editor.on("input",(e=>{handleInputEvent(editor,e,config)})))};const handlePasteEvent=(editor,e,config)=>{const clipboardData=e.clipboardData||window.clipboardData;if(!clipboardData)return;const text=clipboardData.getData("text/plain")||clipboardData.getData("text");if(!text)return;const parsed=parseMediaCMSUrl(text);if(!parsed)return;if(!isDomainAllowed(parsed,config))return;e.preventDefault(),e.stopPropagation();const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{editor.insertContent(embedHtml),editor.selection.collapse(!1)}),0)},handleInputEvent=(editor,e,config)=>{if("insertFromPaste"!==e.inputType&&"insertText"!==e.inputType)return;const node=editor.selection.getNode();if(!node||"P"!==node.nodeName)return;const text=node.textContent||"",parsed=parseMediaCMSUrl(text);if(!parsed||!isDomainAllowed(parsed,config))return;const trimmedHtml=node.innerHTML.trim();if(trimmedHtml!==text.trim()&&!trimmedHtml.startsWith(text.trim()))return;const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{const currentText=node.textContent||"",currentParsed=parseMediaCMSUrl(currentText);currentParsed&¤tParsed.originalUrl===parsed.originalUrl&&(editor.selection.select(node),editor.insertContent(embedHtml))}),100)};_exports.isMediaCMSUrl=text=>null!==parseMediaCMSUrl(text);_exports.convertToEmbed=function(url){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const parsed=parseMediaCMSUrl(url);return parsed?generateEmbedHtml(parsed,options):null}})); - -//# sourceMappingURL=autoconvert.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js.map deleted file mode 100755 index b4e265a2..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/autoconvert.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"autoconvert.min.js","sources":["../src/autoconvert.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 * Tiny MediaCMS Auto-convert module.\n *\n * This module automatically converts pasted MediaCMS URLs into embedded videos.\n * When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),\n * it will be automatically converted to an iframe embed.\n *\n * @module tiny_mediacms/autoconvert\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getData} from './options';\n\n/**\n * Regular expression patterns for MediaCMS URLs.\n * Matches URLs like:\n * - https://deic.mediacms.io/view?m=JpBd1Zvdl\n * - https://example.mediacms.io/view?m=VIDEO_ID\n * - Custom domains configured in the plugin\n */\nconst MEDIACMS_VIEW_URL_PATTERN = /^(https?:\\/\\/[^\\/]+)\\/view\\?m=([a-zA-Z0-9_-]+)$/;\n\n/**\n * Check if a string is a valid MediaCMS view URL.\n *\n * @param {string} text - The text to check\n * @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL\n */\nconst parseMediaCMSUrl = (text) => {\n if (!text || typeof text !== 'string') {\n return null;\n }\n\n const trimmed = text.trim();\n\n // Check for MediaCMS view URL pattern\n const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN);\n if (match) {\n return {\n baseUrl: match[1],\n videoId: match[2],\n originalUrl: trimmed,\n };\n }\n\n return null;\n};\n\n/**\n * Check if the pasted URL's domain is allowed based on configuration.\n *\n * @param {Object} parsed - Parsed URL info\n * @param {Object} config - Plugin configuration\n * @returns {boolean} - True if the domain is allowed\n */\nconst isDomainAllowed = (parsed, config) => {\n // If no specific base URL is configured, allow all MediaCMS domains\n const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl;\n if (!configuredBaseUrl) {\n return true;\n }\n\n // Check if the URL's base matches the configured base URL\n try {\n const configuredUrl = new URL(configuredBaseUrl);\n const pastedUrl = new URL(parsed.baseUrl);\n return configuredUrl.host === pastedUrl.host;\n } catch (e) {\n // If URL parsing fails, allow the conversion\n return true;\n }\n};\n\n/**\n * Generate the iframe embed HTML for a MediaCMS video.\n *\n * @param {Object} parsed - Parsed URL info\n * @param {Object} options - Embed options\n * @returns {string} - The iframe HTML\n */\nconst generateEmbedHtml = (parsed, options = {}) => {\n // Build the embed URL with default options\n const embedUrl = new URL(`${parsed.baseUrl}/embed`);\n embedUrl.searchParams.set('m', parsed.videoId);\n\n // Apply default options (all enabled by default for best user experience)\n embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0');\n embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0');\n embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0');\n embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0');\n\n // Generate responsive iframe HTML matching the template output format.\n // Uses aspect-ratio CSS for responsive sizing (16:9 default).\n // The wrapper will be added by editor for UI (edit button), then stripped on save.\n const html = ``;\n\n return html;\n};\n\n/**\n * Set up auto-conversion for the editor.\n * This registers event handlers to detect pasted MediaCMS URLs.\n *\n * @param {TinyMCE} editor - The TinyMCE editor instance\n */\nexport const setupAutoConvert = (editor) => {\n const config = getData(editor) || {};\n\n // Check if auto-convert is enabled (default: true)\n if (config.autoConvertEnabled === false) {\n return;\n }\n\n // Handle paste events\n editor.on('paste', (e) => {\n handlePasteEvent(editor, e, config);\n });\n\n // Also handle input events for drag-and-drop text or keyboard paste\n editor.on('input', (e) => {\n handleInputEvent(editor, e, config);\n });\n};\n\n/**\n * Handle paste events to detect and convert MediaCMS URLs.\n *\n * @param {TinyMCE} editor - The TinyMCE editor instance\n * @param {Event} e - The paste event\n * @param {Object} config - Plugin configuration\n */\nconst handlePasteEvent = (editor, e, config) => {\n // Get pasted text from clipboard\n const clipboardData = e.clipboardData || window.clipboardData;\n if (!clipboardData) {\n return;\n }\n\n // Try to get plain text first\n const text = clipboardData.getData('text/plain') || clipboardData.getData('text');\n if (!text) {\n return;\n }\n\n // Check if it's a MediaCMS URL\n const parsed = parseMediaCMSUrl(text);\n if (!parsed) {\n return;\n }\n\n // Check if domain is allowed\n if (!isDomainAllowed(parsed, config)) {\n return;\n }\n\n // Prevent default paste behavior\n e.preventDefault();\n e.stopPropagation();\n\n // Generate and insert the embed HTML\n const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});\n\n // Use a slight delay to ensure the editor is ready\n setTimeout(() => {\n editor.insertContent(embedHtml);\n // Move cursor after the inserted content\n editor.selection.collapse(false);\n }, 0);\n};\n\n/**\n * Handle input events to catch URLs that might have been pasted without triggering paste event.\n * This is a fallback for certain browsers/scenarios.\n *\n * @param {TinyMCE} editor - The TinyMCE editor instance\n * @param {Event} e - The input event\n * @param {Object} config - Plugin configuration\n */\nconst handleInputEvent = (editor, e, config) => {\n // Only process inputType 'insertFromPaste' if paste event didn't catch it\n if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') {\n return;\n }\n\n // Get the current node and check if it contains just a URL\n const node = editor.selection.getNode();\n if (!node || node.nodeName !== 'P') {\n return;\n }\n\n // Check if the paragraph contains only a MediaCMS URL\n const text = node.textContent || '';\n const parsed = parseMediaCMSUrl(text);\n\n if (!parsed || !isDomainAllowed(parsed, config)) {\n return;\n }\n\n // Don't convert if there's other content in the paragraph\n const trimmedHtml = node.innerHTML.trim();\n if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) {\n return;\n }\n\n // Generate the embed HTML\n const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});\n\n // Replace the paragraph content with the embed\n // Use a slight delay to let the input event complete\n setTimeout(() => {\n // Re-check that the node still contains the URL (user might have typed more)\n const currentText = node.textContent || '';\n const currentParsed = parseMediaCMSUrl(currentText);\n\n if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) {\n // Select and replace the entire node\n editor.selection.select(node);\n editor.insertContent(embedHtml);\n }\n }, 100);\n};\n\n/**\n * Check if a text is a MediaCMS URL (public helper).\n *\n * @param {string} text - The text to check\n * @returns {boolean} - True if it's a MediaCMS URL\n */\nexport const isMediaCMSUrl = (text) => {\n return parseMediaCMSUrl(text) !== null;\n};\n\n/**\n * Convert a MediaCMS URL to embed HTML (public helper).\n *\n * @param {string} url - The MediaCMS URL\n * @param {Object} options - Embed options\n * @returns {string|null} - The embed HTML or null if not a valid URL\n */\nexport const convertToEmbed = (url, options = {}) => {\n const parsed = parseMediaCMSUrl(url);\n if (!parsed) {\n return null;\n }\n return generateEmbedHtml(parsed, options);\n};\n"],"names":["MEDIACMS_VIEW_URL_PATTERN","parseMediaCMSUrl","text","trimmed","trim","match","baseUrl","videoId","originalUrl","isDomainAllowed","parsed","config","configuredBaseUrl","autoConvertBaseUrl","mediacmsBaseUrl","configuredUrl","URL","pastedUrl","host","e","generateEmbedHtml","options","embedUrl","searchParams","set","showTitle","showRelated","showUserAvatar","linkTitle","html","toString","editor","autoConvertEnabled","on","handlePasteEvent","handleInputEvent","clipboardData","window","getData","preventDefault","stopPropagation","embedHtml","autoConvertOptions","setTimeout","insertContent","selection","collapse","inputType","node","getNode","nodeName","textContent","trimmedHtml","innerHTML","startsWith","currentText","currentParsed","select","url"],"mappings":";;;;;;;;;;;;MAoCMA,0BAA4B,kDAQ5BC,iBAAoBC,WACjBA,MAAwB,iBAATA,YACT,WAGLC,QAAUD,KAAKE,OAGfC,MAAQF,QAAQE,MAAML,kCACxBK,MACO,CACHC,QAASD,MAAM,GACfE,QAASF,MAAM,GACfG,YAAaL,SAId,MAULM,gBAAkB,CAACC,OAAQC,gBAEvBC,kBAAoBD,OAAOE,oBAAsBF,OAAOG,oBACzDF,yBACM,YAKDG,cAAgB,IAAIC,IAAIJ,mBACxBK,UAAY,IAAID,IAAIN,OAAOJ,gBAC1BS,cAAcG,OAASD,UAAUC,KAC1C,MAAOC,UAEE,IAWTC,kBAAoB,SAACV,YAAQW,+DAAU,SAEnCC,SAAW,IAAIN,cAAON,OAAOJ,mBACnCgB,SAASC,aAAaC,IAAI,IAAKd,OAAOH,SAGtCe,SAASC,aAAaC,IAAI,aAAmC,IAAtBH,QAAQI,UAAsB,IAAM,KAC3EH,SAASC,aAAaC,IAAI,eAAuC,IAAxBH,QAAQK,YAAwB,IAAM,KAC/EJ,SAASC,aAAaC,IAAI,kBAA6C,IAA3BH,QAAQM,eAA2B,IAAM,KACrFL,SAASC,aAAaC,IAAI,aAAmC,IAAtBH,QAAQO,UAAsB,IAAM,WAKrEC,KAAO,uBAAgBP,SAASQ,iBAAzB,2HAIND,gCASsBE,eACvBpB,QAAS,oBAAQoB,SAAW,IAGA,IAA9BpB,OAAOqB,qBAKXD,OAAOE,GAAG,SAAUd,IAChBe,iBAAiBH,OAAQZ,EAAGR,WAIhCoB,OAAOE,GAAG,SAAUd,IAChBgB,iBAAiBJ,OAAQZ,EAAGR,mBAW9BuB,iBAAmB,CAACH,OAAQZ,EAAGR,gBAE3ByB,cAAgBjB,EAAEiB,eAAiBC,OAAOD,kBAC3CA,2BAKClC,KAAOkC,cAAcE,QAAQ,eAAiBF,cAAcE,QAAQ,YACrEpC,kBAKCQ,OAAST,iBAAiBC,UAC3BQ,kBAKAD,gBAAgBC,OAAQC,eAK7BQ,EAAEoB,iBACFpB,EAAEqB,wBAGIC,UAAYrB,kBAAkBV,OAAQC,OAAO+B,oBAAsB,IAGzEC,YAAW,KACPZ,OAAOa,cAAcH,WAErBV,OAAOc,UAAUC,UAAS,KAC3B,IAWDX,iBAAmB,CAACJ,OAAQZ,EAAGR,aAEb,oBAAhBQ,EAAE4B,WAAmD,eAAhB5B,EAAE4B,uBAKrCC,KAAOjB,OAAOc,UAAUI,cACzBD,MAA0B,MAAlBA,KAAKE,sBAKZhD,KAAO8C,KAAKG,aAAe,GAC3BzC,OAAST,iBAAiBC,UAE3BQ,SAAWD,gBAAgBC,OAAQC,qBAKlCyC,YAAcJ,KAAKK,UAAUjD,UAC/BgD,cAAgBlD,KAAKE,SAAWgD,YAAYE,WAAWpD,KAAKE,qBAK1DqC,UAAYrB,kBAAkBV,OAAQC,OAAO+B,oBAAsB,IAIzEC,YAAW,WAEDY,YAAcP,KAAKG,aAAe,GAClCK,cAAgBvD,iBAAiBsD,aAEnCC,eAAiBA,cAAchD,cAAgBE,OAAOF,cAEtDuB,OAAOc,UAAUY,OAAOT,MACxBjB,OAAOa,cAAcH,cAE1B,6BASuBvC,MACQ,OAA3BD,iBAAiBC,8BAUE,SAACwD,SAAKrC,+DAAU,SACpCX,OAAST,iBAAiByD,YAC3BhD,OAGEU,kBAAkBV,OAAQW,SAFtB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js deleted file mode 100755 index 3835b5e0..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/commands",["exports","core/str","./common","./iframeembed","editor_tiny/utils"],(function(_exports,_str,_common,_iframeembed,_utils){var obj; -/** - * Tiny Media commands. - * - * @module tiny_mediacms/commands - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0,_iframeembed=(obj=_iframeembed)&&obj.__esModule?obj:{default:obj};const isIframe=node=>"iframe"===node.nodeName.toLowerCase()||node.classList&&node.classList.contains("tiny-iframe-responsive")||node.classList&&node.classList.contains("tiny-mediacms-iframe-wrapper"),setupIframeOverlays=(editor,handleIframeAction)=>{const processIframes=()=>{const editorBody=editor.getBody();if(!editorBody)return;editorBody.querySelectorAll("iframe").forEach((iframe=>{var _iframe$parentElement;if(null!==(_iframe$parentElement=iframe.parentElement)&&void 0!==_iframe$parentElement&&_iframe$parentElement.classList.contains("tiny-mediacms-iframe-wrapper"))return;if(iframe.hasAttribute("data-mce-object")||iframe.hasAttribute("data-mce-placeholder"))return;const wrapper=editor.getDoc().createElement("div");wrapper.className="tiny-mediacms-iframe-wrapper",wrapper.setAttribute("contenteditable","false");const editBtn=editor.getDoc().createElement("button");editBtn.className="tiny-mediacms-edit-btn",editBtn.setAttribute("type","button"),editBtn.setAttribute("title","Edit video embed options"),editBtn.innerHTML='',iframe.parentNode.insertBefore(wrapper,iframe),wrapper.appendChild(iframe),wrapper.appendChild(editBtn)}))},handleOverlayClick=e=>{const editBtn=e.target.closest(".tiny-mediacms-edit-btn");if(!editBtn)return;e.preventDefault(),e.stopPropagation();const wrapper=editBtn.closest(".tiny-mediacms-iframe-wrapper");if(!wrapper)return;wrapper.querySelector("iframe")&&(editor.selection.select(wrapper),handleIframeAction())};editor.on("init",(()=>{(()=>{const editorDoc=editor.getDoc();if(!editorDoc)return;if(editorDoc.getElementById("tiny-mediacms-overlay-styles"))return;const style=editorDoc.createElement("style");style.id="tiny-mediacms-overlay-styles",style.textContent="\n .tiny-mediacms-iframe-wrapper {\n display: inline-block;\n position: relative;\n line-height: 0;\n vertical-align: top;\n }\n .tiny-mediacms-iframe-wrapper iframe {\n display: block;\n }\n .tiny-mediacms-edit-btn {\n position: absolute;\n top: 48px;\n left: 6px;\n width: 28px;\n height: 28px;\n background: #ffffff;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n z-index: 10;\n padding: 0;\n margin: 0;\n box-shadow: 0 2px 6px rgba(0,0,0,0.35);\n transition: transform 0.15s, box-shadow 0.15s;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n }\n .tiny-mediacms-edit-btn:hover {\n transform: scale(1.15);\n box-shadow: 0 3px 10px rgba(0,0,0,0.45);\n }\n .tiny-mediacms-edit-btn svg {\n width: 18px !important;\n height: 18px !important;\n display: block !important;\n }\n ",editorDoc.head.appendChild(style)})(),processIframes(),editor.getBody().addEventListener("click",handleOverlayClick)})),editor.on("SetContent",(()=>{processIframes()})),editor.on("PastePostProcess",(()=>{setTimeout(processIframes,100)})),editor.on("Undo Redo",(()=>{processIframes()})),editor.on("Change",(()=>{setTimeout(processIframes,50)})),editor.on("NodeChange",(()=>{processIframes()}))};_exports.getSetup=async()=>{const[iframeButtonText]=await(0,_str.getStrings)(["iframebuttontitle"].map((key=>({key:key,component:_common.component})))),[iframeButtonImage]=await Promise.all([(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{((editor,iframeButtonText,iframeButtonImage)=>{const handleIframeAction=()=>{new _iframeembed.default(editor).displayDialogue()};editor.ui.registry.addIcon(_common.iframeIcon,iframeButtonImage.html),editor.ui.registry.addToggleButton(_common.iframeButtonName,{icon:_common.iframeIcon,tooltip:iframeButtonText,onAction:handleIframeAction,onSetup:api=>editor.selection.selectorChangedWithUnbind("iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper",api.setActive).unbind}),editor.ui.registry.addMenuItem(_common.iframeMenuItemName,{icon:_common.iframeIcon,text:iframeButtonText,onAction:handleIframeAction}),editor.ui.registry.addContextToolbar(_common.iframeButtonName,{predicate:isIframe,items:_common.iframeButtonName,position:"node",scope:"node"}),editor.ui.registry.addContextMenu(_common.iframeButtonName,{update:isIframe}),setupIframeOverlays(editor,handleIframeAction)})(editor,iframeButtonText,iframeButtonImage)}}})); - -//# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js.map deleted file mode 100755 index c250916c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/commands.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny Media commands.\n *\n * @module tiny_mediacms/commands\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getStrings} from 'core/str';\nimport {\n component,\n iframeButtonName,\n iframeMenuItemName,\n iframeIcon,\n} from './common';\nimport IframeEmbed from './iframeembed';\nimport {getButtonImage} from 'editor_tiny/utils';\n\nconst isIframe = (node) => node.nodeName.toLowerCase() === 'iframe' ||\n (node.classList && node.classList.contains('tiny-iframe-responsive')) ||\n (node.classList && node.classList.contains('tiny-mediacms-iframe-wrapper'));\n\n/**\n * Wrap iframes with overlay containers that allow hover detection.\n * Since iframes capture mouse events, we add an invisible overlay on top\n * that shows the edit button on hover.\n *\n * @param {TinyMCE} editor - The editor instance\n * @param {Function} handleIframeAction - The action to perform when clicking the button\n */\nconst setupIframeOverlays = (editor, handleIframeAction) => {\n /**\n * Process all iframes in the editor and add overlay wrappers.\n */\n const processIframes = () => {\n const editorBody = editor.getBody();\n if (!editorBody) {\n return;\n }\n\n const iframes = editorBody.querySelectorAll('iframe');\n iframes.forEach((iframe) => {\n // Skip if already wrapped\n if (iframe.parentElement?.classList.contains('tiny-mediacms-iframe-wrapper')) {\n return;\n }\n\n // Skip TinyMCE internal iframes\n if (iframe.hasAttribute('data-mce-object') || iframe.hasAttribute('data-mce-placeholder')) {\n return;\n }\n\n // Create wrapper div\n const wrapper = editor.getDoc().createElement('div');\n wrapper.className = 'tiny-mediacms-iframe-wrapper';\n wrapper.setAttribute('contenteditable', 'false');\n\n // Create edit button (positioned inside wrapper, over the iframe)\n const editBtn = editor.getDoc().createElement('button');\n editBtn.className = 'tiny-mediacms-edit-btn';\n editBtn.setAttribute('type', 'button');\n editBtn.setAttribute('title', 'Edit video embed options');\n // Use clean inline SVG to avoid TinyMCE wrapper issues\n editBtn.innerHTML = '' +\n '' +\n '' +\n '';\n\n // Wrap the iframe: insert wrapper, move iframe into it, add button\n iframe.parentNode.insertBefore(wrapper, iframe);\n wrapper.appendChild(iframe);\n wrapper.appendChild(editBtn);\n });\n };\n\n /**\n * Add CSS styles for hover effects to the editor's document.\n */\n const addStyles = () => {\n const editorDoc = editor.getDoc();\n if (!editorDoc) {\n return;\n }\n\n // Check if styles already added\n if (editorDoc.getElementById('tiny-mediacms-overlay-styles')) {\n return;\n }\n\n const style = editorDoc.createElement('style');\n style.id = 'tiny-mediacms-overlay-styles';\n style.textContent = `\n .tiny-mediacms-iframe-wrapper {\n display: inline-block;\n position: relative;\n line-height: 0;\n vertical-align: top;\n }\n .tiny-mediacms-iframe-wrapper iframe {\n display: block;\n }\n .tiny-mediacms-edit-btn {\n position: absolute;\n top: 48px;\n left: 6px;\n width: 28px;\n height: 28px;\n background: #ffffff;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n z-index: 10;\n padding: 0;\n margin: 0;\n box-shadow: 0 2px 6px rgba(0,0,0,0.35);\n transition: transform 0.15s, box-shadow 0.15s;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n }\n .tiny-mediacms-edit-btn:hover {\n transform: scale(1.15);\n box-shadow: 0 3px 10px rgba(0,0,0,0.45);\n }\n .tiny-mediacms-edit-btn svg {\n width: 18px !important;\n height: 18px !important;\n display: block !important;\n }\n `;\n editorDoc.head.appendChild(style);\n };\n\n /**\n * Handle click on the edit button.\n *\n * @param {Event} e - The click event\n */\n const handleOverlayClick = (e) => {\n const target = e.target;\n\n // Check if clicked on edit button or its child (svg/path)\n const editBtn = target.closest('.tiny-mediacms-edit-btn');\n if (!editBtn) {\n return;\n }\n\n e.preventDefault();\n e.stopPropagation();\n\n // Find the associated wrapper and iframe\n const wrapper = editBtn.closest('.tiny-mediacms-iframe-wrapper');\n if (!wrapper) {\n return;\n }\n\n const iframe = wrapper.querySelector('iframe');\n if (!iframe) {\n return;\n }\n\n // Select the wrapper so TinyMCE knows which element is selected\n editor.selection.select(wrapper);\n\n // Open the edit dialog\n handleIframeAction();\n };\n\n // Setup on editor init\n editor.on('init', () => {\n addStyles();\n processIframes();\n\n // Handle clicks on the overlay\n editor.getBody().addEventListener('click', handleOverlayClick);\n });\n\n // Re-process when content changes\n editor.on('SetContent', () => {\n processIframes();\n });\n\n // Re-process when content is pasted\n editor.on('PastePostProcess', () => {\n setTimeout(processIframes, 100);\n });\n\n // Re-process after undo/redo\n editor.on('Undo Redo', () => {\n processIframes();\n });\n\n // Re-process on any content change (covers modal updates)\n editor.on('Change', () => {\n setTimeout(processIframes, 50);\n });\n\n // Re-process when node changes (selection changes)\n editor.on('NodeChange', () => {\n processIframes();\n });\n};\n\nconst registerIframeCommand = (editor, iframeButtonText, iframeButtonImage) => {\n const handleIframeAction = () => {\n const iframeEmbed = new IframeEmbed(editor);\n iframeEmbed.displayDialogue();\n };\n\n // Register the iframe icon\n editor.ui.registry.addIcon(iframeIcon, iframeButtonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing iframe element it will show as toggled on.\n editor.ui.registry.addToggleButton(iframeButtonName, {\n icon: iframeIcon,\n tooltip: iframeButtonText,\n onAction: handleIframeAction,\n onSetup: api => {\n return editor.selection.selectorChangedWithUnbind(\n 'iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper',\n api.setActive\n ).unbind;\n }\n });\n\n editor.ui.registry.addMenuItem(iframeMenuItemName, {\n icon: iframeIcon,\n text: iframeButtonText,\n onAction: handleIframeAction,\n });\n\n editor.ui.registry.addContextToolbar(iframeButtonName, {\n predicate: isIframe,\n items: iframeButtonName,\n position: 'node',\n scope: 'node'\n });\n\n editor.ui.registry.addContextMenu(iframeButtonName, {\n update: isIframe,\n });\n\n // Setup iframe overlays with edit button on hover\n setupIframeOverlays(editor, handleIframeAction);\n};\n\nexport const getSetup = async() => {\n const [\n iframeButtonText,\n ] = await getStrings([\n 'iframebuttontitle',\n ].map((key) => ({key, component})));\n\n const [\n iframeButtonImage,\n ] = await Promise.all([\n getButtonImage('icon', component),\n ]);\n\n // Note: The function returned here must be synchronous and cannot use promises.\n // All promises must be resolved prior to returning the function.\n return (editor) => {\n registerIframeCommand(editor, iframeButtonText, iframeButtonImage);\n };\n};\n"],"names":["isIframe","node","nodeName","toLowerCase","classList","contains","setupIframeOverlays","editor","handleIframeAction","processIframes","editorBody","getBody","querySelectorAll","forEach","iframe","parentElement","_iframe$parentElement","hasAttribute","wrapper","getDoc","createElement","className","setAttribute","editBtn","innerHTML","parentNode","insertBefore","appendChild","handleOverlayClick","e","target","closest","preventDefault","stopPropagation","querySelector","selection","select","on","editorDoc","getElementById","style","id","textContent","head","addStyles","addEventListener","setTimeout","async","iframeButtonText","map","key","component","iframeButtonImage","Promise","all","IframeEmbed","displayDialogue","ui","registry","addIcon","iframeIcon","html","addToggleButton","iframeButtonName","icon","tooltip","onAction","onSetup","api","selectorChangedWithUnbind","setActive","unbind","addMenuItem","iframeMenuItemName","text","addContextToolbar","predicate","items","position","scope","addContextMenu","update","registerIframeCommand"],"mappings":";;;;;;;8JAiCMA,SAAYC,MAAyC,WAAhCA,KAAKC,SAASC,eACpCF,KAAKG,WAAaH,KAAKG,UAAUC,SAAS,2BAC1CJ,KAAKG,WAAaH,KAAKG,UAAUC,SAAS,gCAUzCC,oBAAsB,CAACC,OAAQC,4BAI3BC,eAAiB,WACbC,WAAaH,OAAOI,cACrBD,kBAIWA,WAAWE,iBAAiB,UACpCC,SAASC,oEAETA,OAAOC,gDAAPC,sBAAsBZ,UAAUC,SAAS,0CAKzCS,OAAOG,aAAa,oBAAsBH,OAAOG,aAAa,qCAK5DC,QAAUX,OAAOY,SAASC,cAAc,OAC9CF,QAAQG,UAAY,+BACpBH,QAAQI,aAAa,kBAAmB,eAGlCC,QAAUhB,OAAOY,SAASC,cAAc,UAC9CG,QAAQF,UAAY,yBACpBE,QAAQD,aAAa,OAAQ,UAC7BC,QAAQD,aAAa,QAAS,4BAE9BC,QAAQC,UAAY,0KAMpBV,OAAOW,WAAWC,aAAaR,QAASJ,QACxCI,QAAQS,YAAYb,QACpBI,QAAQS,YAAYJ,aAoEtBK,mBAAsBC,UAIlBN,QAHSM,EAAEC,OAGMC,QAAQ,+BAC1BR,eAILM,EAAEG,iBACFH,EAAEI,wBAGIf,QAAUK,QAAQQ,QAAQ,qCAC3Bb,eAIUA,QAAQgB,cAAc,YAMrC3B,OAAO4B,UAAUC,OAAOlB,SAGxBV,uBAIJD,OAAO8B,GAAG,QAAQ,KA5FA,YACRC,UAAY/B,OAAOY,aACpBmB,oBAKDA,UAAUC,eAAe,6CAIvBC,MAAQF,UAAUlB,cAAc,SACtCoB,MAAMC,GAAK,+BACXD,MAAME,02CAwCNJ,UAAUK,KAAKhB,YAAYa,QAwC3BI,GACAnC,iBAGAF,OAAOI,UAAUkC,iBAAiB,QAASjB,uBAI/CrB,OAAO8B,GAAG,cAAc,KACpB5B,oBAIJF,OAAO8B,GAAG,oBAAoB,KAC1BS,WAAWrC,eAAgB,QAI/BF,OAAO8B,GAAG,aAAa,KACnB5B,oBAIJF,OAAO8B,GAAG,UAAU,KAChBS,WAAWrC,eAAgB,OAI/BF,OAAO8B,GAAG,cAAc,KACpB5B,uCAgDgBsC,gBAEhBC,wBACM,mBAAW,CACjB,qBACFC,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,wBAGlBC,yBACMC,QAAQC,IAAI,EAClB,yBAAe,OAAQH,4BAKnB5C,SA3DkB,EAACA,OAAQyC,iBAAkBI,2BAC/C5C,mBAAqB,KACH,IAAI+C,qBAAYhD,QACxBiD,mBAIhBjD,OAAOkD,GAAGC,SAASC,QAAQC,mBAAYR,kBAAkBS,MAIzDtD,OAAOkD,GAAGC,SAASI,gBAAgBC,yBAAkB,CACjDC,KAAMJ,mBACNK,QAASjB,iBACTkB,SAAU1D,mBACV2D,QAASC,KACE7D,OAAO4B,UAAUkC,0BACpB,kHACAD,IAAIE,WACNC,SAIVhE,OAAOkD,GAAGC,SAASc,YAAYC,2BAAoB,CAC/CT,KAAMJ,mBACNc,KAAM1B,iBACNkB,SAAU1D,qBAGdD,OAAOkD,GAAGC,SAASiB,kBAAkBZ,yBAAkB,CACnDa,UAAW5E,SACX6E,MAAOd,yBACPe,SAAU,OACVC,MAAO,SAGXxE,OAAOkD,GAAGC,SAASsB,eAAejB,yBAAkB,CAChDkB,OAAQjF,WAIZM,oBAAoBC,OAAQC,qBAmBxB0E,CAAsB3E,OAAQyC,iBAAkBI"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js deleted file mode 100755 index 7daa212e..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_mediacms/plugin",component:"tiny_mediacms",iframeButtonName:"tiny_mediacms_iframe",iframeMenuItemName:"tiny_mediacms_iframe",iframeIcon:"tiny_mediacms_iframe"},_exports.default})); - -//# sourceMappingURL=common.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js.map deleted file mode 100755 index 5d06ebcf..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/common.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"common.min.js","sources":["../src/common.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 * Tiny Media common values.\n *\n * @module tiny_mediacms/common\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_mediacms/plugin',\n component: 'tiny_mediacms',\n iframeButtonName: 'tiny_mediacms_iframe',\n iframeMenuItemName: 'tiny_mediacms_iframe',\n iframeIcon: 'tiny_mediacms_iframe',\n};\n"],"names":["pluginName","component","iframeButtonName","iframeMenuItemName","iframeIcon"],"mappings":"sKAuBe,CACXA,WAAY,uBACZC,UAAW,gBACXC,iBAAkB,uBAClBC,mBAAoB,uBACpBC,WAAY"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js deleted file mode 100755 index c2265e1a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{return{contextmenu:(0,_utils.addContextmenuItem)(instanceConfig.contextmenu,_common.iframeButtonName),menu:(menu=instanceConfig.menu,menu.insert.items="".concat(_common.iframeMenuItemName," ").concat(menu.insert.items),menu),toolbar:(toolbar=instanceConfig.toolbar,toolbar.map((section=>("content"===section.name&§ion.items.unshift(_common.iframeButtonName),section))))};var toolbar,menu}})); - -//# sourceMappingURL=configuration.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js.map deleted file mode 100755 index 635c33a4..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/configuration.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"configuration.min.js","sources":["../src/configuration.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 * Tiny Media configuration.\n *\n * @module tiny_mediacms/configuration\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n iframeButtonName,\n iframeMenuItemName,\n} from './common';\nimport {\n addContextmenuItem,\n} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n // Add the Iframe Embed to the insert menu.\n menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;\n\n return menu;\n};\n\nconst configureToolbar = (toolbar) => {\n // The toolbar contains an array of named sections.\n // The Moodle integration ensures that there is a section called 'content'.\n\n return toolbar.map((section) => {\n if (section.name === 'content') {\n // Insert the iframe button at the start of it.\n section.items.unshift(iframeButtonName);\n }\n\n return section;\n });\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.\n return {\n contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),\n menu: configureMenu(instanceConfig.menu),\n toolbar: configureToolbar(instanceConfig.toolbar),\n };\n};\n"],"names":["instanceConfig","contextmenu","iframeButtonName","menu","insert","items","iframeMenuItemName","toolbar","map","section","name","unshift"],"mappings":"wNAoD0BA,uBAEf,CACHC,aAAa,6BAAmBD,eAAeC,YAAaC,0BAC5DC,MAzBeA,KAyBKH,eAAeG,KAvBvCA,KAAKC,OAAOC,gBAAWC,uCAAsBH,KAAKC,OAAOC,OAElDF,MAsBHI,SAnBkBA,QAmBQP,eAAeO,QAftCA,QAAQC,KAAKC,UACK,YAAjBA,QAAQC,MAERD,QAAQJ,MAAMM,QAAQT,0BAGnBO,aAVWF,IAAAA,QAPHJ"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js deleted file mode 100755 index 5a81b3b7..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/embed",["exports","core/templates","core/str","core/modal_events","editor_tiny/utils","editor_tiny/options","./common","./embedmodal","./selectors","./options"],(function(_exports,_templates,_str,ModalEvents,_utils,_options,_common,_embedmodal,_selectors,_options2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_embedmodal=_interopRequireDefault(_embedmodal),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"canShowFilePickerPoster",!1),_defineProperty(this,"canShowFilePickerTrack",!1),_defineProperty(this,"helpStrings",null),_defineProperty(this,"isUpdating",!1),_defineProperty(this,"selectedMedia",null);const permissions=(0,_options2.getEmbedPermissions)(editor);this.canShowFilePicker=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"media"),this.canShowFilePickerPoster=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"image"),this.canShowFilePickerTrack=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"subtitle"),this.editor=editor}async getHelpStrings(){if(!this.helpStrings){const[addSource,tracks,subtitles,captions,descriptions,chapters,metadata]=await(0,_str.getStrings)(["addsource_help","tracks_help","subtitles_help","captions_help","descriptions_help","chapters_help","metadata_help"].map((key=>({key:key,component:_common.component}))));this.helpStrings={addSource:addSource,tracks:tracks,subtitles:subtitles,captions:captions,descriptions:descriptions,chapters:chapters,metadata:metadata}}return this.helpStrings}async getTemplateContext(data){const languages=this.prepareMoodleLang(),helpIcons=Array.from(Object.entries(await this.getHelpStrings())).forEach((_ref=>{let[key,text]=_ref;data["".concat(key.toLowerCase(),"helpicon")]={text:text}}));return Object.assign({},{elementid:this.editor.getElement().id,showfilepicker:this.canShowFilePicker,showfilepickerposter:this.canShowFilePickerPoster,showfilepickertrack:this.canShowFilePickerTrack,langsinstalled:languages.installed,langsavailable:languages.available,link:!0,video:!1,audio:!1,isupdating:this.isUpdating},data,helpIcons)}async displayDialogue(){this.selectedMedia=this.getSelectedMedia();const data=Object.assign({},this.getCurrentEmbedData());this.isUpdating=0!==Object.keys(data).length,this.currentModal=await _embedmodal.default.create({title:(0,_str.getString)("createmedia","tiny_mediacms"),templateContext:await this.getTemplateContext(data)}),await this.registerEventListeners(this.currentModal)}getCurrentEmbedData(){const properties=this.getMediumProperties();if(!properties)return{};const processedProperties={};return processedProperties[properties.type.toLowerCase()]=properties,processedProperties.link=!1,processedProperties}getSelectedMedia(){const mediaElm=this.editor.selection.getNode();return mediaElm?"video"===mediaElm.nodeName.toLowerCase()||"audio"===mediaElm.nodeName.toLowerCase()?mediaElm:mediaElm.querySelector("video")?mediaElm.querySelector("video"):mediaElm.querySelector("audio")?mediaElm.querySelector("audio"):null:null}getMediumProperties(){const boolAttr=(elem,attr)=>elem.hasAttribute(attr)&&(elem.getAttribute(attr)||""===elem.getAttribute(attr)),tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[],medium=this.selectedMedia;return medium?(medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:boolAttr(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)})),{type:"video"===medium.nodeName.toLowerCase()?_selectors.default.EMBED.mediaTypes.video:_selectors.default.EMBED.mediaTypes.audio,sources:sources,poster:medium.getAttribute("poster"),title:medium.getAttribute("title"),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:boolAttr(medium,"autoplay"),loop:boolAttr(medium,"loop"),muted:boolAttr(medium,"muted"),controls:boolAttr(medium,"controls"),tracks:tracks}):null}prepareMoodleLang(){const moodleLangs=(0,_options.getMoodleLang)(this.editor),currentLanguage=(0,_options.getCurrentLanguage)(this.editor);return{installed:Object.entries(moodleLangs.installed).map((_ref2=>{let[lang,code]=_ref2;return{lang:lang,code:code,default:lang===currentLanguage}})),available:Object.entries(moodleLangs.available).map((_ref3=>{let[lang,code]=_ref3;return{lang:lang,code:code,default:lang===currentLanguage}}))}}getMoodleLangObj(subtitleLang){const{available:available}=(0,_options.getMoodleLang)(this.editor);return available[subtitleLang]?{lang:subtitleLang,code:available[subtitleLang]}:null}filePickerCallback(params,element,fpType){if(""!==params.url){const tabPane=element.closest(".tab-pane");if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,tabPane.id===this.editor.getElement().id+"_"+_selectors.default.EMBED.mediaTypes.link.toLowerCase()&&(tabPane.querySelector(_selectors.default.EMBED.elements.name).value=params.file),"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=this.getMoodleLangObj(subtitleLang);if(langObj){const track=element.closest(_selectors.default.EMBED.elements.track);track.querySelector(_selectors.default.EMBED.elements.trackLabel).value=langObj.lang.trim(),track.querySelector(_selectors.default.EMBED.elements.trackLang).value=langObj.code}}}}addMediaSourceComponent(element,callback){const sourceElement=element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource),clone=sourceElement.cloneNode(!0);sourceElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),sourceElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),sourceElement.parentNode.insertBefore(clone,sourceElement.nextSibling),callback&&callback(clone)}removeMediaSourceComponent(element){element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource).remove()}addTrackComponent(element,callback){const trackElement=element.closest(_selectors.default.EMBED.elements.track),clone=trackElement.cloneNode(!0);trackElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),trackElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),trackElement.parentNode.insertBefore(clone,trackElement.nextSibling),callback&&callback(clone)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}getMediumTypeFromTabPane(tabPane){return tabPane.getAttribute("data-medium-type")}getTrackTypeFromTabPane(tabPane){return tabPane.getAttribute("data-track-kind")}getMediaHTML(form){const mediumType=this.getMediumTypeFromTabPane(form.querySelector(".root.tab-content > .tab-pane.active")),tabContent=form.querySelector(_selectors.default.EMBED.elements[mediumType.toLowerCase()+"Pane"]);return this["getMediaHTML"+mediumType[0].toUpperCase()+mediumType.substr(1)](tabContent)}getMediaHTMLLink(tab){const context={url:tab.querySelector(_selectors.default.EMBED.elements.url).value,name:tab.querySelector(_selectors.default.EMBED.elements.name).value||!1};return context.url?_templates.default.renderForPromise("tiny_mediacms/embed_media_link",context):""}getMediaHTMLVideo(tab){const context=this.getContextForMediaHTML(tab);return context.width=tab.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=tab.querySelector(_selectors.default.EMBED.elements.height).value||!1,context.poster=tab.querySelector("".concat(_selectors.default.EMBED.elements.posterSource," ").concat(_selectors.default.EMBED.elements.url)).value||!1,context.sources.length?_templates.default.renderForPromise("tiny_mediacms/embed_media_video",context):""}getMediaHTMLAudio(tab){const context=this.getContextForMediaHTML(tab);return context.sources.length?_templates.default.renderForPromise("tiny_mediacms/embed_media_audio",context):""}getContextForMediaHTML(tab){const tracks=Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.track)).map((track=>({track:track.querySelector(_selectors.default.EMBED.elements.trackSource+" "+_selectors.default.EMBED.elements.url).value,kind:this.getTrackTypeFromTabPane(track.closest(".tab-pane")),label:track.querySelector(_selectors.default.EMBED.elements.trackLabel).value||track.querySelector(_selectors.default.EMBED.elements.trackLang).value,srclang:track.querySelector(_selectors.default.EMBED.elements.trackLang).value,defaultTrack:track.querySelector(_selectors.default.EMBED.elements.trackDefault).checked?"true":null}))).filter((track=>!!track.track));return{sources:Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url)).filter((source=>!!source.value)).map((source=>source.value)),description:tab.querySelector(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url).value||!1,tracks:tracks,showControls:tab.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:tab.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:tab.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:tab.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:tab.querySelector(_selectors.default.EMBED.elements.title).value||!1}}getFilepickerTypeFromElement(element){return element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media"}async clickHandler(e){const element=e.target;if(element.closest(_selectors.default.EMBED.actions.mediaBrowser)){e.preventDefault();const fpType=this.getFilepickerTypeFromElement(element),params=await(0,_utils.displayFilepicker)(this.editor,fpType);this.filePickerCallback(params,element,fpType)}element.closest(_selectors.default.EMBED.elements.mediaSource+" .addcomponent")&&(e.preventDefault(),this.addMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.mediaSource+" .removecomponent")&&(e.preventDefault(),this.removeMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .addcomponent")&&(e.preventDefault(),this.addTrackComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .removecomponent")&&(e.preventDefault(),this.removeTrackComponent(element));const trackDefaultAction=element.closest(_selectors.default.EMBED.elements.trackDefault);if(trackDefaultAction&&trackDefaultAction.checked){const getKind=el=>this.getTrackTypeFromTabPane(el.parentElement.closest(".tab-pane"));element.parentElement.closest(".root.tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}}async handleDialogueSubmission(event,modal){const{html:html}=await this.getMediaHTML(modal.getRoot()[0]);html&&(this.isUpdating?(this.selectedMedia.outerHTML=html,this.isUpdating=!1):this.editor.insertContent(html))}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0];(this.canShowFilePicker||this.canShowFilePickerPoster||this.canShowFilePickerTrack)&&root.addEventListener("click",this.clickHandler.bind(this)),$root.on(ModalEvents.save,this.handleDialogueSubmission.bind(this)),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),$root.on(ModalEvents.shown,(()=>{root.querySelectorAll(_selectors.default.EMBED.elements.trackLang).forEach((dropdown=>{const defaultVal=dropdown.getAttribute("data-value");defaultVal&&(dropdown.value=defaultVal)}))}))}},_exports.default})); - -//# sourceMappingURL=embed.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js.map deleted file mode 100755 index ea4585b0..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/embed.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"embed.min.js","sources":["../src/embed.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 * Tiny Media plugin Embed class for Moodle.\n *\n * @module tiny_mediacms/embed\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {\n getString,\n getStrings,\n} from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';\nimport {component} from \"./common\";\nimport EmbedModal from './embedmodal';\nimport Selectors from './selectors';\nimport {getEmbedPermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\n\nexport default class MediaEmbed {\n editor = null;\n canShowFilePicker = false;\n canShowFilePickerPoster = false;\n canShowFilePickerTrack = false;\n\n /**\n * @property {Object} The names of the alignment options.\n */\n helpStrings = null;\n\n /**\n * @property {boolean} Indicate that the user is updating the media or not.\n */\n isUpdating = false;\n\n /**\n * @property {Object} The currently selected media.\n */\n selectedMedia = null;\n\n constructor(editor) {\n const permissions = getEmbedPermissions(editor);\n\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined');\n this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined');\n this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined');\n\n this.editor = editor;\n }\n\n async getHelpStrings() {\n if (!this.helpStrings) {\n const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([\n 'addsource_help',\n 'tracks_help',\n 'subtitles_help',\n 'captions_help',\n 'descriptions_help',\n 'chapters_help',\n 'metadata_help',\n ].map((key) => ({\n key,\n component,\n })));\n\n this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};\n }\n\n return this.helpStrings;\n }\n\n async getTemplateContext(data) {\n const languages = this.prepareMoodleLang();\n\n const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {\n data[`${key.toLowerCase()}helpicon`] = {text};\n });\n\n return Object.assign({}, {\n elementid: this.editor.getElement().id,\n showfilepicker: this.canShowFilePicker,\n showfilepickerposter: this.canShowFilePickerPoster,\n showfilepickertrack: this.canShowFilePickerTrack,\n langsinstalled: languages.installed,\n langsavailable: languages.available,\n link: true,\n video: false,\n audio: false,\n isupdating: this.isUpdating,\n }, data, helpIcons);\n }\n\n async displayDialogue() {\n this.selectedMedia = this.getSelectedMedia();\n const data = Object.assign({}, this.getCurrentEmbedData());\n this.isUpdating = Object.keys(data).length !== 0;\n\n this.currentModal = await EmbedModal.create({\n title: getString('createmedia', 'tiny_mediacms'),\n templateContext: await this.getTemplateContext(data),\n });\n\n await this.registerEventListeners(this.currentModal);\n }\n\n getCurrentEmbedData() {\n const properties = this.getMediumProperties();\n if (!properties) {\n return {};\n }\n\n const processedProperties = {};\n processedProperties[properties.type.toLowerCase()] = properties;\n processedProperties.link = false;\n\n return processedProperties;\n }\n\n getSelectedMedia() {\n const mediaElm = this.editor.selection.getNode();\n\n if (!mediaElm) {\n return null;\n }\n\n if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {\n return mediaElm;\n }\n\n if (mediaElm.querySelector('video')) {\n return mediaElm.querySelector('video');\n }\n\n if (mediaElm.querySelector('audio')) {\n return mediaElm.querySelector('audio');\n }\n\n return null;\n }\n\n getMediumProperties() {\n const boolAttr = (elem, attr) => {\n // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.\n // So in order to check if attr=\"true\", we need to check if the attribute exists and if the value is empty or true.\n return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));\n };\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n const medium = this.selectedMedia;\n if (!medium) {\n return null;\n }\n medium.querySelectorAll('track').forEach((track) => {\n tracks[track.getAttribute('kind')].push({\n src: track.getAttribute('src'),\n srclang: track.getAttribute('srclang'),\n label: track.getAttribute('label'),\n defaultTrack: boolAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n\n return {\n type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,\n sources,\n poster: medium.getAttribute('poster'),\n title: medium.getAttribute('title'),\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: boolAttr(medium, 'autoplay'),\n loop: boolAttr(medium, 'loop'),\n muted: boolAttr(medium, 'muted'),\n controls: boolAttr(medium, 'controls'),\n tracks,\n };\n }\n\n prepareMoodleLang() {\n const moodleLangs = getMoodleLang(this.editor);\n const currentLanguage = getCurrentLanguage(this.editor);\n\n const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n return {\n installed,\n available,\n };\n }\n\n getMoodleLangObj(subtitleLang) {\n const {available} = getMoodleLang(this.editor);\n\n if (available[subtitleLang]) {\n return {\n lang: subtitleLang,\n code: available[subtitleLang],\n };\n }\n\n return null;\n }\n\n filePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {\n tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;\n }\n\n if (fpType === 'subtitle') {\n // If the file is subtitle file. We need to match the language and label for that file.\n const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];\n const langObj = this.getMoodleLangObj(subtitleLang);\n if (langObj) {\n const track = element.closest(Selectors.EMBED.elements.track);\n track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();\n track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;\n }\n }\n }\n }\n\n addMediaSourceComponent(element, callback) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n const clone = sourceElement.cloneNode(true);\n\n sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeMediaSourceComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n sourceElement.remove();\n }\n\n addTrackComponent(element, callback) {\n const trackElement = element.closest(Selectors.EMBED.elements.track);\n const clone = trackElement.cloneNode(true);\n\n trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n getMediumTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-medium-type');\n }\n\n getTrackTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-track-kind');\n }\n\n getMediaHTML(form) {\n const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));\n const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);\n\n return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);\n }\n\n getMediaHTMLLink(tab) {\n const context = {\n url: tab.querySelector(Selectors.EMBED.elements.url).value,\n name: tab.querySelector(Selectors.EMBED.elements.name).value || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_mediacms/embed_media_link', context) : '';\n }\n\n getMediaHTMLVideo(tab) {\n const context = this.getContextForMediaHTML(tab);\n context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;\n context.poster = tab.querySelector(\n `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`\n ).value || false;\n\n return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_video', context) : '';\n }\n\n getMediaHTMLAudio(tab) {\n const context = this.getContextForMediaHTML(tab);\n\n return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_audio', context) : '';\n }\n\n getContextForMediaHTML(tab) {\n const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({\n track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,\n kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),\n label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||\n track.querySelector(Selectors.EMBED.elements.trackLang).value,\n srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,\n defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? \"true\" : null\n })).filter((track) => !!track.track);\n\n const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url))\n .filter((source) => !!source.value)\n .map((source) => source.value);\n\n return {\n sources,\n description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url).value || false,\n tracks,\n showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: tab.querySelector(Selectors.EMBED.elements.title).value || false\n };\n }\n\n getFilepickerTypeFromElement(element) {\n if (element.closest(Selectors.EMBED.elements.posterSource)) {\n return 'image';\n }\n if (element.closest(Selectors.EMBED.elements.trackSource)) {\n return 'subtitle';\n }\n\n return 'media';\n }\n\n async clickHandler(e) {\n const element = e.target;\n\n const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);\n if (mediaBrowser) {\n e.preventDefault();\n const fpType = this.getFilepickerTypeFromElement(element);\n const params = await displayFilepicker(this.editor, fpType);\n this.filePickerCallback(params, element, fpType);\n }\n\n const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');\n if (addComponentSourceAction) {\n e.preventDefault();\n this.addMediaSourceComponent(element);\n }\n\n const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');\n if (removeComponentSourceAction) {\n e.preventDefault();\n this.removeMediaSourceComponent(element);\n }\n\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\n const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');\n if (removeComponentTrackAction) {\n e.preventDefault();\n this.removeTrackComponent(element);\n }\n\n // Only allow one track per tab to be selected as \"default\".\n const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);\n if (trackDefaultAction && trackDefaultAction.checked) {\n const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));\n\n element.parentElement\n .closest('.root.tab-content')\n .querySelectorAll(Selectors.EMBED.elements.trackDefault)\n .forEach((select) => {\n if (select !== element && getKind(element) === getKind(select)) {\n select.checked = false;\n }\n });\n }\n }\n\n async handleDialogueSubmission(event, modal) {\n const {html} = await this.getMediaHTML(modal.getRoot()[0]);\n if (html) {\n if (this.isUpdating) {\n this.selectedMedia.outerHTML = html;\n this.isUpdating = false;\n } else {\n this.editor.insertContent(html);\n }\n }\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) {\n root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n $root.on(ModalEvents.shown, () => {\n root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n dropdown.value = defaultVal;\n }\n });\n });\n }\n}\n"],"names":["constructor","editor","permissions","canShowFilePicker","filepicker","canShowFilePickerPoster","canShowFilePickerTrack","this","helpStrings","addSource","tracks","subtitles","captions","descriptions","chapters","metadata","map","key","component","data","languages","prepareMoodleLang","helpIcons","Array","from","Object","entries","getHelpStrings","forEach","_ref","text","toLowerCase","assign","elementid","getElement","id","showfilepicker","showfilepickerposter","showfilepickertrack","langsinstalled","installed","langsavailable","available","link","video","audio","isupdating","isUpdating","selectedMedia","getSelectedMedia","getCurrentEmbedData","keys","length","currentModal","EmbedModal","create","title","templateContext","getTemplateContext","registerEventListeners","properties","getMediumProperties","processedProperties","type","mediaElm","selection","getNode","nodeName","querySelector","boolAttr","elem","attr","hasAttribute","getAttribute","sources","medium","querySelectorAll","track","push","src","srclang","label","defaultTrack","source","Selectors","EMBED","mediaTypes","poster","width","height","autoplay","loop","muted","controls","moodleLangs","currentLanguage","_ref2","lang","code","_ref3","getMoodleLangObj","subtitleLang","filePickerCallback","params","element","fpType","url","tabPane","closest","elements","value","name","file","split","slice","langObj","trackLabel","trim","trackLang","addMediaSourceComponent","callback","sourceElement","mediaSource","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","removeMediaSourceComponent","addTrackComponent","trackElement","removeTrackComponent","getMediumTypeFromTabPane","getTrackTypeFromTabPane","getMediaHTML","form","mediumType","tabContent","toUpperCase","substr","getMediaHTMLLink","tab","context","Templates","renderForPromise","getMediaHTMLVideo","getContextForMediaHTML","posterSource","getMediaHTMLAudio","trackSource","kind","trackDefault","checked","filter","description","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","getFilepickerTypeFromElement","e","target","actions","mediaBrowser","preventDefault","trackDefaultAction","getKind","el","parentElement","select","event","modal","html","getRoot","outerHTML","insertContent","getBody","$root","root","addEventListener","clickHandler","bind","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","shown","dropdown","defaultVal"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js deleted file mode 100755 index 70ad4aa9..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/embedmodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class EmbedModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=EmbedModal,_defineProperty(EmbedModal,"TYPE","".concat(_common.component,"/modal")),_defineProperty(EmbedModal,"TEMPLATE","".concat(_common.component,"/embed_media_modal")),_exports.default})); - -//# sourceMappingURL=embedmodal.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js.map deleted file mode 100755 index d51705a6..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/embedmodal.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"embedmodal.min.js","sources":["../src/embedmodal.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 * Embedded Media Management Modal for Tiny.\n *\n * @module tiny_mediacms/embedmodal\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class EmbedModal extends Modal {\n static TYPE = `${component}/modal`;\n static TEMPLATE = `${component}/embed_media_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["EmbedModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,6CADAV,gCAEIU"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js deleted file mode 100755 index 203b0305..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/iframeembed",["exports","core/templates","core/str","core/modal_events","./common","./iframemodal","./selectors","./options"],(function(_exports,_templates,_str,ModalEvents,_common,_iframemodal,_selectors,_options){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_iframemodal=_interopRequireDefault(_iframemodal),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"isUpdating",!1),_defineProperty(this,"selectedIframe",null),_defineProperty(this,"debounceTimer",null),_defineProperty(this,"iframeLibraryLoaded",!1),_defineProperty(this,"selectedLibraryVideo",null),_defineProperty(this,"iframeLibraryUrl","https://temp.web357.com/mediacms/deic-mediacms-embed-videos.html"),this.editor=editor}parseInput(input){if(!input||!input.trim())return null;const iframeMatch=(input=input.trim()).match(/]*src=["']([^"']+)["'][^>]*>/i);return iframeMatch?this.parseEmbedUrl(iframeMatch[1]):input.startsWith("http://")||input.startsWith("https://")?this.parseVideoUrl(input):null}parseVideoUrl(url){try{const urlObj=new URL(url),baseUrl="".concat(urlObj.protocol,"//").concat(urlObj.host);if("/view"===urlObj.pathname&&urlObj.searchParams.has("m"))return{baseUrl:baseUrl,videoId:urlObj.searchParams.get("m"),isEmbed:!1};if("/embed"===urlObj.pathname&&urlObj.searchParams.has("m")){const tParam=urlObj.searchParams.get("t");return{baseUrl:baseUrl,videoId:urlObj.searchParams.get("m"),isEmbed:!0,showTitle:"1"===urlObj.searchParams.get("showTitle"),linkTitle:"1"===urlObj.searchParams.get("linkTitle"),showRelated:"1"===urlObj.searchParams.get("showRelated"),showUserAvatar:"1"===urlObj.searchParams.get("showUserAvatar"),startAt:tParam?this.secondsToTimeString(parseInt(tParam)):null}}return{baseUrl:baseUrl,rawUrl:url,isGeneric:!0}}catch(e){return null}}parseEmbedUrl(url){return this.parseVideoUrl(url)}secondsToTimeString(seconds){const mins=Math.floor(seconds/60),secs=seconds%60;return"".concat(mins,":").concat(secs.toString().padStart(2,"0"))}timeStringToSeconds(timeStr){if(!timeStr||!timeStr.trim())return null;if((timeStr=timeStr.trim()).includes(":")){const parts=timeStr.split(":");return 60*(parseInt(parts[0])||0)+(parseInt(parts[1])||0)}const secs=parseInt(timeStr);return isNaN(secs)?null:secs}buildEmbedUrl(parsed,options){if(parsed.isGeneric)return parsed.rawUrl;const url=new URL("".concat(parsed.baseUrl,"/embed"));if(url.searchParams.set("m",parsed.videoId),url.searchParams.set("showTitle",options.showTitle?"1":"0"),url.searchParams.set("showRelated",options.showRelated?"1":"0"),url.searchParams.set("showUserAvatar",options.showUserAvatar?"1":"0"),url.searchParams.set("linkTitle",options.linkTitle?"1":"0"),options.startAtEnabled&&options.startAt){const seconds=this.timeStringToSeconds(options.startAt);null!==seconds&&seconds>0&&url.searchParams.set("t",seconds.toString())}return url.toString()}async getTemplateContext(){var _this=this;let data=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};const editorData=(0,_options.getData)(this.editor),autoConvertOptions=(null==editorData?void 0:editorData.autoConvertOptions)||{},getDefault=function(key){let fallback=!(arguments.length>1&&void 0!==arguments[1])||arguments[1];return _this.isUpdating&&void 0!==data[key]?data[key]:void 0!==autoConvertOptions[key]?autoConvertOptions[key]:fallback};return{elementid:this.editor.getElement().id,isupdating:this.isUpdating,url:data.url||"",showTitle:getDefault("showTitle"),linkTitle:getDefault("linkTitle"),showRelated:getDefault("showRelated"),showUserAvatar:getDefault("showUserAvatar"),responsive:!1!==data.responsive,startAtEnabled:data.startAtEnabled||!1,startAt:data.startAt||"0:00",width:data.width||560,height:data.height||315,is16_9:!data.aspectRatio||"16:9"===data.aspectRatio,is4_3:"4:3"===data.aspectRatio,is1_1:"1:1"===data.aspectRatio,isCustom:"custom"===data.aspectRatio}}async displayDialogue(){this.selectedIframe=this.getSelectedIframe();const data=this.getCurrentIframeData();this.isUpdating=null!==data,this.iframeLibraryLoaded=!1,this.currentModal=await _iframemodal.default.create({title:(0,_str.getString)("iframemodaltitle",_common.component),templateContext:await this.getTemplateContext(data||{})}),await this.registerEventListeners(this.currentModal)}getSelectedIframe(){const node=this.editor.selection.getNode();if("iframe"===node.nodeName.toLowerCase())return node;const iframe=node.querySelector("iframe");if(iframe)return iframe;const wrapper=node.closest(".tiny-mediacms-iframe-wrapper")||node.closest(".tiny-iframe-responsive");return wrapper?wrapper.querySelector("iframe"):null}getCurrentIframeData(){var _parsed$showTitle,_parsed$linkTitle,_parsed$showRelated,_parsed$showUserAvata;if(!this.selectedIframe)return null;const src=this.selectedIframe.getAttribute("src"),parsed=this.parseInput(src),isResponsive=(this.selectedIframe.getAttribute("style")||"").includes("aspect-ratio");return{url:src,width:this.selectedIframe.getAttribute("width")||560,height:this.selectedIframe.getAttribute("height")||315,showTitle:null===(_parsed$showTitle=null==parsed?void 0:parsed.showTitle)||void 0===_parsed$showTitle||_parsed$showTitle,linkTitle:null===(_parsed$linkTitle=null==parsed?void 0:parsed.linkTitle)||void 0===_parsed$linkTitle||_parsed$linkTitle,showRelated:null===(_parsed$showRelated=null==parsed?void 0:parsed.showRelated)||void 0===_parsed$showRelated||_parsed$showRelated,showUserAvatar:null===(_parsed$showUserAvata=null==parsed?void 0:parsed.showUserAvatar)||void 0===_parsed$showUserAvata||_parsed$showUserAvata,responsive:isResponsive,startAtEnabled:null!==(null==parsed?void 0:parsed.startAt),startAt:(null==parsed?void 0:parsed.startAt)||"0:00"}}getFormValues(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form);return{url:form.querySelector(_selectors.default.IFRAME.elements.url).value.trim(),showTitle:form.querySelector(_selectors.default.IFRAME.elements.showTitle).checked,linkTitle:form.querySelector(_selectors.default.IFRAME.elements.linkTitle).checked,showRelated:form.querySelector(_selectors.default.IFRAME.elements.showRelated).checked,showUserAvatar:form.querySelector(_selectors.default.IFRAME.elements.showUserAvatar).checked,responsive:form.querySelector(_selectors.default.IFRAME.elements.responsive).checked,startAtEnabled:form.querySelector(_selectors.default.IFRAME.elements.startAtEnabled).checked,startAt:form.querySelector(_selectors.default.IFRAME.elements.startAt).value.trim(),aspectRatio:form.querySelector(_selectors.default.IFRAME.elements.aspectRatio).value,width:parseInt(form.querySelector(_selectors.default.IFRAME.elements.width).value)||560,height:parseInt(form.querySelector(_selectors.default.IFRAME.elements.height).value)||315}}async generateIframeHtml(values){const parsed=this.parseInput(values.url);if(!parsed)return"";const embedUrl=this.buildEmbedUrl(parsed,values),aspectRatioCalcs={"16:9":"16 / 9","4:3":"4 / 3","1:1":"1 / 1",custom:"".concat(values.width," / ").concat(values.height)},context={src:embedUrl,width:values.width,height:values.height,responsive:values.responsive,aspectRatioCalc:aspectRatioCalcs[values.aspectRatio]||"16 / 9",aspectRatioValue:aspectRatioCalcs[values.aspectRatio]||"16 / 9"},{html:html}=await _templates.default.renderForPromise("tiny_mediacms/iframe_embed_output",context);return html}async updatePreview(root){let updateUrlField=arguments.length>1&&void 0!==arguments[1]&&arguments[1];const values=this.getFormValues(root),previewContainer=root.querySelector(_selectors.default.IFRAME.elements.preview),urlWarning=root.querySelector(_selectors.default.IFRAME.elements.urlWarning);if(!values.url)return previewContainer.innerHTML='Enter a video URL to see preview',void urlWarning.classList.add("d-none");const parsed=this.parseInput(values.url);if(!parsed)return previewContainer.innerHTML='Invalid URL format',void urlWarning.classList.remove("d-none");urlWarning.classList.add("d-none");const embedUrl=this.buildEmbedUrl(parsed,values);if(updateUrlField&&!parsed.isGeneric){root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.url).value=embedUrl}const previewWidth=Math.min(values.width,400),scale=previewWidth/values.width,previewHeight=Math.round(values.height*scale);previewContainer.innerHTML='\n \n ')}handleInputChange(root){let updateUrlField=arguments.length>1&&void 0!==arguments[1]&&arguments[1];clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout((()=>{this.updatePreview(root,updateUrlField)}),500)}handleAspectRatioChange(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form),aspectRatio=form.querySelector(_selectors.default.IFRAME.elements.aspectRatio).value,dimensions=_selectors.default.IFRAME.aspectRatios[aspectRatio];dimensions&&"custom"!==aspectRatio&&(form.querySelector(_selectors.default.IFRAME.elements.width).value=dimensions.width,form.querySelector(_selectors.default.IFRAME.elements.height).value=dimensions.height),this.updatePreview(root)}async handleDialogueSubmission(modal){const root=modal.getRoot()[0],values=this.getFormValues(root);if(!values.url)return;const html=await this.generateIframeHtml(values);if(html)if(this.isUpdating&&this.selectedIframe){const wrapper=this.selectedIframe.closest(".tiny-mediacms-iframe-wrapper")||this.selectedIframe.closest(".tiny-iframe-responsive");wrapper?wrapper.outerHTML=html:this.selectedIframe.outerHTML=html,this.isUpdating=!1,this.editor.fire("Change")}else this.editor.insertContent(html)}async handleRemove(modal){const confirmMessage=await(0,_str.getString)("removeiframeconfirm",_common.component);if(window.confirm(confirmMessage)){if(this.selectedIframe){const wrapper=this.selectedIframe.closest(".tiny-mediacms-iframe-wrapper")||this.selectedIframe.closest(".tiny-iframe-responsive");wrapper?wrapper.remove():this.selectedIframe.remove()}this.isUpdating=!1,modal.hide()}}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0],form=root.querySelector(_selectors.default.IFRAME.elements.form);form.querySelector(_selectors.default.IFRAME.elements.url).addEventListener("input",(()=>this.handleInputChange(root))),[_selectors.default.IFRAME.elements.showTitle,_selectors.default.IFRAME.elements.linkTitle,_selectors.default.IFRAME.elements.showRelated,_selectors.default.IFRAME.elements.showUserAvatar,_selectors.default.IFRAME.elements.startAtEnabled].forEach((selector=>{form.querySelector(selector).addEventListener("change",(()=>this.handleInputChange(root,!0)))})),form.querySelector(_selectors.default.IFRAME.elements.responsive).addEventListener("change",(()=>this.handleInputChange(root,!1))),form.querySelector(_selectors.default.IFRAME.elements.startAt).addEventListener("input",(()=>this.handleInputChange(root,!0))),form.querySelector(_selectors.default.IFRAME.elements.aspectRatio).addEventListener("change",(()=>this.handleAspectRatioChange(root))),form.querySelector(_selectors.default.IFRAME.elements.width).addEventListener("input",(()=>this.handleInputChange(root))),form.querySelector(_selectors.default.IFRAME.elements.height).addEventListener("input",(()=>this.handleInputChange(root))),$root.on(ModalEvents.save,(()=>this.handleDialogueSubmission(modal))),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()}));const removeBtn=root.querySelector(_selectors.default.IFRAME.actions.remove);removeBtn&&removeBtn.addEventListener("click",(()=>this.handleRemove(modal)));form.querySelector(_selectors.default.IFRAME.elements.url).value&&this.updatePreview(root);const iframeLibraryTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabIframeLibraryBtn);if(iframeLibraryTabBtn){iframeLibraryTabBtn.addEventListener("click",(e=>{e.preventDefault(),e.stopPropagation(),this.switchToIframeLibraryTab(root),setTimeout((()=>this.handleIframeLibraryTabClick(root)),100)})),iframeLibraryTabBtn.addEventListener("shown.bs.tab",(()=>this.handleIframeLibraryTabClick(root)));const $iframeLibraryTabBtn=window.jQuery?window.jQuery(iframeLibraryTabBtn):null;$iframeLibraryTabBtn&&$iframeLibraryTabBtn.on("shown.bs.tab",(()=>this.handleIframeLibraryTabClick(root)))}const urlTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabUrlBtn);urlTabBtn&&urlTabBtn.addEventListener("click",(e=>{e.preventDefault(),e.stopPropagation(),this.switchToUrlTab(root)})),this.registerIframeLibraryEventListeners(root)}switchToUrlTab(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form),urlTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabUrlBtn),iframeLibraryTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabIframeLibraryBtn),urlPane=form.querySelector(_selectors.default.IFRAME.elements.paneUrl),iframeLibraryPane=form.querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);urlTabBtn&&(urlTabBtn.classList.add("active"),urlTabBtn.setAttribute("aria-selected","true")),iframeLibraryTabBtn&&(iframeLibraryTabBtn.classList.remove("active"),iframeLibraryTabBtn.setAttribute("aria-selected","false")),urlPane&&urlPane.classList.add("show","active"),iframeLibraryPane&&iframeLibraryPane.classList.remove("show","active")}switchToIframeLibraryTab(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form),urlTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabUrlBtn),iframeLibraryTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabIframeLibraryBtn),urlPane=form.querySelector(_selectors.default.IFRAME.elements.paneUrl),iframeLibraryPane=form.querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);urlTabBtn&&(urlTabBtn.classList.remove("active"),urlTabBtn.setAttribute("aria-selected","false")),iframeLibraryTabBtn&&(iframeLibraryTabBtn.classList.add("active"),iframeLibraryTabBtn.setAttribute("aria-selected","true")),urlPane&&urlPane.classList.remove("show","active"),iframeLibraryPane&&iframeLibraryPane.classList.add("show","active")}registerIframeLibraryEventListeners(root){window.addEventListener("message",(event=>{this.handleIframeLibraryMessage(root,event)}))}handleIframeLibraryTabClick(root){const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary),iframeEl=pane?pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame):null;console.log("handleIframeLibraryTabClick called, iframeLibraryLoaded:",this.iframeLibraryLoaded,"iframe src:",iframeEl?iframeEl.src:"no iframe","pane:",pane),this.iframeLibraryLoaded=!1,this.loadIframeLibrary(root)}loadIframeLibrary(root){const ltiConfig=(0,_options.getLti)(this.editor);console.log("loadIframeLibrary called, LTI config:",ltiConfig),null!=ltiConfig&<iConfig.contentItemUrl?this.loadIframeLibraryViaLti(root,ltiConfig):this.loadIframeLibraryStatic(root)}loadIframeLibraryViaLti(root,ltiConfig){console.log("loadIframeLibraryViaLti called, config:",ltiConfig);const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);if(!pane)return void console.log("paneIframeLibrary not found!");const placeholderEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryPlaceholder),loadingEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryLoading),iframeEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame);if(!iframeEl)return void console.log("iframeEl not found!");placeholderEl&&placeholderEl.classList.add("d-none"),loadingEl&&loadingEl.classList.remove("d-none"),iframeEl.classList.add("d-none");iframeEl.addEventListener("load",(()=>{console.log("LTI iframe loaded"),this.handleIframeLibraryLoad(root)})),console.log("Setting iframe src to LTI content item URL:",ltiConfig.contentItemUrl),iframeEl.src=ltiConfig.contentItemUrl}loadIframeLibraryStatic(root){console.log("loadIframeLibraryStatic called, URL:",this.iframeLibraryUrl);const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);if(!pane)return void console.log("paneIframeLibrary not found!");const placeholderEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryPlaceholder),loadingEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryLoading),iframeEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame);if(console.log("Elements found:",{placeholderEl:placeholderEl,loadingEl:loadingEl,iframeEl:iframeEl}),!iframeEl)return void console.log("iframeEl not found!");placeholderEl&&placeholderEl.classList.add("d-none"),loadingEl&&loadingEl.classList.remove("d-none"),iframeEl.classList.add("d-none");const loadHandler=()=>{console.log("iframe loaded, src:",iframeEl.src),iframeEl.src===this.iframeLibraryUrl&&(this.handleIframeLibraryLoad(root),iframeEl.removeEventListener("load",loadHandler))};iframeEl.addEventListener("load",loadHandler),iframeEl.src=this.iframeLibraryUrl,console.log("iframe src set to:",iframeEl.src)}handleIframeLibraryLoad(root){console.log("handleIframeLibraryLoad called");const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);if(!pane)return;const placeholderEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryPlaceholder),loadingEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryLoading),iframeEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame);placeholderEl&&placeholderEl.classList.add("d-none"),loadingEl&&loadingEl.classList.add("d-none"),iframeEl&&iframeEl.classList.remove("d-none"),this.iframeLibraryLoaded=!0}handleIframeLibraryMessage(root,event){console.log("handleIframeLibraryMessage received:",event.data,"from origin:",event.origin);const data=event.data;if(data){if("videoSelected"===data.type&&data.embedUrl)return console.log("Video selected (videoSelected):",data.embedUrl,"videoId:",data.videoId),void this.selectIframeLibraryVideo(root,data.embedUrl,data.videoId);if("ltiDeepLinkingResponse"!==data.type&&"LtiDeepLinkingResponse"!==data.messageType)if("selectMedia"!==data.action&&"mediaSelected"!==data.action);else{const embedUrl=data.embedUrl||data.url||"",videoId=data.mediaId||data.videoId||data.id||"";embedUrl&&(console.log("Video selected (mediaSelected):",embedUrl,"videoId:",videoId),this.selectIframeLibraryVideo(root,embedUrl,videoId))}else{console.log("LTI Deep Linking response received:",data);const contentItems=data.content_items||data.contentItems||[];if(contentItems.length>0){const item=contentItems[0],embedUrl=item.url||item.embed_url||item.embedUrl||"",videoId=item.id||item.mediaId||"";embedUrl&&(console.log("Video selected (LTI):",embedUrl,"videoId:",videoId),this.selectIframeLibraryVideo(root,embedUrl,videoId))}}}}selectIframeLibraryVideo(root,embedUrl,videoId){root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.url).value=embedUrl,this.switchToUrlTab(root),this.updatePreview(root),this.selectedLibraryVideo={embedUrl:embedUrl,videoId:videoId}}},_exports.default})); - -//# sourceMappingURL=iframeembed.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js.map deleted file mode 100755 index 3168e79a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/iframeembed.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"iframeembed.min.js","sources":["../src/iframeembed.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 * Tiny Media2 Iframe Embed class.\n *\n * @module tiny_mediacms/iframeembed\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport { getString } from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport { component } from './common';\nimport IframeModal from './iframemodal';\nimport Selectors from './selectors';\nimport { getLti, getData } from './options';\n\nexport default class IframeEmbed {\n editor = null;\n currentModal = null;\n isUpdating = false;\n selectedIframe = null;\n debounceTimer = null;\n // Iframe library state\n iframeLibraryLoaded = false;\n selectedLibraryVideo = null;\n iframeLibraryUrl =\n 'https://temp.web357.com/mediacms/deic-mediacms-embed-videos.html';\n\n constructor(editor) {\n this.editor = editor;\n }\n\n /**\n * Parse input to extract video URL or iframe src.\n * Handles: iframe embed code, view URL, or embed URL.\n *\n * @param {string} input - The user input (URL or embed code)\n * @returns {Object|null} Parsed URL info or null if invalid\n */\n parseInput(input) {\n if (!input || !input.trim()) {\n return null;\n }\n\n input = input.trim();\n\n // Check if it's an iframe embed code\n const iframeMatch = input.match(\n /]*src=[\"']([^\"']+)[\"'][^>]*>/i,\n );\n if (iframeMatch) {\n return this.parseEmbedUrl(iframeMatch[1]);\n }\n\n // Check if it's a URL\n if (input.startsWith('http://') || input.startsWith('https://')) {\n return this.parseVideoUrl(input);\n }\n\n return null;\n }\n\n /**\n * Parse a video view URL and convert to embed format.\n *\n * @param {string} url - The video URL\n * @returns {Object|null} Parsed info\n */\n parseVideoUrl(url) {\n try {\n const urlObj = new URL(url);\n const baseUrl = `${urlObj.protocol}//${urlObj.host}`;\n\n // MediaCMS view URL: /view?m=VIDEO_ID\n if (urlObj.pathname === '/view' && urlObj.searchParams.has('m')) {\n return {\n baseUrl: baseUrl,\n videoId: urlObj.searchParams.get('m'),\n isEmbed: false,\n };\n }\n\n // MediaCMS embed URL: /embed?m=VIDEO_ID&options\n if (urlObj.pathname === '/embed' && urlObj.searchParams.has('m')) {\n const tParam = urlObj.searchParams.get('t');\n return {\n baseUrl: baseUrl,\n videoId: urlObj.searchParams.get('m'),\n isEmbed: true,\n showTitle: urlObj.searchParams.get('showTitle') === '1',\n linkTitle: urlObj.searchParams.get('linkTitle') === '1',\n showRelated: urlObj.searchParams.get('showRelated') === '1',\n showUserAvatar:\n urlObj.searchParams.get('showUserAvatar') === '1',\n startAt: tParam\n ? this.secondsToTimeString(parseInt(tParam))\n : null,\n };\n }\n\n // Generic URL - just use as-is\n return {\n baseUrl: baseUrl,\n rawUrl: url,\n isGeneric: true,\n };\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Parse an embed URL.\n *\n * @param {string} url - The embed URL\n * @returns {Object|null} Parsed info\n */\n parseEmbedUrl(url) {\n return this.parseVideoUrl(url);\n }\n\n /**\n * Convert seconds to time string (M:SS format).\n *\n * @param {number} seconds - Time in seconds\n * @returns {string} Time string\n */\n secondsToTimeString(seconds) {\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n }\n\n /**\n * Convert time string to seconds.\n *\n * @param {string} timeStr - Time string (M:SS or just seconds)\n * @returns {number|null} Seconds or null if invalid\n */\n timeStringToSeconds(timeStr) {\n if (!timeStr || !timeStr.trim()) {\n return null;\n }\n timeStr = timeStr.trim();\n\n // Handle M:SS format\n if (timeStr.includes(':')) {\n const parts = timeStr.split(':');\n const mins = parseInt(parts[0]) || 0;\n const secs = parseInt(parts[1]) || 0;\n return mins * 60 + secs;\n }\n\n // Handle plain seconds\n const secs = parseInt(timeStr);\n return isNaN(secs) ? null : secs;\n }\n\n /**\n * Build the embed URL with options.\n *\n * @param {Object} parsed - Parsed URL info\n * @param {Object} options - Embed options\n * @returns {string} The complete embed URL\n */\n buildEmbedUrl(parsed, options) {\n if (parsed.isGeneric) {\n return parsed.rawUrl;\n }\n\n const url = new URL(`${parsed.baseUrl}/embed`);\n url.searchParams.set('m', parsed.videoId);\n\n // Always include all options with 1 or 0\n url.searchParams.set('showTitle', options.showTitle ? '1' : '0');\n url.searchParams.set('showRelated', options.showRelated ? '1' : '0');\n url.searchParams.set(\n 'showUserAvatar',\n options.showUserAvatar ? '1' : '0',\n );\n url.searchParams.set('linkTitle', options.linkTitle ? '1' : '0');\n\n // Add start time if enabled\n if (options.startAtEnabled && options.startAt) {\n const seconds = this.timeStringToSeconds(options.startAt);\n if (seconds !== null && seconds > 0) {\n url.searchParams.set('t', seconds.toString());\n }\n }\n\n return url.toString();\n }\n\n /**\n * Get the template context for the modal.\n *\n * @param {Object} data - Existing data for updating\n * @returns {Object} Template context\n */\n async getTemplateContext(data = {}) {\n // Get admin settings for default checkbox values.\n const editorData = getData(this.editor);\n const autoConvertOptions = editorData?.autoConvertOptions || {};\n\n // Use admin settings as defaults when creating new embed (not updating).\n // When updating, use the values from the existing iframe.\n const getDefault = (key, fallback = true) => {\n if (this.isUpdating && data[key] !== undefined) {\n return data[key];\n }\n return autoConvertOptions[key] !== undefined\n ? autoConvertOptions[key]\n : fallback;\n };\n\n return {\n elementid: this.editor.getElement().id,\n isupdating: this.isUpdating,\n url: data.url || '',\n showTitle: getDefault('showTitle'),\n linkTitle: getDefault('linkTitle'),\n showRelated: getDefault('showRelated'),\n showUserAvatar: getDefault('showUserAvatar'),\n responsive: data.responsive !== false,\n startAtEnabled: data.startAtEnabled || false,\n startAt: data.startAt || '0:00',\n width: data.width || 560,\n height: data.height || 315,\n is16_9: !data.aspectRatio || data.aspectRatio === '16:9',\n is4_3: data.aspectRatio === '4:3',\n is1_1: data.aspectRatio === '1:1',\n isCustom: data.aspectRatio === 'custom',\n };\n }\n\n /**\n * Display the iframe embed dialog.\n */\n async displayDialogue() {\n this.selectedIframe = this.getSelectedIframe();\n const data = this.getCurrentIframeData();\n this.isUpdating = data !== null;\n\n // Reset iframe library state for new modal\n this.iframeLibraryLoaded = false;\n\n this.currentModal = await IframeModal.create({\n title: getString('iframemodaltitle', component),\n templateContext: await this.getTemplateContext(data || {}),\n });\n\n await this.registerEventListeners(this.currentModal);\n }\n\n /**\n * Get the currently selected iframe in the editor.\n *\n * @returns {HTMLElement|null} The iframe element or null\n */\n getSelectedIframe() {\n const node = this.editor.selection.getNode();\n\n if (node.nodeName.toLowerCase() === 'iframe') {\n return node;\n }\n\n // Check if selection contains an iframe wrapper (including overlay wrapper)\n const iframe = node.querySelector('iframe');\n if (iframe) {\n return iframe;\n }\n\n // Check if we're on the overlay or wrapper and need to find the iframe\n const wrapper =\n node.closest('.tiny-mediacms-iframe-wrapper') ||\n node.closest('.tiny-iframe-responsive');\n if (wrapper) {\n return wrapper.querySelector('iframe');\n }\n\n return null;\n }\n\n /**\n * Get current iframe data for editing.\n *\n * @returns {Object|null} Current iframe data or null\n */\n getCurrentIframeData() {\n if (!this.selectedIframe) {\n return null;\n }\n\n const src = this.selectedIframe.getAttribute('src');\n const parsed = this.parseInput(src);\n\n // Check if responsive by looking at style\n const style = this.selectedIframe.getAttribute('style') || '';\n const isResponsive = style.includes('aspect-ratio');\n\n return {\n url: src,\n width: this.selectedIframe.getAttribute('width') || 560,\n height: this.selectedIframe.getAttribute('height') || 315,\n showTitle: parsed?.showTitle ?? true,\n linkTitle: parsed?.linkTitle ?? true,\n showRelated: parsed?.showRelated ?? true,\n showUserAvatar: parsed?.showUserAvatar ?? true,\n responsive: isResponsive,\n startAtEnabled: parsed?.startAt !== null,\n startAt: parsed?.startAt || '0:00',\n };\n }\n\n /**\n * Get form values from the modal.\n *\n * @param {HTMLElement} root - Modal root element\n * @returns {Object} Form values\n */\n getFormValues(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n return {\n url: form.querySelector(Selectors.IFRAME.elements.url).value.trim(),\n showTitle: form.querySelector(Selectors.IFRAME.elements.showTitle)\n .checked,\n linkTitle: form.querySelector(Selectors.IFRAME.elements.linkTitle)\n .checked,\n showRelated: form.querySelector(\n Selectors.IFRAME.elements.showRelated,\n ).checked,\n showUserAvatar: form.querySelector(\n Selectors.IFRAME.elements.showUserAvatar,\n ).checked,\n responsive: form.querySelector(Selectors.IFRAME.elements.responsive)\n .checked,\n startAtEnabled: form.querySelector(\n Selectors.IFRAME.elements.startAtEnabled,\n ).checked,\n startAt: form\n .querySelector(Selectors.IFRAME.elements.startAt)\n .value.trim(),\n aspectRatio: form.querySelector(\n Selectors.IFRAME.elements.aspectRatio,\n ).value,\n width:\n parseInt(\n form.querySelector(Selectors.IFRAME.elements.width).value,\n ) || 560,\n height:\n parseInt(\n form.querySelector(Selectors.IFRAME.elements.height).value,\n ) || 315,\n };\n }\n\n /**\n * Generate the iframe HTML.\n *\n * @param {Object} values - Form values\n * @returns {Promise} Generated HTML\n */\n async generateIframeHtml(values) {\n const parsed = this.parseInput(values.url);\n if (!parsed) {\n return '';\n }\n\n const embedUrl = this.buildEmbedUrl(parsed, values);\n\n // Calculate aspect ratio values for CSS\n const aspectRatioCalcs = {\n '16:9': '16 / 9',\n '4:3': '4 / 3',\n '1:1': '1 / 1',\n custom: `${values.width} / ${values.height}`,\n };\n\n const context = {\n src: embedUrl,\n width: values.width,\n height: values.height,\n responsive: values.responsive,\n aspectRatioCalc: aspectRatioCalcs[values.aspectRatio] || '16 / 9',\n aspectRatioValue: aspectRatioCalcs[values.aspectRatio] || '16 / 9',\n };\n\n const { html } = await Templates.renderForPromise(\n 'tiny_mediacms/iframe_embed_output',\n context,\n );\n return html;\n }\n\n /**\n * Update the preview in the modal.\n *\n * @param {HTMLElement} root - Modal root element\n * @param {boolean} updateUrlField - Whether to update the URL field with new embed options\n */\n async updatePreview(root, updateUrlField = false) {\n const values = this.getFormValues(root);\n const previewContainer = root.querySelector(\n Selectors.IFRAME.elements.preview,\n );\n const urlWarning = root.querySelector(\n Selectors.IFRAME.elements.urlWarning,\n );\n\n if (!values.url) {\n previewContainer.innerHTML =\n 'Enter a video URL to see preview';\n urlWarning.classList.add('d-none');\n return;\n }\n\n const parsed = this.parseInput(values.url);\n if (!parsed) {\n previewContainer.innerHTML =\n 'Invalid URL format';\n urlWarning.classList.remove('d-none');\n return;\n }\n\n urlWarning.classList.add('d-none');\n const embedUrl = this.buildEmbedUrl(parsed, values);\n\n // Update the URL field if requested (when embed options change)\n if (updateUrlField && !parsed.isGeneric) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const urlInput = form.querySelector(Selectors.IFRAME.elements.url);\n urlInput.value = embedUrl;\n }\n\n // Show a scaled preview\n const previewWidth = Math.min(values.width, 400);\n const scale = previewWidth / values.width;\n const previewHeight = Math.round(values.height * scale);\n\n previewContainer.innerHTML = `\n \n `;\n }\n\n /**\n * Handle form input changes with debounce.\n *\n * @param {HTMLElement} root - Modal root element\n * @param {boolean} updateUrlField - Whether to update the URL field\n */\n handleInputChange(root, updateUrlField = false) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = setTimeout(() => {\n this.updatePreview(root, updateUrlField);\n }, 500);\n }\n\n /**\n * Handle aspect ratio change - update dimensions.\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleAspectRatioChange(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const aspectRatio = form.querySelector(\n Selectors.IFRAME.elements.aspectRatio,\n ).value;\n const dimensions = Selectors.IFRAME.aspectRatios[aspectRatio];\n\n // Only update dimensions for predefined ratios, not 'custom'\n if (dimensions && aspectRatio !== 'custom') {\n form.querySelector(Selectors.IFRAME.elements.width).value =\n dimensions.width;\n form.querySelector(Selectors.IFRAME.elements.height).value =\n dimensions.height;\n }\n\n this.updatePreview(root);\n }\n\n /**\n * Handle dialog submission.\n *\n * @param {Object} modal - The modal instance\n */\n async handleDialogueSubmission(modal) {\n const root = modal.getRoot()[0];\n const values = this.getFormValues(root);\n\n if (!values.url) {\n return;\n }\n\n const html = await this.generateIframeHtml(values);\n if (html) {\n if (this.isUpdating && this.selectedIframe) {\n // Replace existing iframe (including wrapper if present)\n // Check for both old wrapper and new overlay wrapper\n const wrapper =\n this.selectedIframe.closest(\n '.tiny-mediacms-iframe-wrapper',\n ) || this.selectedIframe.closest('.tiny-iframe-responsive');\n if (wrapper) {\n wrapper.outerHTML = html;\n } else {\n this.selectedIframe.outerHTML = html;\n }\n this.isUpdating = false;\n // Fire change event to trigger overlay reprocessing\n this.editor.fire('Change');\n } else {\n this.editor.insertContent(html);\n }\n }\n }\n\n /**\n * Handle video removal from the editor.\n *\n * @param {Object} modal - The modal instance\n */\n async handleRemove(modal) {\n // Get confirmation string\n const confirmMessage = await getString(\n 'removeiframeconfirm',\n component,\n );\n\n // Show confirmation dialog\n // eslint-disable-next-line no-alert\n if (!window.confirm(confirmMessage)) {\n return;\n }\n\n if (this.selectedIframe) {\n // Remove the iframe (including wrapper if present)\n const wrapper =\n this.selectedIframe.closest('.tiny-mediacms-iframe-wrapper') ||\n this.selectedIframe.closest('.tiny-iframe-responsive');\n if (wrapper) {\n wrapper.remove();\n } else {\n this.selectedIframe.remove();\n }\n }\n\n // Close the modal\n this.isUpdating = false;\n modal.hide();\n }\n\n /**\n * Register event listeners for the modal.\n *\n * @param {Object} modal - The modal instance\n */\n async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n\n // Input change handlers\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // URL input\n form.querySelector(Selectors.IFRAME.elements.url).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n\n // Embed option checkboxes - these should update the URL field when changed\n [\n Selectors.IFRAME.elements.showTitle,\n Selectors.IFRAME.elements.linkTitle,\n Selectors.IFRAME.elements.showRelated,\n Selectors.IFRAME.elements.showUserAvatar,\n Selectors.IFRAME.elements.startAtEnabled,\n ].forEach((selector) => {\n form.querySelector(selector).addEventListener('change', () =>\n this.handleInputChange(root, true), // Update URL field when embed options change\n );\n });\n\n // Responsive checkbox - doesn't affect URL, only display\n form.querySelector(Selectors.IFRAME.elements.responsive).addEventListener('change', () =>\n this.handleInputChange(root, false),\n );\n\n // Start at time input - should update URL field\n form.querySelector(Selectors.IFRAME.elements.startAt).addEventListener(\n 'input',\n () => this.handleInputChange(root, true),\n );\n\n // Aspect ratio change\n form.querySelector(\n Selectors.IFRAME.elements.aspectRatio,\n ).addEventListener('change', () => this.handleAspectRatioChange(root));\n\n // Dimension inputs\n form.querySelector(Selectors.IFRAME.elements.width).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n form.querySelector(Selectors.IFRAME.elements.height).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n\n // Modal events\n $root.on(ModalEvents.save, () => this.handleDialogueSubmission(modal));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n\n // Remove button handler (only present when updating)\n const removeBtn = root.querySelector(Selectors.IFRAME.actions.remove);\n if (removeBtn) {\n removeBtn.addEventListener('click', () => this.handleRemove(modal));\n }\n\n // Initial preview if we have a URL\n const urlInput = form.querySelector(Selectors.IFRAME.elements.url);\n if (urlInput.value) {\n this.updatePreview(root);\n }\n\n // Tab change handler - load iframe library when switching to iframe library tab\n const iframeLibraryTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabIframeLibraryBtn,\n );\n if (iframeLibraryTabBtn) {\n iframeLibraryTabBtn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Manually handle tab switching for Bootstrap 5\n this.switchToIframeLibraryTab(root);\n\n // Small delay to ensure tab pane is visible before loading iframe\n setTimeout(() => this.handleIframeLibraryTabClick(root), 100);\n });\n // Also handle Bootstrap tab events (if Bootstrap handles it)\n iframeLibraryTabBtn.addEventListener('shown.bs.tab', () =>\n this.handleIframeLibraryTabClick(root),\n );\n // jQuery Bootstrap 4 event\n const $iframeLibraryTabBtn = window.jQuery\n ? window.jQuery(iframeLibraryTabBtn)\n : null;\n if ($iframeLibraryTabBtn) {\n $iframeLibraryTabBtn.on('shown.bs.tab', () =>\n this.handleIframeLibraryTabClick(root),\n );\n }\n }\n\n // Tab change handler for URL tab\n const urlTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabUrlBtn,\n );\n if (urlTabBtn) {\n urlTabBtn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Manually handle tab switching\n this.switchToUrlTab(root);\n });\n }\n\n // Iframe library event listeners\n this.registerIframeLibraryEventListeners(root);\n }\n\n /**\n * Switch to the URL tab.\n *\n * @param {HTMLElement} root - Modal root element\n */\n switchToUrlTab(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // Get tab buttons\n const urlTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabUrlBtn,\n );\n const iframeLibraryTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabIframeLibraryBtn,\n );\n\n // Get tab panes\n const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl);\n const iframeLibraryPane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n // Update tab button states\n if (urlTabBtn) {\n urlTabBtn.classList.add('active');\n urlTabBtn.setAttribute('aria-selected', 'true');\n }\n if (iframeLibraryTabBtn) {\n iframeLibraryTabBtn.classList.remove('active');\n iframeLibraryTabBtn.setAttribute('aria-selected', 'false');\n }\n\n // Update tab pane visibility\n if (urlPane) {\n urlPane.classList.add('show', 'active');\n }\n if (iframeLibraryPane) {\n iframeLibraryPane.classList.remove('show', 'active');\n }\n }\n\n /**\n * Switch to the iframe library tab.\n *\n * @param {HTMLElement} root - Modal root element\n */\n switchToIframeLibraryTab(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // Get tab buttons\n const urlTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabUrlBtn,\n );\n const iframeLibraryTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabIframeLibraryBtn,\n );\n\n // Get tab panes\n const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl);\n const iframeLibraryPane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n // Update tab button states\n if (urlTabBtn) {\n urlTabBtn.classList.remove('active');\n urlTabBtn.setAttribute('aria-selected', 'false');\n }\n if (iframeLibraryTabBtn) {\n iframeLibraryTabBtn.classList.add('active');\n iframeLibraryTabBtn.setAttribute('aria-selected', 'true');\n }\n\n // Update tab pane visibility\n if (urlPane) {\n urlPane.classList.remove('show', 'active');\n }\n if (iframeLibraryPane) {\n iframeLibraryPane.classList.add('show', 'active');\n }\n }\n\n /**\n * Register event listeners for the iframe library.\n *\n * @param {HTMLElement} root - Modal root element\n */\n registerIframeLibraryEventListeners(root) {\n // Listen for messages from the iframe (for video selection)\n window.addEventListener('message', (event) => {\n this.handleIframeLibraryMessage(root, event);\n });\n }\n\n /**\n * Handle iframe library tab click - always refetch content (no caching).\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleIframeLibraryTabClick(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n const iframeEl = pane\n ? pane.querySelector(Selectors.IFRAME.elements.iframeLibraryFrame)\n : null;\n\n // eslint-disable-next-line no-console\n console.log(\n 'handleIframeLibraryTabClick called, iframeLibraryLoaded:',\n this.iframeLibraryLoaded,\n 'iframe src:',\n iframeEl ? iframeEl.src : 'no iframe',\n 'pane:',\n pane,\n );\n\n // Always refetch content when tab is clicked (no caching)\n // Reset the loaded state to ensure fresh content is fetched\n this.iframeLibraryLoaded = false;\n this.loadIframeLibrary(root);\n }\n\n /**\n * Load the iframe library using LTI flow or fallback to static URL.\n *\n * @param {HTMLElement} root - Modal root element\n */\n loadIframeLibrary(root) {\n const ltiConfig = getLti(this.editor);\n\n // eslint-disable-next-line no-console\n console.log('loadIframeLibrary called, LTI config:', ltiConfig);\n\n // Check if LTI is configured with a content item URL\n if (ltiConfig?.contentItemUrl) {\n this.loadIframeLibraryViaLti(root, ltiConfig);\n } else {\n // Fallback to static URL if LTI not configured\n this.loadIframeLibraryStatic(root);\n }\n }\n\n /**\n * Load the iframe library via LTI Deep Linking (Content Item Selection).\n * Sets the iframe src to contentitem.php which initiates the LTI Deep Linking flow.\n * This sends an LtiDeepLinkingRequest message, which will redirect to the\n * tool's content selection interface (e.g., /lti/select-media/).\n *\n * @param {HTMLElement} root - Modal root element\n * @param {Object} ltiConfig - LTI configuration\n */\n loadIframeLibraryViaLti(root, ltiConfig) {\n // eslint-disable-next-line no-console\n console.log('loadIframeLibraryViaLti called, config:', ltiConfig);\n\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n if (!pane) {\n // eslint-disable-next-line no-console\n console.log('paneIframeLibrary not found!');\n return;\n }\n\n const placeholderEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryPlaceholder,\n );\n const loadingEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryLoading,\n );\n const iframeEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryFrame,\n );\n\n if (!iframeEl) {\n // eslint-disable-next-line no-console\n console.log('iframeEl not found!');\n return;\n }\n\n // Hide placeholder, show loading state\n if (placeholderEl) {\n placeholderEl.classList.add('d-none');\n }\n if (loadingEl) {\n loadingEl.classList.remove('d-none');\n }\n iframeEl.classList.add('d-none');\n\n // Set up load listener - note: this may fire multiple times during LTI redirects\n const loadHandler = () => {\n // eslint-disable-next-line no-console\n console.log('LTI iframe loaded');\n this.handleIframeLibraryLoad(root);\n };\n iframeEl.addEventListener('load', loadHandler);\n\n // Set the iframe src to the content item URL\n // This initiates the LTI Deep Linking flow:\n // 1. contentitem.php initiates OIDC login\n // 2. LTI provider authenticates\n // 3. Moodle sends LtiDeepLinkingRequest\n // 4. Tool provider shows content selection interface (e.g., /lti/select-media/)\n // eslint-disable-next-line no-console\n console.log(\n 'Setting iframe src to LTI content item URL:',\n ltiConfig.contentItemUrl,\n );\n iframeEl.src = ltiConfig.contentItemUrl;\n }\n\n /**\n * Load the iframe library using static URL (fallback).\n *\n * @param {HTMLElement} root - Modal root element\n */\n loadIframeLibraryStatic(root) {\n // eslint-disable-next-line no-console\n console.log(\n 'loadIframeLibraryStatic called, URL:',\n this.iframeLibraryUrl,\n );\n\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n if (!pane) {\n // eslint-disable-next-line no-console\n console.log('paneIframeLibrary not found!');\n return;\n }\n\n const placeholderEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryPlaceholder,\n );\n const loadingEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryLoading,\n );\n const iframeEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryFrame,\n );\n\n // eslint-disable-next-line no-console\n console.log('Elements found:', { placeholderEl, loadingEl, iframeEl });\n\n if (!iframeEl) {\n // eslint-disable-next-line no-console\n console.log('iframeEl not found!');\n return;\n }\n\n // Hide placeholder, show loading state\n if (placeholderEl) {\n placeholderEl.classList.add('d-none');\n }\n if (loadingEl) {\n loadingEl.classList.remove('d-none');\n }\n iframeEl.classList.add('d-none');\n\n // Set up load listener before setting src\n const loadHandler = () => {\n // eslint-disable-next-line no-console\n console.log('iframe loaded, src:', iframeEl.src);\n // Only handle if the src matches our target URL\n if (iframeEl.src === this.iframeLibraryUrl) {\n this.handleIframeLibraryLoad(root);\n // Remove the listener after successful load\n iframeEl.removeEventListener('load', loadHandler);\n }\n };\n iframeEl.addEventListener('load', loadHandler);\n\n // Set the iframe source\n iframeEl.src = this.iframeLibraryUrl;\n // eslint-disable-next-line no-console\n console.log('iframe src set to:', iframeEl.src);\n }\n\n /**\n * Handle iframe library load event.\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleIframeLibraryLoad(root) {\n // eslint-disable-next-line no-console\n console.log('handleIframeLibraryLoad called');\n\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n if (!pane) {\n return;\n }\n\n const placeholderEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryPlaceholder,\n );\n const loadingEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryLoading,\n );\n const iframeEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryFrame,\n );\n\n // Hide placeholder and loading, show iframe\n if (placeholderEl) {\n placeholderEl.classList.add('d-none');\n }\n if (loadingEl) {\n loadingEl.classList.add('d-none');\n }\n if (iframeEl) {\n iframeEl.classList.remove('d-none');\n }\n\n this.iframeLibraryLoaded = true;\n }\n\n /**\n * Handle messages from the iframe library (for video selection).\n * Supports both custom videoSelected messages and LTI Deep Linking responses.\n *\n * @param {HTMLElement} root - Modal root element\n * @param {MessageEvent} event - The message event\n */\n handleIframeLibraryMessage(root, event) {\n // eslint-disable-next-line no-console\n console.log(\n 'handleIframeLibraryMessage received:',\n event.data,\n 'from origin:',\n event.origin,\n );\n\n const data = event.data;\n\n if (!data) {\n return;\n }\n\n // Handle custom videoSelected message format (from static iframe or custom MediaCMS implementation)\n if (data.type === 'videoSelected' && data.embedUrl) {\n // eslint-disable-next-line no-console\n console.log(\n 'Video selected (videoSelected):',\n data.embedUrl,\n 'videoId:',\n data.videoId,\n );\n this.selectIframeLibraryVideo(root, data.embedUrl, data.videoId);\n return;\n }\n\n // Handle LTI Deep Linking response format\n if (\n data.type === 'ltiDeepLinkingResponse' ||\n data.messageType === 'LtiDeepLinkingResponse'\n ) {\n // eslint-disable-next-line no-console\n console.log('LTI Deep Linking response received:', data);\n const contentItems = data.content_items || data.contentItems || [];\n if (contentItems.length > 0) {\n const item = contentItems[0];\n // Extract embed URL from the content item\n const embedUrl =\n item.url || item.embed_url || item.embedUrl || '';\n const videoId = item.id || item.mediaId || '';\n if (embedUrl) {\n // eslint-disable-next-line no-console\n console.log(\n 'Video selected (LTI):',\n embedUrl,\n 'videoId:',\n videoId,\n );\n this.selectIframeLibraryVideo(root, embedUrl, videoId);\n }\n }\n return;\n }\n\n // Handle MediaCMS specific message format (if different from above)\n if (data.action === 'selectMedia' || data.action === 'mediaSelected') {\n const embedUrl = data.embedUrl || data.url || '';\n const videoId = data.mediaId || data.videoId || data.id || '';\n if (embedUrl) {\n // eslint-disable-next-line no-console\n console.log(\n 'Video selected (mediaSelected):',\n embedUrl,\n 'videoId:',\n videoId,\n );\n this.selectIframeLibraryVideo(root, embedUrl, videoId);\n }\n return;\n }\n }\n\n /**\n * Select a video from the iframe library and populate the URL field.\n *\n * @param {HTMLElement} root - Modal root element\n * @param {string} embedUrl - The embed URL for the video\n * @param {string} videoId - The video ID\n */\n selectIframeLibraryVideo(root, embedUrl, videoId) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // Populate the URL field\n const urlInput = form.querySelector(Selectors.IFRAME.elements.url);\n urlInput.value = embedUrl;\n\n // Switch to the URL tab using our method\n this.switchToUrlTab(root);\n\n // Update the preview\n this.updatePreview(root);\n\n // Store the selected video\n this.selectedLibraryVideo = { embedUrl, videoId };\n }\n}\n"],"names":["constructor","editor","parseInput","input","trim","iframeMatch","match","this","parseEmbedUrl","startsWith","parseVideoUrl","url","urlObj","URL","baseUrl","protocol","host","pathname","searchParams","has","videoId","get","isEmbed","tParam","showTitle","linkTitle","showRelated","showUserAvatar","startAt","secondsToTimeString","parseInt","rawUrl","isGeneric","e","seconds","mins","Math","floor","secs","toString","padStart","timeStringToSeconds","timeStr","includes","parts","split","isNaN","buildEmbedUrl","parsed","options","set","startAtEnabled","data","editorData","autoConvertOptions","getDefault","key","fallback","_this","isUpdating","undefined","elementid","getElement","id","isupdating","responsive","width","height","is16_9","aspectRatio","is4_3","is1_1","isCustom","selectedIframe","getSelectedIframe","getCurrentIframeData","iframeLibraryLoaded","currentModal","IframeModal","create","title","component","templateContext","getTemplateContext","registerEventListeners","node","selection","getNode","nodeName","toLowerCase","iframe","querySelector","wrapper","closest","src","getAttribute","isResponsive","getFormValues","root","form","Selectors","IFRAME","elements","value","checked","values","embedUrl","aspectRatioCalcs","custom","context","aspectRatioCalc","aspectRatioValue","html","Templates","renderForPromise","updateUrlField","previewContainer","preview","urlWarning","innerHTML","classList","add","remove","previewWidth","min","scale","previewHeight","round","handleInputChange","clearTimeout","debounceTimer","setTimeout","updatePreview","handleAspectRatioChange","dimensions","aspectRatios","modal","getRoot","generateIframeHtml","outerHTML","fire","insertContent","confirmMessage","window","confirm","hide","getBody","$root","addEventListener","forEach","selector","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","removeBtn","actions","handleRemove","iframeLibraryTabBtn","tabIframeLibraryBtn","preventDefault","stopPropagation","switchToIframeLibraryTab","handleIframeLibraryTabClick","$iframeLibraryTabBtn","jQuery","urlTabBtn","tabUrlBtn","switchToUrlTab","registerIframeLibraryEventListeners","urlPane","paneUrl","iframeLibraryPane","paneIframeLibrary","setAttribute","event","handleIframeLibraryMessage","pane","iframeEl","iframeLibraryFrame","console","log","loadIframeLibrary","ltiConfig","contentItemUrl","loadIframeLibraryViaLti","loadIframeLibraryStatic","placeholderEl","iframeLibraryPlaceholder","loadingEl","iframeLibraryLoading","handleIframeLibraryLoad","iframeLibraryUrl","loadHandler","removeEventListener","origin","type","selectIframeLibraryVideo","messageType","action","mediaId","contentItems","content_items","length","item","embed_url","selectedLibraryVideo"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js deleted file mode 100755 index 178a27eb..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/iframemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class IframeModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=IframeModal,_defineProperty(IframeModal,"TYPE","".concat(_common.component,"/iframemodal")),_defineProperty(IframeModal,"TEMPLATE","".concat(_common.component,"/iframe_embed_modal")),_exports.default})); - -//# sourceMappingURL=iframemodal.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js.map deleted file mode 100755 index 7ab1d3b8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/iframemodal.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"iframemodal.min.js","sources":["../src/iframemodal.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 * Iframe Embed Modal for Tiny Media2.\n *\n * @module tiny_mediacms/iframemodal\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class IframeModal extends Modal {\n static TYPE = `${component}/iframemodal`;\n static TEMPLATE = `${component}/iframe_embed_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["IframeModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"kaA0BqBA,oBAAoBC,eAIrCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,kEAlBHN,6BACAU,mDADAV,iCAEIU"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js deleted file mode 100755 index 4e8c1d43..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/image",["exports","./selectors","./imagemodal","./options","editor_tiny/options","tiny_mediacms/imageinsert","tiny_mediacms/imagedetails","core/prefetch","core/str","tiny_mediacms/imagehelpers"],(function(_exports,_selectors,_imagemodal,_options,_options2,_imageinsert,_imagedetails,_prefetch,_str,_imagehelpers){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal),(0,_prefetch.prefetchStrings)("tiny_mediacms",["imageurlrequired","sizecustom_help"]);return _exports.default=class{constructor(editor){_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"root",null),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone};Promise.all([(0,_imagehelpers.bodyImageInsert)(templateContext,this.root),(0,_imagehelpers.footerImageInsert)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadPreviewImage",(async function(url){this.startImageLoading();const image=new Image;image.src=url,image.addEventListener("error",(async()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=await(0,_str.getString)("imageurlrequired","tiny_mediacms"),(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.stopImageLoading()})),image.addEventListener("load",(async()=>{const currentImageData=await this.getCurrentImageData();let templateContext=await this.getTemplateContext(currentImageData);templateContext.sizecustomhelpicon={text:await(0,_str.getString)("sizecustom_help","tiny_mediacms")},Promise.all([(0,_imagehelpers.bodyImageDetails)(templateContext,this.root),(0,_imagehelpers.footerImageDetails)(templateContext,this.root)]).then((()=>{this.stopImageLoading()})).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,url,image).init()})).catch((error=>{window.console.log(error)}))}))}));const permissions=(0,_options.getImagePermissions)(editor),options=(0,_options2.getFilePicker)(editor,"image");this.canShowFilePicker=permissions.filepicker&&void 0!==options&&Object.keys(options.repositories).length>0,this.canShowDropZone=void 0!==options&&Object.values(options.repositories).some((repository=>"upload"===repository.type)),this.editor=editor}async displayDialogue(){const currentImageData=await this.getCurrentImageData();this.currentModal=await _imagemodal.default.create(),this.root=this.currentModal.getRoot()[0],currentImageData&¤tImageData.src?this.loadPreviewImage(currentImageData.src):this.loadInsertImage()}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,presentation:!1,customStyle:""};this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>(0,_imagehelpers.isPercentageValue)(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>(0,_imagehelpers.isPercentageValue)(String(image.height))?image.height:parseInt(image.height,10))(image);return 0!==height&&(properties.height=height),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}startImageLoading(){(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}stopImageLoading(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}},_exports.default})); - -//# sourceMappingURL=image.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js.map deleted file mode 100755 index 4e8dd218..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/image.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"image.min.js","sources":["../src/image.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 * Tiny Media plugin Image class for Moodle.\n *\n * @module tiny_mediacms/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport ImageModal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\nimport {ImageInsert} from 'tiny_mediacms/imageinsert';\nimport {ImageDetails} from 'tiny_mediacms/imagedetails';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {\n bodyImageInsert,\n footerImageInsert,\n bodyImageDetails,\n footerImageDetails,\n showElements,\n hideElements,\n isPercentageValue,\n} from 'tiny_mediacms/imagehelpers';\n\nprefetchStrings('tiny_mediacms', [\n 'imageurlrequired',\n 'sizecustom_help',\n]);\n\nexport default class MediaImage {\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n /**\n * @type {HTMLElement|null} The root element.\n */\n root = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n const options = getFilePicker(editor, 'image');\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker\n && (typeof options !== 'undefined')\n && Object.keys(options.repositories).length > 0;\n // Indicates whether the drop zone area can be shown.\n this.canShowDropZone = (typeof options !== 'undefined') &&\n Object.values(options.repositories).some(repository => repository.type === 'upload');\n\n this.editor = editor;\n }\n\n async displayDialogue() {\n const currentImageData = await this.getCurrentImageData();\n this.currentModal = await ImageModal.create();\n this.root = this.currentModal.getRoot()[0];\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n } else {\n this.loadInsertImage();\n }\n }\n\n /**\n * Displays an insert image view asynchronously.\n *\n * @returns {Promise}\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n };\n\n Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n /**\n * Asynchronously loads and previews an image from the provided URL.\n *\n * @param {string} url - The URL of the image to load and preview.\n * @returns {Promise}\n */\n loadPreviewImage = async function(url) {\n this.startImageLoading();\n const image = new Image();\n image.src = url;\n image.addEventListener('error', async() => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms');\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n });\n\n image.addEventListener('load', async() => {\n const currentImageData = await this.getCurrentImageData();\n let templateContext = await this.getTemplateContext(currentImageData);\n templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')};\n\n Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])\n .then(() => {\n this.stopImageLoading();\n return;\n })\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n url,\n image,\n );\n imagedetails.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n });\n };\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n presentation: false,\n customStyle: '', // Custom CSS styles applied to the image.\n };\n\n const getImageHeight = (image) => {\n if (!isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n startImageLoading() {\n showElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n hideElements(Selectors.IMAGE.elements.insertImage, this.root);\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n stopImageLoading() {\n hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n showElements(Selectors.IMAGE.elements.insertImage, this.root);\n }\n}\n"],"names":["constructor","editor","async","templateContext","elementid","this","id","showfilepicker","canShowFilePicker","showdropzone","canShowDropZone","Promise","all","root","then","ImageInsert","currentModal","init","catch","error","window","console","log","url","startImageLoading","image","Image","src","addEventListener","querySelector","Selectors","IMAGE","elements","urlWarning","innerHTML","stopImageLoading","currentImageData","getCurrentImageData","getTemplateContext","sizecustomhelpicon","text","ImageDetails","permissions","options","filepicker","Object","keys","repositories","length","values","some","repository","type","ImageModal","create","getRoot","loadPreviewImage","loadInsertImage","data","selectedImageProperties","getSelectedImageProperties","properties","haspreview","alt","presentation","getSelectedImage","selectedImage","width","height","customStyle","style","cssText","String","parseInt","getImageWidth","getImageHeight","getAttribute","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","loaderIcon","insertImage"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js deleted file mode 100755 index 35850139..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/imagedetails",["exports","core/config","core/modal_events","core/notification","core/pending","./selectors","core/templates","core/str","tiny_mediacms/imageinsert","tiny_mediacms/imagehelpers"],(function(_exports,_config,_modal_events,_notification,_pending,_selectors,_templates,_str,_imageinsert,_imagehelpers){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.ImageDetails=void 0,_config=_interopRequireDefault(_config),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates);_exports.ImageDetails=class{constructor(root,editor,currentModal,canShowFilePicker,canShowDropZone,currentUrl,_image){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"init",(function(){this.currentModal.setTitle((0,_str.getString)("imagedetails","tiny_mediacms")),this.imageTypeChecked(),this.presentationChanged(),this.storeImageDimensions(this.image),this.setImageDimensions(),this.registerEventListeners()})),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone};Promise.all([(0,_imagehelpers.bodyImageInsert)(templateContext,this.root),(0,_imagehelpers.footerImageInsert)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"setImageDimensions",(()=>{const imagePreviewBox=this.root.querySelector(_selectors.default.IMAGE.elements.previewBox),image=this.root.querySelector(_selectors.default.IMAGE.elements.preview),widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),updateImageDimensions=()=>{const boxWidth=imagePreviewBox.clientWidth,boxHeight=imagePreviewBox.clientHeight,dimensions=this.fitSquareIntoBox(widthField.value,heightField.value,boxWidth,boxHeight);image.style.width="".concat(dimensions.width,"px"),image.style.height="".concat(dimensions.height,"px")};0===imagePreviewBox.clientWidth?this.currentModal.getRoot().on(_modal_events.default.shown,(()=>{updateImageDimensions()})):updateImageDimensions()})),_defineProperty(this,"fitSquareIntoBox",((squareWidth,squareHeight,boxWidth,boxHeight)=>{if(squareWidth(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.root.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="";const constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain);if((0,_imagehelpers.isPercentageValue)(currentWidth)&&(0,_imagehelpers.isPercentageValue)(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(100*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(100*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}((currentWidth,currentHeight)=>{this.rawImageDimensions.width===currentWidth&&this.rawImageDimensions.height===currentHeight?(this.currentWidth=this.rawImageDimensions.width,this.currentHeight=this.rawImageDimensions.height,this.sizeChecked("original")):(this.currentWidth=currentWidth,this.currentHeight=currentHeight,this.sizeChecked("custom"))})(Number(currentWidth),Number(currentHeight))}sizeChecked(option){const widthInput=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightInput=this.root.querySelector(_selectors.default.IMAGE.elements.height);if("original"===option)this.sizeOriginalChecked(),widthInput.value=this.rawImageDimensions.width,heightInput.value=this.rawImageDimensions.height;else if("custom"===option&&(this.sizeCustomChecked(),widthInput.value=this.currentWidth,heightInput.value=this.currentHeight,this.currentWidth===this.rawImageDimensions.width&&this.currentHeight===this.rawImageDimensions.height)){this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked=!0}this.autoAdjustSize()}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!(0,_imagehelpers.isPercentageValue)(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),getKeyField=()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)};if(this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked){const keyField=getKeyField(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)}this.currentWidth=Number(widthField.value)!==this.rawImageDimensions.width?widthField.value:this.currentWidth,this.currentHeight=Number(heightField.value)!==this.rawImageDimensions.height?heightField.value:this.currentHeight}sizeOriginalChecked(){this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked=!0,this.root.querySelector(_selectors.default.IMAGE.elements.sizeCustom).checked=!1,(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.properties,this.root)}sizeCustomChecked(){this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked=!1,this.root.querySelector(_selectors.default.IMAGE.elements.sizeCustom).checked=!0,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.properties,this.root)}presentationChanged(){const presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation);this.root.querySelector(_selectors.default.IMAGE.elements.alt).disabled=presentation.checked,this.handleKeyupCharacterCount()}imageTypeChecked(){const isExternalUrl=!1===new RegExp("".concat(_config.default.wwwroot)).test(this.currentUrl);if((0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.url,this.root),isExternalUrl)this.setFilenameLabel(decodeURI(this.currentUrl));else{const filename=this.currentUrl.split("/").pop().split("?")[0];this.setFilenameLabel(decodeURI(filename))}}setFilenameLabel(label){const urlLabelEle=this.root.querySelector(_selectors.default.IMAGE.elements.fileNameLabel);urlLabelEle&&(urlLabelEle.innerHTML=label,urlLabelEle.setAttribute("title",label))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.root.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}hasErrorUrlField(){const urlError=""===this.currentUrl;return urlError?(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root):(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return imageAltError?(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.altWarning,this.root):(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWaaltWarningrning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[],constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked,sizeOriginal=this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked;return constrain||sizeOriginal?classList.push(_selectors.default.IMAGE.styles.responsive):classList.pop(_selectors.default.IMAGE.styles.responsive),{url:this.currentUrl,alt:this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.root.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.root.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.root.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_mediacms:setImage");if(""===this.currentUrl)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.root.querySelector(_selectors.default.IMAGE.elements.width).value;if(!(0,_imagehelpers.isPercentageValue)(width)&&isNaN(parseInt(width,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.root.querySelector(_selectors.default.IMAGE.elements.height).value;if(!(0,_imagehelpers.isPercentageValue)(height)&&isNaN(parseInt(height,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_mediacms/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch((error=>{window.console.log(error)}))}deleteImage(){_notification.default.deleteCancelPromise((0,_str.getString)("deleteimage","tiny_mediacms"),(0,_str.getString)("deleteimagewarning","tiny_mediacms")).then((()=>{(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.altWarning,this.root),this.loadInsertImage()})).catch((error=>{window.console.log(error)}))}registerEventListeners(){this.root.querySelector(_selectors.default.IMAGE.actions.submit).addEventListener("click",(e=>{e.preventDefault(),this.setImage()}));const deleteImageEle=this.root.querySelector(_selectors.default.IMAGE.actions.deleteImage);deleteImageEle.addEventListener("click",(()=>{this.deleteImage()})),deleteImageEle.addEventListener("keydown",(e=>{"Enter"===e.key&&this.deleteImage()})),this.root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.sizeOriginal)&&this.sizeChecked("original");e.target.closest(_selectors.default.IMAGE.elements.sizeCustom)&&this.sizeChecked("custom")})),this.root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged()}}),!0),this.root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()})),this.root.addEventListener("input",(e=>{const widthEle=e.target.closest(_selectors.default.IMAGE.elements.width);widthEle&&(widthEle.value=""===widthEle.value?0:Number(widthEle.value),this.autoAdjustSize());const heightEle=e.target.closest(_selectors.default.IMAGE.elements.height);heightEle&&(heightEle.value=""===heightEle.value?0:Number(heightEle.value),this.autoAdjustSize(!0))}))}handleKeyupCharacterCount(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value;this.root.querySelector("#currentcount").innerHTML=alt.length}}})); - -//# sourceMappingURL=imagedetails.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js.map deleted file mode 100755 index 0d7ebb6c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imagedetails.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imagedetails.min.js","sources":["../src/imagedetails.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 * Tiny media plugin image details class for Moodle.\n *\n * @module tiny_mediacms/imagedetails\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Config from 'core/config';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from './selectors';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {ImageInsert} from 'tiny_mediacms/imageinsert';\nimport {\n bodyImageInsert,\n footerImageInsert,\n showElements,\n hideElements,\n isPercentageValue,\n} from 'tiny_mediacms/imagehelpers';\n\nexport class ImageDetails {\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n rawImageDimensions = null;\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n currentUrl,\n image,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n this.currentUrl = currentUrl;\n this.image = image;\n }\n\n init = function() {\n this.currentModal.setTitle(getString('imagedetails', 'tiny_mediacms'));\n this.imageTypeChecked();\n this.presentationChanged();\n this.storeImageDimensions(this.image);\n this.setImageDimensions();\n this.registerEventListeners();\n };\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n };\n\n Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.root.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = '';\n\n // Ensure the checkbox always in unchecked status when an image loads at first.\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n\n /**\n * Sets the selected size option based on current width and height values.\n *\n * @param {number} currentWidth - The current width value.\n * @param {number} currentHeight - The current height value.\n */\n const setSelectedSize = (currentWidth, currentHeight) => {\n if (this.rawImageDimensions.width === currentWidth &&\n this.rawImageDimensions.height === currentHeight\n ) {\n this.currentWidth = this.rawImageDimensions.width;\n this.currentHeight = this.rawImageDimensions.height;\n this.sizeChecked('original');\n } else {\n this.currentWidth = currentWidth;\n this.currentHeight = currentHeight;\n this.sizeChecked('custom');\n }\n };\n\n setSelectedSize(Number(currentWidth), Number(currentHeight));\n }\n\n /**\n * Handles the selection of image size options and updates the form inputs accordingly.\n *\n * @param {string} option - The selected image size option (\"original\" or \"custom\").\n */\n sizeChecked(option) {\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n if (option === \"original\") {\n this.sizeOriginalChecked();\n widthInput.value = this.rawImageDimensions.width;\n heightInput.value = this.rawImageDimensions.height;\n } else if (option === \"custom\") {\n this.sizeCustomChecked();\n widthInput.value = this.currentWidth;\n heightInput.value = this.currentHeight;\n\n // If the current size is equal to the original size, then check the Keep proportion checkbox.\n if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {\n const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n constrainField.checked = true;\n }\n }\n this.autoAdjustSize();\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n // Now update with the new values.\n const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (constrainField.checked) {\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n\n // Store the custom width and height to reuse.\n this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;\n this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;\n }\n\n /**\n * Sets the dimensions of the image preview element based on user input and constraints.\n */\n setImageDimensions = () => {\n const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox);\n const image = this.root.querySelector(Selectors.IMAGE.elements.preview);\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const updateImageDimensions = () => {\n // Get the latest dimensions of the preview box for responsiveness.\n const boxWidth = imagePreviewBox.clientWidth;\n const boxHeight = imagePreviewBox.clientHeight;\n // Get the new width and height for the image.\n const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight);\n image.style.width = `${dimensions.width}px`;\n image.style.height = `${dimensions.height}px`;\n };\n // If the client size is zero, then get the new dimensions once the modal is shown.\n if (imagePreviewBox.clientWidth === 0) {\n // Call the shown event.\n this.currentModal.getRoot().on(ModalEvents.shown, () => {\n updateImageDimensions();\n });\n } else {\n updateImageDimensions();\n }\n };\n\n /**\n * Handles the selection of the \"Original Size\" option and updates the form elements accordingly.\n */\n sizeOriginalChecked() {\n this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;\n this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;\n hideElements(Selectors.IMAGE.elements.properties, this.root);\n }\n\n /**\n * Handles the selection of the \"Custom Size\" option and updates the form elements accordingly.\n */\n sizeCustomChecked() {\n this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;\n this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;\n showElements(Selectors.IMAGE.elements.properties, this.root);\n }\n\n /**\n * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.\n */\n presentationChanged() {\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);\n alt.disabled = presentation.checked;\n\n // Counting the image description characters.\n this.handleKeyupCharacterCount();\n }\n\n /**\n * This function checks whether an image URL is local (within the same website's domain) or external (from an external source).\n * Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface.\n * If the image is local then we only show it's filename.\n * If the image is external then it will show full URL and it can be updated.\n */\n imageTypeChecked() {\n const regex = new RegExp(`${Config.wwwroot}`);\n\n // True if the URL is from external, otherwise false.\n const isExternalUrl = regex.test(this.currentUrl) === false;\n\n // Hide the URL input.\n hideElements(Selectors.IMAGE.elements.url, this.root);\n\n if (!isExternalUrl) {\n // Split the URL by '/' to get an array of segments.\n const segments = this.currentUrl.split('/');\n // Get the last segment, which should be the filename.\n const filename = segments.pop().split('?')[0];\n // Show the file name.\n this.setFilenameLabel(decodeURI(filename));\n } else {\n\n this.setFilenameLabel(decodeURI(this.currentUrl));\n }\n }\n\n /**\n * Set the string for the URL label element.\n *\n * @param {string} label - The label text to set.\n */\n setFilenameLabel(label) {\n const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel);\n if (urlLabelEle) {\n urlLabelEle.innerHTML = label;\n urlLabelEle.setAttribute(\"title\", label);\n }\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.root.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n hasErrorUrlField() {\n const urlError = this.currentUrl === '';\n if (urlError) {\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n if (imageAltError) {\n showElements(Selectors.IMAGE.elements.altWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked;\n const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked;\n if (constrain || sizeOriginal) {\n // If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class.\n classList.push(Selectors.IMAGE.styles.responsive);\n } else {\n // Otherwise, remove it.\n classList.pop(Selectors.IMAGE.styles.responsive);\n }\n\n return {\n url: this.currentUrl,\n alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.root.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.root.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_mediacms:setImage');\n const url = this.currentUrl;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.root.querySelector(Selectors.IMAGE.elements.width).value;\n if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.root.querySelector(Selectors.IMAGE.elements.height).value;\n if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_mediacms/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch(error => {\n window.console.log(error);\n });\n }\n\n /**\n * Deletes the image after confirming with the user and loads the insert image page.\n */\n deleteImage() {\n Notification.deleteCancelPromise(\n getString('deleteimage', 'tiny_mediacms'),\n getString('deleteimagewarning', 'tiny_mediacms'),\n ).then(() => {\n hideElements(Selectors.IMAGE.elements.altWarning, this.root);\n // Removing the image in the preview will bring the user to the insert page.\n this.loadInsertImage();\n return;\n }).catch(error => {\n window.console.log(error);\n });\n }\n\n registerEventListeners() {\n const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit);\n submitAction.addEventListener('click', (e) => {\n e.preventDefault();\n this.setImage();\n });\n\n const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage);\n deleteImageEle.addEventListener('click', () => {\n this.deleteImage();\n });\n deleteImageEle.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n this.deleteImage();\n }\n });\n\n this.root.addEventListener('change', (e) => {\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize();\n }\n\n const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal);\n if (sizeOriginalEle) {\n this.sizeChecked('original');\n }\n\n const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom);\n if (sizeCustomEle) {\n this.sizeChecked('custom');\n }\n });\n\n this.root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n }\n }, true);\n\n // Character count.\n this.root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n // Avoid empty value.\n widthEle.value = widthEle.value === \"\" ? 0 : Number(widthEle.value);\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n // Avoid empty value.\n heightEle.value = heightEle.value === \"\" ? 0 : Number(heightEle.value);\n this.autoAdjustSize(true);\n }\n });\n }\n\n handleKeyupCharacterCount() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.root.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n /**\n * Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio.\n *\n * @param {number} squareWidth - The width of the square.\n * @param {number} squareHeight - The height of the square.\n * @param {number} boxWidth - The width of the box.\n * @param {number} boxHeight - The height of the box.\n * @returns {Object} An object with the new width and height of the square to fit in the box.\n */\n fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => {\n if (squareWidth < boxWidth && squareHeight < boxHeight) {\n // If the square is smaller than the box, keep its dimensions.\n return {\n width: squareWidth,\n height: squareHeight,\n };\n }\n // Calculate the scaling factor based on the minimum scaling required to fit in the box.\n const widthScaleFactor = boxWidth / squareWidth;\n const heightScaleFactor = boxHeight / squareHeight;\n const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor);\n // Scale the square's dimensions based on the aspect ratio and the minimum scaling factor.\n const newWidth = squareWidth * minScaleFactor;\n const newHeight = squareHeight * minScaleFactor;\n return {\n width: newWidth,\n height: newHeight,\n };\n };\n}\n"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","currentUrl","image","WIDTH","HEIGHT","setTitle","imageTypeChecked","presentationChanged","storeImageDimensions","this","setImageDimensions","registerEventListeners","async","templateContext","elementid","id","showfilepicker","showdropzone","Promise","all","then","ImageInsert","init","catch","error","window","console","log","imagePreviewBox","querySelector","Selectors","IMAGE","elements","previewBox","preview","widthField","width","heightField","height","updateImageDimensions","boxWidth","clientWidth","boxHeight","clientHeight","dimensions","fitSquareIntoBox","value","style","getRoot","on","ModalEvents","shown","squareWidth","squareHeight","widthScaleFactor","heightScaleFactor","minScaleFactor","Math","min","rawImageDimensions","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","setAttribute","src","display","constrain","checked","disabled","widthRatio","round","parseInt","heightRatio","sizeChecked","setSelectedSize","Number","option","widthInput","heightInput","sizeOriginalChecked","sizeCustomChecked","autoAdjustSize","forceHeight","normalizeFieldData","fieldData","isPercentageValue","field","percentValue","pixelSize","type","getKeyField","currentValue","keyField","relativeField","sizeOriginal","sizeCustom","properties","presentation","alt","handleKeyupCharacterCount","isExternalUrl","RegExp","Config","wwwroot","test","url","setFilenameLabel","decodeURI","filename","split","pop","label","urlLabelEle","fileNameLabel","innerHTML","toggleAriaInvalid","selectors","predicate","forEach","selector","querySelectorAll","hasErrorUrlField","urlError","urlWarning","hasErrorAltField","imageAltError","altWarning","urlWaaltWarningrning","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","html","insertContent","destroy","deleteImage","deleteCancelPromise","loadInsertImage","actions","submit","addEventListener","e","preventDefault","deleteImageEle","key","target","closest","nodeType","Node","ELEMENT_NODE","widthEle","heightEle","length"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js deleted file mode 100755 index 60b5e2de..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/imagehelpers",["exports","core/templates"],(function(_exports,_templates){var obj; -/** - * Tiny media plugin image helpers. - * - * @module tiny_mediacms/imagehelpers - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showElements=_exports.isPercentageValue=_exports.hideElements=_exports.footerImageInsert=_exports.footerImageDetails=_exports.bodyImageInsert=_exports.bodyImageDetails=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.bodyImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details",{...templateContext}).then((_ref3=>{let{html:html,js:js}=_ref3;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details_footer",{...templateContext}).then((_ref4=>{let{html:html,js:js}=_ref4;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.showElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.remove("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.remove("d-none")}};_exports.hideElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.add("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.add("d-none")}};_exports.isPercentageValue=value=>value.match(/\d+%/)})); - -//# sourceMappingURL=imagehelpers.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js.map deleted file mode 100755 index d6b87c56..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imagehelpers.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imagehelpers.min.js","sources":["../src/imagehelpers.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 * Tiny media plugin image helpers.\n *\n * @module tiny_mediacms/imagehelpers\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\n\n/**\n * Renders and inserts the body template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const bodyImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the footer template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const footerImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert_footer', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the body template for displaying image details in the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const bodyImageDetails = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the footer template for displaying image details in the modal.\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const footerImageDetails = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details_footer', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Show the element(s).\n *\n * @param {string|string[]} elements - The CSS selector for the elements to toggle.\n * @param {object} root - The CSS selector for the elements to toggle.\n */\nexport const showElements = (elements, root) => {\n if (elements instanceof Array) {\n elements.forEach((elementSelector) => {\n const element = root.querySelector(elementSelector);\n if (element) {\n element.classList.remove('d-none');\n }\n });\n } else {\n const element = root.querySelector(elements);\n if (element) {\n element.classList.remove('d-none');\n }\n }\n};\n\n/**\n * Hide the element(s).\n *\n * @param {string|string[]} elements - The CSS selector for the elements to toggle.\n * @param {object} root - The CSS selector for the elements to toggle.\n */\nexport const hideElements = (elements, root) => {\n if (elements instanceof Array) {\n elements.forEach((elementSelector) => {\n const element = root.querySelector(elementSelector);\n if (element) {\n element.classList.add('d-none');\n }\n });\n } else {\n const element = root.querySelector(elements);\n if (element) {\n element.classList.add('d-none');\n }\n }\n};\n\n/**\n * Checks if the given value is a percentage value.\n *\n * @param {string} value - The value to check.\n * @returns {boolean} True if the value is a percentage value, false otherwise.\n */\nexport const isPercentageValue = (value) => {\n return value.match(/\\d+%/);\n};\n"],"names":["async","templateContext","root","Templates","renderForPromise","then","_ref","html","js","replaceNodeContents","querySelector","catch","error","window","console","log","_ref2","_ref3","_ref4","elements","Array","forEach","elementSelector","element","classList","remove","add","value","match"],"mappings":";;;;;;;4UAgC+BA,MAAMC,gBAAiBC,OAC3CC,mBAAUC,iBAAiB,0CAA2C,IAAIH,kBAChFI,MAAKC,WAACC,KAACA,KAADC,GAAOA,4BACAC,oBAAoBP,KAAKQ,cAAc,gCAAiCH,KAAMC,OAG3FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,qCAWMZ,MAAMC,gBAAiBC,OAC7CC,mBAAUC,iBAAiB,iDAAkD,IAAIH,kBACvFI,MAAKW,YAACT,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,kCAAmCH,KAAMC,OAG7FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,oCAWKZ,MAAMC,gBAAiBC,OAC5CC,mBAAUC,iBAAiB,2CAA4C,IAAIH,kBACjFI,MAAKY,YAACV,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,gCAAiCH,KAAMC,OAG3FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,sCAUOZ,MAAMC,gBAAiBC,OAC9CC,mBAAUC,iBAAiB,kDAAmD,IAAIH,kBACxFI,MAAKa,YAACX,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,kCAAmCH,KAAMC,OAG7FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,gCAUC,CAACO,SAAUjB,WAC/BiB,oBAAoBC,MACpBD,SAASE,SAASC,wBACRC,QAAUrB,KAAKQ,cAAcY,iBAC/BC,SACAA,QAAQC,UAAUC,OAAO,iBAG9B,OACGF,QAAUrB,KAAKQ,cAAcS,UAC/BI,SACAA,QAAQC,UAAUC,OAAO,kCAWT,CAACN,SAAUjB,WAC/BiB,oBAAoBC,MACpBD,SAASE,SAASC,wBACRC,QAAUrB,KAAKQ,cAAcY,iBAC/BC,SACAA,QAAQC,UAAUE,IAAI,iBAG3B,OACGH,QAAUrB,KAAKQ,cAAcS,UAC/BI,SACAA,QAAQC,UAAUE,IAAI,uCAWAC,OACvBA,MAAMC,MAAM"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js deleted file mode 100755 index 1b7a7df6..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/imageinsert",["exports","./selectors","core/dropzone","editor_tiny/uploader","core/prefetch","core/str","./common","editor_tiny/options","editor_tiny/utils","tiny_mediacms/imagedetails","tiny_mediacms/imagehelpers"],(function(_exports,_selectors,_dropzone,_uploader,_prefetch,_str,_common,_options,_utils,_imagedetails,_imagehelpers){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.ImageInsert=void 0,_selectors=_interopRequireDefault(_selectors),_dropzone=_interopRequireDefault(_dropzone),_uploader=_interopRequireDefault(_uploader),(0,_prefetch.prefetchStrings)("tiny_mediacms",["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"]);_exports.ImageInsert=class{constructor(_root,editor,currentModal,canShowFilePicker,canShowDropZone){_defineProperty(this,"init",(async function(){const langStringKeys=["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"],langStringvalues=await(0,_str.getStrings)([...langStringKeys].map((key=>({key:key,component:_common.component}))));if(this.langStrings=Object.fromEntries(langStringKeys.map(((key,index)=>[key,langStringvalues[index]]))),this.currentModal.setTitle(this.langStrings.insertimage),this.canShowDropZone){const dropZoneEle=document.querySelector(_selectors.default.IMAGE.elements.dropzoneContainer);let acceptedTypes=(0,_options.getFilePicker)(this.editor,"image").accepted_types;Array.isArray(acceptedTypes)&&(acceptedTypes=acceptedTypes.join(","));const dropZone=new _dropzone.default(dropZoneEle,acceptedTypes,(files=>{this.handleUploadedFile(files)}));dropZone.setLabel(this.langStrings.addfilesdrop),dropZone.init()}await this.registerEventListeners()})),_defineProperty(this,"isValidUrl",(urlString=>!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3})|localhost)(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*").test(urlString))),_defineProperty(this,"loadPreviewImage",(function(url){this.startImageLoading(),this.currentUrl=url;const image=new Image;image.src=url,image.addEventListener("error",(()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=this.langStrings.imageurlrequired,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.currentUrl="",this.stopImageLoading()})),image.addEventListener("load",(()=>{let templateContext={};templateContext.sizecustomhelpicon={text:this.langStrings.sizecustom_help},Promise.all([(0,_imagehelpers.bodyImageDetails)(templateContext,this.root),(0,_imagehelpers.footerImageDetails)(templateContext,this.root)]).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,this.currentUrl,image).init()})).then((()=>{this.stopImageLoading()})).catch((error=>{window.console.log(error)}))}))})),_defineProperty(this,"updateLoaderIcon",(function(root,langStrings){let progress=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const loaderIcon=root.querySelector(_selectors.default.IMAGE.elements.loaderIconContainer+" div");loaderIcon.innerHTML=null!==progress?"".concat(langStrings.uploading," ").concat(Math.round(progress),"%"):langStrings.loading})),_defineProperty(this,"handleUploadedFile",(async files=>{try{this.startImageLoading();const fileURL=await(0,_uploader.default)(this.editor,"image",files[0],files[0].name,(progress=>{this.updateLoaderIcon(this.root,this.langStrings,progress)}));this.updateLoaderIcon(this.root,this.langStrings),this.filePickerCallback({url:fileURL})}catch(error){this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=void 0!==error.error?error.error:error,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.stopImageLoading()}})),this.root=_root,this.editor=editor,this.currentModal=currentModal,this.canShowFilePicker=canShowFilePicker,this.canShowDropZone=canShowDropZone}toggleUrlButton(){const url=this.root.querySelector(_selectors.default.IMAGE.elements.url).value;this.root.querySelector(_selectors.default.IMAGE.actions.addUrl).disabled=!(""!==url&&this.isValidUrl(url))}urlChanged(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root);const input=this.root.querySelector(_selectors.default.IMAGE.elements.url);input.value&&input.value!==this.currentUrl&&this.loadPreviewImage(input.value)}startImageLoading(){(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToHide=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.urlWarning,_selectors.default.IMAGE.elements.modalFooter];(0,_imagehelpers.hideElements)(elementsToHide,this.root)}stopImageLoading(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToShow=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.modalFooter];(0,_imagehelpers.showElements)(elementsToShow,this.root)}filePickerCallback(params){params.url&&this.loadPreviewImage(params.url)}registerEventListeners(){this.root.addEventListener("click",(async e=>{e.target.closest(_selectors.default.IMAGE.actions.addUrl)&&this.urlChanged();if(e.target.closest(_selectors.default.IMAGE.actions.imageBrowser)&&this.canShowFilePicker){e.preventDefault();const params=await(0,_utils.displayFilepicker)(this.editor,"image");this.filePickerCallback(params)}})),this.root.addEventListener("input",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.toggleUrlButton()}));const fileInput=this.root.querySelector(_selectors.default.IMAGE.elements.fileInput);fileInput&&fileInput.addEventListener("change",(()=>{this.handleUploadedFile(fileInput.files)}))}}})); - -//# sourceMappingURL=imageinsert.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js.map deleted file mode 100755 index dba4778f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imageinsert.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imageinsert.min.js","sources":["../src/imageinsert.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 * Tiny media plugin image insertion class for Moodle.\n *\n * @module tiny_mediacms/imageinsert\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport Dropzone from 'core/dropzone';\nimport uploadFile from 'editor_tiny/uploader';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getStrings} from 'core/str';\nimport {component} from \"./common\";\nimport {getFilePicker} from 'editor_tiny/options';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {ImageDetails} from 'tiny_mediacms/imagedetails';\nimport {\n showElements,\n hideElements,\n bodyImageDetails,\n footerImageDetails,\n} from 'tiny_mediacms/imagehelpers';\n\nprefetchStrings('tiny_mediacms', [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n 'addfilesdrop',\n 'sizecustom_help',\n]);\n\nexport class ImageInsert {\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n }\n\n init = async function() {\n // Get the localization lang strings and turn them into object.\n const langStringKeys = [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n 'addfilesdrop',\n 'sizecustom_help',\n ];\n const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));\n\n // Convert array to object.\n this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));\n this.currentModal.setTitle(this.langStrings.insertimage);\n if (this.canShowDropZone) {\n const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);\n\n // Accepted types can be either a string or an array.\n let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types;\n if (Array.isArray(acceptedTypes)) {\n acceptedTypes = acceptedTypes.join(',');\n }\n\n const dropZone = new Dropzone(\n dropZoneEle,\n acceptedTypes,\n files => {\n this.handleUploadedFile(files);\n }\n );\n dropZone.setLabel(this.langStrings.addfilesdrop);\n dropZone.init();\n }\n await this.registerEventListeners();\n };\n\n /**\n * Enables or disables the URL-related buttons in the footer based on the current URL and input value.\n */\n toggleUrlButton() {\n const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url);\n const url = urlInput.value;\n const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl);\n addUrl.disabled = !(url !== \"\" && this.isValidUrl(url));\n }\n\n /**\n * Check if given string is a valid URL.\n *\n * @param {String} urlString URL the link will point to.\n * @returns {boolean} True is valid, otherwise false.\n */\n isValidUrl = urlString => {\n const urlPattern = new RegExp('^(https?:\\\\/\\\\/)?' + // Protocol.\n '((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|' + // Domain name.\n '((\\\\d{1,3}\\\\.){3}\\\\d{1,3})|localhost)' + // OR ip (v4) address, localhost.\n '(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*'); // Port and path.\n return !!urlPattern.test(urlString);\n };\n\n /**\n * Handles changes in the image URL input field and loads a preview of the image if the URL has changed.\n */\n urlChanged() {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n const input = this.root.querySelector(Selectors.IMAGE.elements.url);\n if (input.value && input.value !== this.currentUrl) {\n this.loadPreviewImage(input.value);\n }\n }\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n *\n * @param {string} url - The URL of the image to load and display.\n */\n loadPreviewImage = function(url) {\n this.startImageLoading();\n this.currentUrl = url;\n const image = new Image();\n image.src = url;\n image.addEventListener('error', () => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.currentUrl = \"\";\n this.stopImageLoading();\n });\n\n image.addEventListener('load', () => {\n let templateContext = {};\n templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};\n Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n this.currentUrl,\n image,\n );\n imagedetails.init();\n return;\n }).then(() => {\n this.stopImageLoading();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n });\n };\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n startImageLoading() {\n showElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n const elementsToHide = [\n Selectors.IMAGE.elements.insertImage,\n Selectors.IMAGE.elements.urlWarning,\n Selectors.IMAGE.elements.modalFooter,\n ];\n hideElements(elementsToHide, this.root);\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n stopImageLoading() {\n hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n const elementsToShow = [\n Selectors.IMAGE.elements.insertImage,\n Selectors.IMAGE.elements.modalFooter,\n ];\n showElements(elementsToShow, this.root);\n }\n\n filePickerCallback(params) {\n if (params.url) {\n this.loadPreviewImage(params.url);\n }\n }\n\n /**\n * Updates the content of the loader icon.\n *\n * @param {HTMLElement} root - The root element containing the loader icon.\n * @param {object} langStrings - An object containing language strings.\n * @param {number|null} progress - The progress percentage (optional).\n * @returns {void}\n */\n updateLoaderIcon = (root, langStrings, progress = null) => {\n const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');\n loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;\n };\n\n /**\n * Handles the uploaded file, initiates the upload process, and updates the UI during the upload.\n *\n * @param {FileList} files - The list of files to upload (usually from a file input field).\n * @returns {Promise} A promise that resolves when the file is uploaded and processed.\n */\n handleUploadedFile = async(files) => {\n try {\n this.startImageLoading();\n const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {\n this.updateLoaderIcon(this.root, this.langStrings, progress);\n });\n // Set the loader icon content to \"loading\" after the file upload completes.\n this.updateLoaderIcon(this.root, this.langStrings);\n this.filePickerCallback({url: fileURL});\n } catch (error) {\n // Handle the error.\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n }\n };\n\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl);\n if (addUrlEle) {\n this.urlChanged();\n }\n\n const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n const params = await displayFilepicker(this.editor, 'image');\n this.filePickerCallback(params);\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.toggleUrlButton();\n }\n });\n\n const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);\n if (fileInput) {\n fileInput.addEventListener('change', () => {\n this.handleUploadedFile(fileInput.files);\n });\n }\n }\n}"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","async","langStringKeys","langStringvalues","map","key","component","langStrings","Object","fromEntries","index","setTitle","this","insertimage","dropZoneEle","document","querySelector","Selectors","IMAGE","elements","dropzoneContainer","acceptedTypes","accepted_types","Array","isArray","join","dropZone","Dropzone","files","handleUploadedFile","setLabel","addfilesdrop","init","registerEventListeners","urlString","RegExp","test","url","startImageLoading","currentUrl","image","Image","src","addEventListener","urlWarning","innerHTML","imageurlrequired","stopImageLoading","templateContext","sizecustomhelpicon","text","sizecustom_help","Promise","all","then","ImageDetails","catch","error","window","console","log","progress","loaderIcon","loaderIconContainer","uploading","Math","round","loading","fileURL","name","updateLoaderIcon","filePickerCallback","undefined","toggleUrlButton","value","actions","addUrl","disabled","isValidUrl","urlChanged","input","loadPreviewImage","elementsToHide","insertImage","modalFooter","elementsToShow","params","e","target","closest","imageBrowser","preventDefault","fileInput"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js deleted file mode 100755 index fbd7b061..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/imagemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class ImageModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=ImageModal,_defineProperty(ImageModal,"TYPE","".concat(_common.component,"/imagemodal")),_defineProperty(ImageModal,"TEMPLATE","".concat(_common.component,"/insert_image_modal")),ImageModal.registerModalType(),_exports.default})); - -//# sourceMappingURL=imagemodal.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js.map deleted file mode 100755 index 47669ad8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/imagemodal.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imagemodal.min.js","sources":["../src/imagemodal.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 * Image Modal for Tiny.\n *\n * @module tiny_mediacms/imagemodal\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class ImageModal extends Modal {\n static TYPE = `${component}/imagemodal`;\n static TEMPLATE = `${component}/insert_image_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n\nImageModal.registerModalType();\n"],"names":["ImageModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component","registerModalType"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,kDADAV,gCAEIU,0CAoBzBV,WAAWW"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js deleted file mode 100755 index dd736626..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/manager",["exports","core/templates","core/str","core/modal","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,_modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_config=_interopRequireDefault(_config);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}async displayDialogue(){const modal=await _modal.default.create({large:!0,title:(0,_str.getString)("mediamanagerproperties","tiny_mediacms"),body:_templates.default.render("tiny_mediacms/mm2_iframe",{src:this.getIframeURL()}),removeOnClose:!0,show:!0});return modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal}selectFirstElement(){const iframe=document.getElementById("mm2-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))}getIframeURL(){const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/mediacms/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()}},_exports.default})); - -//# sourceMappingURL=manager.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js.map deleted file mode 100755 index 2fc47acf..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/manager.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"manager.min.js","sources":["../src/manager.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 * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_mediacms/manager\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport default class MediaManager {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n async displayDialogue() {\n const modal = await Modal.create({\n large: true,\n title: getString('mediamanagerproperties', 'tiny_mediacms'),\n body: Templates.render('tiny_mediacms/mm2_iframe', {\n src: this.getIframeURL()\n }),\n removeOnClose: true,\n show: true,\n });\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }\n\n // It will select the first element in the file manager.\n selectFirstElement() {\n const iframe = document.getElementById('mm2-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n }\n\n getIframeURL() {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n }\n}\n"],"names":["constructor","editor","data","area","params","itemid","fpoptions","image","modal","Modal","create","large","title","body","Templates","render","src","this","getIframeURL","removeOnClose","show","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","document","querySelector","style","cssText","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","toString"],"mappings":"mmDAmCIA,YAAYC,sCAHH,kCACF,WAGEA,OAASA,aACRC,MAAO,oBAAQD,aAChBE,KAAOD,KAAKE,OAAOD,UACnBA,KAAKE,OAASH,KAAKI,UAAUC,MAAMF,qCAIlCG,YAAcC,eAAMC,OAAO,CAC7BC,OAAO,EACPC,OAAO,kBAAU,yBAA0B,iBAC3CC,KAAMC,mBAAUC,OAAO,2BAA4B,CAC/CC,IAAKC,KAAKC,iBAEdC,eAAe,EACfC,MAAM,WAEVZ,MAAMa,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAGTC,SAASC,cAAc,aAAaC,MAAMC,2BACnCrB,MAIXiB,2BACUK,OAASJ,SAASK,eAAe,cACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcV,YACnCS,UAAUR,cAAc,gBAAiB,OACnCU,sBAAwBF,UAAUR,cAAc,gCAClDU,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,QAIXf,qBACUsB,IAAM,IAAIC,cAAOC,gBAAOC,yDAC9BH,IAAII,aAAaC,OAAO,YAAa5B,KAAKhB,OAAO6C,aAAaC,QACzD,MAAMC,OAAO/B,KAAKd,KACnBqC,IAAII,aAAaC,OAAOG,IAAK/B,KAAKd,KAAK6C,aAEpCR,IAAIS"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js deleted file mode 100755 index ed1f7e68..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js +++ /dev/null @@ -1,11 +0,0 @@ -define("tiny_mediacms/options",["exports","editor_tiny/options","./common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=_exports.getLti=_exports.getImagePermissions=_exports.getEmbedPermissions=_exports.getData=void 0; -/** - * Options helper for Tiny Media plugin. - * - * @module tiny_mediacms/options - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -const dataName=(0,_options.getPluginOptionName)(_common.pluginName,"data"),permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions"),ltiName=(0,_options.getPluginOptionName)(_common.pluginName,"lti");_exports.register=editor=>{const registerOption=editor.options.register;registerOption(permissionsName,{processor:"object",default:{image:{filepicker:!1}}}),registerOption(dataName,{processor:"object",default:{mediacmsApiUrl:"",mediacmsBaseUrl:"",mediacmsPageSize:12,autoConvertEnabled:!0,autoConvertBaseUrl:"",autoConvertOptions:{showTitle:!0,linkTitle:!0,showRelated:!0,showUserAvatar:!0}}}),registerOption(ltiName,{processor:"object",default:{toolId:0,courseId:0,contentItemUrl:""}})};const getPermissions=editor=>editor.options.get(permissionsName);_exports.getPermissions=getPermissions;_exports.getImagePermissions=editor=>getPermissions(editor).image;_exports.getEmbedPermissions=editor=>getPermissions(editor).embed;_exports.getData=editor=>editor.options.get(dataName);_exports.getLti=editor=>editor.options.get(ltiName)})); - -//# sourceMappingURL=options.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js.map deleted file mode 100755 index 545b140d..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/options.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"options.min.js","sources":["../src/options.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 * Options helper for Tiny Media plugin.\n *\n * @module tiny_mediacms/options\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getPluginOptionName} from 'editor_tiny/options';\nimport {pluginName} from './common';\n\nconst dataName = getPluginOptionName(pluginName, 'data');\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\nconst ltiName = getPluginOptionName(pluginName, 'lti');\n\n/**\n * Register the options for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n */\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(permissionsName, {\n processor: 'object',\n \"default\": {\n image: {\n filepicker: false,\n }\n },\n });\n\n registerOption(dataName, {\n processor: 'object',\n \"default\": {\n // MediaCMS video library configuration\n mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'\n mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'\n mediacmsPageSize: 12,\n // Auto-conversion settings\n autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs\n autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)\n autoConvertOptions: {\n // Default embed options for auto-converted videos\n showTitle: true,\n linkTitle: true,\n showRelated: true,\n showUserAvatar: true,\n },\n },\n });\n\n registerOption(ltiName, {\n processor: 'object',\n \"default\": {\n // LTI configuration for MediaCMS iframe library\n toolId: 0, // LTI external tool ID\n courseId: 0, // Current course ID\n contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking\n },\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getImagePermissions = (editor) => getPermissions(editor).image;\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getEmbedPermissions = (editor) => getPermissions(editor).embed;\n\n/**\n * Get the data configuration for the Media Manager.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getData = (editor) => editor.options.get(dataName);\n\n/**\n * Get the LTI configuration for the MediaCMS iframe library.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getLti = (editor) => editor.options.get(ltiName);\n"],"names":["dataName","pluginName","permissionsName","ltiName","editor","registerOption","options","register","processor","image","filepicker","mediacmsApiUrl","mediacmsBaseUrl","mediacmsPageSize","autoConvertEnabled","autoConvertBaseUrl","autoConvertOptions","showTitle","linkTitle","showRelated","showUserAvatar","toolId","courseId","contentItemUrl","getPermissions","get","embed"],"mappings":";;;;;;;;MA0BMA,UAAW,gCAAoBC,mBAAY,QAC3CC,iBAAkB,gCAAoBD,mBAAY,eAClDE,SAAU,gCAAoBF,mBAAY,yBAOvBG,eACfC,eAAiBD,OAAOE,QAAQC,SAEtCF,eAAeH,gBAAiB,CAC5BM,UAAW,iBACA,CACPC,MAAO,CACHC,YAAY,MAKxBL,eAAeL,SAAU,CACrBQ,UAAW,iBACA,CAEPG,eAAgB,GAChBC,gBAAiB,GACjBC,iBAAkB,GAElBC,oBAAoB,EACpBC,mBAAoB,GACpBC,mBAAoB,CAEhBC,WAAW,EACXC,WAAW,EACXC,aAAa,EACbC,gBAAgB,MAK5Bf,eAAeF,QAAS,CACpBK,UAAW,iBACA,CAEPa,OAAQ,EACRC,SAAU,EACVC,eAAgB,aAWfC,eAAkBpB,QAAWA,OAAOE,QAAQmB,IAAIvB,qFAQzBE,QAAWoB,eAAepB,QAAQK,mCAQlCL,QAAWoB,eAAepB,QAAQsB,uBAQ9CtB,QAAWA,OAAOE,QAAQmB,IAAIzB,0BAQ/BI,QAAWA,OAAOE,QAAQmB,IAAItB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js deleted file mode 100755 index 2aba4fa7..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./commands","./configuration","./options","./autoconvert"],(function(_exports,_loader,_utils,_common,Commands,Configuration,Options,_autoconvert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} -/** - * Tiny Media plugin for Moodle. - * - * @module tiny_mediacms/plugin - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Commands=_interopRequireWildcard(Commands),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);const isMediaCMSUrl=url=>{if(!url)return!1;try{const urlObj=new URL(url);return("/embed"===urlObj.pathname||"/view"===urlObj.pathname)&&urlObj.searchParams.has("m")}catch(e){return!1}},MEDIACMS_URL_PATTERN=/(^|>|\s)(https?:\/\/[^\s<>"]+\/(?:embed|view)\?m=[^\s<>"]+)(<|\s|$)/g;var _default=new Promise((async resolve=>{const[tinyMCE,setupCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),Commands.getSetup(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupCommands(editor),(0,_autoconvert.setupAutoConvert)(editor),editor.on("BeforeSetContent",(e=>{e.content&&"string"==typeof e.content&&(e.content=e.content.replace(MEDIACMS_URL_PATTERN,((match,before,url,after)=>isMediaCMSUrl(url)?before+(url=>{let embedUrl=url;try{const urlObj=new URL(url);"/view"===urlObj.pathname&&(urlObj.pathname="/embed",embedUrl=urlObj.toString())}catch(e){}return''})(url)+after:match)))})),editor.on("GetContent",(e=>{if("html"===e.format){const tempDiv=document.createElement("div");tempDiv.innerHTML=e.content,tempDiv.querySelectorAll(".tiny-mediacms-edit-btn").forEach((btn=>btn.remove())),tempDiv.querySelectorAll("iframe").forEach((iframe=>{const src=iframe.getAttribute("src");if(isMediaCMSUrl(src)){const wrapper=iframe.closest(".tiny-mediacms-iframe-wrapper")||iframe.closest(".tiny-iframe-responsive"),urlText=document.createTextNode(src),p=document.createElement("p");p.appendChild(urlText),wrapper?(wrapper.parentNode.insertBefore(p,wrapper),wrapper.remove()):(iframe.parentNode.insertBefore(p,iframe),iframe.remove())}})),tempDiv.querySelectorAll(".tiny-mediacms-iframe-wrapper").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),tempDiv.querySelectorAll(".tiny-iframe-responsive").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),e.content=tempDiv.innerHTML}})),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default})); - -//# sourceMappingURL=plugin.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js.map deleted file mode 100755 index 94524a51..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/plugin.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"plugin.min.js","sources":["../src/plugin.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 * Tiny Media plugin for Moodle.\n *\n * @module tiny_mediacms/plugin\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\n\nimport {component, pluginName} from './common';\nimport * as Commands from './commands';\nimport * as Configuration from './configuration';\nimport * as Options from './options';\nimport {setupAutoConvert} from './autoconvert';\n\n/**\n * Check if a URL is a MediaCMS URL (embed or view).\n *\n * @param {string} url - The URL to check\n * @returns {boolean} True if it's a MediaCMS URL\n */\nconst isMediaCMSUrl = (url) => {\n if (!url) {\n return false;\n }\n try {\n const urlObj = new URL(url);\n // Match both /embed and /view paths with ?m= parameter\n return (urlObj.pathname === '/embed' || urlObj.pathname === '/view') && urlObj.searchParams.has('m');\n } catch (e) {\n return false;\n }\n};\n\n/**\n * Convert a MediaCMS URL (embed or view) to an iframe HTML string.\n * If it's a view URL, it will be converted to embed URL.\n *\n * @param {string} url - The MediaCMS URL\n * @returns {string} The iframe HTML\n */\nconst mediaCMSUrlToIframe = (url) => {\n // Convert view URL to embed URL if needed\n let embedUrl = url;\n try {\n const urlObj = new URL(url);\n if (urlObj.pathname === '/view') {\n urlObj.pathname = '/embed';\n embedUrl = urlObj.toString();\n }\n } catch (e) {\n // Keep original URL if parsing fails\n }\n\n return ``;\n};\n\n/**\n * Regular expression to match standalone MediaCMS URLs in content.\n * Matches URLs that are on their own line or surrounded by whitespace/tags.\n * The URL must contain /embed?m= or /view?m= pattern.\n */\nconst MEDIACMS_URL_PATTERN = /(^|>|\\s)(https?:\\/\\/[^\\s<>\"]+\\/(?:embed|view)\\?m=[^\\s<>\"]+)(<|\\s|$)/g;\n\n// eslint-disable-next-line no-async-promise-executor\nexport default new Promise(async(resolve) => {\n const [\n tinyMCE,\n setupCommands,\n pluginMetadata,\n ] = await Promise.all([\n getTinyMCE(),\n Commands.getSetup(),\n getPluginMetadata(component, pluginName),\n ]);\n\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n\n // Setup the Commands (buttons, menu items, and so on).\n setupCommands(editor);\n\n // Setup auto-conversion of pasted MediaCMS URLs.\n setupAutoConvert(editor);\n\n // Convert MediaCMS URLs to iframes when content is loaded into the editor.\n // This handles content from the database that was saved as just URLs.\n editor.on('BeforeSetContent', (e) => {\n if (e.content && typeof e.content === 'string') {\n // Replace standalone MediaCMS URLs with iframes\n e.content = e.content.replace(MEDIACMS_URL_PATTERN, (match, before, url, after) => {\n // Verify it's a valid MediaCMS URL\n if (isMediaCMSUrl(url)) {\n return before + mediaCMSUrlToIframe(url) + after;\n }\n return match;\n });\n }\n });\n\n // Convert MediaCMS iframes back to just embed URLs when saving.\n // This stores only the URL in the database, not the full iframe HTML.\n editor.on('GetContent', (e) => {\n if (e.format === 'html') {\n // Create a temporary container to manipulate the HTML\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = e.content;\n\n // Remove edit buttons\n tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove());\n\n // Process all iframes - convert MediaCMS iframes to just URLs\n tempDiv.querySelectorAll('iframe').forEach(iframe => {\n const src = iframe.getAttribute('src');\n if (isMediaCMSUrl(src)) {\n // Check if iframe is inside a wrapper\n const wrapper = iframe.closest('.tiny-mediacms-iframe-wrapper') ||\n iframe.closest('.tiny-iframe-responsive');\n\n // Create a text node with just the URL\n const urlText = document.createTextNode(src);\n\n // Wrap in a paragraph for proper formatting\n const p = document.createElement('p');\n p.appendChild(urlText);\n\n if (wrapper) {\n // Replace the entire wrapper with the URL\n wrapper.parentNode.insertBefore(p, wrapper);\n wrapper.remove();\n } else {\n // Replace just the iframe with the URL\n iframe.parentNode.insertBefore(p, iframe);\n iframe.remove();\n }\n }\n });\n\n // Clean up any remaining wrappers that might not have had MediaCMS iframes\n tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n e.content = tempDiv.innerHTML;\n }\n });\n\n return pluginMetadata;\n });\n\n // Resolve the Media Plugin and include configuration.\n resolve([`${component}/plugin`, Configuration]);\n});\n"],"names":["isMediaCMSUrl","url","urlObj","URL","pathname","searchParams","has","e","MEDIACMS_URL_PATTERN","Promise","async","tinyMCE","setupCommands","pluginMetadata","all","Commands","getSetup","component","pluginName","PluginManager","add","editor","Options","register","on","content","replace","match","before","after","embedUrl","toString","mediaCMSUrlToIframe","format","tempDiv","document","createElement","innerHTML","querySelectorAll","forEach","btn","remove","iframe","src","getAttribute","wrapper","closest","urlText","createTextNode","p","appendChild","parentNode","insertBefore","querySelector","resolve","Configuration"],"mappings":";;;;;;;oOAqCMA,cAAiBC,UACdA,WACM,YAGDC,OAAS,IAAIC,IAAIF,YAEK,WAApBC,OAAOE,UAA6C,UAApBF,OAAOE,WAAyBF,OAAOG,aAAaC,IAAI,KAClG,MAAOC,UACE,IAkCTC,qBAAuB,oFAGd,IAAIC,SAAQC,MAAAA,gBAEnBC,QACAC,cACAC,sBACMJ,QAAQK,IAAI,EAClB,wBACAC,SAASC,YACT,4BAAkBC,kBAAWC,sBAGjCP,QAAQQ,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAGjBT,cAAcS,0CAGGA,QAIjBA,OAAOG,GAAG,oBAAqBjB,IACvBA,EAAEkB,SAAgC,iBAAdlB,EAAEkB,UAEtBlB,EAAEkB,QAAUlB,EAAEkB,QAAQC,QAAQlB,sBAAsB,CAACmB,MAAOC,OAAQ3B,IAAK4B,QAEjE7B,cAAcC,KACP2B,OAvDF3B,CAAAA,UAErB6B,SAAW7B,cAELC,OAAS,IAAIC,IAAIF,KACC,UAApBC,OAAOE,WACPF,OAAOE,SAAW,SAClB0B,SAAW5B,OAAO6B,YAExB,MAAOxB,UAIF,uBAAgBuB,eAAhB,qHA0C6BE,CAAoB/B,KAAO4B,MAExCF,YAOnBN,OAAOG,GAAG,cAAejB,OACJ,SAAbA,EAAE0B,OAAmB,OAEfC,QAAUC,SAASC,cAAc,OACvCF,QAAQG,UAAY9B,EAAEkB,QAGtBS,QAAQI,iBAAiB,2BAA2BC,SAAQC,KAAOA,IAAIC,WAGvEP,QAAQI,iBAAiB,UAAUC,SAAQG,eACjCC,IAAMD,OAAOE,aAAa,UAC5B5C,cAAc2C,KAAM,OAEdE,QAAUH,OAAOI,QAAQ,kCACfJ,OAAOI,QAAQ,2BAGzBC,QAAUZ,SAASa,eAAeL,KAGlCM,EAAId,SAASC,cAAc,KACjCa,EAAEC,YAAYH,SAEVF,SAEAA,QAAQM,WAAWC,aAAaH,EAAGJ,SACnCA,QAAQJ,WAGRC,OAAOS,WAAWC,aAAaH,EAAGP,QAClCA,OAAOD,cAMnBP,QAAQI,iBAAiB,iCAAiCC,SAAQM,gBACxDH,OAASG,QAAQQ,cAAc,UACjCX,QACAG,QAAQM,WAAWC,aAAaV,OAAQG,SAE5CA,QAAQJ,YAGZP,QAAQI,iBAAiB,2BAA2BC,SAAQM,gBAClDH,OAASG,QAAQQ,cAAc,UACjCX,QACAG,QAAQM,WAAWC,aAAaV,OAAQG,SAE5CA,QAAQJ,YAGZlC,EAAEkB,QAAUS,QAAQG,cAIrBxB,kBAIXyC,QAAQ,WAAIrC,6BAAoBsC"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js deleted file mode 100755 index 06c5ee30..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default})); - -//# sourceMappingURL=selectors.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js.map deleted file mode 100755 index e90972a5..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/selectors.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 * Tiny Media plugin helper function to build queryable data selectors.\n *\n * @module tiny_mediacms/selectors\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n IMAGE: {\n actions: {\n submit: '.tiny_imagecms_urlentrysubmit',\n imageBrowser: '.openimagecmsbrowser',\n addUrl: '.tiny_imagecms_addurl',\n deleteImage: '.tiny_imagecms_deleteicon',\n },\n elements: {\n form: 'form.tiny_imagecms_form',\n alignSettings: '.tiny_imagecms_button',\n alt: '.tiny_imagecms_altentry',\n altWarning: '.tiny_imagecms_altwarning',\n height: '.tiny_imagecms_heightentry',\n width: '.tiny_imagecms_widthentry',\n url: '.tiny_imagecms_urlentry',\n urlWarning: '.tiny_imagecms_urlwarning',\n size: '.tiny_imagecms_size',\n presentation: '.tiny_imagecms_presentation',\n constrain: '.tiny_imagecms_constrain',\n customStyle: '.tiny_imagecms_customstyle',\n preview: '.tiny_imagecms_preview',\n previewBox: '.tiny_imagecms_preview_box',\n loaderIcon: '.tiny_imagecms_loader',\n loaderIconContainer: '.tiny_imagecms_loader_container',\n insertImage: '.tiny_imagecms_insert_image',\n modalFooter: '.modal-footer',\n dropzoneContainer: '.tiny_imagecms_dropzone_container',\n fileInput: '#tiny_imagecms_fileinput',\n fileNameLabel: '.tiny_imagecms_filename',\n sizeOriginal: '.tiny_imagecms_sizeoriginal',\n sizeCustom: '.tiny_imagecms_sizecustom',\n properties: '.tiny_imagecms_properties',\n },\n styles: {\n responsive: 'img-fluid',\n },\n },\n EMBED: {\n actions: {\n submit: '.tiny_mediacms_submit',\n mediaBrowser: '.openmediacmsbrowser',\n },\n elements: {\n form: 'form.tiny_mediacms_form',\n source: '.tiny_mediacms_source',\n track: '.tiny_mediacms_track',\n mediaSource: '.tiny_mediacms_media_source',\n linkSource: '.tiny_mediacms_link_source',\n linkSize: '.tiny_mediacms_link_size',\n posterSource: '.tiny_mediacms_poster_source',\n posterSize: '.tiny_mediacms_poster_size',\n displayOptions: '.tiny_mediacms_display_options',\n name: '.tiny_mediacms_name_entry',\n title: '.tiny_mediacms_title_entry',\n url: '.tiny_mediacms_url_entry',\n width: '.tiny_mediacms_width_entry',\n height: '.tiny_mediacms_height_entry',\n trackSource: '.tiny_mediacms_track_source',\n trackKind: '.tiny_mediacms_track_kind_entry',\n trackLabel: '.tiny_mediacms_track_label_entry',\n trackLang: '.tiny_mediacms_track_lang_entry',\n trackDefault: '.tiny_mediacms_track_default',\n mediaControl: '.tiny_mediacms_controls',\n mediaAutoplay: '.tiny_mediacms_autoplay',\n mediaMute: '.tiny_mediacms_mute',\n mediaLoop: '.tiny_mediacms_loop',\n advancedSettings: '.tiny_mediacms_advancedsettings',\n linkTab: 'li[data-medium-type=\"link\"]',\n videoTab: 'li[data-medium-type=\"video\"]',\n audioTab: 'li[data-medium-type=\"audio\"]',\n linkPane: '.tab-pane[data-medium-type=\"link\"]',\n videoPane: '.tab-pane[data-medium-type=\"video\"]',\n audioPane: '.tab-pane[data-medium-type=\"audio\"]',\n trackSubtitlesTab: 'li[data-track-kind=\"subtitles\"]',\n trackCaptionsTab: 'li[data-track-kind=\"captions\"]',\n trackDescriptionsTab: 'li[data-track-kind=\"descriptions\"]',\n trackChaptersTab: 'li[data-track-kind=\"chapters\"]',\n trackMetadataTab: 'li[data-track-kind=\"metadata\"]',\n trackSubtitlesPane: '.tab-pane[data-track-kind=\"subtitles\"]',\n trackCaptionsPane: '.tab-pane[data-track-kind=\"captions\"]',\n trackDescriptionsPane: '.tab-pane[data-track-kind=\"descriptions\"]',\n trackChaptersPane: '.tab-pane[data-track-kind=\"chapters\"]',\n trackMetadataPane: '.tab-pane[data-track-kind=\"metadata\"]',\n },\n mediaTypes: {\n link: 'LINK',\n video: 'VIDEO',\n audio: 'AUDIO',\n },\n trackKinds: {\n subtitles: 'SUBTITLES',\n captions: 'CAPTIONS',\n descriptions: 'DESCRIPTIONS',\n chapters: 'CHAPTERS',\n metadata: 'METADATA',\n },\n },\n IFRAME: {\n actions: {\n remove: '[data-action=\"remove\"]',\n },\n elements: {\n form: 'form.tiny_iframecms_form',\n url: '.tiny_iframecms_url',\n urlWarning: '.tiny_iframecms_url_warning',\n showTitle: '.tiny_iframecms_showtitle',\n linkTitle: '.tiny_iframecms_linktitle',\n showRelated: '.tiny_iframecms_showrelated',\n showUserAvatar: '.tiny_iframecms_showuseravatar',\n responsive: '.tiny_iframecms_responsive',\n startAt: '.tiny_iframecms_startat',\n startAtEnabled: '.tiny_iframecms_startat_enabled',\n aspectRatio: '.tiny_iframecms_aspectratio',\n width: '.tiny_iframecms_width',\n height: '.tiny_iframecms_height',\n preview: '.tiny_iframecms_preview',\n previewContainer: '.tiny_iframecms_preview_container',\n // Tab elements\n tabs: '.tiny_iframecms_tabs',\n tabUrlBtn: '.tiny_iframecms_tab_url_btn',\n tabIframeLibraryBtn: '.tiny_iframecms_tab_iframe_library_btn',\n paneUrl: '.tiny_iframecms_pane_url',\n paneIframeLibrary: '.tiny_iframecms_pane_iframe_library',\n // Iframe library elements\n iframeLibraryContainer: '.tiny_iframecms_iframe_library_container',\n iframeLibraryPlaceholder:\n '.tiny_iframecms_iframe_library_placeholder',\n iframeLibraryLoading: '.tiny_iframecms_iframe_library_loading',\n iframeLibraryFrame: '.tiny_iframecms_iframe_library_frame',\n },\n aspectRatios: {\n '16:9': { width: 560, height: 315 },\n '4:3': { width: 560, height: 420 },\n '1:1': { width: 400, height: 400 },\n custom: null,\n },\n },\n};\n"],"names":["IMAGE","actions","submit","imageBrowser","addUrl","deleteImage","elements","form","alignSettings","alt","altWarning","height","width","url","urlWarning","size","presentation","constrain","customStyle","preview","previewBox","loaderIcon","loaderIconContainer","insertImage","modalFooter","dropzoneContainer","fileInput","fileNameLabel","sizeOriginal","sizeCustom","properties","styles","responsive","EMBED","mediaBrowser","source","track","mediaSource","linkSource","linkSize","posterSource","posterSize","displayOptions","name","title","trackSource","trackKind","trackLabel","trackLang","trackDefault","mediaControl","mediaAutoplay","mediaMute","mediaLoop","advancedSettings","linkTab","videoTab","audioTab","linkPane","videoPane","audioPane","trackSubtitlesTab","trackCaptionsTab","trackDescriptionsTab","trackChaptersTab","trackMetadataTab","trackSubtitlesPane","trackCaptionsPane","trackDescriptionsPane","trackChaptersPane","trackMetadataPane","mediaTypes","link","video","audio","trackKinds","subtitles","captions","descriptions","chapters","metadata","IFRAME","remove","showTitle","linkTitle","showRelated","showUserAvatar","startAt","startAtEnabled","aspectRatio","previewContainer","tabs","tabUrlBtn","tabIframeLibraryBtn","paneUrl","paneIframeLibrary","iframeLibraryContainer","iframeLibraryPlaceholder","iframeLibraryLoading","iframeLibraryFrame","aspectRatios","custom"],"mappings":"yKAuBe,CACXA,MAAO,CACHC,QAAS,CACLC,OAAQ,gCACRC,aAAc,uBACdC,OAAQ,wBACRC,YAAa,6BAEjBC,SAAU,CACNC,KAAM,0BACNC,cAAe,wBACfC,IAAK,0BACLC,WAAY,4BACZC,OAAQ,6BACRC,MAAO,4BACPC,IAAK,0BACLC,WAAY,4BACZC,KAAM,sBACNC,aAAc,8BACdC,UAAW,2BACXC,YAAa,6BACbC,QAAS,yBACTC,WAAY,6BACZC,WAAY,wBACZC,oBAAqB,kCACrBC,YAAa,8BACbC,YAAa,gBACbC,kBAAmB,oCACnBC,UAAW,2BACXC,cAAe,0BACfC,aAAc,8BACdC,WAAY,4BACZC,WAAY,6BAEhBC,OAAQ,CACJC,WAAY,cAGpBC,MAAO,CACHhC,QAAS,CACLC,OAAQ,wBACRgC,aAAc,wBAElB5B,SAAU,CACNC,KAAM,0BACN4B,OAAQ,wBACRC,MAAO,uBACPC,YAAa,8BACbC,WAAY,6BACZC,SAAU,2BACVC,aAAc,+BACdC,WAAY,6BACZC,eAAgB,iCAChBC,KAAM,4BACNC,MAAO,6BACP/B,IAAK,2BACLD,MAAO,6BACPD,OAAQ,8BACRkC,YAAa,8BACbC,UAAW,kCACXC,WAAY,mCACZC,UAAW,kCACXC,aAAc,+BACdC,aAAc,0BACdC,cAAe,0BACfC,UAAW,sBACXC,UAAW,sBACXC,iBAAkB,kCAClBC,QAAS,8BACTC,SAAU,+BACVC,SAAU,+BACVC,SAAU,qCACVC,UAAW,sCACXC,UAAW,sCACXC,kBAAmB,kCACnBC,iBAAkB,iCAClBC,qBAAsB,qCACtBC,iBAAkB,iCAClBC,iBAAkB,iCAClBC,mBAAoB,yCACpBC,kBAAmB,wCACnBC,sBAAuB,4CACvBC,kBAAmB,wCACnBC,kBAAmB,yCAEvBC,WAAY,CACRC,KAAM,OACNC,MAAO,QACPC,MAAO,SAEXC,WAAY,CACRC,UAAW,YACXC,SAAU,WACVC,aAAc,eACdC,SAAU,WACVC,SAAU,aAGlBC,OAAQ,CACJhF,QAAS,CACLiF,OAAQ,0BAEZ5E,SAAU,CACNC,KAAM,2BACNM,IAAK,sBACLC,WAAY,8BACZqE,UAAW,4BACXC,UAAW,4BACXC,YAAa,8BACbC,eAAgB,iCAChBtD,WAAY,6BACZuD,QAAS,0BACTC,eAAgB,kCAChBC,YAAa,8BACb7E,MAAO,wBACPD,OAAQ,yBACRQ,QAAS,0BACTuE,iBAAkB,oCAElBC,KAAM,uBACNC,UAAW,8BACXC,oBAAqB,yCACrBC,QAAS,2BACTC,kBAAmB,sCAEnBC,uBAAwB,2CACxBC,yBACI,6CACJC,qBAAsB,yCACtBC,mBAAoB,wCAExBC,aAAc,QACF,CAAExF,MAAO,IAAKD,OAAQ,WACvB,CAAEC,MAAO,IAAKD,OAAQ,WACtB,CAAEC,MAAO,IAAKD,OAAQ,KAC7B0F,OAAQ"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js b/lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js deleted file mode 100755 index 2e03a1ff..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/usedfiles",["exports","core/templates","core/config"],(function(_exports,Templates,_config){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,Templates=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj} -/** - * Tiny Media Manager usedfiles. - * - * @module tiny_mediacms/usedfiles - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */(Templates),_config=(obj=_config)&&obj.__esModule?obj:{default:obj};class UsedFileManager{constructor(files,userContext,itemId,elementId){this.files=files,this.userContext=userContext,this.itemId=itemId,this.elementId=elementId}getElementId(){return this.elementId}getUsedFiles(){const editor=window.parent.tinymce.EditorManager.get(this.getElementId());if(!editor)return window.console.error("Editor not found for ".concat(this.getElementId())),[];const content=editor.getContent(),baseUrl="".concat(_config.default.wwwroot,"/draftfile.php/").concat(this.userContext,"/user/draft/").concat(this.itemId,"/"),pattern=new RegExp("[\"']"+baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")+"(?.+?)[\\?\"']","gm");return[...content.matchAll(pattern)].map((match=>decodeURIComponent(match.groups.filename)))}findUnusedFiles(usedFiles){return Object.entries(this.files).filter((_ref=>{let[filename]=_ref;return!usedFiles.includes(filename)})).map((_ref2=>{let[filename]=_ref2;return filename}))}findMissingFiles(usedFiles){return usedFiles.filter((filename=>!this.files.hasOwnProperty(filename)))}updateFiles(){const form=document.querySelector("form"),usedFiles=this.getUsedFiles(),unusedFiles=this.findUnusedFiles(usedFiles),missingFiles=this.findMissingFiles(usedFiles);return form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox=>{unusedFiles.includes(checkbox.dataset.filename)||checkbox.closest(".fitem").remove()})),form.classList.toggle("has-missing-files",!!missingFiles.length),form.classList.toggle("has-unused-files",!!unusedFiles.length),Templates.renderForPromise("tiny_mediacms/missingfiles",{missingFiles:missingFiles}).then((_ref3=>{let{html:html,js:js}=_ref3;Templates.replaceNodeContents(form.querySelector(".missing-files"),html,js)}))}}_exports.init=(files,usercontext,itemid,elementid)=>{const manager=new UsedFileManager(files,usercontext,itemid,elementid);return manager.updateFiles(),manager}})); - -//# sourceMappingURL=usedfiles.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js.map b/lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js.map deleted file mode 100755 index 1af09ca8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/build/usedfiles.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"usedfiles.min.js","sources":["../src/usedfiles.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 * Tiny Media Manager usedfiles.\n *\n * @module tiny_mediacms/usedfiles\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Templates from 'core/templates';\nimport Config from 'core/config';\n\nclass UsedFileManager {\n constructor(files, userContext, itemId, elementId) {\n this.files = files;\n this.userContext = userContext;\n this.itemId = itemId;\n this.elementId = elementId;\n }\n\n getElementId() {\n return this.elementId;\n }\n\n getUsedFiles() {\n const editor = window.parent.tinymce.EditorManager.get(this.getElementId());\n if (!editor) {\n window.console.error(`Editor not found for ${this.getElementId()}`);\n return [];\n }\n const content = editor.getContent();\n const baseUrl = `${Config.wwwroot}/draftfile.php/${this.userContext}/user/draft/${this.itemId}/`;\n const pattern = new RegExp(\"[\\\"']\" + baseUrl.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&') + \"(?.+?)[\\\\?\\\"']\", 'gm');\n\n const usedFiles = [...content.matchAll(pattern)].map((match) => decodeURIComponent(match.groups.filename));\n\n return usedFiles;\n }\n\n // Return an array of unused files.\n findUnusedFiles(usedFiles) {\n return Object.entries(this.files)\n .filter(([filename]) => !usedFiles.includes(filename))\n .map(([filename]) => filename);\n }\n\n // Return an array of missing files.\n findMissingFiles(usedFiles) {\n return usedFiles.filter((filename) => !this.files.hasOwnProperty(filename));\n }\n\n updateFiles() {\n const form = document.querySelector('form');\n const usedFiles = this.getUsedFiles();\n const unusedFiles = this.findUnusedFiles(usedFiles);\n const missingFiles = this.findMissingFiles(usedFiles);\n\n form.querySelectorAll('input[type=checkbox][name^=\"deletefile\"]').forEach((checkbox) => {\n if (!unusedFiles.includes(checkbox.dataset.filename)) {\n checkbox.closest('.fitem').remove();\n }\n });\n\n form.classList.toggle('has-missing-files', !!missingFiles.length);\n form.classList.toggle('has-unused-files', !!unusedFiles.length);\n\n return Templates.renderForPromise('tiny_mediacms/missingfiles', {\n missingFiles,\n }).then(({html, js}) => {\n Templates.replaceNodeContents(form.querySelector('.missing-files'), html, js);\n return;\n });\n }\n}\n\nexport const init = (files, usercontext, itemid, elementid) => {\n const manager = new UsedFileManager(files, usercontext, itemid, elementid);\n manager.updateFiles();\n\n return manager;\n};\n"],"names":["UsedFileManager","constructor","files","userContext","itemId","elementId","getElementId","this","getUsedFiles","editor","window","parent","tinymce","EditorManager","get","console","error","content","getContent","baseUrl","Config","wwwroot","pattern","RegExp","replace","matchAll","map","match","decodeURIComponent","groups","filename","findUnusedFiles","usedFiles","Object","entries","filter","_ref","includes","_ref2","findMissingFiles","hasOwnProperty","updateFiles","form","document","querySelector","unusedFiles","missingFiles","querySelectorAll","forEach","checkbox","dataset","closest","remove","classList","toggle","length","Templates","renderForPromise","then","_ref3","html","js","replaceNodeContents","usercontext","itemid","elementid","manager"],"mappings":";;;;;;;+EA0BMA,gBACFC,YAAYC,MAAOC,YAAaC,OAAQC,gBAC/BH,MAAQA,WACRC,YAAcA,iBACdC,OAASA,YACTC,UAAYA,UAGrBC,sBACWC,KAAKF,UAGhBG,qBACUC,OAASC,OAAOC,OAAOC,QAAQC,cAAcC,IAAIP,KAAKD,oBACvDG,cACDC,OAAOK,QAAQC,qCAA8BT,KAAKD,iBAC3C,SAELW,QAAUR,OAAOS,aACjBC,kBAAaC,gBAAOC,kCAAyBd,KAAKJ,mCAA0BI,KAAKH,YACjFkB,QAAU,IAAIC,OAAO,QAAUJ,QAAQK,QAAQ,wBAAyB,QAAU,2BAA4B,YAElG,IAAIP,QAAQQ,SAASH,UAAUI,KAAKC,OAAUC,mBAAmBD,MAAME,OAAOC,YAMpGC,gBAAgBC,kBACLC,OAAOC,QAAQ3B,KAAKL,OACtBiC,QAAOC,WAAEN,sBAAeE,UAAUK,SAASP,aAC3CJ,KAAIY,YAAER,uBAAcA,YAI7BS,iBAAiBP,kBACNA,UAAUG,QAAQL,WAAcvB,KAAKL,MAAMsC,eAAeV,YAGrEW,oBACUC,KAAOC,SAASC,cAAc,QAC9BZ,UAAYzB,KAAKC,eACjBqC,YAActC,KAAKwB,gBAAgBC,WACnCc,aAAevC,KAAKgC,iBAAiBP,kBAE3CU,KAAKK,iBAAiB,4CAA4CC,SAASC,WAClEJ,YAAYR,SAASY,SAASC,QAAQpB,WACvCmB,SAASE,QAAQ,UAAUC,YAInCV,KAAKW,UAAUC,OAAO,sBAAuBR,aAAaS,QAC1Db,KAAKW,UAAUC,OAAO,qBAAsBT,YAAYU,QAEjDC,UAAUC,iBAAiB,6BAA8B,CAC5DX,aAAAA,eACDY,MAAKC,YAACC,KAACA,KAADC,GAAOA,UACZL,UAAUM,oBAAoBpB,KAAKE,cAAc,kBAAmBgB,KAAMC,sBAMlE,CAAC3D,MAAO6D,YAAaC,OAAQC,mBACvCC,QAAU,IAAIlE,gBAAgBE,MAAO6D,YAAaC,OAAQC,kBAChEC,QAAQzB,cAEDyB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/autoconvert.js b/lms-plugins/mediacms-moodle/tiny/amd/src/autoconvert.js deleted file mode 100755 index 7a53da4a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/autoconvert.js +++ /dev/null @@ -1,264 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny MediaCMS Auto-convert module. - * - * This module automatically converts pasted MediaCMS URLs into embedded videos. - * When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl), - * it will be automatically converted to an iframe embed. - * - * @module tiny_mediacms/autoconvert - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import {getData} from './options'; - -/** - * Regular expression patterns for MediaCMS URLs. - * Matches URLs like: - * - https://deic.mediacms.io/view?m=JpBd1Zvdl - * - https://example.mediacms.io/view?m=VIDEO_ID - * - Custom domains configured in the plugin - */ -const MEDIACMS_VIEW_URL_PATTERN = /^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/; - -/** - * Check if a string is a valid MediaCMS view URL. - * - * @param {string} text - The text to check - * @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL - */ -const parseMediaCMSUrl = (text) => { - if (!text || typeof text !== 'string') { - return null; - } - - const trimmed = text.trim(); - - // Check for MediaCMS view URL pattern - const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN); - if (match) { - return { - baseUrl: match[1], - videoId: match[2], - originalUrl: trimmed, - }; - } - - return null; -}; - -/** - * Check if the pasted URL's domain is allowed based on configuration. - * - * @param {Object} parsed - Parsed URL info - * @param {Object} config - Plugin configuration - * @returns {boolean} - True if the domain is allowed - */ -const isDomainAllowed = (parsed, config) => { - // If no specific base URL is configured, allow all MediaCMS domains - const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl; - if (!configuredBaseUrl) { - return true; - } - - // Check if the URL's base matches the configured base URL - try { - const configuredUrl = new URL(configuredBaseUrl); - const pastedUrl = new URL(parsed.baseUrl); - return configuredUrl.host === pastedUrl.host; - } catch (e) { - // If URL parsing fails, allow the conversion - return true; - } -}; - -/** - * Generate the iframe embed HTML for a MediaCMS video. - * - * @param {Object} parsed - Parsed URL info - * @param {Object} options - Embed options - * @returns {string} - The iframe HTML - */ -const generateEmbedHtml = (parsed, options = {}) => { - // Build the embed URL with default options - const embedUrl = new URL(`${parsed.baseUrl}/embed`); - embedUrl.searchParams.set('m', parsed.videoId); - - // Apply default options (all enabled by default for best user experience) - embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0'); - embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0'); - embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0'); - embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0'); - - // Generate responsive iframe HTML matching the template output format. - // Uses aspect-ratio CSS for responsive sizing (16:9 default). - // The wrapper will be added by editor for UI (edit button), then stripped on save. - const html = ``; - - return html; -}; - -/** - * Set up auto-conversion for the editor. - * This registers event handlers to detect pasted MediaCMS URLs. - * - * @param {TinyMCE} editor - The TinyMCE editor instance - */ -export const setupAutoConvert = (editor) => { - const config = getData(editor) || {}; - - // Check if auto-convert is enabled (default: true) - if (config.autoConvertEnabled === false) { - return; - } - - // Handle paste events - editor.on('paste', (e) => { - handlePasteEvent(editor, e, config); - }); - - // Also handle input events for drag-and-drop text or keyboard paste - editor.on('input', (e) => { - handleInputEvent(editor, e, config); - }); -}; - -/** - * Handle paste events to detect and convert MediaCMS URLs. - * - * @param {TinyMCE} editor - The TinyMCE editor instance - * @param {Event} e - The paste event - * @param {Object} config - Plugin configuration - */ -const handlePasteEvent = (editor, e, config) => { - // Get pasted text from clipboard - const clipboardData = e.clipboardData || window.clipboardData; - if (!clipboardData) { - return; - } - - // Try to get plain text first - const text = clipboardData.getData('text/plain') || clipboardData.getData('text'); - if (!text) { - return; - } - - // Check if it's a MediaCMS URL - const parsed = parseMediaCMSUrl(text); - if (!parsed) { - return; - } - - // Check if domain is allowed - if (!isDomainAllowed(parsed, config)) { - return; - } - - // Prevent default paste behavior - e.preventDefault(); - e.stopPropagation(); - - // Generate and insert the embed HTML - const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {}); - - // Use a slight delay to ensure the editor is ready - setTimeout(() => { - editor.insertContent(embedHtml); - // Move cursor after the inserted content - editor.selection.collapse(false); - }, 0); -}; - -/** - * Handle input events to catch URLs that might have been pasted without triggering paste event. - * This is a fallback for certain browsers/scenarios. - * - * @param {TinyMCE} editor - The TinyMCE editor instance - * @param {Event} e - The input event - * @param {Object} config - Plugin configuration - */ -const handleInputEvent = (editor, e, config) => { - // Only process inputType 'insertFromPaste' if paste event didn't catch it - if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') { - return; - } - - // Get the current node and check if it contains just a URL - const node = editor.selection.getNode(); - if (!node || node.nodeName !== 'P') { - return; - } - - // Check if the paragraph contains only a MediaCMS URL - const text = node.textContent || ''; - const parsed = parseMediaCMSUrl(text); - - if (!parsed || !isDomainAllowed(parsed, config)) { - return; - } - - // Don't convert if there's other content in the paragraph - const trimmedHtml = node.innerHTML.trim(); - if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) { - return; - } - - // Generate the embed HTML - const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {}); - - // Replace the paragraph content with the embed - // Use a slight delay to let the input event complete - setTimeout(() => { - // Re-check that the node still contains the URL (user might have typed more) - const currentText = node.textContent || ''; - const currentParsed = parseMediaCMSUrl(currentText); - - if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) { - // Select and replace the entire node - editor.selection.select(node); - editor.insertContent(embedHtml); - } - }, 100); -}; - -/** - * Check if a text is a MediaCMS URL (public helper). - * - * @param {string} text - The text to check - * @returns {boolean} - True if it's a MediaCMS URL - */ -export const isMediaCMSUrl = (text) => { - return parseMediaCMSUrl(text) !== null; -}; - -/** - * Convert a MediaCMS URL to embed HTML (public helper). - * - * @param {string} url - The MediaCMS URL - * @param {Object} options - Embed options - * @returns {string|null} - The embed HTML or null if not a valid URL - */ -export const convertToEmbed = (url, options = {}) => { - const parsed = parseMediaCMSUrl(url); - if (!parsed) { - return null; - } - return generateEmbedHtml(parsed, options); -}; diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/commands.js b/lms-plugins/mediacms-moodle/tiny/amd/src/commands.js deleted file mode 100755 index 27ad9b76..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/commands.js +++ /dev/null @@ -1,282 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media commands. - * - * @module tiny_mediacms/commands - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import {getStrings} from 'core/str'; -import { - component, - iframeButtonName, - iframeMenuItemName, - iframeIcon, -} from './common'; -import IframeEmbed from './iframeembed'; -import {getButtonImage} from 'editor_tiny/utils'; - -const isIframe = (node) => node.nodeName.toLowerCase() === 'iframe' || - (node.classList && node.classList.contains('tiny-iframe-responsive')) || - (node.classList && node.classList.contains('tiny-mediacms-iframe-wrapper')); - -/** - * Wrap iframes with overlay containers that allow hover detection. - * Since iframes capture mouse events, we add an invisible overlay on top - * that shows the edit button on hover. - * - * @param {TinyMCE} editor - The editor instance - * @param {Function} handleIframeAction - The action to perform when clicking the button - */ -const setupIframeOverlays = (editor, handleIframeAction) => { - /** - * Process all iframes in the editor and add overlay wrappers. - */ - const processIframes = () => { - const editorBody = editor.getBody(); - if (!editorBody) { - return; - } - - const iframes = editorBody.querySelectorAll('iframe'); - iframes.forEach((iframe) => { - // Skip if already wrapped - if (iframe.parentElement?.classList.contains('tiny-mediacms-iframe-wrapper')) { - return; - } - - // Skip TinyMCE internal iframes - if (iframe.hasAttribute('data-mce-object') || iframe.hasAttribute('data-mce-placeholder')) { - return; - } - - // Create wrapper div - const wrapper = editor.getDoc().createElement('div'); - wrapper.className = 'tiny-mediacms-iframe-wrapper'; - wrapper.setAttribute('contenteditable', 'false'); - - // Create edit button (positioned inside wrapper, over the iframe) - const editBtn = editor.getDoc().createElement('button'); - editBtn.className = 'tiny-mediacms-edit-btn'; - editBtn.setAttribute('type', 'button'); - editBtn.setAttribute('title', 'Edit video embed options'); - // Use clean inline SVG to avoid TinyMCE wrapper issues - editBtn.innerHTML = '' + - '' + - '' + - ''; - - // Wrap the iframe: insert wrapper, move iframe into it, add button - iframe.parentNode.insertBefore(wrapper, iframe); - wrapper.appendChild(iframe); - wrapper.appendChild(editBtn); - }); - }; - - /** - * Add CSS styles for hover effects to the editor's document. - */ - const addStyles = () => { - const editorDoc = editor.getDoc(); - if (!editorDoc) { - return; - } - - // Check if styles already added - if (editorDoc.getElementById('tiny-mediacms-overlay-styles')) { - return; - } - - const style = editorDoc.createElement('style'); - style.id = 'tiny-mediacms-overlay-styles'; - style.textContent = ` - .tiny-mediacms-iframe-wrapper { - display: inline-block; - position: relative; - line-height: 0; - vertical-align: top; - } - .tiny-mediacms-iframe-wrapper iframe { - display: block; - } - .tiny-mediacms-edit-btn { - position: absolute; - top: 48px; - left: 6px; - width: 28px; - height: 28px; - background: #ffffff; - border: none; - border-radius: 50%; - cursor: pointer; - z-index: 10; - padding: 0; - margin: 0; - box-shadow: 0 2px 6px rgba(0,0,0,0.35); - transition: transform 0.15s, box-shadow 0.15s; - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - } - .tiny-mediacms-edit-btn:hover { - transform: scale(1.15); - box-shadow: 0 3px 10px rgba(0,0,0,0.45); - } - .tiny-mediacms-edit-btn svg { - width: 18px !important; - height: 18px !important; - display: block !important; - } - `; - editorDoc.head.appendChild(style); - }; - - /** - * Handle click on the edit button. - * - * @param {Event} e - The click event - */ - const handleOverlayClick = (e) => { - const target = e.target; - - // Check if clicked on edit button or its child (svg/path) - const editBtn = target.closest('.tiny-mediacms-edit-btn'); - if (!editBtn) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - // Find the associated wrapper and iframe - const wrapper = editBtn.closest('.tiny-mediacms-iframe-wrapper'); - if (!wrapper) { - return; - } - - const iframe = wrapper.querySelector('iframe'); - if (!iframe) { - return; - } - - // Select the wrapper so TinyMCE knows which element is selected - editor.selection.select(wrapper); - - // Open the edit dialog - handleIframeAction(); - }; - - // Setup on editor init - editor.on('init', () => { - addStyles(); - processIframes(); - - // Handle clicks on the overlay - editor.getBody().addEventListener('click', handleOverlayClick); - }); - - // Re-process when content changes - editor.on('SetContent', () => { - processIframes(); - }); - - // Re-process when content is pasted - editor.on('PastePostProcess', () => { - setTimeout(processIframes, 100); - }); - - // Re-process after undo/redo - editor.on('Undo Redo', () => { - processIframes(); - }); - - // Re-process on any content change (covers modal updates) - editor.on('Change', () => { - setTimeout(processIframes, 50); - }); - - // Re-process when node changes (selection changes) - editor.on('NodeChange', () => { - processIframes(); - }); -}; - -const registerIframeCommand = (editor, iframeButtonText, iframeButtonImage) => { - const handleIframeAction = () => { - const iframeEmbed = new IframeEmbed(editor); - iframeEmbed.displayDialogue(); - }; - - // Register the iframe icon - editor.ui.registry.addIcon(iframeIcon, iframeButtonImage.html); - - // Register the Menu Button as a toggle. - // This means that when highlighted over an existing iframe element it will show as toggled on. - editor.ui.registry.addToggleButton(iframeButtonName, { - icon: iframeIcon, - tooltip: iframeButtonText, - onAction: handleIframeAction, - onSetup: api => { - return editor.selection.selectorChangedWithUnbind( - 'iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper', - api.setActive - ).unbind; - } - }); - - editor.ui.registry.addMenuItem(iframeMenuItemName, { - icon: iframeIcon, - text: iframeButtonText, - onAction: handleIframeAction, - }); - - editor.ui.registry.addContextToolbar(iframeButtonName, { - predicate: isIframe, - items: iframeButtonName, - position: 'node', - scope: 'node' - }); - - editor.ui.registry.addContextMenu(iframeButtonName, { - update: isIframe, - }); - - // Setup iframe overlays with edit button on hover - setupIframeOverlays(editor, handleIframeAction); -}; - -export const getSetup = async() => { - const [ - iframeButtonText, - ] = await getStrings([ - 'iframebuttontitle', - ].map((key) => ({key, component}))); - - const [ - iframeButtonImage, - ] = await Promise.all([ - getButtonImage('icon', component), - ]); - - // Note: The function returned here must be synchronous and cannot use promises. - // All promises must be resolved prior to returning the function. - return (editor) => { - registerIframeCommand(editor, iframeButtonText, iframeButtonImage); - }; -}; diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/common.js b/lms-plugins/mediacms-moodle/tiny/amd/src/common.js deleted file mode 100755 index cf20c3c0..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/common.js +++ /dev/null @@ -1,30 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media common values. - * - * @module tiny_mediacms/common - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -export default { - pluginName: 'tiny_mediacms/plugin', - component: 'tiny_mediacms', - iframeButtonName: 'tiny_mediacms_iframe', - iframeMenuItemName: 'tiny_mediacms_iframe', - iframeIcon: 'tiny_mediacms_iframe', -}; diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/configuration.js b/lms-plugins/mediacms-moodle/tiny/amd/src/configuration.js deleted file mode 100755 index 3eb2569a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/configuration.js +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media configuration. - * - * @module tiny_mediacms/configuration - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import { - iframeButtonName, - iframeMenuItemName, -} from './common'; -import { - addContextmenuItem, -} from 'editor_tiny/utils'; - -const configureMenu = (menu) => { - // Add the Iframe Embed to the insert menu. - menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`; - - return menu; -}; - -const configureToolbar = (toolbar) => { - // The toolbar contains an array of named sections. - // The Moodle integration ensures that there is a section called 'content'. - - return toolbar.map((section) => { - if (section.name === 'content') { - // Insert the iframe button at the start of it. - section.items.unshift(iframeButtonName); - } - - return section; - }); -}; - -export const configure = (instanceConfig) => { - // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars. - return { - contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName), - menu: configureMenu(instanceConfig.menu), - toolbar: configureToolbar(instanceConfig.toolbar), - }; -}; diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/embed.js b/lms-plugins/mediacms-moodle/tiny/amd/src/embed.js deleted file mode 100755 index c31563b3..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/embed.js +++ /dev/null @@ -1,467 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin Embed class for Moodle. - * - * @module tiny_mediacms/embed - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; -import { - getString, - getStrings, -} from 'core/str'; -import * as ModalEvents from 'core/modal_events'; -import {displayFilepicker} from 'editor_tiny/utils'; -import {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options'; -import {component} from "./common"; -import EmbedModal from './embedmodal'; -import Selectors from './selectors'; -import {getEmbedPermissions} from './options'; -import {getFilePicker} from 'editor_tiny/options'; - -export default class MediaEmbed { - editor = null; - canShowFilePicker = false; - canShowFilePickerPoster = false; - canShowFilePickerTrack = false; - - /** - * @property {Object} The names of the alignment options. - */ - helpStrings = null; - - /** - * @property {boolean} Indicate that the user is updating the media or not. - */ - isUpdating = false; - - /** - * @property {Object} The currently selected media. - */ - selectedMedia = null; - - constructor(editor) { - const permissions = getEmbedPermissions(editor); - - // Indicates whether the file picker can be shown. - this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined'); - this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined'); - this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined'); - - this.editor = editor; - } - - async getHelpStrings() { - if (!this.helpStrings) { - const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([ - 'addsource_help', - 'tracks_help', - 'subtitles_help', - 'captions_help', - 'descriptions_help', - 'chapters_help', - 'metadata_help', - ].map((key) => ({ - key, - component, - }))); - - this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata}; - } - - return this.helpStrings; - } - - async getTemplateContext(data) { - const languages = this.prepareMoodleLang(); - - const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => { - data[`${key.toLowerCase()}helpicon`] = {text}; - }); - - return Object.assign({}, { - elementid: this.editor.getElement().id, - showfilepicker: this.canShowFilePicker, - showfilepickerposter: this.canShowFilePickerPoster, - showfilepickertrack: this.canShowFilePickerTrack, - langsinstalled: languages.installed, - langsavailable: languages.available, - link: true, - video: false, - audio: false, - isupdating: this.isUpdating, - }, data, helpIcons); - } - - async displayDialogue() { - this.selectedMedia = this.getSelectedMedia(); - const data = Object.assign({}, this.getCurrentEmbedData()); - this.isUpdating = Object.keys(data).length !== 0; - - this.currentModal = await EmbedModal.create({ - title: getString('createmedia', 'tiny_mediacms'), - templateContext: await this.getTemplateContext(data), - }); - - await this.registerEventListeners(this.currentModal); - } - - getCurrentEmbedData() { - const properties = this.getMediumProperties(); - if (!properties) { - return {}; - } - - const processedProperties = {}; - processedProperties[properties.type.toLowerCase()] = properties; - processedProperties.link = false; - - return processedProperties; - } - - getSelectedMedia() { - const mediaElm = this.editor.selection.getNode(); - - if (!mediaElm) { - return null; - } - - if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') { - return mediaElm; - } - - if (mediaElm.querySelector('video')) { - return mediaElm.querySelector('video'); - } - - if (mediaElm.querySelector('audio')) { - return mediaElm.querySelector('audio'); - } - - return null; - } - - getMediumProperties() { - const boolAttr = (elem, attr) => { - // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes. - // So in order to check if attr="true", we need to check if the attribute exists and if the value is empty or true. - return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === '')); - }; - - const tracks = { - subtitles: [], - captions: [], - descriptions: [], - chapters: [], - metadata: [] - }; - const sources = []; - - const medium = this.selectedMedia; - if (!medium) { - return null; - } - medium.querySelectorAll('track').forEach((track) => { - tracks[track.getAttribute('kind')].push({ - src: track.getAttribute('src'), - srclang: track.getAttribute('srclang'), - label: track.getAttribute('label'), - defaultTrack: boolAttr(track, 'default') - }); - }); - - medium.querySelectorAll('source').forEach((source) => { - sources.push(source.src); - }); - - return { - type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio, - sources, - poster: medium.getAttribute('poster'), - title: medium.getAttribute('title'), - width: medium.getAttribute('width'), - height: medium.getAttribute('height'), - autoplay: boolAttr(medium, 'autoplay'), - loop: boolAttr(medium, 'loop'), - muted: boolAttr(medium, 'muted'), - controls: boolAttr(medium, 'controls'), - tracks, - }; - } - - prepareMoodleLang() { - const moodleLangs = getMoodleLang(this.editor); - const currentLanguage = getCurrentLanguage(this.editor); - - const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({ - lang, - code, - "default": lang === currentLanguage, - })); - - const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({ - lang, - code, - "default": lang === currentLanguage, - })); - - return { - installed, - available, - }; - } - - getMoodleLangObj(subtitleLang) { - const {available} = getMoodleLang(this.editor); - - if (available[subtitleLang]) { - return { - lang: subtitleLang, - code: available[subtitleLang], - }; - } - - return null; - } - - filePickerCallback(params, element, fpType) { - if (params.url !== '') { - const tabPane = element.closest('.tab-pane'); - element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url; - - if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) { - tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file; - } - - if (fpType === 'subtitle') { - // If the file is subtitle file. We need to match the language and label for that file. - const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0]; - const langObj = this.getMoodleLangObj(subtitleLang); - if (langObj) { - const track = element.closest(Selectors.EMBED.elements.track); - track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim(); - track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code; - } - } - } - } - - addMediaSourceComponent(element, callback) { - const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource); - const clone = sourceElement.cloneNode(true); - - sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden'); - sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden'); - - sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling); - - if (callback) { - callback(clone); - } - } - - removeMediaSourceComponent(element) { - const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource); - sourceElement.remove(); - } - - addTrackComponent(element, callback) { - const trackElement = element.closest(Selectors.EMBED.elements.track); - const clone = trackElement.cloneNode(true); - - trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden'); - trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden'); - - trackElement.parentNode.insertBefore(clone, trackElement.nextSibling); - - if (callback) { - callback(clone); - } - } - - removeTrackComponent(element) { - const sourceElement = element.closest(Selectors.EMBED.elements.track); - sourceElement.remove(); - } - - getMediumTypeFromTabPane(tabPane) { - return tabPane.getAttribute('data-medium-type'); - } - - getTrackTypeFromTabPane(tabPane) { - return tabPane.getAttribute('data-track-kind'); - } - - getMediaHTML(form) { - const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active')); - const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']); - - return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent); - } - - getMediaHTMLLink(tab) { - const context = { - url: tab.querySelector(Selectors.EMBED.elements.url).value, - name: tab.querySelector(Selectors.EMBED.elements.name).value || false - }; - - return context.url ? Templates.renderForPromise('tiny_mediacms/embed_media_link', context) : ''; - } - - getMediaHTMLVideo(tab) { - const context = this.getContextForMediaHTML(tab); - context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false; - context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false; - context.poster = tab.querySelector( - `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}` - ).value || false; - - return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_video', context) : ''; - } - - getMediaHTMLAudio(tab) { - const context = this.getContextForMediaHTML(tab); - - return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_audio', context) : ''; - } - - getContextForMediaHTML(tab) { - const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({ - track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value, - kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')), - label: track.querySelector(Selectors.EMBED.elements.trackLabel).value || - track.querySelector(Selectors.EMBED.elements.trackLang).value, - srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value, - defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? "true" : null - })).filter((track) => !!track.track); - - const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' ' - + Selectors.EMBED.elements.url)) - .filter((source) => !!source.value) - .map((source) => source.value); - - return { - sources, - description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' ' - + Selectors.EMBED.elements.url).value || false, - tracks, - showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked, - autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked, - muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked, - loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked, - title: tab.querySelector(Selectors.EMBED.elements.title).value || false - }; - } - - getFilepickerTypeFromElement(element) { - if (element.closest(Selectors.EMBED.elements.posterSource)) { - return 'image'; - } - if (element.closest(Selectors.EMBED.elements.trackSource)) { - return 'subtitle'; - } - - return 'media'; - } - - async clickHandler(e) { - const element = e.target; - - const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser); - if (mediaBrowser) { - e.preventDefault(); - const fpType = this.getFilepickerTypeFromElement(element); - const params = await displayFilepicker(this.editor, fpType); - this.filePickerCallback(params, element, fpType); - } - - const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent'); - if (addComponentSourceAction) { - e.preventDefault(); - this.addMediaSourceComponent(element); - } - - const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent'); - if (removeComponentSourceAction) { - e.preventDefault(); - this.removeMediaSourceComponent(element); - } - - const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent'); - if (addComponentTrackAction) { - e.preventDefault(); - this.addTrackComponent(element); - } - - const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent'); - if (removeComponentTrackAction) { - e.preventDefault(); - this.removeTrackComponent(element); - } - - // Only allow one track per tab to be selected as "default". - const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault); - if (trackDefaultAction && trackDefaultAction.checked) { - const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane')); - - element.parentElement - .closest('.root.tab-content') - .querySelectorAll(Selectors.EMBED.elements.trackDefault) - .forEach((select) => { - if (select !== element && getKind(element) === getKind(select)) { - select.checked = false; - } - }); - } - } - - async handleDialogueSubmission(event, modal) { - const {html} = await this.getMediaHTML(modal.getRoot()[0]); - if (html) { - if (this.isUpdating) { - this.selectedMedia.outerHTML = html; - this.isUpdating = false; - } else { - this.editor.insertContent(html); - } - } - } - - async registerEventListeners(modal) { - await modal.getBody(); - const $root = modal.getRoot(); - const root = $root[0]; - if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) { - root.addEventListener('click', this.clickHandler.bind(this)); - } - - $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this)); - $root.on(ModalEvents.hidden, () => { - this.currentModal.destroy(); - }); - $root.on(ModalEvents.shown, () => { - root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => { - const defaultVal = dropdown.getAttribute('data-value'); - if (defaultVal) { - dropdown.value = defaultVal; - } - }); - }); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/embedmodal.js b/lms-plugins/mediacms-moodle/tiny/amd/src/embedmodal.js deleted file mode 100755 index 96d86853..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/embedmodal.js +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Embedded Media Management Modal for Tiny. - * - * @module tiny_mediacms/embedmodal - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Modal from 'core/modal'; -import {component} from './common'; - -export default class EmbedModal extends Modal { - static TYPE = `${component}/modal`; - static TEMPLATE = `${component}/embed_media_modal`; - - registerEventListeners() { - // Call the parent registration. - super.registerEventListeners(); - - // Register to close on save/cancel. - this.registerCloseOnSave(); - this.registerCloseOnCancel(); - } - - configure(modalConfig) { - modalConfig.large = true; - modalConfig.removeOnClose = true; - modalConfig.show = true; - - super.configure(modalConfig); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/iframeembed.js b/lms-plugins/mediacms-moodle/tiny/amd/src/iframeembed.js deleted file mode 100755 index 9f7b41c4..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/iframeembed.js +++ /dev/null @@ -1,1130 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media2 Iframe Embed class. - * - * @module tiny_mediacms/iframeembed - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; -import { getString } from 'core/str'; -import * as ModalEvents from 'core/modal_events'; -import { component } from './common'; -import IframeModal from './iframemodal'; -import Selectors from './selectors'; -import { getLti, getData } from './options'; - -export default class IframeEmbed { - editor = null; - currentModal = null; - isUpdating = false; - selectedIframe = null; - debounceTimer = null; - // Iframe library state - iframeLibraryLoaded = false; - selectedLibraryVideo = null; - iframeLibraryUrl = - 'https://temp.web357.com/mediacms/deic-mediacms-embed-videos.html'; - - constructor(editor) { - this.editor = editor; - } - - /** - * Parse input to extract video URL or iframe src. - * Handles: iframe embed code, view URL, or embed URL. - * - * @param {string} input - The user input (URL or embed code) - * @returns {Object|null} Parsed URL info or null if invalid - */ - parseInput(input) { - if (!input || !input.trim()) { - return null; - } - - input = input.trim(); - - // Check if it's an iframe embed code - const iframeMatch = input.match( - /]*src=["']([^"']+)["'][^>]*>/i, - ); - if (iframeMatch) { - return this.parseEmbedUrl(iframeMatch[1]); - } - - // Check if it's a URL - if (input.startsWith('http://') || input.startsWith('https://')) { - return this.parseVideoUrl(input); - } - - return null; - } - - /** - * Parse a video view URL and convert to embed format. - * - * @param {string} url - The video URL - * @returns {Object|null} Parsed info - */ - parseVideoUrl(url) { - try { - const urlObj = new URL(url); - const baseUrl = `${urlObj.protocol}//${urlObj.host}`; - - // MediaCMS view URL: /view?m=VIDEO_ID - if (urlObj.pathname === '/view' && urlObj.searchParams.has('m')) { - return { - baseUrl: baseUrl, - videoId: urlObj.searchParams.get('m'), - isEmbed: false, - }; - } - - // MediaCMS embed URL: /embed?m=VIDEO_ID&options - if (urlObj.pathname === '/embed' && urlObj.searchParams.has('m')) { - const tParam = urlObj.searchParams.get('t'); - return { - baseUrl: baseUrl, - videoId: urlObj.searchParams.get('m'), - isEmbed: true, - showTitle: urlObj.searchParams.get('showTitle') === '1', - linkTitle: urlObj.searchParams.get('linkTitle') === '1', - showRelated: urlObj.searchParams.get('showRelated') === '1', - showUserAvatar: - urlObj.searchParams.get('showUserAvatar') === '1', - startAt: tParam - ? this.secondsToTimeString(parseInt(tParam)) - : null, - }; - } - - // Generic URL - just use as-is - return { - baseUrl: baseUrl, - rawUrl: url, - isGeneric: true, - }; - } catch (e) { - return null; - } - } - - /** - * Parse an embed URL. - * - * @param {string} url - The embed URL - * @returns {Object|null} Parsed info - */ - parseEmbedUrl(url) { - return this.parseVideoUrl(url); - } - - /** - * Convert seconds to time string (M:SS format). - * - * @param {number} seconds - Time in seconds - * @returns {string} Time string - */ - secondsToTimeString(seconds) { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; - } - - /** - * Convert time string to seconds. - * - * @param {string} timeStr - Time string (M:SS or just seconds) - * @returns {number|null} Seconds or null if invalid - */ - timeStringToSeconds(timeStr) { - if (!timeStr || !timeStr.trim()) { - return null; - } - timeStr = timeStr.trim(); - - // Handle M:SS format - if (timeStr.includes(':')) { - const parts = timeStr.split(':'); - const mins = parseInt(parts[0]) || 0; - const secs = parseInt(parts[1]) || 0; - return mins * 60 + secs; - } - - // Handle plain seconds - const secs = parseInt(timeStr); - return isNaN(secs) ? null : secs; - } - - /** - * Build the embed URL with options. - * - * @param {Object} parsed - Parsed URL info - * @param {Object} options - Embed options - * @returns {string} The complete embed URL - */ - buildEmbedUrl(parsed, options) { - if (parsed.isGeneric) { - return parsed.rawUrl; - } - - const url = new URL(`${parsed.baseUrl}/embed`); - url.searchParams.set('m', parsed.videoId); - - // Always include all options with 1 or 0 - url.searchParams.set('showTitle', options.showTitle ? '1' : '0'); - url.searchParams.set('showRelated', options.showRelated ? '1' : '0'); - url.searchParams.set( - 'showUserAvatar', - options.showUserAvatar ? '1' : '0', - ); - url.searchParams.set('linkTitle', options.linkTitle ? '1' : '0'); - - // Add start time if enabled - if (options.startAtEnabled && options.startAt) { - const seconds = this.timeStringToSeconds(options.startAt); - if (seconds !== null && seconds > 0) { - url.searchParams.set('t', seconds.toString()); - } - } - - return url.toString(); - } - - /** - * Get the template context for the modal. - * - * @param {Object} data - Existing data for updating - * @returns {Object} Template context - */ - async getTemplateContext(data = {}) { - // Get admin settings for default checkbox values. - const editorData = getData(this.editor); - const autoConvertOptions = editorData?.autoConvertOptions || {}; - - // Use admin settings as defaults when creating new embed (not updating). - // When updating, use the values from the existing iframe. - const getDefault = (key, fallback = true) => { - if (this.isUpdating && data[key] !== undefined) { - return data[key]; - } - return autoConvertOptions[key] !== undefined - ? autoConvertOptions[key] - : fallback; - }; - - return { - elementid: this.editor.getElement().id, - isupdating: this.isUpdating, - url: data.url || '', - showTitle: getDefault('showTitle'), - linkTitle: getDefault('linkTitle'), - showRelated: getDefault('showRelated'), - showUserAvatar: getDefault('showUserAvatar'), - responsive: data.responsive !== false, - startAtEnabled: data.startAtEnabled || false, - startAt: data.startAt || '0:00', - width: data.width || 560, - height: data.height || 315, - is16_9: !data.aspectRatio || data.aspectRatio === '16:9', - is4_3: data.aspectRatio === '4:3', - is1_1: data.aspectRatio === '1:1', - isCustom: data.aspectRatio === 'custom', - }; - } - - /** - * Display the iframe embed dialog. - */ - async displayDialogue() { - this.selectedIframe = this.getSelectedIframe(); - const data = this.getCurrentIframeData(); - this.isUpdating = data !== null; - - // Reset iframe library state for new modal - this.iframeLibraryLoaded = false; - - this.currentModal = await IframeModal.create({ - title: getString('iframemodaltitle', component), - templateContext: await this.getTemplateContext(data || {}), - }); - - await this.registerEventListeners(this.currentModal); - } - - /** - * Get the currently selected iframe in the editor. - * - * @returns {HTMLElement|null} The iframe element or null - */ - getSelectedIframe() { - const node = this.editor.selection.getNode(); - - if (node.nodeName.toLowerCase() === 'iframe') { - return node; - } - - // Check if selection contains an iframe wrapper (including overlay wrapper) - const iframe = node.querySelector('iframe'); - if (iframe) { - return iframe; - } - - // Check if we're on the overlay or wrapper and need to find the iframe - const wrapper = - node.closest('.tiny-mediacms-iframe-wrapper') || - node.closest('.tiny-iframe-responsive'); - if (wrapper) { - return wrapper.querySelector('iframe'); - } - - return null; - } - - /** - * Get current iframe data for editing. - * - * @returns {Object|null} Current iframe data or null - */ - getCurrentIframeData() { - if (!this.selectedIframe) { - return null; - } - - const src = this.selectedIframe.getAttribute('src'); - const parsed = this.parseInput(src); - - // Check if responsive by looking at style - const style = this.selectedIframe.getAttribute('style') || ''; - const isResponsive = style.includes('aspect-ratio'); - - return { - url: src, - width: this.selectedIframe.getAttribute('width') || 560, - height: this.selectedIframe.getAttribute('height') || 315, - showTitle: parsed?.showTitle ?? true, - linkTitle: parsed?.linkTitle ?? true, - showRelated: parsed?.showRelated ?? true, - showUserAvatar: parsed?.showUserAvatar ?? true, - responsive: isResponsive, - startAtEnabled: parsed?.startAt !== null, - startAt: parsed?.startAt || '0:00', - }; - } - - /** - * Get form values from the modal. - * - * @param {HTMLElement} root - Modal root element - * @returns {Object} Form values - */ - getFormValues(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - return { - url: form.querySelector(Selectors.IFRAME.elements.url).value.trim(), - showTitle: form.querySelector(Selectors.IFRAME.elements.showTitle) - .checked, - linkTitle: form.querySelector(Selectors.IFRAME.elements.linkTitle) - .checked, - showRelated: form.querySelector( - Selectors.IFRAME.elements.showRelated, - ).checked, - showUserAvatar: form.querySelector( - Selectors.IFRAME.elements.showUserAvatar, - ).checked, - responsive: form.querySelector(Selectors.IFRAME.elements.responsive) - .checked, - startAtEnabled: form.querySelector( - Selectors.IFRAME.elements.startAtEnabled, - ).checked, - startAt: form - .querySelector(Selectors.IFRAME.elements.startAt) - .value.trim(), - aspectRatio: form.querySelector( - Selectors.IFRAME.elements.aspectRatio, - ).value, - width: - parseInt( - form.querySelector(Selectors.IFRAME.elements.width).value, - ) || 560, - height: - parseInt( - form.querySelector(Selectors.IFRAME.elements.height).value, - ) || 315, - }; - } - - /** - * Generate the iframe HTML. - * - * @param {Object} values - Form values - * @returns {Promise} Generated HTML - */ - async generateIframeHtml(values) { - const parsed = this.parseInput(values.url); - if (!parsed) { - return ''; - } - - const embedUrl = this.buildEmbedUrl(parsed, values); - - // Calculate aspect ratio values for CSS - const aspectRatioCalcs = { - '16:9': '16 / 9', - '4:3': '4 / 3', - '1:1': '1 / 1', - custom: `${values.width} / ${values.height}`, - }; - - const context = { - src: embedUrl, - width: values.width, - height: values.height, - responsive: values.responsive, - aspectRatioCalc: aspectRatioCalcs[values.aspectRatio] || '16 / 9', - aspectRatioValue: aspectRatioCalcs[values.aspectRatio] || '16 / 9', - }; - - const { html } = await Templates.renderForPromise( - 'tiny_mediacms/iframe_embed_output', - context, - ); - return html; - } - - /** - * Update the preview in the modal. - * - * @param {HTMLElement} root - Modal root element - * @param {boolean} updateUrlField - Whether to update the URL field with new embed options - */ - async updatePreview(root, updateUrlField = false) { - const values = this.getFormValues(root); - const previewContainer = root.querySelector( - Selectors.IFRAME.elements.preview, - ); - const urlWarning = root.querySelector( - Selectors.IFRAME.elements.urlWarning, - ); - - if (!values.url) { - previewContainer.innerHTML = - 'Enter a video URL to see preview'; - urlWarning.classList.add('d-none'); - return; - } - - const parsed = this.parseInput(values.url); - if (!parsed) { - previewContainer.innerHTML = - 'Invalid URL format'; - urlWarning.classList.remove('d-none'); - return; - } - - urlWarning.classList.add('d-none'); - const embedUrl = this.buildEmbedUrl(parsed, values); - - // Update the URL field if requested (when embed options change) - if (updateUrlField && !parsed.isGeneric) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - const urlInput = form.querySelector(Selectors.IFRAME.elements.url); - urlInput.value = embedUrl; - } - - // Show a scaled preview - const previewWidth = Math.min(values.width, 400); - const scale = previewWidth / values.width; - const previewHeight = Math.round(values.height * scale); - - previewContainer.innerHTML = ` - - `; - } - - /** - * Handle form input changes with debounce. - * - * @param {HTMLElement} root - Modal root element - * @param {boolean} updateUrlField - Whether to update the URL field - */ - handleInputChange(root, updateUrlField = false) { - clearTimeout(this.debounceTimer); - this.debounceTimer = setTimeout(() => { - this.updatePreview(root, updateUrlField); - }, 500); - } - - /** - * Handle aspect ratio change - update dimensions. - * - * @param {HTMLElement} root - Modal root element - */ - handleAspectRatioChange(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - const aspectRatio = form.querySelector( - Selectors.IFRAME.elements.aspectRatio, - ).value; - const dimensions = Selectors.IFRAME.aspectRatios[aspectRatio]; - - // Only update dimensions for predefined ratios, not 'custom' - if (dimensions && aspectRatio !== 'custom') { - form.querySelector(Selectors.IFRAME.elements.width).value = - dimensions.width; - form.querySelector(Selectors.IFRAME.elements.height).value = - dimensions.height; - } - - this.updatePreview(root); - } - - /** - * Handle dialog submission. - * - * @param {Object} modal - The modal instance - */ - async handleDialogueSubmission(modal) { - const root = modal.getRoot()[0]; - const values = this.getFormValues(root); - - if (!values.url) { - return; - } - - const html = await this.generateIframeHtml(values); - if (html) { - if (this.isUpdating && this.selectedIframe) { - // Replace existing iframe (including wrapper if present) - // Check for both old wrapper and new overlay wrapper - const wrapper = - this.selectedIframe.closest( - '.tiny-mediacms-iframe-wrapper', - ) || this.selectedIframe.closest('.tiny-iframe-responsive'); - if (wrapper) { - wrapper.outerHTML = html; - } else { - this.selectedIframe.outerHTML = html; - } - this.isUpdating = false; - // Fire change event to trigger overlay reprocessing - this.editor.fire('Change'); - } else { - this.editor.insertContent(html); - } - } - } - - /** - * Handle video removal from the editor. - * - * @param {Object} modal - The modal instance - */ - async handleRemove(modal) { - // Get confirmation string - const confirmMessage = await getString( - 'removeiframeconfirm', - component, - ); - - // Show confirmation dialog - // eslint-disable-next-line no-alert - if (!window.confirm(confirmMessage)) { - return; - } - - if (this.selectedIframe) { - // Remove the iframe (including wrapper if present) - const wrapper = - this.selectedIframe.closest('.tiny-mediacms-iframe-wrapper') || - this.selectedIframe.closest('.tiny-iframe-responsive'); - if (wrapper) { - wrapper.remove(); - } else { - this.selectedIframe.remove(); - } - } - - // Close the modal - this.isUpdating = false; - modal.hide(); - } - - /** - * Register event listeners for the modal. - * - * @param {Object} modal - The modal instance - */ - async registerEventListeners(modal) { - await modal.getBody(); - const $root = modal.getRoot(); - const root = $root[0]; - - // Input change handlers - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // URL input - form.querySelector(Selectors.IFRAME.elements.url).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - - // Embed option checkboxes - these should update the URL field when changed - [ - Selectors.IFRAME.elements.showTitle, - Selectors.IFRAME.elements.linkTitle, - Selectors.IFRAME.elements.showRelated, - Selectors.IFRAME.elements.showUserAvatar, - Selectors.IFRAME.elements.startAtEnabled, - ].forEach((selector) => { - form.querySelector(selector).addEventListener('change', () => - this.handleInputChange(root, true), // Update URL field when embed options change - ); - }); - - // Responsive checkbox - doesn't affect URL, only display - form.querySelector(Selectors.IFRAME.elements.responsive).addEventListener('change', () => - this.handleInputChange(root, false), - ); - - // Start at time input - should update URL field - form.querySelector(Selectors.IFRAME.elements.startAt).addEventListener( - 'input', - () => this.handleInputChange(root, true), - ); - - // Aspect ratio change - form.querySelector( - Selectors.IFRAME.elements.aspectRatio, - ).addEventListener('change', () => this.handleAspectRatioChange(root)); - - // Dimension inputs - form.querySelector(Selectors.IFRAME.elements.width).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - form.querySelector(Selectors.IFRAME.elements.height).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - - // Modal events - $root.on(ModalEvents.save, () => this.handleDialogueSubmission(modal)); - $root.on(ModalEvents.hidden, () => { - this.currentModal.destroy(); - }); - - // Remove button handler (only present when updating) - const removeBtn = root.querySelector(Selectors.IFRAME.actions.remove); - if (removeBtn) { - removeBtn.addEventListener('click', () => this.handleRemove(modal)); - } - - // Initial preview if we have a URL - const urlInput = form.querySelector(Selectors.IFRAME.elements.url); - if (urlInput.value) { - this.updatePreview(root); - } - - // Tab change handler - load iframe library when switching to iframe library tab - const iframeLibraryTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabIframeLibraryBtn, - ); - if (iframeLibraryTabBtn) { - iframeLibraryTabBtn.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - // Manually handle tab switching for Bootstrap 5 - this.switchToIframeLibraryTab(root); - - // Small delay to ensure tab pane is visible before loading iframe - setTimeout(() => this.handleIframeLibraryTabClick(root), 100); - }); - // Also handle Bootstrap tab events (if Bootstrap handles it) - iframeLibraryTabBtn.addEventListener('shown.bs.tab', () => - this.handleIframeLibraryTabClick(root), - ); - // jQuery Bootstrap 4 event - const $iframeLibraryTabBtn = window.jQuery - ? window.jQuery(iframeLibraryTabBtn) - : null; - if ($iframeLibraryTabBtn) { - $iframeLibraryTabBtn.on('shown.bs.tab', () => - this.handleIframeLibraryTabClick(root), - ); - } - } - - // Tab change handler for URL tab - const urlTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabUrlBtn, - ); - if (urlTabBtn) { - urlTabBtn.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - // Manually handle tab switching - this.switchToUrlTab(root); - }); - } - - // Iframe library event listeners - this.registerIframeLibraryEventListeners(root); - } - - /** - * Switch to the URL tab. - * - * @param {HTMLElement} root - Modal root element - */ - switchToUrlTab(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // Get tab buttons - const urlTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabUrlBtn, - ); - const iframeLibraryTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabIframeLibraryBtn, - ); - - // Get tab panes - const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl); - const iframeLibraryPane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - // Update tab button states - if (urlTabBtn) { - urlTabBtn.classList.add('active'); - urlTabBtn.setAttribute('aria-selected', 'true'); - } - if (iframeLibraryTabBtn) { - iframeLibraryTabBtn.classList.remove('active'); - iframeLibraryTabBtn.setAttribute('aria-selected', 'false'); - } - - // Update tab pane visibility - if (urlPane) { - urlPane.classList.add('show', 'active'); - } - if (iframeLibraryPane) { - iframeLibraryPane.classList.remove('show', 'active'); - } - } - - /** - * Switch to the iframe library tab. - * - * @param {HTMLElement} root - Modal root element - */ - switchToIframeLibraryTab(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // Get tab buttons - const urlTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabUrlBtn, - ); - const iframeLibraryTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabIframeLibraryBtn, - ); - - // Get tab panes - const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl); - const iframeLibraryPane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - // Update tab button states - if (urlTabBtn) { - urlTabBtn.classList.remove('active'); - urlTabBtn.setAttribute('aria-selected', 'false'); - } - if (iframeLibraryTabBtn) { - iframeLibraryTabBtn.classList.add('active'); - iframeLibraryTabBtn.setAttribute('aria-selected', 'true'); - } - - // Update tab pane visibility - if (urlPane) { - urlPane.classList.remove('show', 'active'); - } - if (iframeLibraryPane) { - iframeLibraryPane.classList.add('show', 'active'); - } - } - - /** - * Register event listeners for the iframe library. - * - * @param {HTMLElement} root - Modal root element - */ - registerIframeLibraryEventListeners(root) { - // Listen for messages from the iframe (for video selection) - window.addEventListener('message', (event) => { - this.handleIframeLibraryMessage(root, event); - }); - } - - /** - * Handle iframe library tab click - always refetch content (no caching). - * - * @param {HTMLElement} root - Modal root element - */ - handleIframeLibraryTabClick(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - const iframeEl = pane - ? pane.querySelector(Selectors.IFRAME.elements.iframeLibraryFrame) - : null; - - // eslint-disable-next-line no-console - console.log( - 'handleIframeLibraryTabClick called, iframeLibraryLoaded:', - this.iframeLibraryLoaded, - 'iframe src:', - iframeEl ? iframeEl.src : 'no iframe', - 'pane:', - pane, - ); - - // Always refetch content when tab is clicked (no caching) - // Reset the loaded state to ensure fresh content is fetched - this.iframeLibraryLoaded = false; - this.loadIframeLibrary(root); - } - - /** - * Load the iframe library using LTI flow or fallback to static URL. - * - * @param {HTMLElement} root - Modal root element - */ - loadIframeLibrary(root) { - const ltiConfig = getLti(this.editor); - - // eslint-disable-next-line no-console - console.log('loadIframeLibrary called, LTI config:', ltiConfig); - - // Check if LTI is configured with a content item URL - if (ltiConfig?.contentItemUrl) { - this.loadIframeLibraryViaLti(root, ltiConfig); - } else { - // Fallback to static URL if LTI not configured - this.loadIframeLibraryStatic(root); - } - } - - /** - * Load the iframe library via LTI Deep Linking (Content Item Selection). - * Sets the iframe src to contentitem.php which initiates the LTI Deep Linking flow. - * This sends an LtiDeepLinkingRequest message, which will redirect to the - * tool's content selection interface (e.g., /lti/select-media/). - * - * @param {HTMLElement} root - Modal root element - * @param {Object} ltiConfig - LTI configuration - */ - loadIframeLibraryViaLti(root, ltiConfig) { - // eslint-disable-next-line no-console - console.log('loadIframeLibraryViaLti called, config:', ltiConfig); - - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - if (!pane) { - // eslint-disable-next-line no-console - console.log('paneIframeLibrary not found!'); - return; - } - - const placeholderEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryPlaceholder, - ); - const loadingEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryLoading, - ); - const iframeEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryFrame, - ); - - if (!iframeEl) { - // eslint-disable-next-line no-console - console.log('iframeEl not found!'); - return; - } - - // Hide placeholder, show loading state - if (placeholderEl) { - placeholderEl.classList.add('d-none'); - } - if (loadingEl) { - loadingEl.classList.remove('d-none'); - } - iframeEl.classList.add('d-none'); - - // Set up load listener - note: this may fire multiple times during LTI redirects - const loadHandler = () => { - // eslint-disable-next-line no-console - console.log('LTI iframe loaded'); - this.handleIframeLibraryLoad(root); - }; - iframeEl.addEventListener('load', loadHandler); - - // Set the iframe src to the content item URL - // This initiates the LTI Deep Linking flow: - // 1. contentitem.php initiates OIDC login - // 2. LTI provider authenticates - // 3. Moodle sends LtiDeepLinkingRequest - // 4. Tool provider shows content selection interface (e.g., /lti/select-media/) - // eslint-disable-next-line no-console - console.log( - 'Setting iframe src to LTI content item URL:', - ltiConfig.contentItemUrl, - ); - iframeEl.src = ltiConfig.contentItemUrl; - } - - /** - * Load the iframe library using static URL (fallback). - * - * @param {HTMLElement} root - Modal root element - */ - loadIframeLibraryStatic(root) { - // eslint-disable-next-line no-console - console.log( - 'loadIframeLibraryStatic called, URL:', - this.iframeLibraryUrl, - ); - - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - if (!pane) { - // eslint-disable-next-line no-console - console.log('paneIframeLibrary not found!'); - return; - } - - const placeholderEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryPlaceholder, - ); - const loadingEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryLoading, - ); - const iframeEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryFrame, - ); - - // eslint-disable-next-line no-console - console.log('Elements found:', { placeholderEl, loadingEl, iframeEl }); - - if (!iframeEl) { - // eslint-disable-next-line no-console - console.log('iframeEl not found!'); - return; - } - - // Hide placeholder, show loading state - if (placeholderEl) { - placeholderEl.classList.add('d-none'); - } - if (loadingEl) { - loadingEl.classList.remove('d-none'); - } - iframeEl.classList.add('d-none'); - - // Set up load listener before setting src - const loadHandler = () => { - // eslint-disable-next-line no-console - console.log('iframe loaded, src:', iframeEl.src); - // Only handle if the src matches our target URL - if (iframeEl.src === this.iframeLibraryUrl) { - this.handleIframeLibraryLoad(root); - // Remove the listener after successful load - iframeEl.removeEventListener('load', loadHandler); - } - }; - iframeEl.addEventListener('load', loadHandler); - - // Set the iframe source - iframeEl.src = this.iframeLibraryUrl; - // eslint-disable-next-line no-console - console.log('iframe src set to:', iframeEl.src); - } - - /** - * Handle iframe library load event. - * - * @param {HTMLElement} root - Modal root element - */ - handleIframeLibraryLoad(root) { - // eslint-disable-next-line no-console - console.log('handleIframeLibraryLoad called'); - - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - if (!pane) { - return; - } - - const placeholderEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryPlaceholder, - ); - const loadingEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryLoading, - ); - const iframeEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryFrame, - ); - - // Hide placeholder and loading, show iframe - if (placeholderEl) { - placeholderEl.classList.add('d-none'); - } - if (loadingEl) { - loadingEl.classList.add('d-none'); - } - if (iframeEl) { - iframeEl.classList.remove('d-none'); - } - - this.iframeLibraryLoaded = true; - } - - /** - * Handle messages from the iframe library (for video selection). - * Supports both custom videoSelected messages and LTI Deep Linking responses. - * - * @param {HTMLElement} root - Modal root element - * @param {MessageEvent} event - The message event - */ - handleIframeLibraryMessage(root, event) { - // eslint-disable-next-line no-console - console.log( - 'handleIframeLibraryMessage received:', - event.data, - 'from origin:', - event.origin, - ); - - const data = event.data; - - if (!data) { - return; - } - - // Handle custom videoSelected message format (from static iframe or custom MediaCMS implementation) - if (data.type === 'videoSelected' && data.embedUrl) { - // eslint-disable-next-line no-console - console.log( - 'Video selected (videoSelected):', - data.embedUrl, - 'videoId:', - data.videoId, - ); - this.selectIframeLibraryVideo(root, data.embedUrl, data.videoId); - return; - } - - // Handle LTI Deep Linking response format - if ( - data.type === 'ltiDeepLinkingResponse' || - data.messageType === 'LtiDeepLinkingResponse' - ) { - // eslint-disable-next-line no-console - console.log('LTI Deep Linking response received:', data); - const contentItems = data.content_items || data.contentItems || []; - if (contentItems.length > 0) { - const item = contentItems[0]; - // Extract embed URL from the content item - const embedUrl = - item.url || item.embed_url || item.embedUrl || ''; - const videoId = item.id || item.mediaId || ''; - if (embedUrl) { - // eslint-disable-next-line no-console - console.log( - 'Video selected (LTI):', - embedUrl, - 'videoId:', - videoId, - ); - this.selectIframeLibraryVideo(root, embedUrl, videoId); - } - } - return; - } - - // Handle MediaCMS specific message format (if different from above) - if (data.action === 'selectMedia' || data.action === 'mediaSelected') { - const embedUrl = data.embedUrl || data.url || ''; - const videoId = data.mediaId || data.videoId || data.id || ''; - if (embedUrl) { - // eslint-disable-next-line no-console - console.log( - 'Video selected (mediaSelected):', - embedUrl, - 'videoId:', - videoId, - ); - this.selectIframeLibraryVideo(root, embedUrl, videoId); - } - return; - } - } - - /** - * Select a video from the iframe library and populate the URL field. - * - * @param {HTMLElement} root - Modal root element - * @param {string} embedUrl - The embed URL for the video - * @param {string} videoId - The video ID - */ - selectIframeLibraryVideo(root, embedUrl, videoId) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // Populate the URL field - const urlInput = form.querySelector(Selectors.IFRAME.elements.url); - urlInput.value = embedUrl; - - // Switch to the URL tab using our method - this.switchToUrlTab(root); - - // Update the preview - this.updatePreview(root); - - // Store the selected video - this.selectedLibraryVideo = { embedUrl, videoId }; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/iframemodal.js b/lms-plugins/mediacms-moodle/tiny/amd/src/iframemodal.js deleted file mode 100755 index f0fc1486..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/iframemodal.js +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Iframe Embed Modal for Tiny Media2. - * - * @module tiny_mediacms/iframemodal - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Modal from 'core/modal'; -import {component} from './common'; - -export default class IframeModal extends Modal { - static TYPE = `${component}/iframemodal`; - static TEMPLATE = `${component}/iframe_embed_modal`; - - registerEventListeners() { - // Call the parent registration. - super.registerEventListeners(); - - // Register to close on save/cancel. - this.registerCloseOnSave(); - this.registerCloseOnCancel(); - } - - configure(modalConfig) { - modalConfig.large = true; - modalConfig.removeOnClose = true; - modalConfig.show = true; - - super.configure(modalConfig); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/image.js b/lms-plugins/mediacms-moodle/tiny/amd/src/image.js deleted file mode 100755 index e3519d4c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/image.js +++ /dev/null @@ -1,273 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin Image class for Moodle. - * - * @module tiny_mediacms/image - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Selectors from './selectors'; -import ImageModal from './imagemodal'; -import {getImagePermissions} from './options'; -import {getFilePicker} from 'editor_tiny/options'; -import {ImageInsert} from 'tiny_mediacms/imageinsert'; -import {ImageDetails} from 'tiny_mediacms/imagedetails'; -import {prefetchStrings} from 'core/prefetch'; -import {getString} from 'core/str'; -import { - bodyImageInsert, - footerImageInsert, - bodyImageDetails, - footerImageDetails, - showElements, - hideElements, - isPercentageValue, -} from 'tiny_mediacms/imagehelpers'; - -prefetchStrings('tiny_mediacms', [ - 'imageurlrequired', - 'sizecustom_help', -]); - -export default class MediaImage { - canShowFilePicker = false; - editor = null; - currentModal = null; - /** - * @type {HTMLElement|null} The root element. - */ - root = null; - - constructor(editor) { - const permissions = getImagePermissions(editor); - const options = getFilePicker(editor, 'image'); - // Indicates whether the file picker can be shown. - this.canShowFilePicker = permissions.filepicker - && (typeof options !== 'undefined') - && Object.keys(options.repositories).length > 0; - // Indicates whether the drop zone area can be shown. - this.canShowDropZone = (typeof options !== 'undefined') && - Object.values(options.repositories).some(repository => repository.type === 'upload'); - - this.editor = editor; - } - - async displayDialogue() { - const currentImageData = await this.getCurrentImageData(); - this.currentModal = await ImageModal.create(); - this.root = this.currentModal.getRoot()[0]; - if (currentImageData && currentImageData.src) { - this.loadPreviewImage(currentImageData.src); - } else { - this.loadInsertImage(); - } - } - - /** - * Displays an insert image view asynchronously. - * - * @returns {Promise} - */ - loadInsertImage = async function() { - const templateContext = { - elementid: this.editor.id, - showfilepicker: this.canShowFilePicker, - showdropzone: this.canShowDropZone, - }; - - Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)]) - .then(() => { - const imageinsert = new ImageInsert( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - ); - imageinsert.init(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }; - - async getTemplateContext(data) { - return { - elementid: this.editor.id, - showfilepicker: this.canShowFilePicker, - ...data, - }; - } - - async getCurrentImageData() { - const selectedImageProperties = this.getSelectedImageProperties(); - if (!selectedImageProperties) { - return {}; - } - - const properties = {...selectedImageProperties}; - - if (properties.src) { - properties.haspreview = true; - } - - if (!properties.alt) { - properties.presentation = true; - } - - return properties; - } - - /** - * Asynchronously loads and previews an image from the provided URL. - * - * @param {string} url - The URL of the image to load and preview. - * @returns {Promise} - */ - loadPreviewImage = async function(url) { - this.startImageLoading(); - const image = new Image(); - image.src = url; - image.addEventListener('error', async() => { - const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); - urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms'); - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - this.stopImageLoading(); - }); - - image.addEventListener('load', async() => { - const currentImageData = await this.getCurrentImageData(); - let templateContext = await this.getTemplateContext(currentImageData); - templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')}; - - Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)]) - .then(() => { - this.stopImageLoading(); - return; - }) - .then(() => { - const imagedetails = new ImageDetails( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - url, - image, - ); - imagedetails.init(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }); - }; - - getSelectedImageProperties() { - const image = this.getSelectedImage(); - if (!image) { - this.selectedImage = null; - return null; - } - - const properties = { - src: null, - alt: null, - width: null, - height: null, - presentation: false, - customStyle: '', // Custom CSS styles applied to the image. - }; - - const getImageHeight = (image) => { - if (!isPercentageValue(String(image.height))) { - return parseInt(image.height, 10); - } - - return image.height; - }; - - const getImageWidth = (image) => { - if (!isPercentageValue(String(image.width))) { - return parseInt(image.width, 10); - } - - return image.width; - }; - - // Get the current selection. - this.selectedImage = image; - - properties.customStyle = image.style.cssText; - - const width = getImageWidth(image); - if (width !== 0) { - properties.width = width; - } - - const height = getImageHeight(image); - if (height !== 0) { - properties.height = height; - } - - properties.src = image.getAttribute('src'); - properties.alt = image.getAttribute('alt') || ''; - properties.presentation = (image.getAttribute('role') === 'presentation'); - - return properties; - } - - getSelectedImage() { - const imgElm = this.editor.selection.getNode(); - const figureElm = this.editor.dom.getParent(imgElm, 'figure.image'); - if (figureElm) { - return this.editor.dom.select('img', figureElm)[0]; - } - - if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) { - return null; - } - return imgElm; - } - - isPlaceholderImage(imgElm) { - if (imgElm.nodeName.toUpperCase() !== 'IMG') { - return false; - } - - return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder')); - } - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - startImageLoading() { - showElements(Selectors.IMAGE.elements.loaderIcon, this.root); - hideElements(Selectors.IMAGE.elements.insertImage, this.root); - } - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - stopImageLoading() { - hideElements(Selectors.IMAGE.elements.loaderIcon, this.root); - showElements(Selectors.IMAGE.elements.insertImage, this.root); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/imagedetails.js b/lms-plugins/mediacms-moodle/tiny/amd/src/imagedetails.js deleted file mode 100755 index adf35793..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/imagedetails.js +++ /dev/null @@ -1,614 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny media plugin image details class for Moodle. - * - * @module tiny_mediacms/imagedetails - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Config from 'core/config'; -import ModalEvents from 'core/modal_events'; -import Notification from 'core/notification'; -import Pending from 'core/pending'; -import Selectors from './selectors'; -import Templates from 'core/templates'; -import {getString} from 'core/str'; -import {ImageInsert} from 'tiny_mediacms/imageinsert'; -import { - bodyImageInsert, - footerImageInsert, - showElements, - hideElements, - isPercentageValue, -} from 'tiny_mediacms/imagehelpers'; - -export class ImageDetails { - DEFAULTS = { - WIDTH: 160, - HEIGHT: 160, - }; - - rawImageDimensions = null; - - constructor( - root, - editor, - currentModal, - canShowFilePicker, - canShowDropZone, - currentUrl, - image, - ) { - this.root = root; - this.editor = editor; - this.currentModal = currentModal; - this.canShowFilePicker = canShowFilePicker; - this.canShowDropZone = canShowDropZone; - this.currentUrl = currentUrl; - this.image = image; - } - - init = function() { - this.currentModal.setTitle(getString('imagedetails', 'tiny_mediacms')); - this.imageTypeChecked(); - this.presentationChanged(); - this.storeImageDimensions(this.image); - this.setImageDimensions(); - this.registerEventListeners(); - }; - - /** - * Loads and displays a preview image based on the provided URL, and handles image loading events. - */ - loadInsertImage = async function() { - const templateContext = { - elementid: this.editor.id, - showfilepicker: this.canShowFilePicker, - showdropzone: this.canShowDropZone, - }; - - Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)]) - .then(() => { - const imageinsert = new ImageInsert( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - ); - imageinsert.init(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }; - - storeImageDimensions(image) { - // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG). - this.rawImageDimensions = { - width: image.width || this.DEFAULTS.WIDTH, - height: image.height || this.DEFAULTS.HEIGHT, - }; - - const getCurrentWidth = (element) => { - if (element.value === '') { - element.value = this.rawImageDimensions.width; - } - return element.value; - }; - - const getCurrentHeight = (element) => { - if (element.value === '') { - element.value = this.rawImageDimensions.height; - } - return element.value; - }; - - const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width); - const currentWidth = getCurrentWidth(widthInput); - - const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height); - const currentHeight = getCurrentHeight(heightInput); - - const preview = this.root.querySelector(Selectors.IMAGE.elements.preview); - preview.setAttribute('src', image.src); - preview.style.display = ''; - - // Ensure the checkbox always in unchecked status when an image loads at first. - const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain); - if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) { - constrain.checked = currentWidth === currentHeight; - } else if (image.width === 0 || image.height === 0) { - // If we don't have both dimensions of the image, we can't auto-size it, so disable control. - constrain.disabled = 'disabled'; - } else { - // This is the same as comparing to 3 decimal places. - const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width); - const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height); - constrain.checked = widthRatio === heightRatio; - } - - /** - * Sets the selected size option based on current width and height values. - * - * @param {number} currentWidth - The current width value. - * @param {number} currentHeight - The current height value. - */ - const setSelectedSize = (currentWidth, currentHeight) => { - if (this.rawImageDimensions.width === currentWidth && - this.rawImageDimensions.height === currentHeight - ) { - this.currentWidth = this.rawImageDimensions.width; - this.currentHeight = this.rawImageDimensions.height; - this.sizeChecked('original'); - } else { - this.currentWidth = currentWidth; - this.currentHeight = currentHeight; - this.sizeChecked('custom'); - } - }; - - setSelectedSize(Number(currentWidth), Number(currentHeight)); - } - - /** - * Handles the selection of image size options and updates the form inputs accordingly. - * - * @param {string} option - The selected image size option ("original" or "custom"). - */ - sizeChecked(option) { - const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height); - if (option === "original") { - this.sizeOriginalChecked(); - widthInput.value = this.rawImageDimensions.width; - heightInput.value = this.rawImageDimensions.height; - } else if (option === "custom") { - this.sizeCustomChecked(); - widthInput.value = this.currentWidth; - heightInput.value = this.currentHeight; - - // If the current size is equal to the original size, then check the Keep proportion checkbox. - if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) { - const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain); - constrainField.checked = true; - } - } - this.autoAdjustSize(); - } - - autoAdjustSize(forceHeight = false) { - // If we do not know the image size, do not do anything. - if (!this.rawImageDimensions) { - return; - } - - const widthField = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightField = this.root.querySelector(Selectors.IMAGE.elements.height); - - const normalizeFieldData = (fieldData) => { - fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value); - if (fieldData.isPercentageValue) { - fieldData.percentValue = parseInt(fieldData.field.value, 10); - fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue; - } else { - fieldData.pixelSize = parseInt(fieldData.field.value, 10); - fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100; - } - - return fieldData; - }; - - const getKeyField = () => { - const getValue = () => { - if (forceHeight) { - return { - field: heightField, - type: 'height', - }; - } else { - return { - field: widthField, - type: 'width', - }; - } - }; - - const currentValue = getValue(); - if (currentValue.field.value === '') { - currentValue.field.value = this.rawImageDimensions[currentValue.type]; - } - - return normalizeFieldData(currentValue); - }; - - const getRelativeField = () => { - if (forceHeight) { - return normalizeFieldData({ - field: widthField, - type: 'width', - }); - } else { - return normalizeFieldData({ - field: heightField, - type: 'height', - }); - } - }; - - // Now update with the new values. - const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain); - if (constrainField.checked) { - const keyField = getKeyField(); - const relativeField = getRelativeField(); - // We are keeping the image in proportion. - // Calculate the size for the relative field. - if (keyField.isPercentageValue) { - // In proportion, so the percentages are the same. - relativeField.field.value = keyField.field.value; - relativeField.percentValue = keyField.percentValue; - } else { - relativeField.pixelSize = Math.round( - keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type] - ); - relativeField.field.value = relativeField.pixelSize; - } - } - - // Store the custom width and height to reuse. - this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth; - this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight; - } - - /** - * Sets the dimensions of the image preview element based on user input and constraints. - */ - setImageDimensions = () => { - const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox); - const image = this.root.querySelector(Selectors.IMAGE.elements.preview); - const widthField = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightField = this.root.querySelector(Selectors.IMAGE.elements.height); - - const updateImageDimensions = () => { - // Get the latest dimensions of the preview box for responsiveness. - const boxWidth = imagePreviewBox.clientWidth; - const boxHeight = imagePreviewBox.clientHeight; - // Get the new width and height for the image. - const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight); - image.style.width = `${dimensions.width}px`; - image.style.height = `${dimensions.height}px`; - }; - // If the client size is zero, then get the new dimensions once the modal is shown. - if (imagePreviewBox.clientWidth === 0) { - // Call the shown event. - this.currentModal.getRoot().on(ModalEvents.shown, () => { - updateImageDimensions(); - }); - } else { - updateImageDimensions(); - } - }; - - /** - * Handles the selection of the "Original Size" option and updates the form elements accordingly. - */ - sizeOriginalChecked() { - this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true; - this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false; - hideElements(Selectors.IMAGE.elements.properties, this.root); - } - - /** - * Handles the selection of the "Custom Size" option and updates the form elements accordingly. - */ - sizeCustomChecked() { - this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false; - this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true; - showElements(Selectors.IMAGE.elements.properties, this.root); - } - - /** - * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly. - */ - presentationChanged() { - const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation); - const alt = this.root.querySelector(Selectors.IMAGE.elements.alt); - alt.disabled = presentation.checked; - - // Counting the image description characters. - this.handleKeyupCharacterCount(); - } - - /** - * This function checks whether an image URL is local (within the same website's domain) or external (from an external source). - * Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface. - * If the image is local then we only show it's filename. - * If the image is external then it will show full URL and it can be updated. - */ - imageTypeChecked() { - const regex = new RegExp(`${Config.wwwroot}`); - - // True if the URL is from external, otherwise false. - const isExternalUrl = regex.test(this.currentUrl) === false; - - // Hide the URL input. - hideElements(Selectors.IMAGE.elements.url, this.root); - - if (!isExternalUrl) { - // Split the URL by '/' to get an array of segments. - const segments = this.currentUrl.split('/'); - // Get the last segment, which should be the filename. - const filename = segments.pop().split('?')[0]; - // Show the file name. - this.setFilenameLabel(decodeURI(filename)); - } else { - - this.setFilenameLabel(decodeURI(this.currentUrl)); - } - } - - /** - * Set the string for the URL label element. - * - * @param {string} label - The label text to set. - */ - setFilenameLabel(label) { - const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel); - if (urlLabelEle) { - urlLabelEle.innerHTML = label; - urlLabelEle.setAttribute("title", label); - } - } - - toggleAriaInvalid(selectors, predicate) { - selectors.forEach((selector) => { - const elements = this.root.querySelectorAll(selector); - elements.forEach((element) => element.setAttribute('aria-invalid', predicate)); - }); - } - - hasErrorUrlField() { - const urlError = this.currentUrl === ''; - if (urlError) { - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - } else { - hideElements(Selectors.IMAGE.elements.urlWarning, this.root); - } - this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError); - - return urlError; - } - - hasErrorAltField() { - const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value; - const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked; - const imageAltError = alt === '' && !presentation; - if (imageAltError) { - showElements(Selectors.IMAGE.elements.altWarning, this.root); - } else { - hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root); - } - this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError); - - return imageAltError; - } - - updateWarning() { - const urlError = this.hasErrorUrlField(); - const imageAltError = this.hasErrorAltField(); - - return urlError || imageAltError; - } - - getImageContext() { - // Check if there are any accessibility issues. - if (this.updateWarning()) { - return null; - } - - const classList = []; - const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked; - const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked; - if (constrain || sizeOriginal) { - // If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class. - classList.push(Selectors.IMAGE.styles.responsive); - } else { - // Otherwise, remove it. - classList.pop(Selectors.IMAGE.styles.responsive); - } - - return { - url: this.currentUrl, - alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value, - width: this.root.querySelector(Selectors.IMAGE.elements.width).value, - height: this.root.querySelector(Selectors.IMAGE.elements.height).value, - presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked, - customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value, - classlist: classList.join(' '), - }; - } - - setImage() { - const pendingPromise = new Pending('tiny_mediacms:setImage'); - const url = this.currentUrl; - if (url === '') { - return; - } - - // Check if there are any accessibility issues. - if (this.updateWarning()) { - pendingPromise.resolve(); - return; - } - - // Check for invalid width or height. - const width = this.root.querySelector(Selectors.IMAGE.elements.width).value; - if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) { - this.root.querySelector(Selectors.IMAGE.elements.width).focus(); - pendingPromise.resolve(); - return; - } - - const height = this.root.querySelector(Selectors.IMAGE.elements.height).value; - if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) { - this.root.querySelector(Selectors.IMAGE.elements.height).focus(); - pendingPromise.resolve(); - return; - } - - Templates.render('tiny_mediacms/image', this.getImageContext()) - .then((html) => { - this.editor.insertContent(html); - this.currentModal.destroy(); - pendingPromise.resolve(); - - return html; - }) - .catch(error => { - window.console.log(error); - }); - } - - /** - * Deletes the image after confirming with the user and loads the insert image page. - */ - deleteImage() { - Notification.deleteCancelPromise( - getString('deleteimage', 'tiny_mediacms'), - getString('deleteimagewarning', 'tiny_mediacms'), - ).then(() => { - hideElements(Selectors.IMAGE.elements.altWarning, this.root); - // Removing the image in the preview will bring the user to the insert page. - this.loadInsertImage(); - return; - }).catch(error => { - window.console.log(error); - }); - } - - registerEventListeners() { - const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit); - submitAction.addEventListener('click', (e) => { - e.preventDefault(); - this.setImage(); - }); - - const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage); - deleteImageEle.addEventListener('click', () => { - this.deleteImage(); - }); - deleteImageEle.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - this.deleteImage(); - } - }); - - this.root.addEventListener('change', (e) => { - const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation); - if (presentationEle) { - this.presentationChanged(); - } - - const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain); - if (constrainEle) { - this.autoAdjustSize(); - } - - const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal); - if (sizeOriginalEle) { - this.sizeChecked('original'); - } - - const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom); - if (sizeCustomEle) { - this.sizeChecked('custom'); - } - }); - - this.root.addEventListener('blur', (e) => { - if (e.target.nodeType === Node.ELEMENT_NODE) { - - const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation); - if (presentationEle) { - this.presentationChanged(); - } - } - }, true); - - // Character count. - this.root.addEventListener('keyup', (e) => { - const altEle = e.target.closest(Selectors.IMAGE.elements.alt); - if (altEle) { - this.handleKeyupCharacterCount(); - } - }); - - this.root.addEventListener('input', (e) => { - const widthEle = e.target.closest(Selectors.IMAGE.elements.width); - if (widthEle) { - // Avoid empty value. - widthEle.value = widthEle.value === "" ? 0 : Number(widthEle.value); - this.autoAdjustSize(); - } - - const heightEle = e.target.closest(Selectors.IMAGE.elements.height); - if (heightEle) { - // Avoid empty value. - heightEle.value = heightEle.value === "" ? 0 : Number(heightEle.value); - this.autoAdjustSize(true); - } - }); - } - - handleKeyupCharacterCount() { - const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value; - const current = this.root.querySelector('#currentcount'); - current.innerHTML = alt.length; - } - - /** - * Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio. - * - * @param {number} squareWidth - The width of the square. - * @param {number} squareHeight - The height of the square. - * @param {number} boxWidth - The width of the box. - * @param {number} boxHeight - The height of the box. - * @returns {Object} An object with the new width and height of the square to fit in the box. - */ - fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => { - if (squareWidth < boxWidth && squareHeight < boxHeight) { - // If the square is smaller than the box, keep its dimensions. - return { - width: squareWidth, - height: squareHeight, - }; - } - // Calculate the scaling factor based on the minimum scaling required to fit in the box. - const widthScaleFactor = boxWidth / squareWidth; - const heightScaleFactor = boxHeight / squareHeight; - const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor); - // Scale the square's dimensions based on the aspect ratio and the minimum scaling factor. - const newWidth = squareWidth * minScaleFactor; - const newHeight = squareHeight * minScaleFactor; - return { - width: newWidth, - height: newHeight, - }; - }; -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/imagehelpers.js b/lms-plugins/mediacms-moodle/tiny/amd/src/imagehelpers.js deleted file mode 100755 index a59367b9..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/imagehelpers.js +++ /dev/null @@ -1,149 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny media plugin image helpers. - * - * @module tiny_mediacms/imagehelpers - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; - -/** - * Renders and inserts the body template for inserting an image into the modal. - * - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const bodyImageInsert = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Renders and inserts the footer template for inserting an image into the modal. - * - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const footerImageInsert = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert_footer', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Renders and inserts the body template for displaying image details in the modal. - * - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const bodyImageDetails = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Renders and inserts the footer template for displaying image details in the modal. - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const footerImageDetails = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details_footer', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Show the element(s). - * - * @param {string|string[]} elements - The CSS selector for the elements to toggle. - * @param {object} root - The CSS selector for the elements to toggle. - */ -export const showElements = (elements, root) => { - if (elements instanceof Array) { - elements.forEach((elementSelector) => { - const element = root.querySelector(elementSelector); - if (element) { - element.classList.remove('d-none'); - } - }); - } else { - const element = root.querySelector(elements); - if (element) { - element.classList.remove('d-none'); - } - } -}; - -/** - * Hide the element(s). - * - * @param {string|string[]} elements - The CSS selector for the elements to toggle. - * @param {object} root - The CSS selector for the elements to toggle. - */ -export const hideElements = (elements, root) => { - if (elements instanceof Array) { - elements.forEach((elementSelector) => { - const element = root.querySelector(elementSelector); - if (element) { - element.classList.add('d-none'); - } - }); - } else { - const element = root.querySelector(elements); - if (element) { - element.classList.add('d-none'); - } - } -}; - -/** - * Checks if the given value is a percentage value. - * - * @param {string} value - The value to check. - * @returns {boolean} True if the value is a percentage value, false otherwise. - */ -export const isPercentageValue = (value) => { - return value.match(/\d+%/); -}; diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/imageinsert.js b/lms-plugins/mediacms-moodle/tiny/amd/src/imageinsert.js deleted file mode 100755 index 5941bc0e..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/imageinsert.js +++ /dev/null @@ -1,282 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny media plugin image insertion class for Moodle. - * - * @module tiny_mediacms/imageinsert - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Selectors from './selectors'; -import Dropzone from 'core/dropzone'; -import uploadFile from 'editor_tiny/uploader'; -import {prefetchStrings} from 'core/prefetch'; -import {getStrings} from 'core/str'; -import {component} from "./common"; -import {getFilePicker} from 'editor_tiny/options'; -import {displayFilepicker} from 'editor_tiny/utils'; -import {ImageDetails} from 'tiny_mediacms/imagedetails'; -import { - showElements, - hideElements, - bodyImageDetails, - footerImageDetails, -} from 'tiny_mediacms/imagehelpers'; - -prefetchStrings('tiny_mediacms', [ - 'insertimage', - 'enterurl', - 'enterurlor', - 'imageurlrequired', - 'uploading', - 'loading', - 'addfilesdrop', - 'sizecustom_help', -]); - -export class ImageInsert { - - constructor( - root, - editor, - currentModal, - canShowFilePicker, - canShowDropZone, - ) { - this.root = root; - this.editor = editor; - this.currentModal = currentModal; - this.canShowFilePicker = canShowFilePicker; - this.canShowDropZone = canShowDropZone; - } - - init = async function() { - // Get the localization lang strings and turn them into object. - const langStringKeys = [ - 'insertimage', - 'enterurl', - 'enterurlor', - 'imageurlrequired', - 'uploading', - 'loading', - 'addfilesdrop', - 'sizecustom_help', - ]; - const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component}))); - - // Convert array to object. - this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]])); - this.currentModal.setTitle(this.langStrings.insertimage); - if (this.canShowDropZone) { - const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer); - - // Accepted types can be either a string or an array. - let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types; - if (Array.isArray(acceptedTypes)) { - acceptedTypes = acceptedTypes.join(','); - } - - const dropZone = new Dropzone( - dropZoneEle, - acceptedTypes, - files => { - this.handleUploadedFile(files); - } - ); - dropZone.setLabel(this.langStrings.addfilesdrop); - dropZone.init(); - } - await this.registerEventListeners(); - }; - - /** - * Enables or disables the URL-related buttons in the footer based on the current URL and input value. - */ - toggleUrlButton() { - const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url); - const url = urlInput.value; - const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl); - addUrl.disabled = !(url !== "" && this.isValidUrl(url)); - } - - /** - * Check if given string is a valid URL. - * - * @param {String} urlString URL the link will point to. - * @returns {boolean} True is valid, otherwise false. - */ - isValidUrl = urlString => { - const urlPattern = new RegExp('^(https?:\\/\\/)?' + // Protocol. - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name. - '((\\d{1,3}\\.){3}\\d{1,3})|localhost)' + // OR ip (v4) address, localhost. - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path. - return !!urlPattern.test(urlString); - }; - - /** - * Handles changes in the image URL input field and loads a preview of the image if the URL has changed. - */ - urlChanged() { - hideElements(Selectors.IMAGE.elements.urlWarning, this.root); - const input = this.root.querySelector(Selectors.IMAGE.elements.url); - if (input.value && input.value !== this.currentUrl) { - this.loadPreviewImage(input.value); - } - } - - /** - * Loads and displays a preview image based on the provided URL, and handles image loading events. - * - * @param {string} url - The URL of the image to load and display. - */ - loadPreviewImage = function(url) { - this.startImageLoading(); - this.currentUrl = url; - const image = new Image(); - image.src = url; - image.addEventListener('error', () => { - const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); - urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired; - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - this.currentUrl = ""; - this.stopImageLoading(); - }); - - image.addEventListener('load', () => { - let templateContext = {}; - templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help}; - Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)]) - .then(() => { - const imagedetails = new ImageDetails( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - this.currentUrl, - image, - ); - imagedetails.init(); - return; - }).then(() => { - this.stopImageLoading(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }); - }; - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - startImageLoading() { - showElements(Selectors.IMAGE.elements.loaderIcon, this.root); - const elementsToHide = [ - Selectors.IMAGE.elements.insertImage, - Selectors.IMAGE.elements.urlWarning, - Selectors.IMAGE.elements.modalFooter, - ]; - hideElements(elementsToHide, this.root); - } - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - stopImageLoading() { - hideElements(Selectors.IMAGE.elements.loaderIcon, this.root); - const elementsToShow = [ - Selectors.IMAGE.elements.insertImage, - Selectors.IMAGE.elements.modalFooter, - ]; - showElements(elementsToShow, this.root); - } - - filePickerCallback(params) { - if (params.url) { - this.loadPreviewImage(params.url); - } - } - - /** - * Updates the content of the loader icon. - * - * @param {HTMLElement} root - The root element containing the loader icon. - * @param {object} langStrings - An object containing language strings. - * @param {number|null} progress - The progress percentage (optional). - * @returns {void} - */ - updateLoaderIcon = (root, langStrings, progress = null) => { - const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div'); - loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading; - }; - - /** - * Handles the uploaded file, initiates the upload process, and updates the UI during the upload. - * - * @param {FileList} files - The list of files to upload (usually from a file input field). - * @returns {Promise} A promise that resolves when the file is uploaded and processed. - */ - handleUploadedFile = async(files) => { - try { - this.startImageLoading(); - const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => { - this.updateLoaderIcon(this.root, this.langStrings, progress); - }); - // Set the loader icon content to "loading" after the file upload completes. - this.updateLoaderIcon(this.root, this.langStrings); - this.filePickerCallback({url: fileURL}); - } catch (error) { - // Handle the error. - const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); - urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error; - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - this.stopImageLoading(); - } - }; - - registerEventListeners() { - this.root.addEventListener('click', async(e) => { - const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl); - if (addUrlEle) { - this.urlChanged(); - } - - const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser); - if (imageBrowserAction && this.canShowFilePicker) { - e.preventDefault(); - const params = await displayFilepicker(this.editor, 'image'); - this.filePickerCallback(params); - } - }); - - this.root.addEventListener('input', (e) => { - const urlEle = e.target.closest(Selectors.IMAGE.elements.url); - if (urlEle) { - this.toggleUrlButton(); - } - }); - - const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput); - if (fileInput) { - fileInput.addEventListener('change', () => { - this.handleUploadedFile(fileInput.files); - }); - } - } -} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/imagemodal.js b/lms-plugins/mediacms-moodle/tiny/amd/src/imagemodal.js deleted file mode 100755 index 0f38b6d8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/imagemodal.js +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Image Modal for Tiny. - * - * @module tiny_mediacms/imagemodal - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Modal from 'core/modal'; -import {component} from './common'; - -export default class ImageModal extends Modal { - static TYPE = `${component}/imagemodal`; - static TEMPLATE = `${component}/insert_image_modal`; - - registerEventListeners() { - // Call the parent registration. - super.registerEventListeners(); - - // Register to close on save/cancel. - this.registerCloseOnSave(); - this.registerCloseOnCancel(); - } - - configure(modalConfig) { - modalConfig.large = true; - modalConfig.removeOnClose = true; - modalConfig.show = true; - - super.configure(modalConfig); - } -} - -ImageModal.registerModalType(); diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/manager.js b/lms-plugins/mediacms-moodle/tiny/amd/src/manager.js deleted file mode 100755 index 0f4c5f4f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/manager.js +++ /dev/null @@ -1,86 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media Manager plugin class for Moodle. - * - * @module tiny_mediacms/manager - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; -import {getString} from 'core/str'; -import Modal from 'core/modal'; -import * as ModalEvents from 'core/modal_events'; -import {getData} from './options'; -import Config from 'core/config'; - -export default class MediaManager { - - editor = null; - area = null; - - constructor(editor) { - this.editor = editor; - const data = getData(editor); - this.area = data.params.area; - this.area.itemid = data.fpoptions.image.itemid; - } - - async displayDialogue() { - const modal = await Modal.create({ - large: true, - title: getString('mediamanagerproperties', 'tiny_mediacms'), - body: Templates.render('tiny_mediacms/mm2_iframe', { - src: this.getIframeURL() - }), - removeOnClose: true, - show: true, - }); - modal.getRoot().on(ModalEvents.bodyRendered, () => { - this.selectFirstElement(); - }); - - document.querySelector('.modal-lg').style.cssText = `max-width: 850px`; - return modal; - } - - // It will select the first element in the file manager. - selectFirstElement() { - const iframe = document.getElementById('mm2-iframe'); - iframe.addEventListener('load', function() { - let intervalId = setInterval(function() { - const iDocument = iframe.contentWindow.document; - if (iDocument.querySelector('.filemanager')) { - const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])'); - if (firstFocusableElement) { - firstFocusableElement.focus(); - } - clearInterval(intervalId); - } - }, 200); - }); - } - - getIframeURL() { - const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`); - url.searchParams.append('elementid', this.editor.getElement().id); - for (const key in this.area) { - url.searchParams.append(key, this.area[key]); - } - return url.toString(); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/options.js b/lms-plugins/mediacms-moodle/tiny/amd/src/options.js deleted file mode 100755 index 7c5446f7..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/options.js +++ /dev/null @@ -1,117 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Options helper for Tiny Media plugin. - * - * @module tiny_mediacms/options - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import {getPluginOptionName} from 'editor_tiny/options'; -import {pluginName} from './common'; - -const dataName = getPluginOptionName(pluginName, 'data'); -const permissionsName = getPluginOptionName(pluginName, 'permissions'); -const ltiName = getPluginOptionName(pluginName, 'lti'); - -/** - * Register the options for the Tiny Media plugin. - * - * @param {TinyMCE} editor - */ -export const register = (editor) => { - const registerOption = editor.options.register; - - registerOption(permissionsName, { - processor: 'object', - "default": { - image: { - filepicker: false, - } - }, - }); - - registerOption(dataName, { - processor: 'object', - "default": { - // MediaCMS video library configuration - mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media' - mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io' - mediacmsPageSize: 12, - // Auto-conversion settings - autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs - autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains) - autoConvertOptions: { - // Default embed options for auto-converted videos - showTitle: true, - linkTitle: true, - showRelated: true, - showUserAvatar: true, - }, - }, - }); - - registerOption(ltiName, { - processor: 'object', - "default": { - // LTI configuration for MediaCMS iframe library - toolId: 0, // LTI external tool ID - courseId: 0, // Current course ID - contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking - }, - }); -}; - -/** - * Get the permissions configuration for the Tiny Media plugin. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getPermissions = (editor) => editor.options.get(permissionsName); - -/** - * Get the permissions configuration for the Tiny Media plugin. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getImagePermissions = (editor) => getPermissions(editor).image; - -/** - * Get the permissions configuration for the Tiny Media plugin. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getEmbedPermissions = (editor) => getPermissions(editor).embed; - -/** - * Get the data configuration for the Media Manager. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getData = (editor) => editor.options.get(dataName); - -/** - * Get the LTI configuration for the MediaCMS iframe library. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getLti = (editor) => editor.options.get(ltiName); diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/plugin.js b/lms-plugins/mediacms-moodle/tiny/amd/src/plugin.js deleted file mode 100755 index 07985724..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/plugin.js +++ /dev/null @@ -1,184 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin for Moodle. - * - * @module tiny_mediacms/plugin - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -import {getTinyMCE} from 'editor_tiny/loader'; -import {getPluginMetadata} from 'editor_tiny/utils'; - -import {component, pluginName} from './common'; -import * as Commands from './commands'; -import * as Configuration from './configuration'; -import * as Options from './options'; -import {setupAutoConvert} from './autoconvert'; - -/** - * Check if a URL is a MediaCMS URL (embed or view). - * - * @param {string} url - The URL to check - * @returns {boolean} True if it's a MediaCMS URL - */ -const isMediaCMSUrl = (url) => { - if (!url) { - return false; - } - try { - const urlObj = new URL(url); - // Match both /embed and /view paths with ?m= parameter - return (urlObj.pathname === '/embed' || urlObj.pathname === '/view') && urlObj.searchParams.has('m'); - } catch (e) { - return false; - } -}; - -/** - * Convert a MediaCMS URL (embed or view) to an iframe HTML string. - * If it's a view URL, it will be converted to embed URL. - * - * @param {string} url - The MediaCMS URL - * @returns {string} The iframe HTML - */ -const mediaCMSUrlToIframe = (url) => { - // Convert view URL to embed URL if needed - let embedUrl = url; - try { - const urlObj = new URL(url); - if (urlObj.pathname === '/view') { - urlObj.pathname = '/embed'; - embedUrl = urlObj.toString(); - } - } catch (e) { - // Keep original URL if parsing fails - } - - return ``; -}; - -/** - * Regular expression to match standalone MediaCMS URLs in content. - * Matches URLs that are on their own line or surrounded by whitespace/tags. - * The URL must contain /embed?m= or /view?m= pattern. - */ -const MEDIACMS_URL_PATTERN = /(^|>|\s)(https?:\/\/[^\s<>"]+\/(?:embed|view)\?m=[^\s<>"]+)(<|\s|$)/g; - -// eslint-disable-next-line no-async-promise-executor -export default new Promise(async(resolve) => { - const [ - tinyMCE, - setupCommands, - pluginMetadata, - ] = await Promise.all([ - getTinyMCE(), - Commands.getSetup(), - getPluginMetadata(component, pluginName), - ]); - - tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => { - // Register options. - Options.register(editor); - - // Setup the Commands (buttons, menu items, and so on). - setupCommands(editor); - - // Setup auto-conversion of pasted MediaCMS URLs. - setupAutoConvert(editor); - - // Convert MediaCMS URLs to iframes when content is loaded into the editor. - // This handles content from the database that was saved as just URLs. - editor.on('BeforeSetContent', (e) => { - if (e.content && typeof e.content === 'string') { - // Replace standalone MediaCMS URLs with iframes - e.content = e.content.replace(MEDIACMS_URL_PATTERN, (match, before, url, after) => { - // Verify it's a valid MediaCMS URL - if (isMediaCMSUrl(url)) { - return before + mediaCMSUrlToIframe(url) + after; - } - return match; - }); - } - }); - - // Convert MediaCMS iframes back to just embed URLs when saving. - // This stores only the URL in the database, not the full iframe HTML. - editor.on('GetContent', (e) => { - if (e.format === 'html') { - // Create a temporary container to manipulate the HTML - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = e.content; - - // Remove edit buttons - tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove()); - - // Process all iframes - convert MediaCMS iframes to just URLs - tempDiv.querySelectorAll('iframe').forEach(iframe => { - const src = iframe.getAttribute('src'); - if (isMediaCMSUrl(src)) { - // Check if iframe is inside a wrapper - const wrapper = iframe.closest('.tiny-mediacms-iframe-wrapper') || - iframe.closest('.tiny-iframe-responsive'); - - // Create a text node with just the URL - const urlText = document.createTextNode(src); - - // Wrap in a paragraph for proper formatting - const p = document.createElement('p'); - p.appendChild(urlText); - - if (wrapper) { - // Replace the entire wrapper with the URL - wrapper.parentNode.insertBefore(p, wrapper); - wrapper.remove(); - } else { - // Replace just the iframe with the URL - iframe.parentNode.insertBefore(p, iframe); - iframe.remove(); - } - } - }); - - // Clean up any remaining wrappers that might not have had MediaCMS iframes - tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => { - const iframe = wrapper.querySelector('iframe'); - if (iframe) { - wrapper.parentNode.insertBefore(iframe, wrapper); - } - wrapper.remove(); - }); - - tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => { - const iframe = wrapper.querySelector('iframe'); - if (iframe) { - wrapper.parentNode.insertBefore(iframe, wrapper); - } - wrapper.remove(); - }); - - e.content = tempDiv.innerHTML; - } - }); - - return pluginMetadata; - }); - - // Resolve the Media Plugin and include configuration. - resolve([`${component}/plugin`, Configuration]); -}); diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/selectors.js b/lms-plugins/mediacms-moodle/tiny/amd/src/selectors.js deleted file mode 100755 index e5149c16..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/selectors.js +++ /dev/null @@ -1,162 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin helper function to build queryable data selectors. - * - * @module tiny_mediacms/selectors - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -export default { - IMAGE: { - actions: { - submit: '.tiny_imagecms_urlentrysubmit', - imageBrowser: '.openimagecmsbrowser', - addUrl: '.tiny_imagecms_addurl', - deleteImage: '.tiny_imagecms_deleteicon', - }, - elements: { - form: 'form.tiny_imagecms_form', - alignSettings: '.tiny_imagecms_button', - alt: '.tiny_imagecms_altentry', - altWarning: '.tiny_imagecms_altwarning', - height: '.tiny_imagecms_heightentry', - width: '.tiny_imagecms_widthentry', - url: '.tiny_imagecms_urlentry', - urlWarning: '.tiny_imagecms_urlwarning', - size: '.tiny_imagecms_size', - presentation: '.tiny_imagecms_presentation', - constrain: '.tiny_imagecms_constrain', - customStyle: '.tiny_imagecms_customstyle', - preview: '.tiny_imagecms_preview', - previewBox: '.tiny_imagecms_preview_box', - loaderIcon: '.tiny_imagecms_loader', - loaderIconContainer: '.tiny_imagecms_loader_container', - insertImage: '.tiny_imagecms_insert_image', - modalFooter: '.modal-footer', - dropzoneContainer: '.tiny_imagecms_dropzone_container', - fileInput: '#tiny_imagecms_fileinput', - fileNameLabel: '.tiny_imagecms_filename', - sizeOriginal: '.tiny_imagecms_sizeoriginal', - sizeCustom: '.tiny_imagecms_sizecustom', - properties: '.tiny_imagecms_properties', - }, - styles: { - responsive: 'img-fluid', - }, - }, - EMBED: { - actions: { - submit: '.tiny_mediacms_submit', - mediaBrowser: '.openmediacmsbrowser', - }, - elements: { - form: 'form.tiny_mediacms_form', - source: '.tiny_mediacms_source', - track: '.tiny_mediacms_track', - mediaSource: '.tiny_mediacms_media_source', - linkSource: '.tiny_mediacms_link_source', - linkSize: '.tiny_mediacms_link_size', - posterSource: '.tiny_mediacms_poster_source', - posterSize: '.tiny_mediacms_poster_size', - displayOptions: '.tiny_mediacms_display_options', - name: '.tiny_mediacms_name_entry', - title: '.tiny_mediacms_title_entry', - url: '.tiny_mediacms_url_entry', - width: '.tiny_mediacms_width_entry', - height: '.tiny_mediacms_height_entry', - trackSource: '.tiny_mediacms_track_source', - trackKind: '.tiny_mediacms_track_kind_entry', - trackLabel: '.tiny_mediacms_track_label_entry', - trackLang: '.tiny_mediacms_track_lang_entry', - trackDefault: '.tiny_mediacms_track_default', - mediaControl: '.tiny_mediacms_controls', - mediaAutoplay: '.tiny_mediacms_autoplay', - mediaMute: '.tiny_mediacms_mute', - mediaLoop: '.tiny_mediacms_loop', - advancedSettings: '.tiny_mediacms_advancedsettings', - linkTab: 'li[data-medium-type="link"]', - videoTab: 'li[data-medium-type="video"]', - audioTab: 'li[data-medium-type="audio"]', - linkPane: '.tab-pane[data-medium-type="link"]', - videoPane: '.tab-pane[data-medium-type="video"]', - audioPane: '.tab-pane[data-medium-type="audio"]', - trackSubtitlesTab: 'li[data-track-kind="subtitles"]', - trackCaptionsTab: 'li[data-track-kind="captions"]', - trackDescriptionsTab: 'li[data-track-kind="descriptions"]', - trackChaptersTab: 'li[data-track-kind="chapters"]', - trackMetadataTab: 'li[data-track-kind="metadata"]', - trackSubtitlesPane: '.tab-pane[data-track-kind="subtitles"]', - trackCaptionsPane: '.tab-pane[data-track-kind="captions"]', - trackDescriptionsPane: '.tab-pane[data-track-kind="descriptions"]', - trackChaptersPane: '.tab-pane[data-track-kind="chapters"]', - trackMetadataPane: '.tab-pane[data-track-kind="metadata"]', - }, - mediaTypes: { - link: 'LINK', - video: 'VIDEO', - audio: 'AUDIO', - }, - trackKinds: { - subtitles: 'SUBTITLES', - captions: 'CAPTIONS', - descriptions: 'DESCRIPTIONS', - chapters: 'CHAPTERS', - metadata: 'METADATA', - }, - }, - IFRAME: { - actions: { - remove: '[data-action="remove"]', - }, - elements: { - form: 'form.tiny_iframecms_form', - url: '.tiny_iframecms_url', - urlWarning: '.tiny_iframecms_url_warning', - showTitle: '.tiny_iframecms_showtitle', - linkTitle: '.tiny_iframecms_linktitle', - showRelated: '.tiny_iframecms_showrelated', - showUserAvatar: '.tiny_iframecms_showuseravatar', - responsive: '.tiny_iframecms_responsive', - startAt: '.tiny_iframecms_startat', - startAtEnabled: '.tiny_iframecms_startat_enabled', - aspectRatio: '.tiny_iframecms_aspectratio', - width: '.tiny_iframecms_width', - height: '.tiny_iframecms_height', - preview: '.tiny_iframecms_preview', - previewContainer: '.tiny_iframecms_preview_container', - // Tab elements - tabs: '.tiny_iframecms_tabs', - tabUrlBtn: '.tiny_iframecms_tab_url_btn', - tabIframeLibraryBtn: '.tiny_iframecms_tab_iframe_library_btn', - paneUrl: '.tiny_iframecms_pane_url', - paneIframeLibrary: '.tiny_iframecms_pane_iframe_library', - // Iframe library elements - iframeLibraryContainer: '.tiny_iframecms_iframe_library_container', - iframeLibraryPlaceholder: - '.tiny_iframecms_iframe_library_placeholder', - iframeLibraryLoading: '.tiny_iframecms_iframe_library_loading', - iframeLibraryFrame: '.tiny_iframecms_iframe_library_frame', - }, - aspectRatios: { - '16:9': { width: 560, height: 315 }, - '4:3': { width: 560, height: 420 }, - '1:1': { width: 400, height: 400 }, - custom: null, - }, - }, -}; diff --git a/lms-plugins/mediacms-moodle/tiny/amd/src/usedfiles.js b/lms-plugins/mediacms-moodle/tiny/amd/src/usedfiles.js deleted file mode 100755 index 1a418046..00000000 --- a/lms-plugins/mediacms-moodle/tiny/amd/src/usedfiles.js +++ /dev/null @@ -1,95 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media Manager usedfiles. - * - * @module tiny_mediacms/usedfiles - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import * as Templates from 'core/templates'; -import Config from 'core/config'; - -class UsedFileManager { - constructor(files, userContext, itemId, elementId) { - this.files = files; - this.userContext = userContext; - this.itemId = itemId; - this.elementId = elementId; - } - - getElementId() { - return this.elementId; - } - - getUsedFiles() { - const editor = window.parent.tinymce.EditorManager.get(this.getElementId()); - if (!editor) { - window.console.error(`Editor not found for ${this.getElementId()}`); - return []; - } - const content = editor.getContent(); - const baseUrl = `${Config.wwwroot}/draftfile.php/${this.userContext}/user/draft/${this.itemId}/`; - const pattern = new RegExp("[\"']" + baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "(?.+?)[\\?\"']", 'gm'); - - const usedFiles = [...content.matchAll(pattern)].map((match) => decodeURIComponent(match.groups.filename)); - - return usedFiles; - } - - // Return an array of unused files. - findUnusedFiles(usedFiles) { - return Object.entries(this.files) - .filter(([filename]) => !usedFiles.includes(filename)) - .map(([filename]) => filename); - } - - // Return an array of missing files. - findMissingFiles(usedFiles) { - return usedFiles.filter((filename) => !this.files.hasOwnProperty(filename)); - } - - updateFiles() { - const form = document.querySelector('form'); - const usedFiles = this.getUsedFiles(); - const unusedFiles = this.findUnusedFiles(usedFiles); - const missingFiles = this.findMissingFiles(usedFiles); - - form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox) => { - if (!unusedFiles.includes(checkbox.dataset.filename)) { - checkbox.closest('.fitem').remove(); - } - }); - - form.classList.toggle('has-missing-files', !!missingFiles.length); - form.classList.toggle('has-unused-files', !!unusedFiles.length); - - return Templates.renderForPromise('tiny_mediacms/missingfiles', { - missingFiles, - }).then(({html, js}) => { - Templates.replaceNodeContents(form.querySelector('.missing-files'), html, js); - return; - }); - } -} - -export const init = (files, usercontext, itemid, elementid) => { - const manager = new UsedFileManager(files, usercontext, itemid, elementid); - manager.updateFiles(); - - return manager; -}; diff --git a/lms-plugins/mediacms-moodle/tiny/build_instructions.md b/lms-plugins/mediacms-moodle/tiny/build_instructions.md deleted file mode 100644 index 9f74fb31..00000000 --- a/lms-plugins/mediacms-moodle/tiny/build_instructions.md +++ /dev/null @@ -1,33 +0,0 @@ -# JavaScript Build Instructions - -These instructions explain how to manually rebuild the JavaScript modules for the TinyMCE plugin. Moodle requires AMD modules, but the source code is written in ES6. - -### Prerequisites -* Node.js 16+ (Node 22 recommended) - -### Build Steps - -1. **Navigate to the package root:** - ```bash - cd lms-plugins/mediacms-moodle - ``` - -2. **Initialize dependencies:** - (Only needed the first time) - ```bash - npm init -y - npm install --save-dev @babel/core @babel/cli @babel/preset-env @babel/plugin-transform-modules-amd - ``` - -3. **Run the Build:** - This command uses the local Babel binary to avoid version conflicts and transpiles the code to AMD format. - ```bash - ./node_modules/.bin/babel tiny/mediacms/amd/src --out-dir tiny/mediacms/amd/build --presets=@babel/preset-env --plugins=@babel/plugin-transform-modules-amd - ``` - -4. **Minify (Optional but Recommended):** - Moodle loads `.min.js` files by default. This creates copies for production use. - ```bash - cd tiny/mediacms/amd/build - for f in *.js; do cp "$f" "${f%.js}.min.js"; done - ``` diff --git a/lms-plugins/mediacms-moodle/tiny/classes/form/manage_files_form.php b/lms-plugins/mediacms-moodle/tiny/classes/form/manage_files_form.php deleted file mode 100755 index 00d021a1..00000000 --- a/lms-plugins/mediacms-moodle/tiny/classes/form/manage_files_form.php +++ /dev/null @@ -1,118 +0,0 @@ -. - -/** - * Atto text editor manage files plugin form. - * - * @package tiny_mediacms - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace tiny_mediacms\form; - -use html_writer; - -defined('MOODLE_INTERNAL') || die(); - -require_once("{$CFG->libdir}/formslib.php"); - -/** - * Form allowing to edit files in one draft area. - * - * @package tiny_mediacms - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class manage_files_form extends \moodleform { - - public function definition() { - global $PAGE, $USER; - $mform = $this->_form; - - $mform->setDisableShortforms(true); - - $itemid = $this->_customdata['draftitemid']; - $elementid = $this->_customdata['elementid']; - $options = $this->_customdata['options']; - $files = $this->_customdata['files']; - $usercontext = $this->_customdata['context']; - $removeorphaneddrafts = $this->_customdata['removeorphaneddrafts'] ?? false; - - $mform->addElement('header', 'filemanagerhdr', get_string('filemanager', 'tiny_mediacms')); - - $mform->addElement('hidden', 'itemid'); - $mform->setType('itemid', PARAM_INT); - - $mform->addElement('hidden', 'maxbytes'); - $mform->setType('maxbytes', PARAM_INT); - - $mform->addElement('hidden', 'subdirs'); - $mform->setType('subdirs', PARAM_INT); - - $mform->addElement('hidden', 'accepted_types'); - $mform->setType('accepted_types', PARAM_RAW); - - $mform->addElement('hidden', 'return_types'); - $mform->setType('return_types', PARAM_INT); - - $mform->addElement('hidden', 'context'); - $mform->setType('context', PARAM_INT); - - $mform->addElement('hidden', 'areamaxbytes'); - $mform->setType('areamaxbytes', PARAM_INT); - - $mform->addElement('hidden', 'elementid'); - $mform->setType('elementid', PARAM_TEXT); - - $mform->addElement('filemanager', 'files_filemanager', '', null, $options); - - // Let the user know that any drafts not referenced in the text will be removed automatically. - if ($removeorphaneddrafts) { - $mform->addElement('static', '', '', html_writer::tag( - 'div', - get_string('unusedfilesremovalnotice', 'tiny_mediacms') - )); - } - - $mform->addElement('header', 'missingfileshdr', get_string('missingfiles', 'tiny_mediacms')); - $mform->addElement('static', '', '', - html_writer::tag( - 'div', - html_writer::tag('div', get_string('hasmissingfiles', 'tiny_mediacms')) . - html_writer::tag('div', '', ['class' => 'missing-files'] - ), - ['class' => 'file-status']) - ); - - $mform->addElement('header', 'deletefileshdr', get_string('unusedfilesheader', 'tiny_mediacms')); - $mform->addElement('static', '', '', html_writer::tag('div', get_string('unusedfilesdesc', 'tiny_mediacms'))); - - foreach ($files as $hash => $file) { - $mform->addElement('checkbox', "deletefile[{$hash}]", '', $file, ['data-filename' => $file]); - $mform->setType("deletefile[{$hash}]", PARAM_INT); - } - - $mform->addElement('submit', 'delete', get_string('deleteselected', 'tiny_mediacms')); - - $PAGE->requires->js_call_amd('tiny_mediacms/usedfiles', 'init', [ - 'files' => array_flip($files), - 'usercontext' => $usercontext->id, - 'itemid' => $itemid, - 'elementid' => $elementid, - ]); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/classes/plugininfo.php b/lms-plugins/mediacms-moodle/tiny/classes/plugininfo.php deleted file mode 100755 index 32c6c9df..00000000 --- a/lms-plugins/mediacms-moodle/tiny/classes/plugininfo.php +++ /dev/null @@ -1,226 +0,0 @@ -. - -namespace tiny_mediacms; - -use context; -use context_course; -use context_module; -use editor_tiny\editor; -use editor_tiny\plugin; -use editor_tiny\plugin_with_buttons; -use editor_tiny\plugin_with_configuration; -use editor_tiny\plugin_with_menuitems; -use moodle_url; - -/** - * Tiny media plugin. - * - * @package tiny_media - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems, plugin_with_configuration { - /** - * Whether the plugin is enabled - * - * @param context $context The context that the editor is used within - * @param array $options The options passed in when requesting the editor - * @param array $fpoptions The filepicker options passed in when requesting the editor - * @param editor $editor The editor instance in which the plugin is initialised - * @return boolean - */ - public static function is_enabled( - context $context, - array $options, - array $fpoptions, - ?editor $editor = null - ): bool { - // Disabled if: - // - Not logged in or guest. - // - Files are not allowed. - // - Only URL are supported. - $canhavefiles = !empty($options['maxfiles']); - $canhaveexternalfiles = !empty($options['return_types']) && ($options['return_types'] & FILE_EXTERNAL); - - return isloggedin() && !isguestuser() && ($canhavefiles || $canhaveexternalfiles); - } - - public static function get_available_buttons(): array { - return [ - 'tiny_mediacms/tiny_mediacms_image', - 'tiny_mediacms/tiny_mediacms_video', - 'tiny_mediacms/tiny_mediacms_iframe', - ]; - } - - public static function get_available_menuitems(): array { - return [ - 'tiny_mediacms/tiny_mediacms_image', - 'tiny_mediacms/tiny_mediacms_video', - 'tiny_mediacms/tiny_mediacms_iframe', - ]; - } - - public static function get_plugin_configuration_for_context( - context $context, - array $options, - array $fpoptions, - ?editor $editor = null - ): array { - - // TODO Fetch the actual permissions. - $permissions = [ - 'image' => [ - 'filepicker' => true, - ], - 'embed' => [ - 'filepicker' => true, - ] - ]; - - // Get LTI configuration for MediaCMS iframe library. - $lticonfig = self::get_lti_configuration($context); - - // Get auto-convert configuration. - $autoconvertconfig = self::get_autoconvert_configuration(); - - return array_merge([ - 'permissions' => $permissions, - ], self::get_file_manager_configuration($context, $options, $fpoptions), $lticonfig, $autoconvertconfig); - } - - /** - * Get the auto-convert configuration for pasted MediaCMS URLs. - * - * @return array Auto-convert configuration data - */ - protected static function get_autoconvert_configuration(): array { - $baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl'); - - // Helper function to get config with default value of true. - $getboolconfig = function($name) { - $value = get_config('tiny_mediacms', $name); - // If the setting hasn't been saved yet (false/empty), default to true. - // Only return false if explicitly set to '0'. - return $value !== '0' && $value !== 0; - }; - - return [ - 'data' => [ - 'autoConvertEnabled' => true, // Always enabled - 'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '', - 'autoConvertOptions' => [ - 'showTitle' => $getboolconfig('autoconvert_showtitle'), - 'linkTitle' => $getboolconfig('autoconvert_linktitle'), - 'showRelated' => $getboolconfig('autoconvert_showrelated'), - 'showUserAvatar' => $getboolconfig('autoconvert_showuseravatar'), - ], - ], - ]; - } - - /** - * Get the LTI configuration for the MediaCMS iframe library. - * - * @param context $context The context that the editor is used within - * @return array LTI configuration data - */ - protected static function get_lti_configuration(context $context): array { - global $COURSE; - - // Get the configured LTI tool ID from plugin settings. - $ltitoolid = get_config('tiny_mediacms', 'ltitoolid'); - - // Determine the course ID from context. - $courseid = 0; - if ($context instanceof context_course) { - $courseid = $context->instanceid; - } else if ($context instanceof context_module) { - // Get the course from the module context. - $coursecontext = $context->get_course_context(false); - if ($coursecontext) { - $courseid = $coursecontext->instanceid; - } - } else if (!empty($COURSE->id) && $COURSE->id != SITEID) { - // Fall back to the global $COURSE if available and not the site. - $courseid = $COURSE->id; - } - - // Build the content item URL for LTI Deep Linking. - // This URL initiates the LTI Deep Linking flow which allows users - // to select content (like videos) from the tool provider. - $contentitemurl = ''; - if (!empty($ltitoolid) && $courseid > 0) { - $contentitemurl = (new moodle_url('/mod/lti/contentitem.php', [ - 'id' => $ltitoolid, - 'course' => $courseid, - 'title' => 'MediaCMS Library', - 'return_types' => 1 // LTI_DEEPLINKING_RETURN_TYPE_LTI_LINK - ]))->out(false); - } - - return [ - 'lti' => [ - 'toolId' => !empty($ltitoolid) ? (int) $ltitoolid : 0, - 'courseId' => $courseid, - 'contentItemUrl' => $contentitemurl, - ], - ]; - } - - protected static function get_file_manager_configuration( - context $context, - array $options, - array $fpoptions - ): array { - global $USER; - - $params = [ - 'area' => [], - 'usercontext' => \context_user::instance($USER->id)->id, - ]; - - $keys = [ - 'itemid', - 'areamaxbytes', - 'maxbytes', - 'subdirs', - 'return_types', - 'removeorphaneddrafts', - ]; - if (isset($options['context'])) { - if (is_object($options['context'])) { - $params['area']['context'] = $options['context']->id; - } else { - $params['area']['context'] = $options['context']; - } - } - foreach ($keys as $key) { - if (isset($options[$key])) { - $params['area'][$key] = $options[$key]; - } - } - - return [ - 'storeinrepo' => true, - 'data' => [ - 'params' => $params, - 'fpoptions' => $fpoptions, - ], - ]; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/classes/privacy/provider.php b/lms-plugins/mediacms-moodle/tiny/classes/privacy/provider.php deleted file mode 100755 index 14eeaf6f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/classes/privacy/provider.php +++ /dev/null @@ -1,30 +0,0 @@ -. - -namespace tiny_mediacms\privacy; - -/** - * Privacy Subsystem implementation for the media plugin for TinyMCE. - * - * @package tiny_media - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class provider implements \core_privacy\local\metadata\null_provider { - public static function get_reason(): string { - return 'privacy:metadata'; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/lang/en/deprecated.txt b/lms-plugins/mediacms-moodle/tiny/lang/en/deprecated.txt deleted file mode 100755 index 431de560..00000000 --- a/lms-plugins/mediacms-moodle/tiny/lang/en/deprecated.txt +++ /dev/null @@ -1,7 +0,0 @@ -alignment,tiny_media -alignment_bottom,tiny_media -alignment_left,tiny_media -alignment_middle,tiny_media -alignment_right,tiny_media -alignment_top,tiny_media -helplinktext,tiny_media diff --git a/lms-plugins/mediacms-moodle/tiny/lang/en/tiny_mediacms.php b/lms-plugins/mediacms-moodle/tiny/lang/en/tiny_mediacms.php deleted file mode 100755 index 0463f9eb..00000000 --- a/lms-plugins/mediacms-moodle/tiny/lang/en/tiny_mediacms.php +++ /dev/null @@ -1,199 +0,0 @@ -. - -/** - * Strings for component 'tiny_mediacms', language 'en'. - * - * @package tiny_mediacms - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['addcaptionstrack'] = 'Add caption track'; -$string['addchapterstrack'] = 'Add chapter track'; -$string['adddescriptionstrack'] = 'Add description track'; -$string['addfilesdrop'] = 'Drag and drop an image to upload, or click to select'; -$string['addmetadatatrack'] = 'Add metadata track'; -$string['addsource_help'] = 'You are recommended to provide an alternative media source, as desktop and mobile browsers support different file formats.'; -$string['addsource'] = 'Add alternative source'; -$string['addsubtitlestrack'] = 'Add subtitle track'; -$string['addurl'] = 'Add'; -$string['advancedsettings'] = 'Advanced settings'; -$string['audio'] = 'Audio'; -$string['audiosourcelabel'] = 'Audio source URL'; -$string['autoplay'] = 'Play automatically'; -$string['browserepositories'] = 'Browse repositories...'; -$string['browserepositoriesimage'] = 'Browse repositories'; -$string['captions_help'] = 'Captions may be used to describe everything happening in the track, including non-verbal sounds such as a phone ringing.'; -$string['captions'] = 'Captions'; -$string['captionssourcelabel'] = 'Caption track URL'; -$string['chapters_help'] = 'Chapter titles may be provided for use in navigating the media resource.'; -$string['chapters'] = 'Chapters'; -$string['chapterssourcelabel'] = 'Chapter track URL'; -$string['constrain'] = 'Keep proportion'; -$string['controls'] = 'Show controls'; -$string['createmedia'] = 'Insert favorite media'; -$string['default'] = 'Default'; -$string['deleteimage'] = 'Delete image'; -$string['deleteimagewarning'] = 'Are you sure you want to remove the image?'; -$string['deleteselected'] = 'Delete selected files'; -$string['descriptions_help'] = 'Audio descriptions may be used to provide a narration which explains visual details not apparent from the audio alone.'; -$string['descriptions'] = 'Descriptions'; -$string['descriptionssourcelabel'] = 'Description track URL'; -$string['displayoptions'] = 'Display options'; -$string['enteralt'] = 'How would you describe this image to someone who can\'t see it?'; -$string['entername'] = 'Name'; -$string['entersource'] = 'Source URL'; -$string['entertitle'] = 'Title'; -$string['enterurl'] = 'Add via URL'; -$string['enterurlor'] = 'Or add via URL'; -$string['filemanager'] = 'Favorite file manager'; -$string['hasmissingfiles'] = 'Warning! The following files that are referenced in the text area appear to be missing:'; -$string['height'] = 'Height'; -$string['imagebuttontitle'] = 'Favorite Image'; -$string['imagedetails'] = 'Image details'; -$string['imageproperties'] = 'Image properties'; -$string['imageurlrequired'] = 'An image must have a valid URL.'; -$string['insertimage'] = 'Insert favorite image'; -$string['label'] = 'Label'; -$string['languagesavailable'] = 'Languages available'; -$string['languagesinstalled'] = 'Languages installed'; -$string['link'] = 'Link'; -$string['loading'] = 'Preparing the image'; -$string['loop'] = 'Loop'; -$string['managefiles'] = 'Manage favorite files'; -$string['mediabuttontitle'] = 'Favorite Multimedia'; -$string['mediamanagerbuttontitle'] = 'Favorite media manager'; -$string['mediamanagerproperties'] = 'Favorite media manager'; -$string['metadata_help'] = 'Metadata tracks, for use from a script, may be used only if the player supports metadata.'; -$string['metadata'] = 'Metadata'; -$string['metadatasourcelabel'] = 'Metadata track URL'; -$string['missingfiles'] = 'Missing files'; -$string['mute'] = 'Muted'; -$string['pluginname'] = 'MediaCMS'; -$string['presentation'] = 'This image is decorative only'; -$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.'; -$string['privacy:metadata'] = 'The favorite media plugin for TinyMCE does not store any personal data.'; -$string['remove'] = 'Remove'; -$string['repositorynotpermitted'] = 'Paste an image link in the field below.'; -$string['repositoryuploadnotpermitted'] = 'Paste an image link in the field below or
click the Browse Repositories button.'; -$string['saveimage'] = 'Save'; -$string['size'] = 'Width x height (in pixels)'; -$string['sizecustom'] = 'Custom size'; -$string['sizecustom_help'] = 'This image is just a preview. Changes to its size will be visible after you save it.'; -$string['sizeoriginal'] = 'Original size'; -$string['srclang'] = 'Language'; -$string['subtitles_help'] = 'Subtitles may be used to provide a transcription or translation of the dialogue.'; -$string['subtitles'] = 'Subtitles'; -$string['subtitlessourcelabel'] = 'Subtitle track URL'; -$string['tracks_help'] = 'Subtitles, captions, chapters and descriptions can be added via a WebVTT (Web Video Text Tracks) format file. Track labels will be shown in the selection drop-down menu. For each type of track, any track set as default will be pre-selected at the start of the video.'; -$string['tracks'] = 'Subtitles and captions'; -$string['unusedfilesdesc'] = 'The following embedded files are not used in the text area:'; -$string['unusedfilesheader'] = 'Unused files'; -$string['unusedfilesremovalnotice'] = 'Any unused files will be automatically deleted when saving changes.'; -$string['updatemedia'] = 'Update favorite media'; -$string['uploading'] = 'Uploading'; -$string['video'] = 'Video'; -$string['videoheight'] = 'Video height'; -$string['videosourcelabel'] = 'Video source URL'; -$string['videowidth'] = 'Video width'; -$string['width'] = 'Width'; - -// Iframe embed strings. -$string['iframebuttontitle'] = 'Insert MediaCMS Media'; -$string['iframemodaltitle'] = 'Insert MediaCMS Media'; -$string['iframeurl'] = 'MediaCMS Video URL or embed code'; -$string['iframeurlplaceholder'] = 'Paste MediaCMS Video URL or iframe embed code'; -$string['iframeurlinvalid'] = 'Please enter a valid MediaCMS Video URL or embed code'; -$string['embedoptions'] = 'Embed Options'; -$string['showtitle'] = 'Show title'; -$string['linktitle'] = 'Link title'; -$string['showrelated'] = 'Show related'; -$string['showuseravatar'] = 'Show user avatar'; -$string['responsive'] = 'Responsive'; -$string['startat'] = 'Start at'; -$string['aspectratio'] = 'Aspect Ratio'; -$string['aspectratio_16_9'] = '16:9'; -$string['aspectratio_4_3'] = '4:3'; -$string['aspectratio_1_1'] = '1:1'; -$string['aspectratio_custom'] = 'Custom'; -$string['dimensions'] = 'Dimensions'; -$string['preview'] = 'Preview'; -$string['insertiframe'] = 'Insert video'; -$string['updateiframe'] = 'Update video'; -$string['removeiframe'] = 'Remove video'; -$string['removeiframeconfirm'] = 'Are you sure you want to remove this video from the editor?'; - -// Iframe modal tabs. -$string['tabembedurl'] = 'Embed URL'; -$string['tabvideolibrary'] = 'Video Library'; -$string['tabvideolibraryiframe'] = 'Media Library'; - -// Video library strings. -$string['librarysearchplaceholder'] = 'Search videos...'; -$string['librarysortnewest'] = 'Newest first'; -$string['librarysortoldest'] = 'Oldest first'; -$string['librarysorttitle'] = 'Title A-Z'; -$string['librarysortviews'] = 'Most views'; -$string['libraryloading'] = 'Loading videos...'; -$string['libraryempty'] = 'No videos found'; -$string['libraryerror'] = 'Failed to load videos'; -$string['libraryretry'] = 'Retry'; -$string['libraryprev'] = 'Previous'; -$string['librarynext'] = 'Next'; -$string['libraryselect'] = 'Select'; -$string['librarypage'] = 'Page {$a->current} of {$a->total}'; -$string['libraryvideoselected'] = 'Video selected. Configure embed options below.'; - -// LTI settings strings. -$string['ltitoolid'] = 'LTI Tool'; -$string['ltitoolid_desc'] = 'Select the External Tool configuration for MediaCMS. This enables the authenticated video library in the editor.'; -$string['noltitoolsfound'] = 'No LTI tools found'; -$string['choose'] = 'Choose...'; -$string['ltitoolid_help'] = 'To find the LTI tool ID, go to Site administration > Plugins > Activity modules > External tool > Manage tools. The ID is shown in the URL when editing a tool (e.g., id=2).'; - -// Iframe library from LTI strings. -$string['iframelibraryloading'] = 'Loading MediaCMS video library...'; -$string['iframelibraryplaceholder'] = 'Click here to load the video library'; -$string['iframelibraryerror'] = 'Failed to load the video library. Please check your LTI configuration.'; -$string['iframelibrarynotconfigured'] = 'The MediaCMS LTI tool has not been configured. Please contact your administrator.'; - -// Auto-convert settings strings. -$string['autoconvertheading'] = 'Auto-convert MediaCMS URLs'; -$string['autoconvertheading_desc'] = 'Configure automatic conversion of pasted MediaCMS URLs to embedded videos.'; -$string['autoconvertenabled'] = 'Enable auto-convert'; -$string['autoconvertenabled_desc'] = 'When enabled, pasting a MediaCMS video URL (e.g., https://lti.mediacms.io/view?m=VIDEO_ID) into the editor will automatically convert it to an embedded video player.'; -$string['autoconvert_baseurl'] = 'MediaCMS URL'; -$string['autoconvert_baseurl_desc'] = 'The base URL of your MediaCMS instance (e.g., https://lti.mediacms.io). If specified, only URLs from this domain will be auto-converted. Leave empty to allow any MediaCMS URL.'; -$string['autoconvert_showtitle'] = 'Show video title'; -$string['autoconvert_showtitle_desc'] = 'Display the video title in the embedded player.'; -$string['autoconvert_linktitle'] = 'Link video title'; -$string['autoconvert_linktitle_desc'] = 'Make the video title clickable, linking to the original video page.'; -$string['autoconvert_showrelated'] = 'Show related videos'; -$string['autoconvert_showrelated_desc'] = 'Display related videos after the current video ends.'; -$string['autoconvert_showuseravatar'] = 'Show user avatar'; -$string['autoconvert_showuseravatar_desc'] = 'Display the uploader\'s avatar in the embedded player.'; - -// Deprecated since Moodle 4.4. -$string['alignment'] = 'Alignment'; -$string['alignment_bottom'] = 'Bottom'; -$string['alignment_left'] = 'Left'; -$string['alignment_middle'] = 'Middle'; -$string['alignment_right'] = 'Right'; -$string['alignment_top'] = 'Top'; - -// Deprecated since Moodle 4.5. -$string['helplinktext'] = 'Favorite media helper'; diff --git a/lms-plugins/mediacms-moodle/tiny/manage.php b/lms-plugins/mediacms-moodle/tiny/manage.php deleted file mode 100755 index 844953cc..00000000 --- a/lms-plugins/mediacms-moodle/tiny/manage.php +++ /dev/null @@ -1,148 +0,0 @@ -. - -/** - * Manage files in user draft area attached to texteditor. - * - * @package tiny_mediacms - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require(__DIR__ . '/../../../../../config.php'); -require_once($CFG->libdir . '/filestorage/file_storage.php'); -require_once($CFG->dirroot . '/repository/lib.php'); - -$itemid = required_param('itemid', PARAM_INT) ?? 0; -$maxbytes = optional_param('maxbytes', 0, PARAM_INT); -$subdirs = optional_param('subdirs', 0, PARAM_INT); -$acceptedtypes = optional_param('accepted_types', '*', PARAM_RAW); // TODO Not yet passed to this script. -$returntypes = optional_param('return_types', null, PARAM_INT); -$areamaxbytes = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT); -$contextid = optional_param('context', SYSCONTEXTID, PARAM_INT); -$elementid = optional_param('elementid', '', PARAM_TEXT); -$removeorphaneddrafts = optional_param('removeorphaneddrafts', 0, PARAM_INT); - -$context = context::instance_by_id($contextid); -if ($context->contextlevel == CONTEXT_MODULE) { - // Module context. - $cm = $DB->get_record('course_modules', ['id' => $context->instanceid]); - require_login($cm->course, true, $cm); -} else if (($coursecontext = $context->get_course_context(false)) && $coursecontext->id != SITEID) { - // Course context or block inside the course. - require_login($coursecontext->instanceid); - $PAGE->set_context($context); -} else { - // Block that is not inside the course, user or system context. - require_login(); - $PAGE->set_context($context); -} - -// Guests can never manage files. -if (isguestuser()) { - throw new \moodle_exception('noguest'); -} - -$title = get_string('managefiles', 'tiny_mediacms'); - -$url = new moodle_url('/lib/editor/tiny/plugins/mediacms/manage.php', [ - 'itemid' => $itemid, - 'maxbytes' => $maxbytes, - 'subdirs' => $subdirs, - 'accepted_types' => $acceptedtypes, - 'return_types' => $returntypes, - 'areamaxbytes' => $areamaxbytes, - 'context' => $contextid, - 'elementid' => $elementid, - 'removeorphaneddrafts' => $removeorphaneddrafts, -]); - -$PAGE->set_url($url); -$PAGE->set_title($title); -$PAGE->set_heading($title); -$PAGE->set_pagelayout('popup'); - -if ($returntypes !== null) { - // Links are allowed in textarea but never allowed in filemanager. - $returntypes = $returntypes & ~FILE_EXTERNAL; -} - -// These are the options required for the filepicker. -$options = [ - 'subdirs' => $subdirs, - 'maxbytes' => $maxbytes, - 'maxfiles' => -1, - 'accepted_types' => $acceptedtypes, - 'areamaxbytes' => $areamaxbytes, - 'return_types' => $returntypes, - 'context' => $context -]; - -$usercontext = context_user::instance($USER->id); -$fs = get_file_storage(); -$files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $itemid, '/', !empty($subdirs), false); -$filenames = []; -foreach ($files as $file) { - $filenames[$file->get_pathnamehash()] = ltrim($file->get_filepath(), '/') . $file->get_filename(); -} - -$mform = new tiny_mediacms\form\manage_files_form(null, [ - 'context' => $usercontext, - 'options' => $options, - 'draftitemid' => $itemid, - 'files' => $filenames, - 'elementid' => $elementid, - 'removeorphaneddrafts' => $removeorphaneddrafts, - ], 'post', '', [ - 'id' => 'tiny_mediacms_form', - ] -); - -if ($data = $mform->get_data()) { - if (!empty($data->deletefile)) { - foreach (array_keys($data->deletefile) as $filehash) { - if ($file = $fs->get_file_by_hash($filehash)) { - // Make sure the user didn't modify the filehash to delete another file. - if ($file->get_component() !== 'user' || $file->get_filearea() !== 'draft') { - // The file must belong to the user/draft area. - continue; - } - if ($file->get_contextid() != $usercontext->id) { - // The user must own the file - that is it must be in their user draft file area. - continue; - } - if ($file->get_itemid() != $itemid) { - // It must be the file they requested be deleted. - continue; - } - $file->delete(); - } - } - } - // Redirect to prevent re-posting the form. - redirect($url); -} - -$mform->set_data(array_merge($options, [ - 'files_filemanager' => $itemid, - 'itemid' => $itemid, - 'elementid' => $elementid, - 'context' => $context->id, -])); - -echo $OUTPUT->header(); -$mform->display(); -echo $OUTPUT->footer(); diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md b/lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md deleted file mode 100755 index d1ac4526..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/AUTOCONVERT.md +++ /dev/null @@ -1,206 +0,0 @@ -# MediaCMS URL Auto-Convert Feature - -This feature automatically converts pasted MediaCMS video URLs into embedded video players within the TinyMCE editor. - -## Overview - -When a user pastes a MediaCMS video URL like: -``` -https://deic.mediacms.io/view?m=JpBd1Zvdl -``` - -It is automatically converted to an embedded video player: -```html -
- -
-``` - -## Supported URL Formats - -The auto-convert feature recognizes MediaCMS view URLs in this format: -- `https://[domain]/view?m=[VIDEO_ID]` - -Examples: -- `https://deic.mediacms.io/view?m=JpBd1Zvdl` -- `https://your-mediacms-instance.com/view?m=abc123` - -## Configuration - -### Accessing Settings - -1. Log in to Moodle as an administrator -2. Navigate to: **Site administration** → **Plugins** → **Text editors** → **TinyMCE editor** → **MediaCMS** -3. Scroll to the **Auto-convert MediaCMS URLs** section - -### Available Settings - -| Setting | Description | Default | -|---------|-------------|---------| -| **Enable auto-convert** | Turn the auto-convert feature on or off | Enabled | -| **MediaCMS base URL** | Restrict auto-conversion to a specific MediaCMS domain | Empty (allow all) | -| **Show video title** | Display the video title in the embedded player | Enabled | -| **Link video title** | Make the video title clickable, linking to the original video page | Enabled | -| **Show related videos** | Display related videos after the current video ends | Enabled | -| **Show user avatar** | Display the uploader's avatar in the embedded player | Enabled | - -### Settings Location in Moodle - -The settings are stored in the Moodle database under the `tiny_mediacms` plugin configuration: - -- `tiny_mediacms/autoconvertenabled` - Enable/disable auto-convert -- `tiny_mediacms/autoconvert_baseurl` - MediaCMS base URL (e.g., https://deic.mediacms.io) -- `tiny_mediacms/autoconvert_showtitle` - Show title option -- `tiny_mediacms/autoconvert_linktitle` - Link title option -- `tiny_mediacms/autoconvert_showrelated` - Show related option -- `tiny_mediacms/autoconvert_showuseravatar` - Show user avatar option - -### Base URL Configuration - -The **MediaCMS base URL** setting controls which MediaCMS instances are recognized for auto-conversion: - -- **Empty (default)**: Any MediaCMS URL will be auto-converted (e.g., URLs from any `*/view?m=*` pattern) -- **Specific URL**: Only URLs from the specified domain will be auto-converted - -Example configurations: -- `https://deic.mediacms.io` - Only convert URLs from deic.mediacms.io -- `https://media.myuniversity.edu` - Only convert URLs from your institution's MediaCMS - -## Technical Details - -### File Structure - -``` -amd/src/ -├── autoconvert.js # Main auto-convert module -├── plugin.js # Plugin initialization (imports autoconvert) -└── options.js # Configuration options definition - -classes/ -└── plugininfo.php # Passes PHP settings to JavaScript - -settings.php # Admin settings page definition - -lang/en/ -└── tiny_mediacms.php # Language strings for settings -``` - -### How It Works - -1. **Paste Detection**: The `autoconvert.js` module listens for `paste` events on the TinyMCE editor -2. **URL Validation**: When text is pasted, it checks if it matches the MediaCMS URL pattern -3. **HTML Generation**: If valid, it generates the responsive iframe HTML with configured options -4. **Content Insertion**: The original URL is replaced with the embedded video - -### JavaScript Configuration - -The settings are passed from PHP to JavaScript via the `plugininfo.php` class: - -```php -protected static function get_autoconvert_configuration(): array { - $baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl'); - - return [ - 'data' => [ - 'autoConvertEnabled' => (bool) get_config('tiny_mediacms', 'autoconvertenabled'), - 'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '', - 'autoConvertOptions' => [ - 'showTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_showtitle'), - 'linkTitle' => (bool) get_config('tiny_mediacms', 'autoconvert_linktitle'), - 'showRelated' => (bool) get_config('tiny_mediacms', 'autoconvert_showrelated'), - 'showUserAvatar' => (bool) get_config('tiny_mediacms', 'autoconvert_showuseravatar'), - ], - ], - ]; -} -``` - -### Default Values (in options.js) - -If PHP settings are not configured, the JavaScript uses these defaults: - -```javascript -registerOption(dataName, { - processor: 'object', - "default": { - autoConvertEnabled: true, - autoConvertBaseUrl: '', // Empty = allow all MediaCMS domains - autoConvertOptions: { - showTitle: true, - linkTitle: true, - showRelated: true, - showUserAvatar: true, - }, - }, -}); -``` - -## Customization - -### Disabling Auto-Convert - -To disable the feature entirely: -1. Go to the plugin settings (see "Accessing Settings" above) -2. Uncheck **Enable auto-convert** -3. Save changes - -### Programmatic Configuration - -You can also set these values directly in the database using Moodle's `set_config()` function: - -```php -// Disable auto-convert -set_config('autoconvertenabled', 0, 'tiny_mediacms'); - -// Set the MediaCMS base URL (restrict to specific domain) -set_config('autoconvert_baseurl', 'https://deic.mediacms.io', 'tiny_mediacms'); - -// Customize embed options -set_config('autoconvert_showtitle', 1, 'tiny_mediacms'); -set_config('autoconvert_linktitle', 0, 'tiny_mediacms'); -set_config('autoconvert_showrelated', 0, 'tiny_mediacms'); -set_config('autoconvert_showuseravatar', 1, 'tiny_mediacms'); -``` - -### CLI Configuration - -Using Moodle CLI: - -```bash -# Enable auto-convert -php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvertenabled --set=1 - -# Set the MediaCMS base URL -php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_baseurl --set=https://deic.mediacms.io - -# Disable showing related videos -php admin/cli/cfg.php --component=tiny_mediacms --name=autoconvert_showrelated --set=0 -``` - -## Troubleshooting - -### Auto-convert not working - -1. **Check if enabled**: Verify the setting is enabled in plugin settings -2. **Clear caches**: Purge all caches (Site administration → Development → Purge all caches) -3. **Check URL format**: Ensure the URL matches the pattern `https://[domain]/view?m=[VIDEO_ID]` -4. **Browser console**: Check for JavaScript errors in the browser developer console - -### Rebuilding JavaScript - -If you modify the source files, rebuild using: - -```bash -cd /path/to/moodle -npx grunt amd --root=public/lib/editor/tiny/plugins/mediacms -``` - -Note: Requires Node.js 22.x or compatible version as specified in Moodle's requirements. - -## Version History - -- **1.0.0** - Initial implementation of auto-convert feature diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js deleted file mode 100755 index f310b679..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js +++ /dev/null @@ -1,15 +0,0 @@ -define("tiny_mediacms/autoconvert",["exports","./options"],(function(_exports,_options){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.setupAutoConvert=_exports.isMediaCMSUrl=_exports.convertToEmbed=void 0; -/** - * Tiny MediaCMS Auto-convert module. - * - * This module automatically converts pasted MediaCMS URLs into embedded videos. - * When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl), - * it will be automatically converted to an iframe embed. - * - * @module tiny_mediacms/autoconvert - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -const MEDIACMS_VIEW_URL_PATTERN=/^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/,parseMediaCMSUrl=text=>{if(!text||"string"!=typeof text)return null;const trimmed=text.trim(),match=trimmed.match(MEDIACMS_VIEW_URL_PATTERN);return match?{baseUrl:match[1],videoId:match[2],originalUrl:trimmed}:null},isDomainAllowed=(parsed,config)=>{const configuredBaseUrl=config.autoConvertBaseUrl||config.mediacmsBaseUrl;if(!configuredBaseUrl)return!0;try{const configuredUrl=new URL(configuredBaseUrl),pastedUrl=new URL(parsed.baseUrl);return configuredUrl.host===pastedUrl.host}catch(e){return!0}},generateEmbedHtml=function(parsed){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const embedUrl=new URL("".concat(parsed.baseUrl,"/embed"));embedUrl.searchParams.set("m",parsed.videoId),embedUrl.searchParams.set("showTitle",!1!==options.showTitle?"1":"0"),embedUrl.searchParams.set("showRelated",!1!==options.showRelated?"1":"0"),embedUrl.searchParams.set("showUserAvatar",!1!==options.showUserAvatar?"1":"0"),embedUrl.searchParams.set("linkTitle",!1!==options.linkTitle?"1":"0");const html='';return html};_exports.setupAutoConvert=editor=>{const config=(0,_options.getData)(editor)||{};!1!==config.autoConvertEnabled&&(editor.on("paste",(e=>{handlePasteEvent(editor,e,config)})),editor.on("input",(e=>{handleInputEvent(editor,e,config)})))};const handlePasteEvent=(editor,e,config)=>{const clipboardData=e.clipboardData||window.clipboardData;if(!clipboardData)return;const text=clipboardData.getData("text/plain")||clipboardData.getData("text");if(!text)return;const parsed=parseMediaCMSUrl(text);if(!parsed)return;if(!isDomainAllowed(parsed,config))return;e.preventDefault(),e.stopPropagation();const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{editor.insertContent(embedHtml),editor.selection.collapse(!1)}),0)},handleInputEvent=(editor,e,config)=>{if("insertFromPaste"!==e.inputType&&"insertText"!==e.inputType)return;const node=editor.selection.getNode();if(!node||"P"!==node.nodeName)return;const text=node.textContent||"",parsed=parseMediaCMSUrl(text);if(!parsed||!isDomainAllowed(parsed,config))return;const trimmedHtml=node.innerHTML.trim();if(trimmedHtml!==text.trim()&&!trimmedHtml.startsWith(text.trim()))return;const embedHtml=generateEmbedHtml(parsed,config.autoConvertOptions||{});setTimeout((()=>{const currentText=node.textContent||"",currentParsed=parseMediaCMSUrl(currentText);currentParsed&¤tParsed.originalUrl===parsed.originalUrl&&(editor.selection.select(node),editor.insertContent(embedHtml))}),100)};_exports.isMediaCMSUrl=text=>null!==parseMediaCMSUrl(text);_exports.convertToEmbed=function(url){let options=arguments.length>1&&void 0!==arguments[1]?arguments[1]:{};const parsed=parseMediaCMSUrl(url);return parsed?generateEmbedHtml(parsed,options):null}})); - -//# sourceMappingURL=autoconvert.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js.map deleted file mode 100755 index 36a38568..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/autoconvert.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"autoconvert.min.js","sources":["../src/autoconvert.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 * Tiny MediaCMS Auto-convert module.\n *\n * This module automatically converts pasted MediaCMS URLs into embedded videos.\n * When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl),\n * it will be automatically converted to an iframe embed.\n *\n * @module tiny_mediacms/autoconvert\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getData} from './options';\n\n/**\n * Regular expression patterns for MediaCMS URLs.\n * Matches URLs like:\n * - https://deic.mediacms.io/view?m=JpBd1Zvdl\n * - https://example.mediacms.io/view?m=VIDEO_ID\n * - Custom domains configured in the plugin\n */\nconst MEDIACMS_VIEW_URL_PATTERN = /^(https?:\\/\\/[^\\/]+)\\/view\\?m=([a-zA-Z0-9_-]+)$/;\n\n/**\n * Check if a string is a valid MediaCMS view URL.\n *\n * @param {string} text - The text to check\n * @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL\n */\nconst parseMediaCMSUrl = (text) => {\n if (!text || typeof text !== 'string') {\n return null;\n }\n\n const trimmed = text.trim();\n\n // Check for MediaCMS view URL pattern\n const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN);\n if (match) {\n return {\n baseUrl: match[1],\n videoId: match[2],\n originalUrl: trimmed,\n };\n }\n\n return null;\n};\n\n/**\n * Check if the pasted URL's domain is allowed based on configuration.\n *\n * @param {Object} parsed - Parsed URL info\n * @param {Object} config - Plugin configuration\n * @returns {boolean} - True if the domain is allowed\n */\nconst isDomainAllowed = (parsed, config) => {\n // If no specific base URL is configured, allow all MediaCMS domains\n const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl;\n if (!configuredBaseUrl) {\n return true;\n }\n\n // Check if the URL's base matches the configured base URL\n try {\n const configuredUrl = new URL(configuredBaseUrl);\n const pastedUrl = new URL(parsed.baseUrl);\n return configuredUrl.host === pastedUrl.host;\n } catch (e) {\n // If URL parsing fails, allow the conversion\n return true;\n }\n};\n\n/**\n * Generate the iframe embed HTML for a MediaCMS video.\n *\n * @param {Object} parsed - Parsed URL info\n * @param {Object} options - Embed options\n * @returns {string} - The iframe HTML\n */\nconst generateEmbedHtml = (parsed, options = {}) => {\n // Build the embed URL with default options\n const embedUrl = new URL(`${parsed.baseUrl}/embed`);\n embedUrl.searchParams.set('m', parsed.videoId);\n\n // Apply default options (all enabled by default for best user experience)\n embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0');\n embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0');\n embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0');\n embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0');\n\n // Generate clean iframe HTML (wrapper will be added by editor for UI, then stripped on save)\n const html = ``;\n\n return html;\n};\n\n/**\n * Set up auto-conversion for the editor.\n * This registers event handlers to detect pasted MediaCMS URLs.\n *\n * @param {TinyMCE} editor - The TinyMCE editor instance\n */\nexport const setupAutoConvert = (editor) => {\n const config = getData(editor) || {};\n\n // Check if auto-convert is enabled (default: true)\n if (config.autoConvertEnabled === false) {\n return;\n }\n\n // Handle paste events\n editor.on('paste', (e) => {\n handlePasteEvent(editor, e, config);\n });\n\n // Also handle input events for drag-and-drop text or keyboard paste\n editor.on('input', (e) => {\n handleInputEvent(editor, e, config);\n });\n};\n\n/**\n * Handle paste events to detect and convert MediaCMS URLs.\n *\n * @param {TinyMCE} editor - The TinyMCE editor instance\n * @param {Event} e - The paste event\n * @param {Object} config - Plugin configuration\n */\nconst handlePasteEvent = (editor, e, config) => {\n // Get pasted text from clipboard\n const clipboardData = e.clipboardData || window.clipboardData;\n if (!clipboardData) {\n return;\n }\n\n // Try to get plain text first\n const text = clipboardData.getData('text/plain') || clipboardData.getData('text');\n if (!text) {\n return;\n }\n\n // Check if it's a MediaCMS URL\n const parsed = parseMediaCMSUrl(text);\n if (!parsed) {\n return;\n }\n\n // Check if domain is allowed\n if (!isDomainAllowed(parsed, config)) {\n return;\n }\n\n // Prevent default paste behavior\n e.preventDefault();\n e.stopPropagation();\n\n // Generate and insert the embed HTML\n const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});\n\n // Use a slight delay to ensure the editor is ready\n setTimeout(() => {\n editor.insertContent(embedHtml);\n // Move cursor after the inserted content\n editor.selection.collapse(false);\n }, 0);\n};\n\n/**\n * Handle input events to catch URLs that might have been pasted without triggering paste event.\n * This is a fallback for certain browsers/scenarios.\n *\n * @param {TinyMCE} editor - The TinyMCE editor instance\n * @param {Event} e - The input event\n * @param {Object} config - Plugin configuration\n */\nconst handleInputEvent = (editor, e, config) => {\n // Only process inputType 'insertFromPaste' if paste event didn't catch it\n if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') {\n return;\n }\n\n // Get the current node and check if it contains just a URL\n const node = editor.selection.getNode();\n if (!node || node.nodeName !== 'P') {\n return;\n }\n\n // Check if the paragraph contains only a MediaCMS URL\n const text = node.textContent || '';\n const parsed = parseMediaCMSUrl(text);\n\n if (!parsed || !isDomainAllowed(parsed, config)) {\n return;\n }\n\n // Don't convert if there's other content in the paragraph\n const trimmedHtml = node.innerHTML.trim();\n if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) {\n return;\n }\n\n // Generate the embed HTML\n const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {});\n\n // Replace the paragraph content with the embed\n // Use a slight delay to let the input event complete\n setTimeout(() => {\n // Re-check that the node still contains the URL (user might have typed more)\n const currentText = node.textContent || '';\n const currentParsed = parseMediaCMSUrl(currentText);\n\n if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) {\n // Select and replace the entire node\n editor.selection.select(node);\n editor.insertContent(embedHtml);\n }\n }, 100);\n};\n\n/**\n * Check if a text is a MediaCMS URL (public helper).\n *\n * @param {string} text - The text to check\n * @returns {boolean} - True if it's a MediaCMS URL\n */\nexport const isMediaCMSUrl = (text) => {\n return parseMediaCMSUrl(text) !== null;\n};\n\n/**\n * Convert a MediaCMS URL to embed HTML (public helper).\n *\n * @param {string} url - The MediaCMS URL\n * @param {Object} options - Embed options\n * @returns {string|null} - The embed HTML or null if not a valid URL\n */\nexport const convertToEmbed = (url, options = {}) => {\n const parsed = parseMediaCMSUrl(url);\n if (!parsed) {\n return null;\n }\n return generateEmbedHtml(parsed, options);\n};\n"],"names":["MEDIACMS_VIEW_URL_PATTERN","parseMediaCMSUrl","text","trimmed","trim","match","baseUrl","videoId","originalUrl","isDomainAllowed","parsed","config","configuredBaseUrl","autoConvertBaseUrl","mediacmsBaseUrl","configuredUrl","URL","pastedUrl","host","e","generateEmbedHtml","options","embedUrl","searchParams","set","showTitle","showRelated","showUserAvatar","linkTitle","html","toString","editor","autoConvertEnabled","on","handlePasteEvent","handleInputEvent","clipboardData","window","getData","preventDefault","stopPropagation","embedHtml","autoConvertOptions","setTimeout","insertContent","selection","collapse","inputType","node","getNode","nodeName","textContent","trimmedHtml","innerHTML","startsWith","currentText","currentParsed","select","url"],"mappings":";;;;;;;;;;;;MAoCMA,0BAA4B,kDAQ5BC,iBAAoBC,WACjBA,MAAwB,iBAATA,YACT,WAGLC,QAAUD,KAAKE,OAGfC,MAAQF,QAAQE,MAAML,kCACxBK,MACO,CACHC,QAASD,MAAM,GACfE,QAASF,MAAM,GACfG,YAAaL,SAId,MAULM,gBAAkB,CAACC,OAAQC,gBAEvBC,kBAAoBD,OAAOE,oBAAsBF,OAAOG,oBACzDF,yBACM,YAKDG,cAAgB,IAAIC,IAAIJ,mBACxBK,UAAY,IAAID,IAAIN,OAAOJ,gBAC1BS,cAAcG,OAASD,UAAUC,KAC1C,MAAOC,UAEE,IAWTC,kBAAoB,SAACV,YAAQW,+DAAU,SAEnCC,SAAW,IAAIN,cAAON,OAAOJ,mBACnCgB,SAASC,aAAaC,IAAI,IAAKd,OAAOH,SAGtCe,SAASC,aAAaC,IAAI,aAAmC,IAAtBH,QAAQI,UAAsB,IAAM,KAC3EH,SAASC,aAAaC,IAAI,eAAuC,IAAxBH,QAAQK,YAAwB,IAAM,KAC/EJ,SAASC,aAAaC,IAAI,kBAA6C,IAA3BH,QAAQM,eAA2B,IAAM,KACrFL,SAASC,aAAaC,IAAI,aAAmC,IAAtBH,QAAQO,UAAsB,IAAM,WAGrEC,KAAO,sFAGDP,SAASQ,iBAHR,qDAOND,gCASsBE,eACvBpB,QAAS,oBAAQoB,SAAW,IAGA,IAA9BpB,OAAOqB,qBAKXD,OAAOE,GAAG,SAAUd,IAChBe,iBAAiBH,OAAQZ,EAAGR,WAIhCoB,OAAOE,GAAG,SAAUd,IAChBgB,iBAAiBJ,OAAQZ,EAAGR,mBAW9BuB,iBAAmB,CAACH,OAAQZ,EAAGR,gBAE3ByB,cAAgBjB,EAAEiB,eAAiBC,OAAOD,kBAC3CA,2BAKClC,KAAOkC,cAAcE,QAAQ,eAAiBF,cAAcE,QAAQ,YACrEpC,kBAKCQ,OAAST,iBAAiBC,UAC3BQ,kBAKAD,gBAAgBC,OAAQC,eAK7BQ,EAAEoB,iBACFpB,EAAEqB,wBAGIC,UAAYrB,kBAAkBV,OAAQC,OAAO+B,oBAAsB,IAGzEC,YAAW,KACPZ,OAAOa,cAAcH,WAErBV,OAAOc,UAAUC,UAAS,KAC3B,IAWDX,iBAAmB,CAACJ,OAAQZ,EAAGR,aAEb,oBAAhBQ,EAAE4B,WAAmD,eAAhB5B,EAAE4B,uBAKrCC,KAAOjB,OAAOc,UAAUI,cACzBD,MAA0B,MAAlBA,KAAKE,sBAKZhD,KAAO8C,KAAKG,aAAe,GAC3BzC,OAAST,iBAAiBC,UAE3BQ,SAAWD,gBAAgBC,OAAQC,qBAKlCyC,YAAcJ,KAAKK,UAAUjD,UAC/BgD,cAAgBlD,KAAKE,SAAWgD,YAAYE,WAAWpD,KAAKE,qBAK1DqC,UAAYrB,kBAAkBV,OAAQC,OAAO+B,oBAAsB,IAIzEC,YAAW,WAEDY,YAAcP,KAAKG,aAAe,GAClCK,cAAgBvD,iBAAiBsD,aAEnCC,eAAiBA,cAAchD,cAAgBE,OAAOF,cAEtDuB,OAAOc,UAAUY,OAAOT,MACxBjB,OAAOa,cAAcH,cAE1B,6BASuBvC,MACQ,OAA3BD,iBAAiBC,8BAUE,SAACwD,SAAKrC,+DAAU,SACpCX,OAAST,iBAAiByD,YAC3BhD,OAGEU,kBAAkBV,OAAQW,SAFtB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js deleted file mode 100755 index 3835b5e0..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/commands",["exports","core/str","./common","./iframeembed","editor_tiny/utils"],(function(_exports,_str,_common,_iframeembed,_utils){var obj; -/** - * Tiny Media commands. - * - * @module tiny_mediacms/commands - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.getSetup=void 0,_iframeembed=(obj=_iframeembed)&&obj.__esModule?obj:{default:obj};const isIframe=node=>"iframe"===node.nodeName.toLowerCase()||node.classList&&node.classList.contains("tiny-iframe-responsive")||node.classList&&node.classList.contains("tiny-mediacms-iframe-wrapper"),setupIframeOverlays=(editor,handleIframeAction)=>{const processIframes=()=>{const editorBody=editor.getBody();if(!editorBody)return;editorBody.querySelectorAll("iframe").forEach((iframe=>{var _iframe$parentElement;if(null!==(_iframe$parentElement=iframe.parentElement)&&void 0!==_iframe$parentElement&&_iframe$parentElement.classList.contains("tiny-mediacms-iframe-wrapper"))return;if(iframe.hasAttribute("data-mce-object")||iframe.hasAttribute("data-mce-placeholder"))return;const wrapper=editor.getDoc().createElement("div");wrapper.className="tiny-mediacms-iframe-wrapper",wrapper.setAttribute("contenteditable","false");const editBtn=editor.getDoc().createElement("button");editBtn.className="tiny-mediacms-edit-btn",editBtn.setAttribute("type","button"),editBtn.setAttribute("title","Edit video embed options"),editBtn.innerHTML='',iframe.parentNode.insertBefore(wrapper,iframe),wrapper.appendChild(iframe),wrapper.appendChild(editBtn)}))},handleOverlayClick=e=>{const editBtn=e.target.closest(".tiny-mediacms-edit-btn");if(!editBtn)return;e.preventDefault(),e.stopPropagation();const wrapper=editBtn.closest(".tiny-mediacms-iframe-wrapper");if(!wrapper)return;wrapper.querySelector("iframe")&&(editor.selection.select(wrapper),handleIframeAction())};editor.on("init",(()=>{(()=>{const editorDoc=editor.getDoc();if(!editorDoc)return;if(editorDoc.getElementById("tiny-mediacms-overlay-styles"))return;const style=editorDoc.createElement("style");style.id="tiny-mediacms-overlay-styles",style.textContent="\n .tiny-mediacms-iframe-wrapper {\n display: inline-block;\n position: relative;\n line-height: 0;\n vertical-align: top;\n }\n .tiny-mediacms-iframe-wrapper iframe {\n display: block;\n }\n .tiny-mediacms-edit-btn {\n position: absolute;\n top: 48px;\n left: 6px;\n width: 28px;\n height: 28px;\n background: #ffffff;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n z-index: 10;\n padding: 0;\n margin: 0;\n box-shadow: 0 2px 6px rgba(0,0,0,0.35);\n transition: transform 0.15s, box-shadow 0.15s;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n }\n .tiny-mediacms-edit-btn:hover {\n transform: scale(1.15);\n box-shadow: 0 3px 10px rgba(0,0,0,0.45);\n }\n .tiny-mediacms-edit-btn svg {\n width: 18px !important;\n height: 18px !important;\n display: block !important;\n }\n ",editorDoc.head.appendChild(style)})(),processIframes(),editor.getBody().addEventListener("click",handleOverlayClick)})),editor.on("SetContent",(()=>{processIframes()})),editor.on("PastePostProcess",(()=>{setTimeout(processIframes,100)})),editor.on("Undo Redo",(()=>{processIframes()})),editor.on("Change",(()=>{setTimeout(processIframes,50)})),editor.on("NodeChange",(()=>{processIframes()}))};_exports.getSetup=async()=>{const[iframeButtonText]=await(0,_str.getStrings)(["iframebuttontitle"].map((key=>({key:key,component:_common.component})))),[iframeButtonImage]=await Promise.all([(0,_utils.getButtonImage)("icon",_common.component)]);return editor=>{((editor,iframeButtonText,iframeButtonImage)=>{const handleIframeAction=()=>{new _iframeembed.default(editor).displayDialogue()};editor.ui.registry.addIcon(_common.iframeIcon,iframeButtonImage.html),editor.ui.registry.addToggleButton(_common.iframeButtonName,{icon:_common.iframeIcon,tooltip:iframeButtonText,onAction:handleIframeAction,onSetup:api=>editor.selection.selectorChangedWithUnbind("iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper",api.setActive).unbind}),editor.ui.registry.addMenuItem(_common.iframeMenuItemName,{icon:_common.iframeIcon,text:iframeButtonText,onAction:handleIframeAction}),editor.ui.registry.addContextToolbar(_common.iframeButtonName,{predicate:isIframe,items:_common.iframeButtonName,position:"node",scope:"node"}),editor.ui.registry.addContextMenu(_common.iframeButtonName,{update:isIframe}),setupIframeOverlays(editor,handleIframeAction)})(editor,iframeButtonText,iframeButtonImage)}}})); - -//# sourceMappingURL=commands.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map deleted file mode 100755 index c250916c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/commands.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"commands.min.js","sources":["../src/commands.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 * Tiny Media commands.\n *\n * @module tiny_mediacms/commands\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getStrings} from 'core/str';\nimport {\n component,\n iframeButtonName,\n iframeMenuItemName,\n iframeIcon,\n} from './common';\nimport IframeEmbed from './iframeembed';\nimport {getButtonImage} from 'editor_tiny/utils';\n\nconst isIframe = (node) => node.nodeName.toLowerCase() === 'iframe' ||\n (node.classList && node.classList.contains('tiny-iframe-responsive')) ||\n (node.classList && node.classList.contains('tiny-mediacms-iframe-wrapper'));\n\n/**\n * Wrap iframes with overlay containers that allow hover detection.\n * Since iframes capture mouse events, we add an invisible overlay on top\n * that shows the edit button on hover.\n *\n * @param {TinyMCE} editor - The editor instance\n * @param {Function} handleIframeAction - The action to perform when clicking the button\n */\nconst setupIframeOverlays = (editor, handleIframeAction) => {\n /**\n * Process all iframes in the editor and add overlay wrappers.\n */\n const processIframes = () => {\n const editorBody = editor.getBody();\n if (!editorBody) {\n return;\n }\n\n const iframes = editorBody.querySelectorAll('iframe');\n iframes.forEach((iframe) => {\n // Skip if already wrapped\n if (iframe.parentElement?.classList.contains('tiny-mediacms-iframe-wrapper')) {\n return;\n }\n\n // Skip TinyMCE internal iframes\n if (iframe.hasAttribute('data-mce-object') || iframe.hasAttribute('data-mce-placeholder')) {\n return;\n }\n\n // Create wrapper div\n const wrapper = editor.getDoc().createElement('div');\n wrapper.className = 'tiny-mediacms-iframe-wrapper';\n wrapper.setAttribute('contenteditable', 'false');\n\n // Create edit button (positioned inside wrapper, over the iframe)\n const editBtn = editor.getDoc().createElement('button');\n editBtn.className = 'tiny-mediacms-edit-btn';\n editBtn.setAttribute('type', 'button');\n editBtn.setAttribute('title', 'Edit video embed options');\n // Use clean inline SVG to avoid TinyMCE wrapper issues\n editBtn.innerHTML = '' +\n '' +\n '' +\n '';\n\n // Wrap the iframe: insert wrapper, move iframe into it, add button\n iframe.parentNode.insertBefore(wrapper, iframe);\n wrapper.appendChild(iframe);\n wrapper.appendChild(editBtn);\n });\n };\n\n /**\n * Add CSS styles for hover effects to the editor's document.\n */\n const addStyles = () => {\n const editorDoc = editor.getDoc();\n if (!editorDoc) {\n return;\n }\n\n // Check if styles already added\n if (editorDoc.getElementById('tiny-mediacms-overlay-styles')) {\n return;\n }\n\n const style = editorDoc.createElement('style');\n style.id = 'tiny-mediacms-overlay-styles';\n style.textContent = `\n .tiny-mediacms-iframe-wrapper {\n display: inline-block;\n position: relative;\n line-height: 0;\n vertical-align: top;\n }\n .tiny-mediacms-iframe-wrapper iframe {\n display: block;\n }\n .tiny-mediacms-edit-btn {\n position: absolute;\n top: 48px;\n left: 6px;\n width: 28px;\n height: 28px;\n background: #ffffff;\n border: none;\n border-radius: 50%;\n cursor: pointer;\n z-index: 10;\n padding: 0;\n margin: 0;\n box-shadow: 0 2px 6px rgba(0,0,0,0.35);\n transition: transform 0.15s, box-shadow 0.15s;\n display: flex;\n align-items: center;\n justify-content: center;\n box-sizing: border-box;\n }\n .tiny-mediacms-edit-btn:hover {\n transform: scale(1.15);\n box-shadow: 0 3px 10px rgba(0,0,0,0.45);\n }\n .tiny-mediacms-edit-btn svg {\n width: 18px !important;\n height: 18px !important;\n display: block !important;\n }\n `;\n editorDoc.head.appendChild(style);\n };\n\n /**\n * Handle click on the edit button.\n *\n * @param {Event} e - The click event\n */\n const handleOverlayClick = (e) => {\n const target = e.target;\n\n // Check if clicked on edit button or its child (svg/path)\n const editBtn = target.closest('.tiny-mediacms-edit-btn');\n if (!editBtn) {\n return;\n }\n\n e.preventDefault();\n e.stopPropagation();\n\n // Find the associated wrapper and iframe\n const wrapper = editBtn.closest('.tiny-mediacms-iframe-wrapper');\n if (!wrapper) {\n return;\n }\n\n const iframe = wrapper.querySelector('iframe');\n if (!iframe) {\n return;\n }\n\n // Select the wrapper so TinyMCE knows which element is selected\n editor.selection.select(wrapper);\n\n // Open the edit dialog\n handleIframeAction();\n };\n\n // Setup on editor init\n editor.on('init', () => {\n addStyles();\n processIframes();\n\n // Handle clicks on the overlay\n editor.getBody().addEventListener('click', handleOverlayClick);\n });\n\n // Re-process when content changes\n editor.on('SetContent', () => {\n processIframes();\n });\n\n // Re-process when content is pasted\n editor.on('PastePostProcess', () => {\n setTimeout(processIframes, 100);\n });\n\n // Re-process after undo/redo\n editor.on('Undo Redo', () => {\n processIframes();\n });\n\n // Re-process on any content change (covers modal updates)\n editor.on('Change', () => {\n setTimeout(processIframes, 50);\n });\n\n // Re-process when node changes (selection changes)\n editor.on('NodeChange', () => {\n processIframes();\n });\n};\n\nconst registerIframeCommand = (editor, iframeButtonText, iframeButtonImage) => {\n const handleIframeAction = () => {\n const iframeEmbed = new IframeEmbed(editor);\n iframeEmbed.displayDialogue();\n };\n\n // Register the iframe icon\n editor.ui.registry.addIcon(iframeIcon, iframeButtonImage.html);\n\n // Register the Menu Button as a toggle.\n // This means that when highlighted over an existing iframe element it will show as toggled on.\n editor.ui.registry.addToggleButton(iframeButtonName, {\n icon: iframeIcon,\n tooltip: iframeButtonText,\n onAction: handleIframeAction,\n onSetup: api => {\n return editor.selection.selectorChangedWithUnbind(\n 'iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper',\n api.setActive\n ).unbind;\n }\n });\n\n editor.ui.registry.addMenuItem(iframeMenuItemName, {\n icon: iframeIcon,\n text: iframeButtonText,\n onAction: handleIframeAction,\n });\n\n editor.ui.registry.addContextToolbar(iframeButtonName, {\n predicate: isIframe,\n items: iframeButtonName,\n position: 'node',\n scope: 'node'\n });\n\n editor.ui.registry.addContextMenu(iframeButtonName, {\n update: isIframe,\n });\n\n // Setup iframe overlays with edit button on hover\n setupIframeOverlays(editor, handleIframeAction);\n};\n\nexport const getSetup = async() => {\n const [\n iframeButtonText,\n ] = await getStrings([\n 'iframebuttontitle',\n ].map((key) => ({key, component})));\n\n const [\n iframeButtonImage,\n ] = await Promise.all([\n getButtonImage('icon', component),\n ]);\n\n // Note: The function returned here must be synchronous and cannot use promises.\n // All promises must be resolved prior to returning the function.\n return (editor) => {\n registerIframeCommand(editor, iframeButtonText, iframeButtonImage);\n };\n};\n"],"names":["isIframe","node","nodeName","toLowerCase","classList","contains","setupIframeOverlays","editor","handleIframeAction","processIframes","editorBody","getBody","querySelectorAll","forEach","iframe","parentElement","_iframe$parentElement","hasAttribute","wrapper","getDoc","createElement","className","setAttribute","editBtn","innerHTML","parentNode","insertBefore","appendChild","handleOverlayClick","e","target","closest","preventDefault","stopPropagation","querySelector","selection","select","on","editorDoc","getElementById","style","id","textContent","head","addStyles","addEventListener","setTimeout","async","iframeButtonText","map","key","component","iframeButtonImage","Promise","all","IframeEmbed","displayDialogue","ui","registry","addIcon","iframeIcon","html","addToggleButton","iframeButtonName","icon","tooltip","onAction","onSetup","api","selectorChangedWithUnbind","setActive","unbind","addMenuItem","iframeMenuItemName","text","addContextToolbar","predicate","items","position","scope","addContextMenu","update","registerIframeCommand"],"mappings":";;;;;;;8JAiCMA,SAAYC,MAAyC,WAAhCA,KAAKC,SAASC,eACpCF,KAAKG,WAAaH,KAAKG,UAAUC,SAAS,2BAC1CJ,KAAKG,WAAaH,KAAKG,UAAUC,SAAS,gCAUzCC,oBAAsB,CAACC,OAAQC,4BAI3BC,eAAiB,WACbC,WAAaH,OAAOI,cACrBD,kBAIWA,WAAWE,iBAAiB,UACpCC,SAASC,oEAETA,OAAOC,gDAAPC,sBAAsBZ,UAAUC,SAAS,0CAKzCS,OAAOG,aAAa,oBAAsBH,OAAOG,aAAa,qCAK5DC,QAAUX,OAAOY,SAASC,cAAc,OAC9CF,QAAQG,UAAY,+BACpBH,QAAQI,aAAa,kBAAmB,eAGlCC,QAAUhB,OAAOY,SAASC,cAAc,UAC9CG,QAAQF,UAAY,yBACpBE,QAAQD,aAAa,OAAQ,UAC7BC,QAAQD,aAAa,QAAS,4BAE9BC,QAAQC,UAAY,0KAMpBV,OAAOW,WAAWC,aAAaR,QAASJ,QACxCI,QAAQS,YAAYb,QACpBI,QAAQS,YAAYJ,aAoEtBK,mBAAsBC,UAIlBN,QAHSM,EAAEC,OAGMC,QAAQ,+BAC1BR,eAILM,EAAEG,iBACFH,EAAEI,wBAGIf,QAAUK,QAAQQ,QAAQ,qCAC3Bb,eAIUA,QAAQgB,cAAc,YAMrC3B,OAAO4B,UAAUC,OAAOlB,SAGxBV,uBAIJD,OAAO8B,GAAG,QAAQ,KA5FA,YACRC,UAAY/B,OAAOY,aACpBmB,oBAKDA,UAAUC,eAAe,6CAIvBC,MAAQF,UAAUlB,cAAc,SACtCoB,MAAMC,GAAK,+BACXD,MAAME,02CAwCNJ,UAAUK,KAAKhB,YAAYa,QAwC3BI,GACAnC,iBAGAF,OAAOI,UAAUkC,iBAAiB,QAASjB,uBAI/CrB,OAAO8B,GAAG,cAAc,KACpB5B,oBAIJF,OAAO8B,GAAG,oBAAoB,KAC1BS,WAAWrC,eAAgB,QAI/BF,OAAO8B,GAAG,aAAa,KACnB5B,oBAIJF,OAAO8B,GAAG,UAAU,KAChBS,WAAWrC,eAAgB,OAI/BF,OAAO8B,GAAG,cAAc,KACpB5B,uCAgDgBsC,gBAEhBC,wBACM,mBAAW,CACjB,qBACFC,KAAKC,OAAUA,IAAAA,IAAKC,UAAAA,wBAGlBC,yBACMC,QAAQC,IAAI,EAClB,yBAAe,OAAQH,4BAKnB5C,SA3DkB,EAACA,OAAQyC,iBAAkBI,2BAC/C5C,mBAAqB,KACH,IAAI+C,qBAAYhD,QACxBiD,mBAIhBjD,OAAOkD,GAAGC,SAASC,QAAQC,mBAAYR,kBAAkBS,MAIzDtD,OAAOkD,GAAGC,SAASI,gBAAgBC,yBAAkB,CACjDC,KAAMJ,mBACNK,QAASjB,iBACTkB,SAAU1D,mBACV2D,QAASC,KACE7D,OAAO4B,UAAUkC,0BACpB,kHACAD,IAAIE,WACNC,SAIVhE,OAAOkD,GAAGC,SAASc,YAAYC,2BAAoB,CAC/CT,KAAMJ,mBACNc,KAAM1B,iBACNkB,SAAU1D,qBAGdD,OAAOkD,GAAGC,SAASiB,kBAAkBZ,yBAAkB,CACnDa,UAAW5E,SACX6E,MAAOd,yBACPe,SAAU,OACVC,MAAO,SAGXxE,OAAOkD,GAAGC,SAASsB,eAAejB,yBAAkB,CAChDkB,OAAQjF,WAIZM,oBAAoBC,OAAQC,qBAmBxB0E,CAAsB3E,OAAQyC,iBAAkBI"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js deleted file mode 100755 index 7daa212e..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/common",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={pluginName:"tiny_mediacms/plugin",component:"tiny_mediacms",iframeButtonName:"tiny_mediacms_iframe",iframeMenuItemName:"tiny_mediacms_iframe",iframeIcon:"tiny_mediacms_iframe"},_exports.default})); - -//# sourceMappingURL=common.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map deleted file mode 100755 index 5d06ebcf..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/common.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"common.min.js","sources":["../src/common.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 * Tiny Media common values.\n *\n * @module tiny_mediacms/common\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n pluginName: 'tiny_mediacms/plugin',\n component: 'tiny_mediacms',\n iframeButtonName: 'tiny_mediacms_iframe',\n iframeMenuItemName: 'tiny_mediacms_iframe',\n iframeIcon: 'tiny_mediacms_iframe',\n};\n"],"names":["pluginName","component","iframeButtonName","iframeMenuItemName","iframeIcon"],"mappings":"sKAuBe,CACXA,WAAY,uBACZC,UAAW,gBACXC,iBAAkB,uBAClBC,mBAAoB,uBACpBC,WAAY"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js deleted file mode 100755 index c2265e1a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/configuration",["exports","./common","editor_tiny/utils"],(function(_exports,_common,_utils){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.configure=void 0;_exports.configure=instanceConfig=>{return{contextmenu:(0,_utils.addContextmenuItem)(instanceConfig.contextmenu,_common.iframeButtonName),menu:(menu=instanceConfig.menu,menu.insert.items="".concat(_common.iframeMenuItemName," ").concat(menu.insert.items),menu),toolbar:(toolbar=instanceConfig.toolbar,toolbar.map((section=>("content"===section.name&§ion.items.unshift(_common.iframeButtonName),section))))};var toolbar,menu}})); - -//# sourceMappingURL=configuration.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js.map deleted file mode 100755 index 635c33a4..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/configuration.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"configuration.min.js","sources":["../src/configuration.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 * Tiny Media configuration.\n *\n * @module tiny_mediacms/configuration\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {\n iframeButtonName,\n iframeMenuItemName,\n} from './common';\nimport {\n addContextmenuItem,\n} from 'editor_tiny/utils';\n\nconst configureMenu = (menu) => {\n // Add the Iframe Embed to the insert menu.\n menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`;\n\n return menu;\n};\n\nconst configureToolbar = (toolbar) => {\n // The toolbar contains an array of named sections.\n // The Moodle integration ensures that there is a section called 'content'.\n\n return toolbar.map((section) => {\n if (section.name === 'content') {\n // Insert the iframe button at the start of it.\n section.items.unshift(iframeButtonName);\n }\n\n return section;\n });\n};\n\nexport const configure = (instanceConfig) => {\n // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars.\n return {\n contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName),\n menu: configureMenu(instanceConfig.menu),\n toolbar: configureToolbar(instanceConfig.toolbar),\n };\n};\n"],"names":["instanceConfig","contextmenu","iframeButtonName","menu","insert","items","iframeMenuItemName","toolbar","map","section","name","unshift"],"mappings":"wNAoD0BA,uBAEf,CACHC,aAAa,6BAAmBD,eAAeC,YAAaC,0BAC5DC,MAzBeA,KAyBKH,eAAeG,KAvBvCA,KAAKC,OAAOC,gBAAWC,uCAAsBH,KAAKC,OAAOC,OAElDF,MAsBHI,SAnBkBA,QAmBQP,eAAeO,QAftCA,QAAQC,KAAKC,UACK,YAAjBA,QAAQC,MAERD,QAAQJ,MAAMM,QAAQT,0BAGnBO,aAVWF,IAAAA,QAPHJ"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js deleted file mode 100755 index 5a81b3b7..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/embed",["exports","core/templates","core/str","core/modal_events","editor_tiny/utils","editor_tiny/options","./common","./embedmodal","./selectors","./options"],(function(_exports,_templates,_str,ModalEvents,_utils,_options,_common,_embedmodal,_selectors,_options2){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_embedmodal=_interopRequireDefault(_embedmodal),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"canShowFilePickerPoster",!1),_defineProperty(this,"canShowFilePickerTrack",!1),_defineProperty(this,"helpStrings",null),_defineProperty(this,"isUpdating",!1),_defineProperty(this,"selectedMedia",null);const permissions=(0,_options2.getEmbedPermissions)(editor);this.canShowFilePicker=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"media"),this.canShowFilePickerPoster=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"image"),this.canShowFilePickerTrack=permissions.filepicker&&void 0!==(0,_options.getFilePicker)(editor,"subtitle"),this.editor=editor}async getHelpStrings(){if(!this.helpStrings){const[addSource,tracks,subtitles,captions,descriptions,chapters,metadata]=await(0,_str.getStrings)(["addsource_help","tracks_help","subtitles_help","captions_help","descriptions_help","chapters_help","metadata_help"].map((key=>({key:key,component:_common.component}))));this.helpStrings={addSource:addSource,tracks:tracks,subtitles:subtitles,captions:captions,descriptions:descriptions,chapters:chapters,metadata:metadata}}return this.helpStrings}async getTemplateContext(data){const languages=this.prepareMoodleLang(),helpIcons=Array.from(Object.entries(await this.getHelpStrings())).forEach((_ref=>{let[key,text]=_ref;data["".concat(key.toLowerCase(),"helpicon")]={text:text}}));return Object.assign({},{elementid:this.editor.getElement().id,showfilepicker:this.canShowFilePicker,showfilepickerposter:this.canShowFilePickerPoster,showfilepickertrack:this.canShowFilePickerTrack,langsinstalled:languages.installed,langsavailable:languages.available,link:!0,video:!1,audio:!1,isupdating:this.isUpdating},data,helpIcons)}async displayDialogue(){this.selectedMedia=this.getSelectedMedia();const data=Object.assign({},this.getCurrentEmbedData());this.isUpdating=0!==Object.keys(data).length,this.currentModal=await _embedmodal.default.create({title:(0,_str.getString)("createmedia","tiny_mediacms"),templateContext:await this.getTemplateContext(data)}),await this.registerEventListeners(this.currentModal)}getCurrentEmbedData(){const properties=this.getMediumProperties();if(!properties)return{};const processedProperties={};return processedProperties[properties.type.toLowerCase()]=properties,processedProperties.link=!1,processedProperties}getSelectedMedia(){const mediaElm=this.editor.selection.getNode();return mediaElm?"video"===mediaElm.nodeName.toLowerCase()||"audio"===mediaElm.nodeName.toLowerCase()?mediaElm:mediaElm.querySelector("video")?mediaElm.querySelector("video"):mediaElm.querySelector("audio")?mediaElm.querySelector("audio"):null:null}getMediumProperties(){const boolAttr=(elem,attr)=>elem.hasAttribute(attr)&&(elem.getAttribute(attr)||""===elem.getAttribute(attr)),tracks={subtitles:[],captions:[],descriptions:[],chapters:[],metadata:[]},sources=[],medium=this.selectedMedia;return medium?(medium.querySelectorAll("track").forEach((track=>{tracks[track.getAttribute("kind")].push({src:track.getAttribute("src"),srclang:track.getAttribute("srclang"),label:track.getAttribute("label"),defaultTrack:boolAttr(track,"default")})})),medium.querySelectorAll("source").forEach((source=>{sources.push(source.src)})),{type:"video"===medium.nodeName.toLowerCase()?_selectors.default.EMBED.mediaTypes.video:_selectors.default.EMBED.mediaTypes.audio,sources:sources,poster:medium.getAttribute("poster"),title:medium.getAttribute("title"),width:medium.getAttribute("width"),height:medium.getAttribute("height"),autoplay:boolAttr(medium,"autoplay"),loop:boolAttr(medium,"loop"),muted:boolAttr(medium,"muted"),controls:boolAttr(medium,"controls"),tracks:tracks}):null}prepareMoodleLang(){const moodleLangs=(0,_options.getMoodleLang)(this.editor),currentLanguage=(0,_options.getCurrentLanguage)(this.editor);return{installed:Object.entries(moodleLangs.installed).map((_ref2=>{let[lang,code]=_ref2;return{lang:lang,code:code,default:lang===currentLanguage}})),available:Object.entries(moodleLangs.available).map((_ref3=>{let[lang,code]=_ref3;return{lang:lang,code:code,default:lang===currentLanguage}}))}}getMoodleLangObj(subtitleLang){const{available:available}=(0,_options.getMoodleLang)(this.editor);return available[subtitleLang]?{lang:subtitleLang,code:available[subtitleLang]}:null}filePickerCallback(params,element,fpType){if(""!==params.url){const tabPane=element.closest(".tab-pane");if(element.closest(_selectors.default.EMBED.elements.source).querySelector(_selectors.default.EMBED.elements.url).value=params.url,tabPane.id===this.editor.getElement().id+"_"+_selectors.default.EMBED.mediaTypes.link.toLowerCase()&&(tabPane.querySelector(_selectors.default.EMBED.elements.name).value=params.file),"subtitle"===fpType){const subtitleLang=params.file.split(".vtt")[0].split("-").slice(-1)[0],langObj=this.getMoodleLangObj(subtitleLang);if(langObj){const track=element.closest(_selectors.default.EMBED.elements.track);track.querySelector(_selectors.default.EMBED.elements.trackLabel).value=langObj.lang.trim(),track.querySelector(_selectors.default.EMBED.elements.trackLang).value=langObj.code}}}}addMediaSourceComponent(element,callback){const sourceElement=element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource),clone=sourceElement.cloneNode(!0);sourceElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),sourceElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),sourceElement.parentNode.insertBefore(clone,sourceElement.nextSibling),callback&&callback(clone)}removeMediaSourceComponent(element){element.closest(_selectors.default.EMBED.elements.source+_selectors.default.EMBED.elements.mediaSource).remove()}addTrackComponent(element,callback){const trackElement=element.closest(_selectors.default.EMBED.elements.track),clone=trackElement.cloneNode(!0);trackElement.querySelector(".removecomponent-wrapper").classList.remove("hidden"),trackElement.querySelector(".addcomponent-wrapper").classList.add("hidden"),trackElement.parentNode.insertBefore(clone,trackElement.nextSibling),callback&&callback(clone)}removeTrackComponent(element){element.closest(_selectors.default.EMBED.elements.track).remove()}getMediumTypeFromTabPane(tabPane){return tabPane.getAttribute("data-medium-type")}getTrackTypeFromTabPane(tabPane){return tabPane.getAttribute("data-track-kind")}getMediaHTML(form){const mediumType=this.getMediumTypeFromTabPane(form.querySelector(".root.tab-content > .tab-pane.active")),tabContent=form.querySelector(_selectors.default.EMBED.elements[mediumType.toLowerCase()+"Pane"]);return this["getMediaHTML"+mediumType[0].toUpperCase()+mediumType.substr(1)](tabContent)}getMediaHTMLLink(tab){const context={url:tab.querySelector(_selectors.default.EMBED.elements.url).value,name:tab.querySelector(_selectors.default.EMBED.elements.name).value||!1};return context.url?_templates.default.renderForPromise("tiny_mediacms/embed_media_link",context):""}getMediaHTMLVideo(tab){const context=this.getContextForMediaHTML(tab);return context.width=tab.querySelector(_selectors.default.EMBED.elements.width).value||!1,context.height=tab.querySelector(_selectors.default.EMBED.elements.height).value||!1,context.poster=tab.querySelector("".concat(_selectors.default.EMBED.elements.posterSource," ").concat(_selectors.default.EMBED.elements.url)).value||!1,context.sources.length?_templates.default.renderForPromise("tiny_mediacms/embed_media_video",context):""}getMediaHTMLAudio(tab){const context=this.getContextForMediaHTML(tab);return context.sources.length?_templates.default.renderForPromise("tiny_mediacms/embed_media_audio",context):""}getContextForMediaHTML(tab){const tracks=Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.track)).map((track=>({track:track.querySelector(_selectors.default.EMBED.elements.trackSource+" "+_selectors.default.EMBED.elements.url).value,kind:this.getTrackTypeFromTabPane(track.closest(".tab-pane")),label:track.querySelector(_selectors.default.EMBED.elements.trackLabel).value||track.querySelector(_selectors.default.EMBED.elements.trackLang).value,srclang:track.querySelector(_selectors.default.EMBED.elements.trackLang).value,defaultTrack:track.querySelector(_selectors.default.EMBED.elements.trackDefault).checked?"true":null}))).filter((track=>!!track.track));return{sources:Array.from(tab.querySelectorAll(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url)).filter((source=>!!source.value)).map((source=>source.value)),description:tab.querySelector(_selectors.default.EMBED.elements.mediaSource+" "+_selectors.default.EMBED.elements.url).value||!1,tracks:tracks,showControls:tab.querySelector(_selectors.default.EMBED.elements.mediaControl).checked,autoplay:tab.querySelector(_selectors.default.EMBED.elements.mediaAutoplay).checked,muted:tab.querySelector(_selectors.default.EMBED.elements.mediaMute).checked,loop:tab.querySelector(_selectors.default.EMBED.elements.mediaLoop).checked,title:tab.querySelector(_selectors.default.EMBED.elements.title).value||!1}}getFilepickerTypeFromElement(element){return element.closest(_selectors.default.EMBED.elements.posterSource)?"image":element.closest(_selectors.default.EMBED.elements.trackSource)?"subtitle":"media"}async clickHandler(e){const element=e.target;if(element.closest(_selectors.default.EMBED.actions.mediaBrowser)){e.preventDefault();const fpType=this.getFilepickerTypeFromElement(element),params=await(0,_utils.displayFilepicker)(this.editor,fpType);this.filePickerCallback(params,element,fpType)}element.closest(_selectors.default.EMBED.elements.mediaSource+" .addcomponent")&&(e.preventDefault(),this.addMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.mediaSource+" .removecomponent")&&(e.preventDefault(),this.removeMediaSourceComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .addcomponent")&&(e.preventDefault(),this.addTrackComponent(element));element.closest(_selectors.default.EMBED.elements.track+" .removecomponent")&&(e.preventDefault(),this.removeTrackComponent(element));const trackDefaultAction=element.closest(_selectors.default.EMBED.elements.trackDefault);if(trackDefaultAction&&trackDefaultAction.checked){const getKind=el=>this.getTrackTypeFromTabPane(el.parentElement.closest(".tab-pane"));element.parentElement.closest(".root.tab-content").querySelectorAll(_selectors.default.EMBED.elements.trackDefault).forEach((select=>{select!==element&&getKind(element)===getKind(select)&&(select.checked=!1)}))}}async handleDialogueSubmission(event,modal){const{html:html}=await this.getMediaHTML(modal.getRoot()[0]);html&&(this.isUpdating?(this.selectedMedia.outerHTML=html,this.isUpdating=!1):this.editor.insertContent(html))}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0];(this.canShowFilePicker||this.canShowFilePickerPoster||this.canShowFilePickerTrack)&&root.addEventListener("click",this.clickHandler.bind(this)),$root.on(ModalEvents.save,this.handleDialogueSubmission.bind(this)),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()})),$root.on(ModalEvents.shown,(()=>{root.querySelectorAll(_selectors.default.EMBED.elements.trackLang).forEach((dropdown=>{const defaultVal=dropdown.getAttribute("data-value");defaultVal&&(dropdown.value=defaultVal)}))}))}},_exports.default})); - -//# sourceMappingURL=embed.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map deleted file mode 100755 index ea4585b0..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embed.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"embed.min.js","sources":["../src/embed.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 * Tiny Media plugin Embed class for Moodle.\n *\n * @module tiny_mediacms/embed\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {\n getString,\n getStrings,\n} from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options';\nimport {component} from \"./common\";\nimport EmbedModal from './embedmodal';\nimport Selectors from './selectors';\nimport {getEmbedPermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\n\nexport default class MediaEmbed {\n editor = null;\n canShowFilePicker = false;\n canShowFilePickerPoster = false;\n canShowFilePickerTrack = false;\n\n /**\n * @property {Object} The names of the alignment options.\n */\n helpStrings = null;\n\n /**\n * @property {boolean} Indicate that the user is updating the media or not.\n */\n isUpdating = false;\n\n /**\n * @property {Object} The currently selected media.\n */\n selectedMedia = null;\n\n constructor(editor) {\n const permissions = getEmbedPermissions(editor);\n\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined');\n this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined');\n this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined');\n\n this.editor = editor;\n }\n\n async getHelpStrings() {\n if (!this.helpStrings) {\n const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([\n 'addsource_help',\n 'tracks_help',\n 'subtitles_help',\n 'captions_help',\n 'descriptions_help',\n 'chapters_help',\n 'metadata_help',\n ].map((key) => ({\n key,\n component,\n })));\n\n this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata};\n }\n\n return this.helpStrings;\n }\n\n async getTemplateContext(data) {\n const languages = this.prepareMoodleLang();\n\n const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => {\n data[`${key.toLowerCase()}helpicon`] = {text};\n });\n\n return Object.assign({}, {\n elementid: this.editor.getElement().id,\n showfilepicker: this.canShowFilePicker,\n showfilepickerposter: this.canShowFilePickerPoster,\n showfilepickertrack: this.canShowFilePickerTrack,\n langsinstalled: languages.installed,\n langsavailable: languages.available,\n link: true,\n video: false,\n audio: false,\n isupdating: this.isUpdating,\n }, data, helpIcons);\n }\n\n async displayDialogue() {\n this.selectedMedia = this.getSelectedMedia();\n const data = Object.assign({}, this.getCurrentEmbedData());\n this.isUpdating = Object.keys(data).length !== 0;\n\n this.currentModal = await EmbedModal.create({\n title: getString('createmedia', 'tiny_mediacms'),\n templateContext: await this.getTemplateContext(data),\n });\n\n await this.registerEventListeners(this.currentModal);\n }\n\n getCurrentEmbedData() {\n const properties = this.getMediumProperties();\n if (!properties) {\n return {};\n }\n\n const processedProperties = {};\n processedProperties[properties.type.toLowerCase()] = properties;\n processedProperties.link = false;\n\n return processedProperties;\n }\n\n getSelectedMedia() {\n const mediaElm = this.editor.selection.getNode();\n\n if (!mediaElm) {\n return null;\n }\n\n if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') {\n return mediaElm;\n }\n\n if (mediaElm.querySelector('video')) {\n return mediaElm.querySelector('video');\n }\n\n if (mediaElm.querySelector('audio')) {\n return mediaElm.querySelector('audio');\n }\n\n return null;\n }\n\n getMediumProperties() {\n const boolAttr = (elem, attr) => {\n // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes.\n // So in order to check if attr=\"true\", we need to check if the attribute exists and if the value is empty or true.\n return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === ''));\n };\n\n const tracks = {\n subtitles: [],\n captions: [],\n descriptions: [],\n chapters: [],\n metadata: []\n };\n const sources = [];\n\n const medium = this.selectedMedia;\n if (!medium) {\n return null;\n }\n medium.querySelectorAll('track').forEach((track) => {\n tracks[track.getAttribute('kind')].push({\n src: track.getAttribute('src'),\n srclang: track.getAttribute('srclang'),\n label: track.getAttribute('label'),\n defaultTrack: boolAttr(track, 'default')\n });\n });\n\n medium.querySelectorAll('source').forEach((source) => {\n sources.push(source.src);\n });\n\n return {\n type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio,\n sources,\n poster: medium.getAttribute('poster'),\n title: medium.getAttribute('title'),\n width: medium.getAttribute('width'),\n height: medium.getAttribute('height'),\n autoplay: boolAttr(medium, 'autoplay'),\n loop: boolAttr(medium, 'loop'),\n muted: boolAttr(medium, 'muted'),\n controls: boolAttr(medium, 'controls'),\n tracks,\n };\n }\n\n prepareMoodleLang() {\n const moodleLangs = getMoodleLang(this.editor);\n const currentLanguage = getCurrentLanguage(this.editor);\n\n const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({\n lang,\n code,\n \"default\": lang === currentLanguage,\n }));\n\n return {\n installed,\n available,\n };\n }\n\n getMoodleLangObj(subtitleLang) {\n const {available} = getMoodleLang(this.editor);\n\n if (available[subtitleLang]) {\n return {\n lang: subtitleLang,\n code: available[subtitleLang],\n };\n }\n\n return null;\n }\n\n filePickerCallback(params, element, fpType) {\n if (params.url !== '') {\n const tabPane = element.closest('.tab-pane');\n element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url;\n\n if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) {\n tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file;\n }\n\n if (fpType === 'subtitle') {\n // If the file is subtitle file. We need to match the language and label for that file.\n const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0];\n const langObj = this.getMoodleLangObj(subtitleLang);\n if (langObj) {\n const track = element.closest(Selectors.EMBED.elements.track);\n track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim();\n track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code;\n }\n }\n }\n }\n\n addMediaSourceComponent(element, callback) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n const clone = sourceElement.cloneNode(true);\n\n sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeMediaSourceComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource);\n sourceElement.remove();\n }\n\n addTrackComponent(element, callback) {\n const trackElement = element.closest(Selectors.EMBED.elements.track);\n const clone = trackElement.cloneNode(true);\n\n trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden');\n trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden');\n\n trackElement.parentNode.insertBefore(clone, trackElement.nextSibling);\n\n if (callback) {\n callback(clone);\n }\n }\n\n removeTrackComponent(element) {\n const sourceElement = element.closest(Selectors.EMBED.elements.track);\n sourceElement.remove();\n }\n\n getMediumTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-medium-type');\n }\n\n getTrackTypeFromTabPane(tabPane) {\n return tabPane.getAttribute('data-track-kind');\n }\n\n getMediaHTML(form) {\n const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active'));\n const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']);\n\n return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent);\n }\n\n getMediaHTMLLink(tab) {\n const context = {\n url: tab.querySelector(Selectors.EMBED.elements.url).value,\n name: tab.querySelector(Selectors.EMBED.elements.name).value || false\n };\n\n return context.url ? Templates.renderForPromise('tiny_mediacms/embed_media_link', context) : '';\n }\n\n getMediaHTMLVideo(tab) {\n const context = this.getContextForMediaHTML(tab);\n context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false;\n context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false;\n context.poster = tab.querySelector(\n `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}`\n ).value || false;\n\n return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_video', context) : '';\n }\n\n getMediaHTMLAudio(tab) {\n const context = this.getContextForMediaHTML(tab);\n\n return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_audio', context) : '';\n }\n\n getContextForMediaHTML(tab) {\n const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({\n track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value,\n kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')),\n label: track.querySelector(Selectors.EMBED.elements.trackLabel).value ||\n track.querySelector(Selectors.EMBED.elements.trackLang).value,\n srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value,\n defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? \"true\" : null\n })).filter((track) => !!track.track);\n\n const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url))\n .filter((source) => !!source.value)\n .map((source) => source.value);\n\n return {\n sources,\n description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' '\n + Selectors.EMBED.elements.url).value || false,\n tracks,\n showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked,\n autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked,\n muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked,\n loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked,\n title: tab.querySelector(Selectors.EMBED.elements.title).value || false\n };\n }\n\n getFilepickerTypeFromElement(element) {\n if (element.closest(Selectors.EMBED.elements.posterSource)) {\n return 'image';\n }\n if (element.closest(Selectors.EMBED.elements.trackSource)) {\n return 'subtitle';\n }\n\n return 'media';\n }\n\n async clickHandler(e) {\n const element = e.target;\n\n const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser);\n if (mediaBrowser) {\n e.preventDefault();\n const fpType = this.getFilepickerTypeFromElement(element);\n const params = await displayFilepicker(this.editor, fpType);\n this.filePickerCallback(params, element, fpType);\n }\n\n const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent');\n if (addComponentSourceAction) {\n e.preventDefault();\n this.addMediaSourceComponent(element);\n }\n\n const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent');\n if (removeComponentSourceAction) {\n e.preventDefault();\n this.removeMediaSourceComponent(element);\n }\n\n const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent');\n if (addComponentTrackAction) {\n e.preventDefault();\n this.addTrackComponent(element);\n }\n\n const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent');\n if (removeComponentTrackAction) {\n e.preventDefault();\n this.removeTrackComponent(element);\n }\n\n // Only allow one track per tab to be selected as \"default\".\n const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault);\n if (trackDefaultAction && trackDefaultAction.checked) {\n const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane'));\n\n element.parentElement\n .closest('.root.tab-content')\n .querySelectorAll(Selectors.EMBED.elements.trackDefault)\n .forEach((select) => {\n if (select !== element && getKind(element) === getKind(select)) {\n select.checked = false;\n }\n });\n }\n }\n\n async handleDialogueSubmission(event, modal) {\n const {html} = await this.getMediaHTML(modal.getRoot()[0]);\n if (html) {\n if (this.isUpdating) {\n this.selectedMedia.outerHTML = html;\n this.isUpdating = false;\n } else {\n this.editor.insertContent(html);\n }\n }\n }\n\n async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) {\n root.addEventListener('click', this.clickHandler.bind(this));\n }\n\n $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n $root.on(ModalEvents.shown, () => {\n root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => {\n const defaultVal = dropdown.getAttribute('data-value');\n if (defaultVal) {\n dropdown.value = defaultVal;\n }\n });\n });\n }\n}\n"],"names":["constructor","editor","permissions","canShowFilePicker","filepicker","canShowFilePickerPoster","canShowFilePickerTrack","this","helpStrings","addSource","tracks","subtitles","captions","descriptions","chapters","metadata","map","key","component","data","languages","prepareMoodleLang","helpIcons","Array","from","Object","entries","getHelpStrings","forEach","_ref","text","toLowerCase","assign","elementid","getElement","id","showfilepicker","showfilepickerposter","showfilepickertrack","langsinstalled","installed","langsavailable","available","link","video","audio","isupdating","isUpdating","selectedMedia","getSelectedMedia","getCurrentEmbedData","keys","length","currentModal","EmbedModal","create","title","templateContext","getTemplateContext","registerEventListeners","properties","getMediumProperties","processedProperties","type","mediaElm","selection","getNode","nodeName","querySelector","boolAttr","elem","attr","hasAttribute","getAttribute","sources","medium","querySelectorAll","track","push","src","srclang","label","defaultTrack","source","Selectors","EMBED","mediaTypes","poster","width","height","autoplay","loop","muted","controls","moodleLangs","currentLanguage","_ref2","lang","code","_ref3","getMoodleLangObj","subtitleLang","filePickerCallback","params","element","fpType","url","tabPane","closest","elements","value","name","file","split","slice","langObj","trackLabel","trim","trackLang","addMediaSourceComponent","callback","sourceElement","mediaSource","clone","cloneNode","classList","remove","add","parentNode","insertBefore","nextSibling","removeMediaSourceComponent","addTrackComponent","trackElement","removeTrackComponent","getMediumTypeFromTabPane","getTrackTypeFromTabPane","getMediaHTML","form","mediumType","tabContent","toUpperCase","substr","getMediaHTMLLink","tab","context","Templates","renderForPromise","getMediaHTMLVideo","getContextForMediaHTML","posterSource","getMediaHTMLAudio","trackSource","kind","trackDefault","checked","filter","description","showControls","mediaControl","mediaAutoplay","mediaMute","mediaLoop","getFilepickerTypeFromElement","e","target","actions","mediaBrowser","preventDefault","trackDefaultAction","getKind","el","parentElement","select","event","modal","html","getRoot","outerHTML","insertContent","getBody","$root","root","addEventListener","clickHandler","bind","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","shown","dropdown","defaultVal"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js deleted file mode 100755 index 70ad4aa9..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/embedmodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class EmbedModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=EmbedModal,_defineProperty(EmbedModal,"TYPE","".concat(_common.component,"/modal")),_defineProperty(EmbedModal,"TEMPLATE","".concat(_common.component,"/embed_media_modal")),_exports.default})); - -//# sourceMappingURL=embedmodal.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js.map deleted file mode 100755 index d51705a6..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/embedmodal.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"embedmodal.min.js","sources":["../src/embedmodal.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 * Embedded Media Management Modal for Tiny.\n *\n * @module tiny_mediacms/embedmodal\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class EmbedModal extends Modal {\n static TYPE = `${component}/modal`;\n static TEMPLATE = `${component}/embed_media_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["EmbedModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,6CADAV,gCAEIU"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js deleted file mode 100755 index 4b79b677..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/iframeembed",["exports","core/templates","core/str","core/modal_events","./common","./iframemodal","./selectors","./options"],(function(_exports,_templates,_str,ModalEvents,_common,_iframemodal,_selectors,_options){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_iframemodal=_interopRequireDefault(_iframemodal),_selectors=_interopRequireDefault(_selectors);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"isUpdating",!1),_defineProperty(this,"selectedIframe",null),_defineProperty(this,"debounceTimer",null),_defineProperty(this,"iframeLibraryLoaded",!1),_defineProperty(this,"selectedLibraryVideo",null),_defineProperty(this,"iframeLibraryUrl","https://temp.web357.com/mediacms/deic-mediacms-embed-videos.html"),this.editor=editor}parseInput(input){if(!input||!input.trim())return null;const iframeMatch=(input=input.trim()).match(/]*src=["']([^"']+)["'][^>]*>/i);return iframeMatch?this.parseEmbedUrl(iframeMatch[1]):input.startsWith("http://")||input.startsWith("https://")?this.parseVideoUrl(input):null}parseVideoUrl(url){try{const urlObj=new URL(url),baseUrl="".concat(urlObj.protocol,"//").concat(urlObj.host);if("/view"===urlObj.pathname&&urlObj.searchParams.has("m"))return{baseUrl:baseUrl,videoId:urlObj.searchParams.get("m"),isEmbed:!1};if("/embed"===urlObj.pathname&&urlObj.searchParams.has("m")){const tParam=urlObj.searchParams.get("t");return{baseUrl:baseUrl,videoId:urlObj.searchParams.get("m"),isEmbed:!0,showTitle:"1"===urlObj.searchParams.get("showTitle"),linkTitle:"1"===urlObj.searchParams.get("linkTitle"),showRelated:"1"===urlObj.searchParams.get("showRelated"),showUserAvatar:"1"===urlObj.searchParams.get("showUserAvatar"),startAt:tParam?this.secondsToTimeString(parseInt(tParam)):null}}return{baseUrl:baseUrl,rawUrl:url,isGeneric:!0}}catch(e){return null}}parseEmbedUrl(url){return this.parseVideoUrl(url)}secondsToTimeString(seconds){const mins=Math.floor(seconds/60),secs=seconds%60;return"".concat(mins,":").concat(secs.toString().padStart(2,"0"))}timeStringToSeconds(timeStr){if(!timeStr||!timeStr.trim())return null;if((timeStr=timeStr.trim()).includes(":")){const parts=timeStr.split(":");return 60*(parseInt(parts[0])||0)+(parseInt(parts[1])||0)}const secs=parseInt(timeStr);return isNaN(secs)?null:secs}buildEmbedUrl(parsed,options){if(parsed.isGeneric)return parsed.rawUrl;const url=new URL("".concat(parsed.baseUrl,"/embed"));if(url.searchParams.set("m",parsed.videoId),url.searchParams.set("showTitle",options.showTitle?"1":"0"),url.searchParams.set("showRelated",options.showRelated?"1":"0"),url.searchParams.set("showUserAvatar",options.showUserAvatar?"1":"0"),url.searchParams.set("linkTitle",options.linkTitle?"1":"0"),options.startAtEnabled&&options.startAt){const seconds=this.timeStringToSeconds(options.startAt);null!==seconds&&seconds>0&&url.searchParams.set("t",seconds.toString())}return url.toString()}async getTemplateContext(){let data=arguments.length>0&&void 0!==arguments[0]?arguments[0]:{};return{elementid:this.editor.getElement().id,isupdating:this.isUpdating,url:data.url||"",showTitle:!1!==data.showTitle,linkTitle:!1!==data.linkTitle,showRelated:!1!==data.showRelated,showUserAvatar:!1!==data.showUserAvatar,responsive:!1!==data.responsive,startAtEnabled:data.startAtEnabled||!1,startAt:data.startAt||"0:00",width:data.width||560,height:data.height||315,is16_9:!data.aspectRatio||"16:9"===data.aspectRatio,is4_3:"4:3"===data.aspectRatio,is1_1:"1:1"===data.aspectRatio,isCustom:"custom"===data.aspectRatio}}async displayDialogue(){this.selectedIframe=this.getSelectedIframe();const data=this.getCurrentIframeData();this.isUpdating=null!==data,this.iframeLibraryLoaded=!1,this.currentModal=await _iframemodal.default.create({title:(0,_str.getString)("iframemodaltitle",_common.component),templateContext:await this.getTemplateContext(data||{})}),await this.registerEventListeners(this.currentModal)}getSelectedIframe(){const node=this.editor.selection.getNode();if("iframe"===node.nodeName.toLowerCase())return node;const iframe=node.querySelector("iframe");if(iframe)return iframe;const wrapper=node.closest(".tiny-mediacms-iframe-wrapper")||node.closest(".tiny-iframe-responsive");return wrapper?wrapper.querySelector("iframe"):null}getCurrentIframeData(){var _parsed$showTitle,_parsed$linkTitle,_parsed$showRelated,_parsed$showUserAvata;if(!this.selectedIframe)return null;const src=this.selectedIframe.getAttribute("src"),parsed=this.parseInput(src),isResponsive=(this.selectedIframe.getAttribute("style")||"").includes("aspect-ratio");return{url:src,width:this.selectedIframe.getAttribute("width")||560,height:this.selectedIframe.getAttribute("height")||315,showTitle:null===(_parsed$showTitle=null==parsed?void 0:parsed.showTitle)||void 0===_parsed$showTitle||_parsed$showTitle,linkTitle:null===(_parsed$linkTitle=null==parsed?void 0:parsed.linkTitle)||void 0===_parsed$linkTitle||_parsed$linkTitle,showRelated:null===(_parsed$showRelated=null==parsed?void 0:parsed.showRelated)||void 0===_parsed$showRelated||_parsed$showRelated,showUserAvatar:null===(_parsed$showUserAvata=null==parsed?void 0:parsed.showUserAvatar)||void 0===_parsed$showUserAvata||_parsed$showUserAvata,responsive:isResponsive,startAtEnabled:null!==(null==parsed?void 0:parsed.startAt),startAt:(null==parsed?void 0:parsed.startAt)||"0:00"}}getFormValues(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form);return{url:form.querySelector(_selectors.default.IFRAME.elements.url).value.trim(),showTitle:form.querySelector(_selectors.default.IFRAME.elements.showTitle).checked,linkTitle:form.querySelector(_selectors.default.IFRAME.elements.linkTitle).checked,showRelated:form.querySelector(_selectors.default.IFRAME.elements.showRelated).checked,showUserAvatar:form.querySelector(_selectors.default.IFRAME.elements.showUserAvatar).checked,responsive:form.querySelector(_selectors.default.IFRAME.elements.responsive).checked,startAtEnabled:form.querySelector(_selectors.default.IFRAME.elements.startAtEnabled).checked,startAt:form.querySelector(_selectors.default.IFRAME.elements.startAt).value.trim(),aspectRatio:form.querySelector(_selectors.default.IFRAME.elements.aspectRatio).value,width:parseInt(form.querySelector(_selectors.default.IFRAME.elements.width).value)||560,height:parseInt(form.querySelector(_selectors.default.IFRAME.elements.height).value)||315}}async generateIframeHtml(values){const parsed=this.parseInput(values.url);if(!parsed)return"";const embedUrl=this.buildEmbedUrl(parsed,values),aspectRatioCalcs={"16:9":"16 / 9","4:3":"4 / 3","1:1":"1 / 1",custom:"".concat(values.width," / ").concat(values.height)},context={src:embedUrl,width:values.width,height:values.height,responsive:values.responsive,aspectRatioCalc:aspectRatioCalcs[values.aspectRatio]||"16 / 9",aspectRatioValue:aspectRatioCalcs[values.aspectRatio]||"16 / 9"},{html:html}=await _templates.default.renderForPromise("tiny_mediacms/iframe_embed_output",context);return html}async updatePreview(root){const values=this.getFormValues(root),previewContainer=root.querySelector(_selectors.default.IFRAME.elements.preview),urlWarning=root.querySelector(_selectors.default.IFRAME.elements.urlWarning);if(!values.url)return previewContainer.innerHTML='Enter a video URL to see preview',void urlWarning.classList.add("d-none");const parsed=this.parseInput(values.url);if(!parsed)return previewContainer.innerHTML='Invalid URL format',void urlWarning.classList.remove("d-none");urlWarning.classList.add("d-none");const embedUrl=this.buildEmbedUrl(parsed,values),previewWidth=Math.min(values.width,400),scale=previewWidth/values.width,previewHeight=Math.round(values.height*scale);previewContainer.innerHTML='\n \n ')}handleInputChange(root){clearTimeout(this.debounceTimer),this.debounceTimer=setTimeout((()=>{this.updatePreview(root)}),500)}handleAspectRatioChange(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form),aspectRatio=form.querySelector(_selectors.default.IFRAME.elements.aspectRatio).value,dimensions=_selectors.default.IFRAME.aspectRatios[aspectRatio];dimensions&&"custom"!==aspectRatio&&(form.querySelector(_selectors.default.IFRAME.elements.width).value=dimensions.width,form.querySelector(_selectors.default.IFRAME.elements.height).value=dimensions.height),this.updatePreview(root)}async handleDialogueSubmission(modal){const root=modal.getRoot()[0],values=this.getFormValues(root);if(!values.url)return;const html=await this.generateIframeHtml(values);if(html)if(this.isUpdating&&this.selectedIframe){const wrapper=this.selectedIframe.closest(".tiny-mediacms-iframe-wrapper")||this.selectedIframe.closest(".tiny-iframe-responsive");wrapper?wrapper.outerHTML=html:this.selectedIframe.outerHTML=html,this.isUpdating=!1,this.editor.fire("Change")}else this.editor.insertContent(html)}async handleRemove(modal){const confirmMessage=await(0,_str.getString)("removeiframeconfirm",_common.component);if(window.confirm(confirmMessage)){if(this.selectedIframe){const wrapper=this.selectedIframe.closest(".tiny-mediacms-iframe-wrapper")||this.selectedIframe.closest(".tiny-iframe-responsive");wrapper?wrapper.remove():this.selectedIframe.remove()}this.isUpdating=!1,modal.hide()}}async registerEventListeners(modal){await modal.getBody();const $root=modal.getRoot(),root=$root[0],form=root.querySelector(_selectors.default.IFRAME.elements.form);form.querySelector(_selectors.default.IFRAME.elements.url).addEventListener("input",(()=>this.handleInputChange(root))),[_selectors.default.IFRAME.elements.showTitle,_selectors.default.IFRAME.elements.linkTitle,_selectors.default.IFRAME.elements.showRelated,_selectors.default.IFRAME.elements.showUserAvatar,_selectors.default.IFRAME.elements.responsive,_selectors.default.IFRAME.elements.startAtEnabled].forEach((selector=>{form.querySelector(selector).addEventListener("change",(()=>this.handleInputChange(root)))})),form.querySelector(_selectors.default.IFRAME.elements.startAt).addEventListener("input",(()=>this.handleInputChange(root))),form.querySelector(_selectors.default.IFRAME.elements.aspectRatio).addEventListener("change",(()=>this.handleAspectRatioChange(root))),form.querySelector(_selectors.default.IFRAME.elements.width).addEventListener("input",(()=>this.handleInputChange(root))),form.querySelector(_selectors.default.IFRAME.elements.height).addEventListener("input",(()=>this.handleInputChange(root))),$root.on(ModalEvents.save,(()=>this.handleDialogueSubmission(modal))),$root.on(ModalEvents.hidden,(()=>{this.currentModal.destroy()}));const removeBtn=root.querySelector(_selectors.default.IFRAME.actions.remove);removeBtn&&removeBtn.addEventListener("click",(()=>this.handleRemove(modal)));form.querySelector(_selectors.default.IFRAME.elements.url).value&&this.updatePreview(root);const iframeLibraryTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabIframeLibraryBtn);if(iframeLibraryTabBtn){iframeLibraryTabBtn.addEventListener("click",(e=>{e.preventDefault(),e.stopPropagation(),this.switchToIframeLibraryTab(root),setTimeout((()=>this.handleIframeLibraryTabClick(root)),100)})),iframeLibraryTabBtn.addEventListener("shown.bs.tab",(()=>this.handleIframeLibraryTabClick(root)));const $iframeLibraryTabBtn=window.jQuery?window.jQuery(iframeLibraryTabBtn):null;$iframeLibraryTabBtn&&$iframeLibraryTabBtn.on("shown.bs.tab",(()=>this.handleIframeLibraryTabClick(root)))}const urlTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabUrlBtn);urlTabBtn&&urlTabBtn.addEventListener("click",(e=>{e.preventDefault(),e.stopPropagation(),this.switchToUrlTab(root)})),this.registerIframeLibraryEventListeners(root)}switchToUrlTab(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form),urlTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabUrlBtn),iframeLibraryTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabIframeLibraryBtn),urlPane=form.querySelector(_selectors.default.IFRAME.elements.paneUrl),iframeLibraryPane=form.querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);urlTabBtn&&(urlTabBtn.classList.add("active"),urlTabBtn.setAttribute("aria-selected","true")),iframeLibraryTabBtn&&(iframeLibraryTabBtn.classList.remove("active"),iframeLibraryTabBtn.setAttribute("aria-selected","false")),urlPane&&urlPane.classList.add("show","active"),iframeLibraryPane&&iframeLibraryPane.classList.remove("show","active")}switchToIframeLibraryTab(root){const form=root.querySelector(_selectors.default.IFRAME.elements.form),urlTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabUrlBtn),iframeLibraryTabBtn=form.querySelector(_selectors.default.IFRAME.elements.tabIframeLibraryBtn),urlPane=form.querySelector(_selectors.default.IFRAME.elements.paneUrl),iframeLibraryPane=form.querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);urlTabBtn&&(urlTabBtn.classList.remove("active"),urlTabBtn.setAttribute("aria-selected","false")),iframeLibraryTabBtn&&(iframeLibraryTabBtn.classList.add("active"),iframeLibraryTabBtn.setAttribute("aria-selected","true")),urlPane&&urlPane.classList.remove("show","active"),iframeLibraryPane&&iframeLibraryPane.classList.add("show","active")}registerIframeLibraryEventListeners(root){window.addEventListener("message",(event=>{this.handleIframeLibraryMessage(root,event)}))}handleIframeLibraryTabClick(root){const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary),iframeEl=pane?pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame):null;console.log("handleIframeLibraryTabClick called, iframeLibraryLoaded:",this.iframeLibraryLoaded,"iframe src:",iframeEl?iframeEl.src:"no iframe","pane:",pane),this.iframeLibraryLoaded&&(!iframeEl||iframeEl.src&&""!==iframeEl.src)||this.loadIframeLibrary(root)}loadIframeLibrary(root){const ltiConfig=(0,_options.getLti)(this.editor);console.log("loadIframeLibrary called, LTI config:",ltiConfig),null!=ltiConfig&<iConfig.contentItemUrl?this.loadIframeLibraryViaLti(root,ltiConfig):this.loadIframeLibraryStatic(root)}loadIframeLibraryViaLti(root,ltiConfig){console.log("loadIframeLibraryViaLti called, config:",ltiConfig);const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);if(!pane)return void console.log("paneIframeLibrary not found!");const placeholderEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryPlaceholder),loadingEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryLoading),iframeEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame);if(!iframeEl)return void console.log("iframeEl not found!");placeholderEl&&placeholderEl.classList.add("d-none"),loadingEl&&loadingEl.classList.remove("d-none"),iframeEl.classList.add("d-none");iframeEl.addEventListener("load",(()=>{console.log("LTI iframe loaded"),this.handleIframeLibraryLoad(root)})),console.log("Setting iframe src to LTI content item URL:",ltiConfig.contentItemUrl),iframeEl.src=ltiConfig.contentItemUrl}loadIframeLibraryStatic(root){console.log("loadIframeLibraryStatic called, URL:",this.iframeLibraryUrl);const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);if(!pane)return void console.log("paneIframeLibrary not found!");const placeholderEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryPlaceholder),loadingEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryLoading),iframeEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame);if(console.log("Elements found:",{placeholderEl:placeholderEl,loadingEl:loadingEl,iframeEl:iframeEl}),!iframeEl)return void console.log("iframeEl not found!");placeholderEl&&placeholderEl.classList.add("d-none"),loadingEl&&loadingEl.classList.remove("d-none"),iframeEl.classList.add("d-none");const loadHandler=()=>{console.log("iframe loaded, src:",iframeEl.src),iframeEl.src===this.iframeLibraryUrl&&(this.handleIframeLibraryLoad(root),iframeEl.removeEventListener("load",loadHandler))};iframeEl.addEventListener("load",loadHandler),iframeEl.src=this.iframeLibraryUrl,console.log("iframe src set to:",iframeEl.src)}handleIframeLibraryLoad(root){console.log("handleIframeLibraryLoad called");const pane=root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.paneIframeLibrary);if(!pane)return;const placeholderEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryPlaceholder),loadingEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryLoading),iframeEl=pane.querySelector(_selectors.default.IFRAME.elements.iframeLibraryFrame);placeholderEl&&placeholderEl.classList.add("d-none"),loadingEl&&loadingEl.classList.add("d-none"),iframeEl&&iframeEl.classList.remove("d-none"),this.iframeLibraryLoaded=!0}handleIframeLibraryMessage(root,event){console.log("handleIframeLibraryMessage received:",event.data,"from origin:",event.origin);const data=event.data;if(data){if("videoSelected"===data.type&&data.embedUrl)return console.log("Video selected (videoSelected):",data.embedUrl,"videoId:",data.videoId),void this.selectIframeLibraryVideo(root,data.embedUrl,data.videoId);if("ltiDeepLinkingResponse"!==data.type&&"LtiDeepLinkingResponse"!==data.messageType)if("selectMedia"!==data.action&&"mediaSelected"!==data.action);else{const embedUrl=data.embedUrl||data.url||"",videoId=data.mediaId||data.videoId||data.id||"";embedUrl&&(console.log("Video selected (mediaSelected):",embedUrl,"videoId:",videoId),this.selectIframeLibraryVideo(root,embedUrl,videoId))}else{console.log("LTI Deep Linking response received:",data);const contentItems=data.content_items||data.contentItems||[];if(contentItems.length>0){const item=contentItems[0],embedUrl=item.url||item.embed_url||item.embedUrl||"",videoId=item.id||item.mediaId||"";embedUrl&&(console.log("Video selected (LTI):",embedUrl,"videoId:",videoId),this.selectIframeLibraryVideo(root,embedUrl,videoId))}}}}selectIframeLibraryVideo(root,embedUrl,videoId){root.querySelector(_selectors.default.IFRAME.elements.form).querySelector(_selectors.default.IFRAME.elements.url).value=embedUrl,this.switchToUrlTab(root),this.updatePreview(root),this.selectedLibraryVideo={embedUrl:embedUrl,videoId:videoId}}},_exports.default})); - -//# sourceMappingURL=iframeembed.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js.map deleted file mode 100755 index b673a5f1..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframeembed.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"iframeembed.min.js","sources":["../src/iframeembed.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 * Tiny Media2 Iframe Embed class.\n *\n * @module tiny_mediacms/iframeembed\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport { getString } from 'core/str';\nimport * as ModalEvents from 'core/modal_events';\nimport { component } from './common';\nimport IframeModal from './iframemodal';\nimport Selectors from './selectors';\nimport { getLti } from './options';\n\nexport default class IframeEmbed {\n editor = null;\n currentModal = null;\n isUpdating = false;\n selectedIframe = null;\n debounceTimer = null;\n // Iframe library state\n iframeLibraryLoaded = false;\n selectedLibraryVideo = null;\n iframeLibraryUrl =\n 'https://temp.web357.com/mediacms/deic-mediacms-embed-videos.html';\n\n constructor(editor) {\n this.editor = editor;\n }\n\n /**\n * Parse input to extract video URL or iframe src.\n * Handles: iframe embed code, view URL, or embed URL.\n *\n * @param {string} input - The user input (URL or embed code)\n * @returns {Object|null} Parsed URL info or null if invalid\n */\n parseInput(input) {\n if (!input || !input.trim()) {\n return null;\n }\n\n input = input.trim();\n\n // Check if it's an iframe embed code\n const iframeMatch = input.match(\n /]*src=[\"']([^\"']+)[\"'][^>]*>/i,\n );\n if (iframeMatch) {\n return this.parseEmbedUrl(iframeMatch[1]);\n }\n\n // Check if it's a URL\n if (input.startsWith('http://') || input.startsWith('https://')) {\n return this.parseVideoUrl(input);\n }\n\n return null;\n }\n\n /**\n * Parse a video view URL and convert to embed format.\n *\n * @param {string} url - The video URL\n * @returns {Object|null} Parsed info\n */\n parseVideoUrl(url) {\n try {\n const urlObj = new URL(url);\n const baseUrl = `${urlObj.protocol}//${urlObj.host}`;\n\n // MediaCMS view URL: /view?m=VIDEO_ID\n if (urlObj.pathname === '/view' && urlObj.searchParams.has('m')) {\n return {\n baseUrl: baseUrl,\n videoId: urlObj.searchParams.get('m'),\n isEmbed: false,\n };\n }\n\n // MediaCMS embed URL: /embed?m=VIDEO_ID&options\n if (urlObj.pathname === '/embed' && urlObj.searchParams.has('m')) {\n const tParam = urlObj.searchParams.get('t');\n return {\n baseUrl: baseUrl,\n videoId: urlObj.searchParams.get('m'),\n isEmbed: true,\n showTitle: urlObj.searchParams.get('showTitle') === '1',\n linkTitle: urlObj.searchParams.get('linkTitle') === '1',\n showRelated: urlObj.searchParams.get('showRelated') === '1',\n showUserAvatar:\n urlObj.searchParams.get('showUserAvatar') === '1',\n startAt: tParam\n ? this.secondsToTimeString(parseInt(tParam))\n : null,\n };\n }\n\n // Generic URL - just use as-is\n return {\n baseUrl: baseUrl,\n rawUrl: url,\n isGeneric: true,\n };\n } catch (e) {\n return null;\n }\n }\n\n /**\n * Parse an embed URL.\n *\n * @param {string} url - The embed URL\n * @returns {Object|null} Parsed info\n */\n parseEmbedUrl(url) {\n return this.parseVideoUrl(url);\n }\n\n /**\n * Convert seconds to time string (M:SS format).\n *\n * @param {number} seconds - Time in seconds\n * @returns {string} Time string\n */\n secondsToTimeString(seconds) {\n const mins = Math.floor(seconds / 60);\n const secs = seconds % 60;\n return `${mins}:${secs.toString().padStart(2, '0')}`;\n }\n\n /**\n * Convert time string to seconds.\n *\n * @param {string} timeStr - Time string (M:SS or just seconds)\n * @returns {number|null} Seconds or null if invalid\n */\n timeStringToSeconds(timeStr) {\n if (!timeStr || !timeStr.trim()) {\n return null;\n }\n timeStr = timeStr.trim();\n\n // Handle M:SS format\n if (timeStr.includes(':')) {\n const parts = timeStr.split(':');\n const mins = parseInt(parts[0]) || 0;\n const secs = parseInt(parts[1]) || 0;\n return mins * 60 + secs;\n }\n\n // Handle plain seconds\n const secs = parseInt(timeStr);\n return isNaN(secs) ? null : secs;\n }\n\n /**\n * Build the embed URL with options.\n *\n * @param {Object} parsed - Parsed URL info\n * @param {Object} options - Embed options\n * @returns {string} The complete embed URL\n */\n buildEmbedUrl(parsed, options) {\n if (parsed.isGeneric) {\n return parsed.rawUrl;\n }\n\n const url = new URL(`${parsed.baseUrl}/embed`);\n url.searchParams.set('m', parsed.videoId);\n\n // Always include all options with 1 or 0\n url.searchParams.set('showTitle', options.showTitle ? '1' : '0');\n url.searchParams.set('showRelated', options.showRelated ? '1' : '0');\n url.searchParams.set(\n 'showUserAvatar',\n options.showUserAvatar ? '1' : '0',\n );\n url.searchParams.set('linkTitle', options.linkTitle ? '1' : '0');\n\n // Add start time if enabled\n if (options.startAtEnabled && options.startAt) {\n const seconds = this.timeStringToSeconds(options.startAt);\n if (seconds !== null && seconds > 0) {\n url.searchParams.set('t', seconds.toString());\n }\n }\n\n return url.toString();\n }\n\n /**\n * Get the template context for the modal.\n *\n * @param {Object} data - Existing data for updating\n * @returns {Object} Template context\n */\n async getTemplateContext(data = {}) {\n return {\n elementid: this.editor.getElement().id,\n isupdating: this.isUpdating,\n url: data.url || '',\n showTitle: data.showTitle !== false,\n linkTitle: data.linkTitle !== false,\n showRelated: data.showRelated !== false,\n showUserAvatar: data.showUserAvatar !== false,\n responsive: data.responsive !== false,\n startAtEnabled: data.startAtEnabled || false,\n startAt: data.startAt || '0:00',\n width: data.width || 560,\n height: data.height || 315,\n is16_9: !data.aspectRatio || data.aspectRatio === '16:9',\n is4_3: data.aspectRatio === '4:3',\n is1_1: data.aspectRatio === '1:1',\n isCustom: data.aspectRatio === 'custom',\n };\n }\n\n /**\n * Display the iframe embed dialog.\n */\n async displayDialogue() {\n this.selectedIframe = this.getSelectedIframe();\n const data = this.getCurrentIframeData();\n this.isUpdating = data !== null;\n\n // Reset iframe library state for new modal\n this.iframeLibraryLoaded = false;\n\n this.currentModal = await IframeModal.create({\n title: getString('iframemodaltitle', component),\n templateContext: await this.getTemplateContext(data || {}),\n });\n\n await this.registerEventListeners(this.currentModal);\n }\n\n /**\n * Get the currently selected iframe in the editor.\n *\n * @returns {HTMLElement|null} The iframe element or null\n */\n getSelectedIframe() {\n const node = this.editor.selection.getNode();\n\n if (node.nodeName.toLowerCase() === 'iframe') {\n return node;\n }\n\n // Check if selection contains an iframe wrapper (including overlay wrapper)\n const iframe = node.querySelector('iframe');\n if (iframe) {\n return iframe;\n }\n\n // Check if we're on the overlay or wrapper and need to find the iframe\n const wrapper =\n node.closest('.tiny-mediacms-iframe-wrapper') ||\n node.closest('.tiny-iframe-responsive');\n if (wrapper) {\n return wrapper.querySelector('iframe');\n }\n\n return null;\n }\n\n /**\n * Get current iframe data for editing.\n *\n * @returns {Object|null} Current iframe data or null\n */\n getCurrentIframeData() {\n if (!this.selectedIframe) {\n return null;\n }\n\n const src = this.selectedIframe.getAttribute('src');\n const parsed = this.parseInput(src);\n\n // Check if responsive by looking at style\n const style = this.selectedIframe.getAttribute('style') || '';\n const isResponsive = style.includes('aspect-ratio');\n\n return {\n url: src,\n width: this.selectedIframe.getAttribute('width') || 560,\n height: this.selectedIframe.getAttribute('height') || 315,\n showTitle: parsed?.showTitle ?? true,\n linkTitle: parsed?.linkTitle ?? true,\n showRelated: parsed?.showRelated ?? true,\n showUserAvatar: parsed?.showUserAvatar ?? true,\n responsive: isResponsive,\n startAtEnabled: parsed?.startAt !== null,\n startAt: parsed?.startAt || '0:00',\n };\n }\n\n /**\n * Get form values from the modal.\n *\n * @param {HTMLElement} root - Modal root element\n * @returns {Object} Form values\n */\n getFormValues(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n return {\n url: form.querySelector(Selectors.IFRAME.elements.url).value.trim(),\n showTitle: form.querySelector(Selectors.IFRAME.elements.showTitle)\n .checked,\n linkTitle: form.querySelector(Selectors.IFRAME.elements.linkTitle)\n .checked,\n showRelated: form.querySelector(\n Selectors.IFRAME.elements.showRelated,\n ).checked,\n showUserAvatar: form.querySelector(\n Selectors.IFRAME.elements.showUserAvatar,\n ).checked,\n responsive: form.querySelector(Selectors.IFRAME.elements.responsive)\n .checked,\n startAtEnabled: form.querySelector(\n Selectors.IFRAME.elements.startAtEnabled,\n ).checked,\n startAt: form\n .querySelector(Selectors.IFRAME.elements.startAt)\n .value.trim(),\n aspectRatio: form.querySelector(\n Selectors.IFRAME.elements.aspectRatio,\n ).value,\n width:\n parseInt(\n form.querySelector(Selectors.IFRAME.elements.width).value,\n ) || 560,\n height:\n parseInt(\n form.querySelector(Selectors.IFRAME.elements.height).value,\n ) || 315,\n };\n }\n\n /**\n * Generate the iframe HTML.\n *\n * @param {Object} values - Form values\n * @returns {Promise} Generated HTML\n */\n async generateIframeHtml(values) {\n const parsed = this.parseInput(values.url);\n if (!parsed) {\n return '';\n }\n\n const embedUrl = this.buildEmbedUrl(parsed, values);\n\n // Calculate aspect ratio values for CSS\n const aspectRatioCalcs = {\n '16:9': '16 / 9',\n '4:3': '4 / 3',\n '1:1': '1 / 1',\n custom: `${values.width} / ${values.height}`,\n };\n\n const context = {\n src: embedUrl,\n width: values.width,\n height: values.height,\n responsive: values.responsive,\n aspectRatioCalc: aspectRatioCalcs[values.aspectRatio] || '16 / 9',\n aspectRatioValue: aspectRatioCalcs[values.aspectRatio] || '16 / 9',\n };\n\n const { html } = await Templates.renderForPromise(\n 'tiny_mediacms/iframe_embed_output',\n context,\n );\n return html;\n }\n\n /**\n * Update the preview in the modal.\n *\n * @param {HTMLElement} root - Modal root element\n */\n async updatePreview(root) {\n const values = this.getFormValues(root);\n const previewContainer = root.querySelector(\n Selectors.IFRAME.elements.preview,\n );\n const urlWarning = root.querySelector(\n Selectors.IFRAME.elements.urlWarning,\n );\n\n if (!values.url) {\n previewContainer.innerHTML =\n 'Enter a video URL to see preview';\n urlWarning.classList.add('d-none');\n return;\n }\n\n const parsed = this.parseInput(values.url);\n if (!parsed) {\n previewContainer.innerHTML =\n 'Invalid URL format';\n urlWarning.classList.remove('d-none');\n return;\n }\n\n urlWarning.classList.add('d-none');\n const embedUrl = this.buildEmbedUrl(parsed, values);\n\n // Show a scaled preview\n const previewWidth = Math.min(values.width, 400);\n const scale = previewWidth / values.width;\n const previewHeight = Math.round(values.height * scale);\n\n previewContainer.innerHTML = `\n \n `;\n }\n\n /**\n * Handle form input changes with debounce.\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleInputChange(root) {\n clearTimeout(this.debounceTimer);\n this.debounceTimer = setTimeout(() => {\n this.updatePreview(root);\n }, 500);\n }\n\n /**\n * Handle aspect ratio change - update dimensions.\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleAspectRatioChange(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const aspectRatio = form.querySelector(\n Selectors.IFRAME.elements.aspectRatio,\n ).value;\n const dimensions = Selectors.IFRAME.aspectRatios[aspectRatio];\n\n // Only update dimensions for predefined ratios, not 'custom'\n if (dimensions && aspectRatio !== 'custom') {\n form.querySelector(Selectors.IFRAME.elements.width).value =\n dimensions.width;\n form.querySelector(Selectors.IFRAME.elements.height).value =\n dimensions.height;\n }\n\n this.updatePreview(root);\n }\n\n /**\n * Handle dialog submission.\n *\n * @param {Object} modal - The modal instance\n */\n async handleDialogueSubmission(modal) {\n const root = modal.getRoot()[0];\n const values = this.getFormValues(root);\n\n if (!values.url) {\n return;\n }\n\n const html = await this.generateIframeHtml(values);\n if (html) {\n if (this.isUpdating && this.selectedIframe) {\n // Replace existing iframe (including wrapper if present)\n // Check for both old wrapper and new overlay wrapper\n const wrapper =\n this.selectedIframe.closest(\n '.tiny-mediacms-iframe-wrapper',\n ) || this.selectedIframe.closest('.tiny-iframe-responsive');\n if (wrapper) {\n wrapper.outerHTML = html;\n } else {\n this.selectedIframe.outerHTML = html;\n }\n this.isUpdating = false;\n // Fire change event to trigger overlay reprocessing\n this.editor.fire('Change');\n } else {\n this.editor.insertContent(html);\n }\n }\n }\n\n /**\n * Handle video removal from the editor.\n *\n * @param {Object} modal - The modal instance\n */\n async handleRemove(modal) {\n // Get confirmation string\n const confirmMessage = await getString(\n 'removeiframeconfirm',\n component,\n );\n\n // Show confirmation dialog\n // eslint-disable-next-line no-alert\n if (!window.confirm(confirmMessage)) {\n return;\n }\n\n if (this.selectedIframe) {\n // Remove the iframe (including wrapper if present)\n const wrapper =\n this.selectedIframe.closest('.tiny-mediacms-iframe-wrapper') ||\n this.selectedIframe.closest('.tiny-iframe-responsive');\n if (wrapper) {\n wrapper.remove();\n } else {\n this.selectedIframe.remove();\n }\n }\n\n // Close the modal\n this.isUpdating = false;\n modal.hide();\n }\n\n /**\n * Register event listeners for the modal.\n *\n * @param {Object} modal - The modal instance\n */\n async registerEventListeners(modal) {\n await modal.getBody();\n const $root = modal.getRoot();\n const root = $root[0];\n\n // Input change handlers\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // URL input\n form.querySelector(Selectors.IFRAME.elements.url).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n\n // Checkbox options\n [\n Selectors.IFRAME.elements.showTitle,\n Selectors.IFRAME.elements.linkTitle,\n Selectors.IFRAME.elements.showRelated,\n Selectors.IFRAME.elements.showUserAvatar,\n Selectors.IFRAME.elements.responsive,\n Selectors.IFRAME.elements.startAtEnabled,\n ].forEach((selector) => {\n form.querySelector(selector).addEventListener('change', () =>\n this.handleInputChange(root),\n );\n });\n\n // Start at time input\n form.querySelector(Selectors.IFRAME.elements.startAt).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n\n // Aspect ratio change\n form.querySelector(\n Selectors.IFRAME.elements.aspectRatio,\n ).addEventListener('change', () => this.handleAspectRatioChange(root));\n\n // Dimension inputs\n form.querySelector(Selectors.IFRAME.elements.width).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n form.querySelector(Selectors.IFRAME.elements.height).addEventListener(\n 'input',\n () => this.handleInputChange(root),\n );\n\n // Modal events\n $root.on(ModalEvents.save, () => this.handleDialogueSubmission(modal));\n $root.on(ModalEvents.hidden, () => {\n this.currentModal.destroy();\n });\n\n // Remove button handler (only present when updating)\n const removeBtn = root.querySelector(Selectors.IFRAME.actions.remove);\n if (removeBtn) {\n removeBtn.addEventListener('click', () => this.handleRemove(modal));\n }\n\n // Initial preview if we have a URL\n const urlInput = form.querySelector(Selectors.IFRAME.elements.url);\n if (urlInput.value) {\n this.updatePreview(root);\n }\n\n // Tab change handler - load iframe library when switching to iframe library tab\n const iframeLibraryTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabIframeLibraryBtn,\n );\n if (iframeLibraryTabBtn) {\n iframeLibraryTabBtn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Manually handle tab switching for Bootstrap 5\n this.switchToIframeLibraryTab(root);\n\n // Small delay to ensure tab pane is visible before loading iframe\n setTimeout(() => this.handleIframeLibraryTabClick(root), 100);\n });\n // Also handle Bootstrap tab events (if Bootstrap handles it)\n iframeLibraryTabBtn.addEventListener('shown.bs.tab', () =>\n this.handleIframeLibraryTabClick(root),\n );\n // jQuery Bootstrap 4 event\n const $iframeLibraryTabBtn = window.jQuery\n ? window.jQuery(iframeLibraryTabBtn)\n : null;\n if ($iframeLibraryTabBtn) {\n $iframeLibraryTabBtn.on('shown.bs.tab', () =>\n this.handleIframeLibraryTabClick(root),\n );\n }\n }\n\n // Tab change handler for URL tab\n const urlTabBtn = form.querySelector(\n Selectors.IFRAME.elements.tabUrlBtn,\n );\n if (urlTabBtn) {\n urlTabBtn.addEventListener('click', (e) => {\n e.preventDefault();\n e.stopPropagation();\n\n // Manually handle tab switching\n this.switchToUrlTab(root);\n });\n }\n\n // Iframe library event listeners\n this.registerIframeLibraryEventListeners(root);\n }\n\n /**\n * Switch to the URL tab.\n *\n * @param {HTMLElement} root - Modal root element\n */\n switchToUrlTab(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // Get tab buttons\n const urlTabBtn = form.querySelector(Selectors.IFRAME.elements.tabUrlBtn);\n const iframeLibraryTabBtn = form.querySelector(Selectors.IFRAME.elements.tabIframeLibraryBtn);\n\n // Get tab panes\n const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl);\n const iframeLibraryPane = form.querySelector(Selectors.IFRAME.elements.paneIframeLibrary);\n\n // Update tab button states\n if (urlTabBtn) {\n urlTabBtn.classList.add('active');\n urlTabBtn.setAttribute('aria-selected', 'true');\n }\n if (iframeLibraryTabBtn) {\n iframeLibraryTabBtn.classList.remove('active');\n iframeLibraryTabBtn.setAttribute('aria-selected', 'false');\n }\n\n // Update tab pane visibility\n if (urlPane) {\n urlPane.classList.add('show', 'active');\n }\n if (iframeLibraryPane) {\n iframeLibraryPane.classList.remove('show', 'active');\n }\n }\n\n /**\n * Switch to the iframe library tab.\n *\n * @param {HTMLElement} root - Modal root element\n */\n switchToIframeLibraryTab(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // Get tab buttons\n const urlTabBtn = form.querySelector(Selectors.IFRAME.elements.tabUrlBtn);\n const iframeLibraryTabBtn = form.querySelector(Selectors.IFRAME.elements.tabIframeLibraryBtn);\n\n // Get tab panes\n const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl);\n const iframeLibraryPane = form.querySelector(Selectors.IFRAME.elements.paneIframeLibrary);\n\n // Update tab button states\n if (urlTabBtn) {\n urlTabBtn.classList.remove('active');\n urlTabBtn.setAttribute('aria-selected', 'false');\n }\n if (iframeLibraryTabBtn) {\n iframeLibraryTabBtn.classList.add('active');\n iframeLibraryTabBtn.setAttribute('aria-selected', 'true');\n }\n\n // Update tab pane visibility\n if (urlPane) {\n urlPane.classList.remove('show', 'active');\n }\n if (iframeLibraryPane) {\n iframeLibraryPane.classList.add('show', 'active');\n }\n }\n\n /**\n * Register event listeners for the iframe library.\n *\n * @param {HTMLElement} root - Modal root element\n */\n registerIframeLibraryEventListeners(root) {\n // Listen for messages from the iframe (for video selection)\n window.addEventListener('message', (event) => {\n this.handleIframeLibraryMessage(root, event);\n });\n }\n\n /**\n * Handle iframe library tab click - load iframe if not already loaded.\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleIframeLibraryTabClick(root) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n const iframeEl = pane\n ? pane.querySelector(Selectors.IFRAME.elements.iframeLibraryFrame)\n : null;\n\n // eslint-disable-next-line no-console\n console.log(\n 'handleIframeLibraryTabClick called, iframeLibraryLoaded:',\n this.iframeLibraryLoaded,\n 'iframe src:',\n iframeEl ? iframeEl.src : 'no iframe',\n 'pane:',\n pane,\n );\n\n // Load if not loaded or iframe has no source or empty source\n if (\n !this.iframeLibraryLoaded ||\n (iframeEl && (!iframeEl.src || iframeEl.src === ''))\n ) {\n this.loadIframeLibrary(root);\n }\n }\n\n /**\n * Load the iframe library using LTI flow or fallback to static URL.\n *\n * @param {HTMLElement} root - Modal root element\n */\n loadIframeLibrary(root) {\n const ltiConfig = getLti(this.editor);\n\n // eslint-disable-next-line no-console\n console.log('loadIframeLibrary called, LTI config:', ltiConfig);\n\n // Check if LTI is configured with a content item URL\n if (ltiConfig?.contentItemUrl) {\n this.loadIframeLibraryViaLti(root, ltiConfig);\n } else {\n // Fallback to static URL if LTI not configured\n this.loadIframeLibraryStatic(root);\n }\n }\n\n /**\n * Load the iframe library via LTI Deep Linking (Content Item Selection).\n * Sets the iframe src to contentitem.php which initiates the LTI Deep Linking flow.\n * This sends an LtiDeepLinkingRequest message, which will redirect to the\n * tool's content selection interface (e.g., /lti/select-media/).\n *\n * @param {HTMLElement} root - Modal root element\n * @param {Object} ltiConfig - LTI configuration\n */\n loadIframeLibraryViaLti(root, ltiConfig) {\n // eslint-disable-next-line no-console\n console.log('loadIframeLibraryViaLti called, config:', ltiConfig);\n\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n if (!pane) {\n // eslint-disable-next-line no-console\n console.log('paneIframeLibrary not found!');\n return;\n }\n\n const placeholderEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryPlaceholder,\n );\n const loadingEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryLoading,\n );\n const iframeEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryFrame,\n );\n\n if (!iframeEl) {\n // eslint-disable-next-line no-console\n console.log('iframeEl not found!');\n return;\n }\n\n // Hide placeholder, show loading state\n if (placeholderEl) {\n placeholderEl.classList.add('d-none');\n }\n if (loadingEl) {\n loadingEl.classList.remove('d-none');\n }\n iframeEl.classList.add('d-none');\n\n // Set up load listener - note: this may fire multiple times during LTI redirects\n const loadHandler = () => {\n // eslint-disable-next-line no-console\n console.log('LTI iframe loaded');\n this.handleIframeLibraryLoad(root);\n };\n iframeEl.addEventListener('load', loadHandler);\n\n // Set the iframe src to the content item URL\n // This initiates the LTI Deep Linking flow:\n // 1. contentitem.php initiates OIDC login\n // 2. LTI provider authenticates\n // 3. Moodle sends LtiDeepLinkingRequest\n // 4. Tool provider shows content selection interface (e.g., /lti/select-media/)\n // eslint-disable-next-line no-console\n console.log('Setting iframe src to LTI content item URL:', ltiConfig.contentItemUrl);\n iframeEl.src = ltiConfig.contentItemUrl;\n }\n\n /**\n * Load the iframe library using static URL (fallback).\n *\n * @param {HTMLElement} root - Modal root element\n */\n loadIframeLibraryStatic(root) {\n // eslint-disable-next-line no-console\n console.log(\n 'loadIframeLibraryStatic called, URL:',\n this.iframeLibraryUrl,\n );\n\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n if (!pane) {\n // eslint-disable-next-line no-console\n console.log('paneIframeLibrary not found!');\n return;\n }\n\n const placeholderEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryPlaceholder,\n );\n const loadingEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryLoading,\n );\n const iframeEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryFrame,\n );\n\n // eslint-disable-next-line no-console\n console.log('Elements found:', { placeholderEl, loadingEl, iframeEl });\n\n if (!iframeEl) {\n // eslint-disable-next-line no-console\n console.log('iframeEl not found!');\n return;\n }\n\n // Hide placeholder, show loading state\n if (placeholderEl) {\n placeholderEl.classList.add('d-none');\n }\n if (loadingEl) {\n loadingEl.classList.remove('d-none');\n }\n iframeEl.classList.add('d-none');\n\n // Set up load listener before setting src\n const loadHandler = () => {\n // eslint-disable-next-line no-console\n console.log('iframe loaded, src:', iframeEl.src);\n // Only handle if the src matches our target URL\n if (iframeEl.src === this.iframeLibraryUrl) {\n this.handleIframeLibraryLoad(root);\n // Remove the listener after successful load\n iframeEl.removeEventListener('load', loadHandler);\n }\n };\n iframeEl.addEventListener('load', loadHandler);\n\n // Set the iframe source\n iframeEl.src = this.iframeLibraryUrl;\n // eslint-disable-next-line no-console\n console.log('iframe src set to:', iframeEl.src);\n }\n\n /**\n * Handle iframe library load event.\n *\n * @param {HTMLElement} root - Modal root element\n */\n handleIframeLibraryLoad(root) {\n // eslint-disable-next-line no-console\n console.log('handleIframeLibraryLoad called');\n\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n const pane = form.querySelector(\n Selectors.IFRAME.elements.paneIframeLibrary,\n );\n\n if (!pane) {\n return;\n }\n\n const placeholderEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryPlaceholder,\n );\n const loadingEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryLoading,\n );\n const iframeEl = pane.querySelector(\n Selectors.IFRAME.elements.iframeLibraryFrame,\n );\n\n // Hide placeholder and loading, show iframe\n if (placeholderEl) {\n placeholderEl.classList.add('d-none');\n }\n if (loadingEl) {\n loadingEl.classList.add('d-none');\n }\n if (iframeEl) {\n iframeEl.classList.remove('d-none');\n }\n\n this.iframeLibraryLoaded = true;\n }\n\n /**\n * Handle messages from the iframe library (for video selection).\n * Supports both custom videoSelected messages and LTI Deep Linking responses.\n *\n * @param {HTMLElement} root - Modal root element\n * @param {MessageEvent} event - The message event\n */\n handleIframeLibraryMessage(root, event) {\n // eslint-disable-next-line no-console\n console.log(\n 'handleIframeLibraryMessage received:',\n event.data,\n 'from origin:',\n event.origin,\n );\n\n const data = event.data;\n\n if (!data) {\n return;\n }\n\n // Handle custom videoSelected message format (from static iframe or custom MediaCMS implementation)\n if (data.type === 'videoSelected' && data.embedUrl) {\n // eslint-disable-next-line no-console\n console.log(\n 'Video selected (videoSelected):',\n data.embedUrl,\n 'videoId:',\n data.videoId,\n );\n this.selectIframeLibraryVideo(root, data.embedUrl, data.videoId);\n return;\n }\n\n // Handle LTI Deep Linking response format\n if (\n data.type === 'ltiDeepLinkingResponse' ||\n data.messageType === 'LtiDeepLinkingResponse'\n ) {\n // eslint-disable-next-line no-console\n console.log('LTI Deep Linking response received:', data);\n const contentItems = data.content_items || data.contentItems || [];\n if (contentItems.length > 0) {\n const item = contentItems[0];\n // Extract embed URL from the content item\n const embedUrl =\n item.url || item.embed_url || item.embedUrl || '';\n const videoId = item.id || item.mediaId || '';\n if (embedUrl) {\n // eslint-disable-next-line no-console\n console.log(\n 'Video selected (LTI):',\n embedUrl,\n 'videoId:',\n videoId,\n );\n this.selectIframeLibraryVideo(root, embedUrl, videoId);\n }\n }\n return;\n }\n\n // Handle MediaCMS specific message format (if different from above)\n if (data.action === 'selectMedia' || data.action === 'mediaSelected') {\n const embedUrl = data.embedUrl || data.url || '';\n const videoId = data.mediaId || data.videoId || data.id || '';\n if (embedUrl) {\n // eslint-disable-next-line no-console\n console.log(\n 'Video selected (mediaSelected):',\n embedUrl,\n 'videoId:',\n videoId,\n );\n this.selectIframeLibraryVideo(root, embedUrl, videoId);\n }\n return;\n }\n }\n\n /**\n * Select a video from the iframe library and populate the URL field.\n *\n * @param {HTMLElement} root - Modal root element\n * @param {string} embedUrl - The embed URL for the video\n * @param {string} videoId - The video ID\n */\n selectIframeLibraryVideo(root, embedUrl, videoId) {\n const form = root.querySelector(Selectors.IFRAME.elements.form);\n\n // Populate the URL field\n const urlInput = form.querySelector(Selectors.IFRAME.elements.url);\n urlInput.value = embedUrl;\n\n // Switch to the URL tab using our method\n this.switchToUrlTab(root);\n\n // Update the preview\n this.updatePreview(root);\n\n // Store the selected video\n this.selectedLibraryVideo = { embedUrl, videoId };\n }\n}\n"],"names":["constructor","editor","parseInput","input","trim","iframeMatch","match","this","parseEmbedUrl","startsWith","parseVideoUrl","url","urlObj","URL","baseUrl","protocol","host","pathname","searchParams","has","videoId","get","isEmbed","tParam","showTitle","linkTitle","showRelated","showUserAvatar","startAt","secondsToTimeString","parseInt","rawUrl","isGeneric","e","seconds","mins","Math","floor","secs","toString","padStart","timeStringToSeconds","timeStr","includes","parts","split","isNaN","buildEmbedUrl","parsed","options","set","startAtEnabled","data","elementid","getElement","id","isupdating","isUpdating","responsive","width","height","is16_9","aspectRatio","is4_3","is1_1","isCustom","selectedIframe","getSelectedIframe","getCurrentIframeData","iframeLibraryLoaded","currentModal","IframeModal","create","title","component","templateContext","getTemplateContext","registerEventListeners","node","selection","getNode","nodeName","toLowerCase","iframe","querySelector","wrapper","closest","src","getAttribute","isResponsive","getFormValues","root","form","Selectors","IFRAME","elements","value","checked","values","embedUrl","aspectRatioCalcs","custom","context","aspectRatioCalc","aspectRatioValue","html","Templates","renderForPromise","previewContainer","preview","urlWarning","innerHTML","classList","add","remove","previewWidth","min","scale","previewHeight","round","handleInputChange","clearTimeout","debounceTimer","setTimeout","updatePreview","handleAspectRatioChange","dimensions","aspectRatios","modal","getRoot","generateIframeHtml","outerHTML","fire","insertContent","confirmMessage","window","confirm","hide","getBody","$root","addEventListener","forEach","selector","on","ModalEvents","save","handleDialogueSubmission","hidden","destroy","removeBtn","actions","handleRemove","iframeLibraryTabBtn","tabIframeLibraryBtn","preventDefault","stopPropagation","switchToIframeLibraryTab","handleIframeLibraryTabClick","$iframeLibraryTabBtn","jQuery","urlTabBtn","tabUrlBtn","switchToUrlTab","registerIframeLibraryEventListeners","urlPane","paneUrl","iframeLibraryPane","paneIframeLibrary","setAttribute","event","handleIframeLibraryMessage","pane","iframeEl","iframeLibraryFrame","console","log","loadIframeLibrary","ltiConfig","contentItemUrl","loadIframeLibraryViaLti","loadIframeLibraryStatic","placeholderEl","iframeLibraryPlaceholder","loadingEl","iframeLibraryLoading","handleIframeLibraryLoad","iframeLibraryUrl","loadHandler","removeEventListener","origin","type","selectIframeLibraryVideo","messageType","action","mediaId","contentItems","content_items","length","item","embed_url","selectedLibraryVideo"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js deleted file mode 100755 index 178a27eb..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/iframemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class IframeModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=IframeModal,_defineProperty(IframeModal,"TYPE","".concat(_common.component,"/iframemodal")),_defineProperty(IframeModal,"TEMPLATE","".concat(_common.component,"/iframe_embed_modal")),_exports.default})); - -//# sourceMappingURL=iframemodal.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js.map deleted file mode 100755 index 7ab1d3b8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/iframemodal.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"iframemodal.min.js","sources":["../src/iframemodal.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 * Iframe Embed Modal for Tiny Media2.\n *\n * @module tiny_mediacms/iframemodal\n * @copyright 2024\n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class IframeModal extends Modal {\n static TYPE = `${component}/iframemodal`;\n static TEMPLATE = `${component}/iframe_embed_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n"],"names":["IframeModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component"],"mappings":"kaA0BqBA,oBAAoBC,eAIrCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,kEAlBHN,6BACAU,mDADAV,iCAEIU"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js deleted file mode 100755 index 4e8c1d43..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/image",["exports","./selectors","./imagemodal","./options","editor_tiny/options","tiny_mediacms/imageinsert","tiny_mediacms/imagedetails","core/prefetch","core/str","tiny_mediacms/imagehelpers"],(function(_exports,_selectors,_imagemodal,_options,_options2,_imageinsert,_imagedetails,_prefetch,_str,_imagehelpers){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_selectors=_interopRequireDefault(_selectors),_imagemodal=_interopRequireDefault(_imagemodal),(0,_prefetch.prefetchStrings)("tiny_mediacms",["imageurlrequired","sizecustom_help"]);return _exports.default=class{constructor(editor){_defineProperty(this,"canShowFilePicker",!1),_defineProperty(this,"editor",null),_defineProperty(this,"currentModal",null),_defineProperty(this,"root",null),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone};Promise.all([(0,_imagehelpers.bodyImageInsert)(templateContext,this.root),(0,_imagehelpers.footerImageInsert)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"loadPreviewImage",(async function(url){this.startImageLoading();const image=new Image;image.src=url,image.addEventListener("error",(async()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=await(0,_str.getString)("imageurlrequired","tiny_mediacms"),(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.stopImageLoading()})),image.addEventListener("load",(async()=>{const currentImageData=await this.getCurrentImageData();let templateContext=await this.getTemplateContext(currentImageData);templateContext.sizecustomhelpicon={text:await(0,_str.getString)("sizecustom_help","tiny_mediacms")},Promise.all([(0,_imagehelpers.bodyImageDetails)(templateContext,this.root),(0,_imagehelpers.footerImageDetails)(templateContext,this.root)]).then((()=>{this.stopImageLoading()})).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,url,image).init()})).catch((error=>{window.console.log(error)}))}))}));const permissions=(0,_options.getImagePermissions)(editor),options=(0,_options2.getFilePicker)(editor,"image");this.canShowFilePicker=permissions.filepicker&&void 0!==options&&Object.keys(options.repositories).length>0,this.canShowDropZone=void 0!==options&&Object.values(options.repositories).some((repository=>"upload"===repository.type)),this.editor=editor}async displayDialogue(){const currentImageData=await this.getCurrentImageData();this.currentModal=await _imagemodal.default.create(),this.root=this.currentModal.getRoot()[0],currentImageData&¤tImageData.src?this.loadPreviewImage(currentImageData.src):this.loadInsertImage()}async getTemplateContext(data){return{elementid:this.editor.id,showfilepicker:this.canShowFilePicker,...data}}async getCurrentImageData(){const selectedImageProperties=this.getSelectedImageProperties();if(!selectedImageProperties)return{};const properties={...selectedImageProperties};return properties.src&&(properties.haspreview=!0),properties.alt||(properties.presentation=!0),properties}getSelectedImageProperties(){const image=this.getSelectedImage();if(!image)return this.selectedImage=null,null;const properties={src:null,alt:null,width:null,height:null,presentation:!1,customStyle:""};this.selectedImage=image,properties.customStyle=image.style.cssText;const width=(image=>(0,_imagehelpers.isPercentageValue)(String(image.width))?image.width:parseInt(image.width,10))(image);0!==width&&(properties.width=width);const height=(image=>(0,_imagehelpers.isPercentageValue)(String(image.height))?image.height:parseInt(image.height,10))(image);return 0!==height&&(properties.height=height),properties.src=image.getAttribute("src"),properties.alt=image.getAttribute("alt")||"",properties.presentation="presentation"===image.getAttribute("role"),properties}getSelectedImage(){const imgElm=this.editor.selection.getNode(),figureElm=this.editor.dom.getParent(imgElm,"figure.image");return figureElm?this.editor.dom.select("img",figureElm)[0]:imgElm&&("IMG"!==imgElm.nodeName.toUpperCase()||this.isPlaceholderImage(imgElm))?null:imgElm}isPlaceholderImage(imgElm){return"IMG"===imgElm.nodeName.toUpperCase()&&(imgElm.hasAttribute("data-mce-object")||imgElm.hasAttribute("data-mce-placeholder"))}startImageLoading(){(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}stopImageLoading(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root),(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.insertImage,this.root)}},_exports.default})); - -//# sourceMappingURL=image.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map deleted file mode 100755 index 4e8dd218..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/image.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"image.min.js","sources":["../src/image.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 * Tiny Media plugin Image class for Moodle.\n *\n * @module tiny_mediacms/image\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport ImageModal from './imagemodal';\nimport {getImagePermissions} from './options';\nimport {getFilePicker} from 'editor_tiny/options';\nimport {ImageInsert} from 'tiny_mediacms/imageinsert';\nimport {ImageDetails} from 'tiny_mediacms/imagedetails';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getString} from 'core/str';\nimport {\n bodyImageInsert,\n footerImageInsert,\n bodyImageDetails,\n footerImageDetails,\n showElements,\n hideElements,\n isPercentageValue,\n} from 'tiny_mediacms/imagehelpers';\n\nprefetchStrings('tiny_mediacms', [\n 'imageurlrequired',\n 'sizecustom_help',\n]);\n\nexport default class MediaImage {\n canShowFilePicker = false;\n editor = null;\n currentModal = null;\n /**\n * @type {HTMLElement|null} The root element.\n */\n root = null;\n\n constructor(editor) {\n const permissions = getImagePermissions(editor);\n const options = getFilePicker(editor, 'image');\n // Indicates whether the file picker can be shown.\n this.canShowFilePicker = permissions.filepicker\n && (typeof options !== 'undefined')\n && Object.keys(options.repositories).length > 0;\n // Indicates whether the drop zone area can be shown.\n this.canShowDropZone = (typeof options !== 'undefined') &&\n Object.values(options.repositories).some(repository => repository.type === 'upload');\n\n this.editor = editor;\n }\n\n async displayDialogue() {\n const currentImageData = await this.getCurrentImageData();\n this.currentModal = await ImageModal.create();\n this.root = this.currentModal.getRoot()[0];\n if (currentImageData && currentImageData.src) {\n this.loadPreviewImage(currentImageData.src);\n } else {\n this.loadInsertImage();\n }\n }\n\n /**\n * Displays an insert image view asynchronously.\n *\n * @returns {Promise}\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n };\n\n Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n async getTemplateContext(data) {\n return {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n ...data,\n };\n }\n\n async getCurrentImageData() {\n const selectedImageProperties = this.getSelectedImageProperties();\n if (!selectedImageProperties) {\n return {};\n }\n\n const properties = {...selectedImageProperties};\n\n if (properties.src) {\n properties.haspreview = true;\n }\n\n if (!properties.alt) {\n properties.presentation = true;\n }\n\n return properties;\n }\n\n /**\n * Asynchronously loads and previews an image from the provided URL.\n *\n * @param {string} url - The URL of the image to load and preview.\n * @returns {Promise}\n */\n loadPreviewImage = async function(url) {\n this.startImageLoading();\n const image = new Image();\n image.src = url;\n image.addEventListener('error', async() => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms');\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n });\n\n image.addEventListener('load', async() => {\n const currentImageData = await this.getCurrentImageData();\n let templateContext = await this.getTemplateContext(currentImageData);\n templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')};\n\n Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])\n .then(() => {\n this.stopImageLoading();\n return;\n })\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n url,\n image,\n );\n imagedetails.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n });\n };\n\n getSelectedImageProperties() {\n const image = this.getSelectedImage();\n if (!image) {\n this.selectedImage = null;\n return null;\n }\n\n const properties = {\n src: null,\n alt: null,\n width: null,\n height: null,\n presentation: false,\n customStyle: '', // Custom CSS styles applied to the image.\n };\n\n const getImageHeight = (image) => {\n if (!isPercentageValue(String(image.height))) {\n return parseInt(image.height, 10);\n }\n\n return image.height;\n };\n\n const getImageWidth = (image) => {\n if (!isPercentageValue(String(image.width))) {\n return parseInt(image.width, 10);\n }\n\n return image.width;\n };\n\n // Get the current selection.\n this.selectedImage = image;\n\n properties.customStyle = image.style.cssText;\n\n const width = getImageWidth(image);\n if (width !== 0) {\n properties.width = width;\n }\n\n const height = getImageHeight(image);\n if (height !== 0) {\n properties.height = height;\n }\n\n properties.src = image.getAttribute('src');\n properties.alt = image.getAttribute('alt') || '';\n properties.presentation = (image.getAttribute('role') === 'presentation');\n\n return properties;\n }\n\n getSelectedImage() {\n const imgElm = this.editor.selection.getNode();\n const figureElm = this.editor.dom.getParent(imgElm, 'figure.image');\n if (figureElm) {\n return this.editor.dom.select('img', figureElm)[0];\n }\n\n if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) {\n return null;\n }\n return imgElm;\n }\n\n isPlaceholderImage(imgElm) {\n if (imgElm.nodeName.toUpperCase() !== 'IMG') {\n return false;\n }\n\n return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder'));\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n startImageLoading() {\n showElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n hideElements(Selectors.IMAGE.elements.insertImage, this.root);\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n stopImageLoading() {\n hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n showElements(Selectors.IMAGE.elements.insertImage, this.root);\n }\n}\n"],"names":["constructor","editor","async","templateContext","elementid","this","id","showfilepicker","canShowFilePicker","showdropzone","canShowDropZone","Promise","all","root","then","ImageInsert","currentModal","init","catch","error","window","console","log","url","startImageLoading","image","Image","src","addEventListener","querySelector","Selectors","IMAGE","elements","urlWarning","innerHTML","stopImageLoading","currentImageData","getCurrentImageData","getTemplateContext","sizecustomhelpicon","text","ImageDetails","permissions","options","filepicker","Object","keys","repositories","length","values","some","repository","type","ImageModal","create","getRoot","loadPreviewImage","loadInsertImage","data","selectedImageProperties","getSelectedImageProperties","properties","haspreview","alt","presentation","getSelectedImage","selectedImage","width","height","customStyle","style","cssText","String","parseInt","getImageWidth","getImageHeight","getAttribute","imgElm","selection","getNode","figureElm","dom","getParent","select","nodeName","toUpperCase","isPlaceholderImage","hasAttribute","loaderIcon","insertImage"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js deleted file mode 100755 index 35850139..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/imagedetails",["exports","core/config","core/modal_events","core/notification","core/pending","./selectors","core/templates","core/str","tiny_mediacms/imageinsert","tiny_mediacms/imagehelpers"],(function(_exports,_config,_modal_events,_notification,_pending,_selectors,_templates,_str,_imageinsert,_imagehelpers){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.ImageDetails=void 0,_config=_interopRequireDefault(_config),_modal_events=_interopRequireDefault(_modal_events),_notification=_interopRequireDefault(_notification),_pending=_interopRequireDefault(_pending),_selectors=_interopRequireDefault(_selectors),_templates=_interopRequireDefault(_templates);_exports.ImageDetails=class{constructor(root,editor,currentModal,canShowFilePicker,canShowDropZone,currentUrl,_image){_defineProperty(this,"DEFAULTS",{WIDTH:160,HEIGHT:160}),_defineProperty(this,"rawImageDimensions",null),_defineProperty(this,"init",(function(){this.currentModal.setTitle((0,_str.getString)("imagedetails","tiny_mediacms")),this.imageTypeChecked(),this.presentationChanged(),this.storeImageDimensions(this.image),this.setImageDimensions(),this.registerEventListeners()})),_defineProperty(this,"loadInsertImage",(async function(){const templateContext={elementid:this.editor.id,showfilepicker:this.canShowFilePicker,showdropzone:this.canShowDropZone};Promise.all([(0,_imagehelpers.bodyImageInsert)(templateContext,this.root),(0,_imagehelpers.footerImageInsert)(templateContext,this.root)]).then((()=>{new _imageinsert.ImageInsert(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone).init()})).catch((error=>{window.console.log(error)}))})),_defineProperty(this,"setImageDimensions",(()=>{const imagePreviewBox=this.root.querySelector(_selectors.default.IMAGE.elements.previewBox),image=this.root.querySelector(_selectors.default.IMAGE.elements.preview),widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),updateImageDimensions=()=>{const boxWidth=imagePreviewBox.clientWidth,boxHeight=imagePreviewBox.clientHeight,dimensions=this.fitSquareIntoBox(widthField.value,heightField.value,boxWidth,boxHeight);image.style.width="".concat(dimensions.width,"px"),image.style.height="".concat(dimensions.height,"px")};0===imagePreviewBox.clientWidth?this.currentModal.getRoot().on(_modal_events.default.shown,(()=>{updateImageDimensions()})):updateImageDimensions()})),_defineProperty(this,"fitSquareIntoBox",((squareWidth,squareHeight,boxWidth,boxHeight)=>{if(squareWidth(""===element.value&&(element.value=this.rawImageDimensions.width),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.width)),currentHeight=(element=>(""===element.value&&(element.value=this.rawImageDimensions.height),element.value))(this.root.querySelector(_selectors.default.IMAGE.elements.height)),preview=this.root.querySelector(_selectors.default.IMAGE.elements.preview);preview.setAttribute("src",image.src),preview.style.display="";const constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain);if((0,_imagehelpers.isPercentageValue)(currentWidth)&&(0,_imagehelpers.isPercentageValue)(currentHeight))constrain.checked=currentWidth===currentHeight;else if(0===image.width||0===image.height)constrain.disabled="disabled";else{const widthRatio=Math.round(100*parseInt(currentWidth,10)/image.width),heightRatio=Math.round(100*parseInt(currentHeight,10)/image.height);constrain.checked=widthRatio===heightRatio}((currentWidth,currentHeight)=>{this.rawImageDimensions.width===currentWidth&&this.rawImageDimensions.height===currentHeight?(this.currentWidth=this.rawImageDimensions.width,this.currentHeight=this.rawImageDimensions.height,this.sizeChecked("original")):(this.currentWidth=currentWidth,this.currentHeight=currentHeight,this.sizeChecked("custom"))})(Number(currentWidth),Number(currentHeight))}sizeChecked(option){const widthInput=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightInput=this.root.querySelector(_selectors.default.IMAGE.elements.height);if("original"===option)this.sizeOriginalChecked(),widthInput.value=this.rawImageDimensions.width,heightInput.value=this.rawImageDimensions.height;else if("custom"===option&&(this.sizeCustomChecked(),widthInput.value=this.currentWidth,heightInput.value=this.currentHeight,this.currentWidth===this.rawImageDimensions.width&&this.currentHeight===this.rawImageDimensions.height)){this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked=!0}this.autoAdjustSize()}autoAdjustSize(){let forceHeight=arguments.length>0&&void 0!==arguments[0]&&arguments[0];if(!this.rawImageDimensions)return;const widthField=this.root.querySelector(_selectors.default.IMAGE.elements.width),heightField=this.root.querySelector(_selectors.default.IMAGE.elements.height),normalizeFieldData=fieldData=>(fieldData.isPercentageValue=!!(0,_imagehelpers.isPercentageValue)(fieldData.field.value),fieldData.isPercentageValue?(fieldData.percentValue=parseInt(fieldData.field.value,10),fieldData.pixelSize=this.rawImageDimensions[fieldData.type]/100*fieldData.percentValue):(fieldData.pixelSize=parseInt(fieldData.field.value,10),fieldData.percentValue=fieldData.pixelSize/this.rawImageDimensions[fieldData.type]*100),fieldData),getKeyField=()=>{const currentValue=forceHeight?{field:heightField,type:"height"}:{field:widthField,type:"width"};return""===currentValue.field.value&&(currentValue.field.value=this.rawImageDimensions[currentValue.type]),normalizeFieldData(currentValue)};if(this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked){const keyField=getKeyField(),relativeField=normalizeFieldData(forceHeight?{field:widthField,type:"width"}:{field:heightField,type:"height"});keyField.isPercentageValue?(relativeField.field.value=keyField.field.value,relativeField.percentValue=keyField.percentValue):(relativeField.pixelSize=Math.round(keyField.pixelSize/this.rawImageDimensions[keyField.type]*this.rawImageDimensions[relativeField.type]),relativeField.field.value=relativeField.pixelSize)}this.currentWidth=Number(widthField.value)!==this.rawImageDimensions.width?widthField.value:this.currentWidth,this.currentHeight=Number(heightField.value)!==this.rawImageDimensions.height?heightField.value:this.currentHeight}sizeOriginalChecked(){this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked=!0,this.root.querySelector(_selectors.default.IMAGE.elements.sizeCustom).checked=!1,(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.properties,this.root)}sizeCustomChecked(){this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked=!1,this.root.querySelector(_selectors.default.IMAGE.elements.sizeCustom).checked=!0,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.properties,this.root)}presentationChanged(){const presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation);this.root.querySelector(_selectors.default.IMAGE.elements.alt).disabled=presentation.checked,this.handleKeyupCharacterCount()}imageTypeChecked(){const isExternalUrl=!1===new RegExp("".concat(_config.default.wwwroot)).test(this.currentUrl);if((0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.url,this.root),isExternalUrl)this.setFilenameLabel(decodeURI(this.currentUrl));else{const filename=this.currentUrl.split("/").pop().split("?")[0];this.setFilenameLabel(decodeURI(filename))}}setFilenameLabel(label){const urlLabelEle=this.root.querySelector(_selectors.default.IMAGE.elements.fileNameLabel);urlLabelEle&&(urlLabelEle.innerHTML=label,urlLabelEle.setAttribute("title",label))}toggleAriaInvalid(selectors,predicate){selectors.forEach((selector=>{this.root.querySelectorAll(selector).forEach((element=>element.setAttribute("aria-invalid",predicate)))}))}hasErrorUrlField(){const urlError=""===this.currentUrl;return urlError?(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root):(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.url],urlError),urlError}hasErrorAltField(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,presentation=this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,imageAltError=""===alt&&!presentation;return imageAltError?(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.altWarning,this.root):(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWaaltWarningrning,this.root),this.toggleAriaInvalid([_selectors.default.IMAGE.elements.alt,_selectors.default.IMAGE.elements.presentation],imageAltError),imageAltError}updateWarning(){const urlError=this.hasErrorUrlField(),imageAltError=this.hasErrorAltField();return urlError||imageAltError}getImageContext(){if(this.updateWarning())return null;const classList=[],constrain=this.root.querySelector(_selectors.default.IMAGE.elements.constrain).checked,sizeOriginal=this.root.querySelector(_selectors.default.IMAGE.elements.sizeOriginal).checked;return constrain||sizeOriginal?classList.push(_selectors.default.IMAGE.styles.responsive):classList.pop(_selectors.default.IMAGE.styles.responsive),{url:this.currentUrl,alt:this.root.querySelector(_selectors.default.IMAGE.elements.alt).value,width:this.root.querySelector(_selectors.default.IMAGE.elements.width).value,height:this.root.querySelector(_selectors.default.IMAGE.elements.height).value,presentation:this.root.querySelector(_selectors.default.IMAGE.elements.presentation).checked,customStyle:this.root.querySelector(_selectors.default.IMAGE.elements.customStyle).value,classlist:classList.join(" ")}}setImage(){const pendingPromise=new _pending.default("tiny_mediacms:setImage");if(""===this.currentUrl)return;if(this.updateWarning())return void pendingPromise.resolve();const width=this.root.querySelector(_selectors.default.IMAGE.elements.width).value;if(!(0,_imagehelpers.isPercentageValue)(width)&&isNaN(parseInt(width,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.width).focus(),void pendingPromise.resolve();const height=this.root.querySelector(_selectors.default.IMAGE.elements.height).value;if(!(0,_imagehelpers.isPercentageValue)(height)&&isNaN(parseInt(height,10)))return this.root.querySelector(_selectors.default.IMAGE.elements.height).focus(),void pendingPromise.resolve();_templates.default.render("tiny_mediacms/image",this.getImageContext()).then((html=>(this.editor.insertContent(html),this.currentModal.destroy(),pendingPromise.resolve(),html))).catch((error=>{window.console.log(error)}))}deleteImage(){_notification.default.deleteCancelPromise((0,_str.getString)("deleteimage","tiny_mediacms"),(0,_str.getString)("deleteimagewarning","tiny_mediacms")).then((()=>{(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.altWarning,this.root),this.loadInsertImage()})).catch((error=>{window.console.log(error)}))}registerEventListeners(){this.root.querySelector(_selectors.default.IMAGE.actions.submit).addEventListener("click",(e=>{e.preventDefault(),this.setImage()}));const deleteImageEle=this.root.querySelector(_selectors.default.IMAGE.actions.deleteImage);deleteImageEle.addEventListener("click",(()=>{this.deleteImage()})),deleteImageEle.addEventListener("keydown",(e=>{"Enter"===e.key&&this.deleteImage()})),this.root.addEventListener("change",(e=>{e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged();e.target.closest(_selectors.default.IMAGE.elements.constrain)&&this.autoAdjustSize();e.target.closest(_selectors.default.IMAGE.elements.sizeOriginal)&&this.sizeChecked("original");e.target.closest(_selectors.default.IMAGE.elements.sizeCustom)&&this.sizeChecked("custom")})),this.root.addEventListener("blur",(e=>{if(e.target.nodeType===Node.ELEMENT_NODE){e.target.closest(_selectors.default.IMAGE.elements.presentation)&&this.presentationChanged()}}),!0),this.root.addEventListener("keyup",(e=>{e.target.closest(_selectors.default.IMAGE.elements.alt)&&this.handleKeyupCharacterCount()})),this.root.addEventListener("input",(e=>{const widthEle=e.target.closest(_selectors.default.IMAGE.elements.width);widthEle&&(widthEle.value=""===widthEle.value?0:Number(widthEle.value),this.autoAdjustSize());const heightEle=e.target.closest(_selectors.default.IMAGE.elements.height);heightEle&&(heightEle.value=""===heightEle.value?0:Number(heightEle.value),this.autoAdjustSize(!0))}))}handleKeyupCharacterCount(){const alt=this.root.querySelector(_selectors.default.IMAGE.elements.alt).value;this.root.querySelector("#currentcount").innerHTML=alt.length}}})); - -//# sourceMappingURL=imagedetails.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js.map deleted file mode 100755 index 0d7ebb6c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagedetails.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imagedetails.min.js","sources":["../src/imagedetails.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 * Tiny media plugin image details class for Moodle.\n *\n * @module tiny_mediacms/imagedetails\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Config from 'core/config';\nimport ModalEvents from 'core/modal_events';\nimport Notification from 'core/notification';\nimport Pending from 'core/pending';\nimport Selectors from './selectors';\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport {ImageInsert} from 'tiny_mediacms/imageinsert';\nimport {\n bodyImageInsert,\n footerImageInsert,\n showElements,\n hideElements,\n isPercentageValue,\n} from 'tiny_mediacms/imagehelpers';\n\nexport class ImageDetails {\n DEFAULTS = {\n WIDTH: 160,\n HEIGHT: 160,\n };\n\n rawImageDimensions = null;\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n currentUrl,\n image,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n this.currentUrl = currentUrl;\n this.image = image;\n }\n\n init = function() {\n this.currentModal.setTitle(getString('imagedetails', 'tiny_mediacms'));\n this.imageTypeChecked();\n this.presentationChanged();\n this.storeImageDimensions(this.image);\n this.setImageDimensions();\n this.registerEventListeners();\n };\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n */\n loadInsertImage = async function() {\n const templateContext = {\n elementid: this.editor.id,\n showfilepicker: this.canShowFilePicker,\n showdropzone: this.canShowDropZone,\n };\n\n Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)])\n .then(() => {\n const imageinsert = new ImageInsert(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n );\n imageinsert.init();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n };\n\n storeImageDimensions(image) {\n // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG).\n this.rawImageDimensions = {\n width: image.width || this.DEFAULTS.WIDTH,\n height: image.height || this.DEFAULTS.HEIGHT,\n };\n\n const getCurrentWidth = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.width;\n }\n return element.value;\n };\n\n const getCurrentHeight = (element) => {\n if (element.value === '') {\n element.value = this.rawImageDimensions.height;\n }\n return element.value;\n };\n\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const currentWidth = getCurrentWidth(widthInput);\n\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n const currentHeight = getCurrentHeight(heightInput);\n\n const preview = this.root.querySelector(Selectors.IMAGE.elements.preview);\n preview.setAttribute('src', image.src);\n preview.style.display = '';\n\n // Ensure the checkbox always in unchecked status when an image loads at first.\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) {\n constrain.checked = currentWidth === currentHeight;\n } else if (image.width === 0 || image.height === 0) {\n // If we don't have both dimensions of the image, we can't auto-size it, so disable control.\n constrain.disabled = 'disabled';\n } else {\n // This is the same as comparing to 3 decimal places.\n const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width);\n const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height);\n constrain.checked = widthRatio === heightRatio;\n }\n\n /**\n * Sets the selected size option based on current width and height values.\n *\n * @param {number} currentWidth - The current width value.\n * @param {number} currentHeight - The current height value.\n */\n const setSelectedSize = (currentWidth, currentHeight) => {\n if (this.rawImageDimensions.width === currentWidth &&\n this.rawImageDimensions.height === currentHeight\n ) {\n this.currentWidth = this.rawImageDimensions.width;\n this.currentHeight = this.rawImageDimensions.height;\n this.sizeChecked('original');\n } else {\n this.currentWidth = currentWidth;\n this.currentHeight = currentHeight;\n this.sizeChecked('custom');\n }\n };\n\n setSelectedSize(Number(currentWidth), Number(currentHeight));\n }\n\n /**\n * Handles the selection of image size options and updates the form inputs accordingly.\n *\n * @param {string} option - The selected image size option (\"original\" or \"custom\").\n */\n sizeChecked(option) {\n const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height);\n if (option === \"original\") {\n this.sizeOriginalChecked();\n widthInput.value = this.rawImageDimensions.width;\n heightInput.value = this.rawImageDimensions.height;\n } else if (option === \"custom\") {\n this.sizeCustomChecked();\n widthInput.value = this.currentWidth;\n heightInput.value = this.currentHeight;\n\n // If the current size is equal to the original size, then check the Keep proportion checkbox.\n if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) {\n const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n constrainField.checked = true;\n }\n }\n this.autoAdjustSize();\n }\n\n autoAdjustSize(forceHeight = false) {\n // If we do not know the image size, do not do anything.\n if (!this.rawImageDimensions) {\n return;\n }\n\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const normalizeFieldData = (fieldData) => {\n fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value);\n if (fieldData.isPercentageValue) {\n fieldData.percentValue = parseInt(fieldData.field.value, 10);\n fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue;\n } else {\n fieldData.pixelSize = parseInt(fieldData.field.value, 10);\n fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100;\n }\n\n return fieldData;\n };\n\n const getKeyField = () => {\n const getValue = () => {\n if (forceHeight) {\n return {\n field: heightField,\n type: 'height',\n };\n } else {\n return {\n field: widthField,\n type: 'width',\n };\n }\n };\n\n const currentValue = getValue();\n if (currentValue.field.value === '') {\n currentValue.field.value = this.rawImageDimensions[currentValue.type];\n }\n\n return normalizeFieldData(currentValue);\n };\n\n const getRelativeField = () => {\n if (forceHeight) {\n return normalizeFieldData({\n field: widthField,\n type: 'width',\n });\n } else {\n return normalizeFieldData({\n field: heightField,\n type: 'height',\n });\n }\n };\n\n // Now update with the new values.\n const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain);\n if (constrainField.checked) {\n const keyField = getKeyField();\n const relativeField = getRelativeField();\n // We are keeping the image in proportion.\n // Calculate the size for the relative field.\n if (keyField.isPercentageValue) {\n // In proportion, so the percentages are the same.\n relativeField.field.value = keyField.field.value;\n relativeField.percentValue = keyField.percentValue;\n } else {\n relativeField.pixelSize = Math.round(\n keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type]\n );\n relativeField.field.value = relativeField.pixelSize;\n }\n }\n\n // Store the custom width and height to reuse.\n this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth;\n this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight;\n }\n\n /**\n * Sets the dimensions of the image preview element based on user input and constraints.\n */\n setImageDimensions = () => {\n const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox);\n const image = this.root.querySelector(Selectors.IMAGE.elements.preview);\n const widthField = this.root.querySelector(Selectors.IMAGE.elements.width);\n const heightField = this.root.querySelector(Selectors.IMAGE.elements.height);\n\n const updateImageDimensions = () => {\n // Get the latest dimensions of the preview box for responsiveness.\n const boxWidth = imagePreviewBox.clientWidth;\n const boxHeight = imagePreviewBox.clientHeight;\n // Get the new width and height for the image.\n const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight);\n image.style.width = `${dimensions.width}px`;\n image.style.height = `${dimensions.height}px`;\n };\n // If the client size is zero, then get the new dimensions once the modal is shown.\n if (imagePreviewBox.clientWidth === 0) {\n // Call the shown event.\n this.currentModal.getRoot().on(ModalEvents.shown, () => {\n updateImageDimensions();\n });\n } else {\n updateImageDimensions();\n }\n };\n\n /**\n * Handles the selection of the \"Original Size\" option and updates the form elements accordingly.\n */\n sizeOriginalChecked() {\n this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true;\n this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false;\n hideElements(Selectors.IMAGE.elements.properties, this.root);\n }\n\n /**\n * Handles the selection of the \"Custom Size\" option and updates the form elements accordingly.\n */\n sizeCustomChecked() {\n this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false;\n this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true;\n showElements(Selectors.IMAGE.elements.properties, this.root);\n }\n\n /**\n * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly.\n */\n presentationChanged() {\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation);\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt);\n alt.disabled = presentation.checked;\n\n // Counting the image description characters.\n this.handleKeyupCharacterCount();\n }\n\n /**\n * This function checks whether an image URL is local (within the same website's domain) or external (from an external source).\n * Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface.\n * If the image is local then we only show it's filename.\n * If the image is external then it will show full URL and it can be updated.\n */\n imageTypeChecked() {\n const regex = new RegExp(`${Config.wwwroot}`);\n\n // True if the URL is from external, otherwise false.\n const isExternalUrl = regex.test(this.currentUrl) === false;\n\n // Hide the URL input.\n hideElements(Selectors.IMAGE.elements.url, this.root);\n\n if (!isExternalUrl) {\n // Split the URL by '/' to get an array of segments.\n const segments = this.currentUrl.split('/');\n // Get the last segment, which should be the filename.\n const filename = segments.pop().split('?')[0];\n // Show the file name.\n this.setFilenameLabel(decodeURI(filename));\n } else {\n\n this.setFilenameLabel(decodeURI(this.currentUrl));\n }\n }\n\n /**\n * Set the string for the URL label element.\n *\n * @param {string} label - The label text to set.\n */\n setFilenameLabel(label) {\n const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel);\n if (urlLabelEle) {\n urlLabelEle.innerHTML = label;\n urlLabelEle.setAttribute(\"title\", label);\n }\n }\n\n toggleAriaInvalid(selectors, predicate) {\n selectors.forEach((selector) => {\n const elements = this.root.querySelectorAll(selector);\n elements.forEach((element) => element.setAttribute('aria-invalid', predicate));\n });\n }\n\n hasErrorUrlField() {\n const urlError = this.currentUrl === '';\n if (urlError) {\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError);\n\n return urlError;\n }\n\n hasErrorAltField() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked;\n const imageAltError = alt === '' && !presentation;\n if (imageAltError) {\n showElements(Selectors.IMAGE.elements.altWarning, this.root);\n } else {\n hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root);\n }\n this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError);\n\n return imageAltError;\n }\n\n updateWarning() {\n const urlError = this.hasErrorUrlField();\n const imageAltError = this.hasErrorAltField();\n\n return urlError || imageAltError;\n }\n\n getImageContext() {\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n return null;\n }\n\n const classList = [];\n const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked;\n const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked;\n if (constrain || sizeOriginal) {\n // If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class.\n classList.push(Selectors.IMAGE.styles.responsive);\n } else {\n // Otherwise, remove it.\n classList.pop(Selectors.IMAGE.styles.responsive);\n }\n\n return {\n url: this.currentUrl,\n alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value,\n width: this.root.querySelector(Selectors.IMAGE.elements.width).value,\n height: this.root.querySelector(Selectors.IMAGE.elements.height).value,\n presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked,\n customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value,\n classlist: classList.join(' '),\n };\n }\n\n setImage() {\n const pendingPromise = new Pending('tiny_mediacms:setImage');\n const url = this.currentUrl;\n if (url === '') {\n return;\n }\n\n // Check if there are any accessibility issues.\n if (this.updateWarning()) {\n pendingPromise.resolve();\n return;\n }\n\n // Check for invalid width or height.\n const width = this.root.querySelector(Selectors.IMAGE.elements.width).value;\n if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.width).focus();\n pendingPromise.resolve();\n return;\n }\n\n const height = this.root.querySelector(Selectors.IMAGE.elements.height).value;\n if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) {\n this.root.querySelector(Selectors.IMAGE.elements.height).focus();\n pendingPromise.resolve();\n return;\n }\n\n Templates.render('tiny_mediacms/image', this.getImageContext())\n .then((html) => {\n this.editor.insertContent(html);\n this.currentModal.destroy();\n pendingPromise.resolve();\n\n return html;\n })\n .catch(error => {\n window.console.log(error);\n });\n }\n\n /**\n * Deletes the image after confirming with the user and loads the insert image page.\n */\n deleteImage() {\n Notification.deleteCancelPromise(\n getString('deleteimage', 'tiny_mediacms'),\n getString('deleteimagewarning', 'tiny_mediacms'),\n ).then(() => {\n hideElements(Selectors.IMAGE.elements.altWarning, this.root);\n // Removing the image in the preview will bring the user to the insert page.\n this.loadInsertImage();\n return;\n }).catch(error => {\n window.console.log(error);\n });\n }\n\n registerEventListeners() {\n const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit);\n submitAction.addEventListener('click', (e) => {\n e.preventDefault();\n this.setImage();\n });\n\n const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage);\n deleteImageEle.addEventListener('click', () => {\n this.deleteImage();\n });\n deleteImageEle.addEventListener(\"keydown\", (e) => {\n if (e.key === \"Enter\") {\n this.deleteImage();\n }\n });\n\n this.root.addEventListener('change', (e) => {\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n\n const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain);\n if (constrainEle) {\n this.autoAdjustSize();\n }\n\n const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal);\n if (sizeOriginalEle) {\n this.sizeChecked('original');\n }\n\n const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom);\n if (sizeCustomEle) {\n this.sizeChecked('custom');\n }\n });\n\n this.root.addEventListener('blur', (e) => {\n if (e.target.nodeType === Node.ELEMENT_NODE) {\n\n const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation);\n if (presentationEle) {\n this.presentationChanged();\n }\n }\n }, true);\n\n // Character count.\n this.root.addEventListener('keyup', (e) => {\n const altEle = e.target.closest(Selectors.IMAGE.elements.alt);\n if (altEle) {\n this.handleKeyupCharacterCount();\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const widthEle = e.target.closest(Selectors.IMAGE.elements.width);\n if (widthEle) {\n // Avoid empty value.\n widthEle.value = widthEle.value === \"\" ? 0 : Number(widthEle.value);\n this.autoAdjustSize();\n }\n\n const heightEle = e.target.closest(Selectors.IMAGE.elements.height);\n if (heightEle) {\n // Avoid empty value.\n heightEle.value = heightEle.value === \"\" ? 0 : Number(heightEle.value);\n this.autoAdjustSize(true);\n }\n });\n }\n\n handleKeyupCharacterCount() {\n const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value;\n const current = this.root.querySelector('#currentcount');\n current.innerHTML = alt.length;\n }\n\n /**\n * Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio.\n *\n * @param {number} squareWidth - The width of the square.\n * @param {number} squareHeight - The height of the square.\n * @param {number} boxWidth - The width of the box.\n * @param {number} boxHeight - The height of the box.\n * @returns {Object} An object with the new width and height of the square to fit in the box.\n */\n fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => {\n if (squareWidth < boxWidth && squareHeight < boxHeight) {\n // If the square is smaller than the box, keep its dimensions.\n return {\n width: squareWidth,\n height: squareHeight,\n };\n }\n // Calculate the scaling factor based on the minimum scaling required to fit in the box.\n const widthScaleFactor = boxWidth / squareWidth;\n const heightScaleFactor = boxHeight / squareHeight;\n const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor);\n // Scale the square's dimensions based on the aspect ratio and the minimum scaling factor.\n const newWidth = squareWidth * minScaleFactor;\n const newHeight = squareHeight * minScaleFactor;\n return {\n width: newWidth,\n height: newHeight,\n };\n };\n}\n"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","currentUrl","image","WIDTH","HEIGHT","setTitle","imageTypeChecked","presentationChanged","storeImageDimensions","this","setImageDimensions","registerEventListeners","async","templateContext","elementid","id","showfilepicker","showdropzone","Promise","all","then","ImageInsert","init","catch","error","window","console","log","imagePreviewBox","querySelector","Selectors","IMAGE","elements","previewBox","preview","widthField","width","heightField","height","updateImageDimensions","boxWidth","clientWidth","boxHeight","clientHeight","dimensions","fitSquareIntoBox","value","style","getRoot","on","ModalEvents","shown","squareWidth","squareHeight","widthScaleFactor","heightScaleFactor","minScaleFactor","Math","min","rawImageDimensions","DEFAULTS","currentWidth","element","getCurrentWidth","currentHeight","getCurrentHeight","setAttribute","src","display","constrain","checked","disabled","widthRatio","round","parseInt","heightRatio","sizeChecked","setSelectedSize","Number","option","widthInput","heightInput","sizeOriginalChecked","sizeCustomChecked","autoAdjustSize","forceHeight","normalizeFieldData","fieldData","isPercentageValue","field","percentValue","pixelSize","type","getKeyField","currentValue","keyField","relativeField","sizeOriginal","sizeCustom","properties","presentation","alt","handleKeyupCharacterCount","isExternalUrl","RegExp","Config","wwwroot","test","url","setFilenameLabel","decodeURI","filename","split","pop","label","urlLabelEle","fileNameLabel","innerHTML","toggleAriaInvalid","selectors","predicate","forEach","selector","querySelectorAll","hasErrorUrlField","urlError","urlWarning","hasErrorAltField","imageAltError","altWarning","urlWaaltWarningrning","updateWarning","getImageContext","classList","push","styles","responsive","customStyle","classlist","join","setImage","pendingPromise","Pending","resolve","isNaN","focus","render","html","insertContent","destroy","deleteImage","deleteCancelPromise","loadInsertImage","actions","submit","addEventListener","e","preventDefault","deleteImageEle","key","target","closest","nodeType","Node","ELEMENT_NODE","widthEle","heightEle","length"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js deleted file mode 100755 index 60b5e2de..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/imagehelpers",["exports","core/templates"],(function(_exports,_templates){var obj; -/** - * Tiny media plugin image helpers. - * - * @module tiny_mediacms/imagehelpers - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.showElements=_exports.isPercentageValue=_exports.hideElements=_exports.footerImageInsert=_exports.footerImageDetails=_exports.bodyImageInsert=_exports.bodyImageDetails=void 0,_templates=(obj=_templates)&&obj.__esModule?obj:{default:obj};_exports.bodyImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert",{...templateContext}).then((_ref=>{let{html:html,js:js}=_ref;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageInsert=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_insert_footer",{...templateContext}).then((_ref2=>{let{html:html,js:js}=_ref2;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.bodyImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details",{...templateContext}).then((_ref3=>{let{html:html,js:js}=_ref3;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_body_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.footerImageDetails=async(templateContext,root)=>_templates.default.renderForPromise("tiny_mediacms/insert_image_modal_details_footer",{...templateContext}).then((_ref4=>{let{html:html,js:js}=_ref4;_templates.default.replaceNodeContents(root.querySelector(".tiny_imagecms_footer_template"),html,js)})).catch((error=>{window.console.log(error)}));_exports.showElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.remove("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.remove("d-none")}};_exports.hideElements=(elements,root)=>{if(elements instanceof Array)elements.forEach((elementSelector=>{const element=root.querySelector(elementSelector);element&&element.classList.add("d-none")}));else{const element=root.querySelector(elements);element&&element.classList.add("d-none")}};_exports.isPercentageValue=value=>value.match(/\d+%/)})); - -//# sourceMappingURL=imagehelpers.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js.map deleted file mode 100755 index d6b87c56..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagehelpers.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imagehelpers.min.js","sources":["../src/imagehelpers.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 * Tiny media plugin image helpers.\n *\n * @module tiny_mediacms/imagehelpers\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\n\n/**\n * Renders and inserts the body template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const bodyImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the footer template for inserting an image into the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const footerImageInsert = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert_footer', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the body template for displaying image details in the modal.\n *\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const bodyImageDetails = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Renders and inserts the footer template for displaying image details in the modal.\n * @param {object} templateContext - The context for rendering the template.\n * @param {HTMLElement} root - The root element where the template will be inserted.\n * @returns {Promise}\n */\nexport const footerImageDetails = async(templateContext, root) => {\n return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details_footer', {...templateContext})\n .then(({html, js}) => {\n Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js);\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n};\n\n/**\n * Show the element(s).\n *\n * @param {string|string[]} elements - The CSS selector for the elements to toggle.\n * @param {object} root - The CSS selector for the elements to toggle.\n */\nexport const showElements = (elements, root) => {\n if (elements instanceof Array) {\n elements.forEach((elementSelector) => {\n const element = root.querySelector(elementSelector);\n if (element) {\n element.classList.remove('d-none');\n }\n });\n } else {\n const element = root.querySelector(elements);\n if (element) {\n element.classList.remove('d-none');\n }\n }\n};\n\n/**\n * Hide the element(s).\n *\n * @param {string|string[]} elements - The CSS selector for the elements to toggle.\n * @param {object} root - The CSS selector for the elements to toggle.\n */\nexport const hideElements = (elements, root) => {\n if (elements instanceof Array) {\n elements.forEach((elementSelector) => {\n const element = root.querySelector(elementSelector);\n if (element) {\n element.classList.add('d-none');\n }\n });\n } else {\n const element = root.querySelector(elements);\n if (element) {\n element.classList.add('d-none');\n }\n }\n};\n\n/**\n * Checks if the given value is a percentage value.\n *\n * @param {string} value - The value to check.\n * @returns {boolean} True if the value is a percentage value, false otherwise.\n */\nexport const isPercentageValue = (value) => {\n return value.match(/\\d+%/);\n};\n"],"names":["async","templateContext","root","Templates","renderForPromise","then","_ref","html","js","replaceNodeContents","querySelector","catch","error","window","console","log","_ref2","_ref3","_ref4","elements","Array","forEach","elementSelector","element","classList","remove","add","value","match"],"mappings":";;;;;;;4UAgC+BA,MAAMC,gBAAiBC,OAC3CC,mBAAUC,iBAAiB,0CAA2C,IAAIH,kBAChFI,MAAKC,WAACC,KAACA,KAADC,GAAOA,4BACAC,oBAAoBP,KAAKQ,cAAc,gCAAiCH,KAAMC,OAG3FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,qCAWMZ,MAAMC,gBAAiBC,OAC7CC,mBAAUC,iBAAiB,iDAAkD,IAAIH,kBACvFI,MAAKW,YAACT,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,kCAAmCH,KAAMC,OAG7FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,oCAWKZ,MAAMC,gBAAiBC,OAC5CC,mBAAUC,iBAAiB,2CAA4C,IAAIH,kBACjFI,MAAKY,YAACV,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,gCAAiCH,KAAMC,OAG3FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,sCAUOZ,MAAMC,gBAAiBC,OAC9CC,mBAAUC,iBAAiB,kDAAmD,IAAIH,kBACxFI,MAAKa,YAACX,KAACA,KAADC,GAAOA,6BACAC,oBAAoBP,KAAKQ,cAAc,kCAAmCH,KAAMC,OAG7FG,OAAMC,QACHC,OAAOC,QAAQC,IAAIH,gCAUC,CAACO,SAAUjB,WAC/BiB,oBAAoBC,MACpBD,SAASE,SAASC,wBACRC,QAAUrB,KAAKQ,cAAcY,iBAC/BC,SACAA,QAAQC,UAAUC,OAAO,iBAG9B,OACGF,QAAUrB,KAAKQ,cAAcS,UAC/BI,SACAA,QAAQC,UAAUC,OAAO,kCAWT,CAACN,SAAUjB,WAC/BiB,oBAAoBC,MACpBD,SAASE,SAASC,wBACRC,QAAUrB,KAAKQ,cAAcY,iBAC/BC,SACAA,QAAQC,UAAUE,IAAI,iBAG3B,OACGH,QAAUrB,KAAKQ,cAAcS,UAC/BI,SACAA,QAAQC,UAAUE,IAAI,uCAWAC,OACvBA,MAAMC,MAAM"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js deleted file mode 100755 index 1b7a7df6..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/imageinsert",["exports","./selectors","core/dropzone","editor_tiny/uploader","core/prefetch","core/str","./common","editor_tiny/options","editor_tiny/utils","tiny_mediacms/imagedetails","tiny_mediacms/imagehelpers"],(function(_exports,_selectors,_dropzone,_uploader,_prefetch,_str,_common,_options,_utils,_imagedetails,_imagehelpers){function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.ImageInsert=void 0,_selectors=_interopRequireDefault(_selectors),_dropzone=_interopRequireDefault(_dropzone),_uploader=_interopRequireDefault(_uploader),(0,_prefetch.prefetchStrings)("tiny_mediacms",["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"]);_exports.ImageInsert=class{constructor(_root,editor,currentModal,canShowFilePicker,canShowDropZone){_defineProperty(this,"init",(async function(){const langStringKeys=["insertimage","enterurl","enterurlor","imageurlrequired","uploading","loading","addfilesdrop","sizecustom_help"],langStringvalues=await(0,_str.getStrings)([...langStringKeys].map((key=>({key:key,component:_common.component}))));if(this.langStrings=Object.fromEntries(langStringKeys.map(((key,index)=>[key,langStringvalues[index]]))),this.currentModal.setTitle(this.langStrings.insertimage),this.canShowDropZone){const dropZoneEle=document.querySelector(_selectors.default.IMAGE.elements.dropzoneContainer);let acceptedTypes=(0,_options.getFilePicker)(this.editor,"image").accepted_types;Array.isArray(acceptedTypes)&&(acceptedTypes=acceptedTypes.join(","));const dropZone=new _dropzone.default(dropZoneEle,acceptedTypes,(files=>{this.handleUploadedFile(files)}));dropZone.setLabel(this.langStrings.addfilesdrop),dropZone.init()}await this.registerEventListeners()})),_defineProperty(this,"isValidUrl",(urlString=>!!new RegExp("^(https?:\\/\\/)?((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|((\\d{1,3}\\.){3}\\d{1,3})|localhost)(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*").test(urlString))),_defineProperty(this,"loadPreviewImage",(function(url){this.startImageLoading(),this.currentUrl=url;const image=new Image;image.src=url,image.addEventListener("error",(()=>{this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=this.langStrings.imageurlrequired,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.currentUrl="",this.stopImageLoading()})),image.addEventListener("load",(()=>{let templateContext={};templateContext.sizecustomhelpicon={text:this.langStrings.sizecustom_help},Promise.all([(0,_imagehelpers.bodyImageDetails)(templateContext,this.root),(0,_imagehelpers.footerImageDetails)(templateContext,this.root)]).then((()=>{new _imagedetails.ImageDetails(this.root,this.editor,this.currentModal,this.canShowFilePicker,this.canShowDropZone,this.currentUrl,image).init()})).then((()=>{this.stopImageLoading()})).catch((error=>{window.console.log(error)}))}))})),_defineProperty(this,"updateLoaderIcon",(function(root,langStrings){let progress=arguments.length>2&&void 0!==arguments[2]?arguments[2]:null;const loaderIcon=root.querySelector(_selectors.default.IMAGE.elements.loaderIconContainer+" div");loaderIcon.innerHTML=null!==progress?"".concat(langStrings.uploading," ").concat(Math.round(progress),"%"):langStrings.loading})),_defineProperty(this,"handleUploadedFile",(async files=>{try{this.startImageLoading();const fileURL=await(0,_uploader.default)(this.editor,"image",files[0],files[0].name,(progress=>{this.updateLoaderIcon(this.root,this.langStrings,progress)}));this.updateLoaderIcon(this.root,this.langStrings),this.filePickerCallback({url:fileURL})}catch(error){this.root.querySelector(_selectors.default.IMAGE.elements.urlWarning).innerHTML=void 0!==error.error?error.error:error,(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.urlWarning,this.root),this.stopImageLoading()}})),this.root=_root,this.editor=editor,this.currentModal=currentModal,this.canShowFilePicker=canShowFilePicker,this.canShowDropZone=canShowDropZone}toggleUrlButton(){const url=this.root.querySelector(_selectors.default.IMAGE.elements.url).value;this.root.querySelector(_selectors.default.IMAGE.actions.addUrl).disabled=!(""!==url&&this.isValidUrl(url))}urlChanged(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.urlWarning,this.root);const input=this.root.querySelector(_selectors.default.IMAGE.elements.url);input.value&&input.value!==this.currentUrl&&this.loadPreviewImage(input.value)}startImageLoading(){(0,_imagehelpers.showElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToHide=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.urlWarning,_selectors.default.IMAGE.elements.modalFooter];(0,_imagehelpers.hideElements)(elementsToHide,this.root)}stopImageLoading(){(0,_imagehelpers.hideElements)(_selectors.default.IMAGE.elements.loaderIcon,this.root);const elementsToShow=[_selectors.default.IMAGE.elements.insertImage,_selectors.default.IMAGE.elements.modalFooter];(0,_imagehelpers.showElements)(elementsToShow,this.root)}filePickerCallback(params){params.url&&this.loadPreviewImage(params.url)}registerEventListeners(){this.root.addEventListener("click",(async e=>{e.target.closest(_selectors.default.IMAGE.actions.addUrl)&&this.urlChanged();if(e.target.closest(_selectors.default.IMAGE.actions.imageBrowser)&&this.canShowFilePicker){e.preventDefault();const params=await(0,_utils.displayFilepicker)(this.editor,"image");this.filePickerCallback(params)}})),this.root.addEventListener("input",(e=>{e.target.closest(_selectors.default.IMAGE.elements.url)&&this.toggleUrlButton()}));const fileInput=this.root.querySelector(_selectors.default.IMAGE.elements.fileInput);fileInput&&fileInput.addEventListener("change",(()=>{this.handleUploadedFile(fileInput.files)}))}}})); - -//# sourceMappingURL=imageinsert.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js.map deleted file mode 100755 index dba4778f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imageinsert.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imageinsert.min.js","sources":["../src/imageinsert.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 * Tiny media plugin image insertion class for Moodle.\n *\n * @module tiny_mediacms/imageinsert\n * @copyright 2024 Meirza \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Selectors from './selectors';\nimport Dropzone from 'core/dropzone';\nimport uploadFile from 'editor_tiny/uploader';\nimport {prefetchStrings} from 'core/prefetch';\nimport {getStrings} from 'core/str';\nimport {component} from \"./common\";\nimport {getFilePicker} from 'editor_tiny/options';\nimport {displayFilepicker} from 'editor_tiny/utils';\nimport {ImageDetails} from 'tiny_mediacms/imagedetails';\nimport {\n showElements,\n hideElements,\n bodyImageDetails,\n footerImageDetails,\n} from 'tiny_mediacms/imagehelpers';\n\nprefetchStrings('tiny_mediacms', [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n 'addfilesdrop',\n 'sizecustom_help',\n]);\n\nexport class ImageInsert {\n\n constructor(\n root,\n editor,\n currentModal,\n canShowFilePicker,\n canShowDropZone,\n ) {\n this.root = root;\n this.editor = editor;\n this.currentModal = currentModal;\n this.canShowFilePicker = canShowFilePicker;\n this.canShowDropZone = canShowDropZone;\n }\n\n init = async function() {\n // Get the localization lang strings and turn them into object.\n const langStringKeys = [\n 'insertimage',\n 'enterurl',\n 'enterurlor',\n 'imageurlrequired',\n 'uploading',\n 'loading',\n 'addfilesdrop',\n 'sizecustom_help',\n ];\n const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component})));\n\n // Convert array to object.\n this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]]));\n this.currentModal.setTitle(this.langStrings.insertimage);\n if (this.canShowDropZone) {\n const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer);\n\n // Accepted types can be either a string or an array.\n let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types;\n if (Array.isArray(acceptedTypes)) {\n acceptedTypes = acceptedTypes.join(',');\n }\n\n const dropZone = new Dropzone(\n dropZoneEle,\n acceptedTypes,\n files => {\n this.handleUploadedFile(files);\n }\n );\n dropZone.setLabel(this.langStrings.addfilesdrop);\n dropZone.init();\n }\n await this.registerEventListeners();\n };\n\n /**\n * Enables or disables the URL-related buttons in the footer based on the current URL and input value.\n */\n toggleUrlButton() {\n const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url);\n const url = urlInput.value;\n const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl);\n addUrl.disabled = !(url !== \"\" && this.isValidUrl(url));\n }\n\n /**\n * Check if given string is a valid URL.\n *\n * @param {String} urlString URL the link will point to.\n * @returns {boolean} True is valid, otherwise false.\n */\n isValidUrl = urlString => {\n const urlPattern = new RegExp('^(https?:\\\\/\\\\/)?' + // Protocol.\n '((([a-z\\\\d]([a-z\\\\d-]*[a-z\\\\d])*)\\\\.)+[a-z]{2,}|' + // Domain name.\n '((\\\\d{1,3}\\\\.){3}\\\\d{1,3})|localhost)' + // OR ip (v4) address, localhost.\n '(\\\\:\\\\d+)?(\\\\/[-a-z\\\\d%_.~+]*)*'); // Port and path.\n return !!urlPattern.test(urlString);\n };\n\n /**\n * Handles changes in the image URL input field and loads a preview of the image if the URL has changed.\n */\n urlChanged() {\n hideElements(Selectors.IMAGE.elements.urlWarning, this.root);\n const input = this.root.querySelector(Selectors.IMAGE.elements.url);\n if (input.value && input.value !== this.currentUrl) {\n this.loadPreviewImage(input.value);\n }\n }\n\n /**\n * Loads and displays a preview image based on the provided URL, and handles image loading events.\n *\n * @param {string} url - The URL of the image to load and display.\n */\n loadPreviewImage = function(url) {\n this.startImageLoading();\n this.currentUrl = url;\n const image = new Image();\n image.src = url;\n image.addEventListener('error', () => {\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.currentUrl = \"\";\n this.stopImageLoading();\n });\n\n image.addEventListener('load', () => {\n let templateContext = {};\n templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help};\n Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)])\n .then(() => {\n const imagedetails = new ImageDetails(\n this.root,\n this.editor,\n this.currentModal,\n this.canShowFilePicker,\n this.canShowDropZone,\n this.currentUrl,\n image,\n );\n imagedetails.init();\n return;\n }).then(() => {\n this.stopImageLoading();\n return;\n })\n .catch(error => {\n window.console.log(error);\n });\n });\n };\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n startImageLoading() {\n showElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n const elementsToHide = [\n Selectors.IMAGE.elements.insertImage,\n Selectors.IMAGE.elements.urlWarning,\n Selectors.IMAGE.elements.modalFooter,\n ];\n hideElements(elementsToHide, this.root);\n }\n\n /**\n * Displays the upload loader and disables UI elements while loading a file.\n */\n stopImageLoading() {\n hideElements(Selectors.IMAGE.elements.loaderIcon, this.root);\n const elementsToShow = [\n Selectors.IMAGE.elements.insertImage,\n Selectors.IMAGE.elements.modalFooter,\n ];\n showElements(elementsToShow, this.root);\n }\n\n filePickerCallback(params) {\n if (params.url) {\n this.loadPreviewImage(params.url);\n }\n }\n\n /**\n * Updates the content of the loader icon.\n *\n * @param {HTMLElement} root - The root element containing the loader icon.\n * @param {object} langStrings - An object containing language strings.\n * @param {number|null} progress - The progress percentage (optional).\n * @returns {void}\n */\n updateLoaderIcon = (root, langStrings, progress = null) => {\n const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div');\n loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading;\n };\n\n /**\n * Handles the uploaded file, initiates the upload process, and updates the UI during the upload.\n *\n * @param {FileList} files - The list of files to upload (usually from a file input field).\n * @returns {Promise} A promise that resolves when the file is uploaded and processed.\n */\n handleUploadedFile = async(files) => {\n try {\n this.startImageLoading();\n const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => {\n this.updateLoaderIcon(this.root, this.langStrings, progress);\n });\n // Set the loader icon content to \"loading\" after the file upload completes.\n this.updateLoaderIcon(this.root, this.langStrings);\n this.filePickerCallback({url: fileURL});\n } catch (error) {\n // Handle the error.\n const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning);\n urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error;\n showElements(Selectors.IMAGE.elements.urlWarning, this.root);\n this.stopImageLoading();\n }\n };\n\n registerEventListeners() {\n this.root.addEventListener('click', async(e) => {\n const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl);\n if (addUrlEle) {\n this.urlChanged();\n }\n\n const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser);\n if (imageBrowserAction && this.canShowFilePicker) {\n e.preventDefault();\n const params = await displayFilepicker(this.editor, 'image');\n this.filePickerCallback(params);\n }\n });\n\n this.root.addEventListener('input', (e) => {\n const urlEle = e.target.closest(Selectors.IMAGE.elements.url);\n if (urlEle) {\n this.toggleUrlButton();\n }\n });\n\n const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput);\n if (fileInput) {\n fileInput.addEventListener('change', () => {\n this.handleUploadedFile(fileInput.files);\n });\n }\n }\n}"],"names":["constructor","root","editor","currentModal","canShowFilePicker","canShowDropZone","async","langStringKeys","langStringvalues","map","key","component","langStrings","Object","fromEntries","index","setTitle","this","insertimage","dropZoneEle","document","querySelector","Selectors","IMAGE","elements","dropzoneContainer","acceptedTypes","accepted_types","Array","isArray","join","dropZone","Dropzone","files","handleUploadedFile","setLabel","addfilesdrop","init","registerEventListeners","urlString","RegExp","test","url","startImageLoading","currentUrl","image","Image","src","addEventListener","urlWarning","innerHTML","imageurlrequired","stopImageLoading","templateContext","sizecustomhelpicon","text","sizecustom_help","Promise","all","then","ImageDetails","catch","error","window","console","log","progress","loaderIcon","loaderIconContainer","uploading","Math","round","loading","fileURL","name","updateLoaderIcon","filePickerCallback","undefined","toggleUrlButton","value","actions","addUrl","disabled","isValidUrl","urlChanged","input","loadPreviewImage","elementsToHide","insertImage","modalFooter","elementsToShow","params","e","target","closest","imageBrowser","preventDefault","fileInput"],"mappings":"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"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js deleted file mode 100755 index fbd7b061..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/imagemodal",["exports","core/modal","./common"],(function(_exports,_modal,_common){var obj;function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_modal=(obj=_modal)&&obj.__esModule?obj:{default:obj};class ImageModal extends _modal.default{registerEventListeners(){super.registerEventListeners(),this.registerCloseOnSave(),this.registerCloseOnCancel()}configure(modalConfig){modalConfig.large=!0,modalConfig.removeOnClose=!0,modalConfig.show=!0,super.configure(modalConfig)}}return _exports.default=ImageModal,_defineProperty(ImageModal,"TYPE","".concat(_common.component,"/imagemodal")),_defineProperty(ImageModal,"TEMPLATE","".concat(_common.component,"/insert_image_modal")),ImageModal.registerModalType(),_exports.default})); - -//# sourceMappingURL=imagemodal.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js.map deleted file mode 100755 index 47669ad8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/imagemodal.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"imagemodal.min.js","sources":["../src/imagemodal.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 * Image Modal for Tiny.\n *\n * @module tiny_mediacms/imagemodal\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Modal from 'core/modal';\nimport {component} from './common';\n\nexport default class ImageModal extends Modal {\n static TYPE = `${component}/imagemodal`;\n static TEMPLATE = `${component}/insert_image_modal`;\n\n registerEventListeners() {\n // Call the parent registration.\n super.registerEventListeners();\n\n // Register to close on save/cancel.\n this.registerCloseOnSave();\n this.registerCloseOnCancel();\n }\n\n configure(modalConfig) {\n modalConfig.large = true;\n modalConfig.removeOnClose = true;\n modalConfig.show = true;\n\n super.configure(modalConfig);\n }\n}\n\nImageModal.registerModalType();\n"],"names":["ImageModal","Modal","registerEventListeners","registerCloseOnSave","registerCloseOnCancel","configure","modalConfig","large","removeOnClose","show","component","registerModalType"],"mappings":"iaA0BqBA,mBAAmBC,eAIpCC,+BAEUA,8BAGDC,2BACAC,wBAGTC,UAAUC,aACNA,YAAYC,OAAQ,EACpBD,YAAYE,eAAgB,EAC5BF,YAAYG,MAAO,QAEbJ,UAAUC,iEAlBHN,4BACAU,kDADAV,gCAEIU,0CAoBzBV,WAAWW"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js deleted file mode 100755 index dd736626..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/manager",["exports","core/templates","core/str","core/modal","core/modal_events","./options","core/config"],(function(_exports,_templates,_str,_modal,ModalEvents,_options,_config){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireDefault(obj){return obj&&obj.__esModule?obj:{default:obj}}function _defineProperty(obj,key,value){return key in obj?Object.defineProperty(obj,key,{value:value,enumerable:!0,configurable:!0,writable:!0}):obj[key]=value,obj}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,_templates=_interopRequireDefault(_templates),_modal=_interopRequireDefault(_modal),ModalEvents=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj}(ModalEvents),_config=_interopRequireDefault(_config);return _exports.default=class{constructor(editor){_defineProperty(this,"editor",null),_defineProperty(this,"area",null),this.editor=editor;const data=(0,_options.getData)(editor);this.area=data.params.area,this.area.itemid=data.fpoptions.image.itemid}async displayDialogue(){const modal=await _modal.default.create({large:!0,title:(0,_str.getString)("mediamanagerproperties","tiny_mediacms"),body:_templates.default.render("tiny_mediacms/mm2_iframe",{src:this.getIframeURL()}),removeOnClose:!0,show:!0});return modal.getRoot().on(ModalEvents.bodyRendered,(()=>{this.selectFirstElement()})),document.querySelector(".modal-lg").style.cssText="max-width: 850px",modal}selectFirstElement(){const iframe=document.getElementById("mm2-iframe");iframe.addEventListener("load",(function(){let intervalId=setInterval((function(){const iDocument=iframe.contentWindow.document;if(iDocument.querySelector(".filemanager")){const firstFocusableElement=iDocument.querySelector(".fp-navbar a:not([disabled])");firstFocusableElement&&firstFocusableElement.focus(),clearInterval(intervalId)}}),200)}))}getIframeURL(){const url=new URL("".concat(_config.default.wwwroot,"/lib/editor/tiny/plugins/mediacms/manage.php"));url.searchParams.append("elementid",this.editor.getElement().id);for(const key in this.area)url.searchParams.append(key,this.area[key]);return url.toString()}},_exports.default})); - -//# sourceMappingURL=manager.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map deleted file mode 100755 index 2fc47acf..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/manager.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"manager.min.js","sources":["../src/manager.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 * Tiny Media Manager plugin class for Moodle.\n *\n * @module tiny_mediacms/manager\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport Templates from 'core/templates';\nimport {getString} from 'core/str';\nimport Modal from 'core/modal';\nimport * as ModalEvents from 'core/modal_events';\nimport {getData} from './options';\nimport Config from 'core/config';\n\nexport default class MediaManager {\n\n editor = null;\n area = null;\n\n constructor(editor) {\n this.editor = editor;\n const data = getData(editor);\n this.area = data.params.area;\n this.area.itemid = data.fpoptions.image.itemid;\n }\n\n async displayDialogue() {\n const modal = await Modal.create({\n large: true,\n title: getString('mediamanagerproperties', 'tiny_mediacms'),\n body: Templates.render('tiny_mediacms/mm2_iframe', {\n src: this.getIframeURL()\n }),\n removeOnClose: true,\n show: true,\n });\n modal.getRoot().on(ModalEvents.bodyRendered, () => {\n this.selectFirstElement();\n });\n\n document.querySelector('.modal-lg').style.cssText = `max-width: 850px`;\n return modal;\n }\n\n // It will select the first element in the file manager.\n selectFirstElement() {\n const iframe = document.getElementById('mm2-iframe');\n iframe.addEventListener('load', function() {\n let intervalId = setInterval(function() {\n const iDocument = iframe.contentWindow.document;\n if (iDocument.querySelector('.filemanager')) {\n const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])');\n if (firstFocusableElement) {\n firstFocusableElement.focus();\n }\n clearInterval(intervalId);\n }\n }, 200);\n });\n }\n\n getIframeURL() {\n const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`);\n url.searchParams.append('elementid', this.editor.getElement().id);\n for (const key in this.area) {\n url.searchParams.append(key, this.area[key]);\n }\n return url.toString();\n }\n}\n"],"names":["constructor","editor","data","area","params","itemid","fpoptions","image","modal","Modal","create","large","title","body","Templates","render","src","this","getIframeURL","removeOnClose","show","getRoot","on","ModalEvents","bodyRendered","selectFirstElement","document","querySelector","style","cssText","iframe","getElementById","addEventListener","intervalId","setInterval","iDocument","contentWindow","firstFocusableElement","focus","clearInterval","url","URL","Config","wwwroot","searchParams","append","getElement","id","key","toString"],"mappings":"mmDAmCIA,YAAYC,sCAHH,kCACF,WAGEA,OAASA,aACRC,MAAO,oBAAQD,aAChBE,KAAOD,KAAKE,OAAOD,UACnBA,KAAKE,OAASH,KAAKI,UAAUC,MAAMF,qCAIlCG,YAAcC,eAAMC,OAAO,CAC7BC,OAAO,EACPC,OAAO,kBAAU,yBAA0B,iBAC3CC,KAAMC,mBAAUC,OAAO,2BAA4B,CAC/CC,IAAKC,KAAKC,iBAEdC,eAAe,EACfC,MAAM,WAEVZ,MAAMa,UAAUC,GAAGC,YAAYC,cAAc,UACpCC,wBAGTC,SAASC,cAAc,aAAaC,MAAMC,2BACnCrB,MAIXiB,2BACUK,OAASJ,SAASK,eAAe,cACvCD,OAAOE,iBAAiB,QAAQ,eACxBC,WAAaC,aAAY,iBACnBC,UAAYL,OAAOM,cAAcV,YACnCS,UAAUR,cAAc,gBAAiB,OACnCU,sBAAwBF,UAAUR,cAAc,gCAClDU,uBACAA,sBAAsBC,QAE1BC,cAAcN,eAEnB,QAIXf,qBACUsB,IAAM,IAAIC,cAAOC,gBAAOC,yDAC9BH,IAAII,aAAaC,OAAO,YAAa5B,KAAKhB,OAAO6C,aAAaC,QACzD,MAAMC,OAAO/B,KAAKd,KACnBqC,IAAII,aAAaC,OAAOG,IAAK/B,KAAKd,KAAK6C,aAEpCR,IAAIS"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js deleted file mode 100755 index ed1f7e68..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js +++ /dev/null @@ -1,11 +0,0 @@ -define("tiny_mediacms/options",["exports","editor_tiny/options","./common"],(function(_exports,_options,_common){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.register=_exports.getPermissions=_exports.getLti=_exports.getImagePermissions=_exports.getEmbedPermissions=_exports.getData=void 0; -/** - * Options helper for Tiny Media plugin. - * - * @module tiny_mediacms/options - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -const dataName=(0,_options.getPluginOptionName)(_common.pluginName,"data"),permissionsName=(0,_options.getPluginOptionName)(_common.pluginName,"permissions"),ltiName=(0,_options.getPluginOptionName)(_common.pluginName,"lti");_exports.register=editor=>{const registerOption=editor.options.register;registerOption(permissionsName,{processor:"object",default:{image:{filepicker:!1}}}),registerOption(dataName,{processor:"object",default:{mediacmsApiUrl:"",mediacmsBaseUrl:"",mediacmsPageSize:12,autoConvertEnabled:!0,autoConvertBaseUrl:"",autoConvertOptions:{showTitle:!0,linkTitle:!0,showRelated:!0,showUserAvatar:!0}}}),registerOption(ltiName,{processor:"object",default:{toolId:0,courseId:0,contentItemUrl:""}})};const getPermissions=editor=>editor.options.get(permissionsName);_exports.getPermissions=getPermissions;_exports.getImagePermissions=editor=>getPermissions(editor).image;_exports.getEmbedPermissions=editor=>getPermissions(editor).embed;_exports.getData=editor=>editor.options.get(dataName);_exports.getLti=editor=>editor.options.get(ltiName)})); - -//# sourceMappingURL=options.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map deleted file mode 100755 index 545b140d..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/options.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"options.min.js","sources":["../src/options.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 * Options helper for Tiny Media plugin.\n *\n * @module tiny_mediacms/options\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport {getPluginOptionName} from 'editor_tiny/options';\nimport {pluginName} from './common';\n\nconst dataName = getPluginOptionName(pluginName, 'data');\nconst permissionsName = getPluginOptionName(pluginName, 'permissions');\nconst ltiName = getPluginOptionName(pluginName, 'lti');\n\n/**\n * Register the options for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n */\nexport const register = (editor) => {\n const registerOption = editor.options.register;\n\n registerOption(permissionsName, {\n processor: 'object',\n \"default\": {\n image: {\n filepicker: false,\n }\n },\n });\n\n registerOption(dataName, {\n processor: 'object',\n \"default\": {\n // MediaCMS video library configuration\n mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media'\n mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io'\n mediacmsPageSize: 12,\n // Auto-conversion settings\n autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs\n autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains)\n autoConvertOptions: {\n // Default embed options for auto-converted videos\n showTitle: true,\n linkTitle: true,\n showRelated: true,\n showUserAvatar: true,\n },\n },\n });\n\n registerOption(ltiName, {\n processor: 'object',\n \"default\": {\n // LTI configuration for MediaCMS iframe library\n toolId: 0, // LTI external tool ID\n courseId: 0, // Current course ID\n contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking\n },\n });\n};\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getPermissions = (editor) => editor.options.get(permissionsName);\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getImagePermissions = (editor) => getPermissions(editor).image;\n\n/**\n * Get the permissions configuration for the Tiny Media plugin.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getEmbedPermissions = (editor) => getPermissions(editor).embed;\n\n/**\n * Get the data configuration for the Media Manager.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getData = (editor) => editor.options.get(dataName);\n\n/**\n * Get the LTI configuration for the MediaCMS iframe library.\n *\n * @param {TinyMCE} editor\n * @returns {object}\n */\nexport const getLti = (editor) => editor.options.get(ltiName);\n"],"names":["dataName","pluginName","permissionsName","ltiName","editor","registerOption","options","register","processor","image","filepicker","mediacmsApiUrl","mediacmsBaseUrl","mediacmsPageSize","autoConvertEnabled","autoConvertBaseUrl","autoConvertOptions","showTitle","linkTitle","showRelated","showUserAvatar","toolId","courseId","contentItemUrl","getPermissions","get","embed"],"mappings":";;;;;;;;MA0BMA,UAAW,gCAAoBC,mBAAY,QAC3CC,iBAAkB,gCAAoBD,mBAAY,eAClDE,SAAU,gCAAoBF,mBAAY,yBAOvBG,eACfC,eAAiBD,OAAOE,QAAQC,SAEtCF,eAAeH,gBAAiB,CAC5BM,UAAW,iBACA,CACPC,MAAO,CACHC,YAAY,MAKxBL,eAAeL,SAAU,CACrBQ,UAAW,iBACA,CAEPG,eAAgB,GAChBC,gBAAiB,GACjBC,iBAAkB,GAElBC,oBAAoB,EACpBC,mBAAoB,GACpBC,mBAAoB,CAEhBC,WAAW,EACXC,WAAW,EACXC,aAAa,EACbC,gBAAgB,MAK5Bf,eAAeF,QAAS,CACpBK,UAAW,iBACA,CAEPa,OAAQ,EACRC,SAAU,EACVC,eAAgB,aAWfC,eAAkBpB,QAAWA,OAAOE,QAAQmB,IAAIvB,qFAQzBE,QAAWoB,eAAepB,QAAQK,mCAQlCL,QAAWoB,eAAepB,QAAQsB,uBAQ9CtB,QAAWA,OAAOE,QAAQmB,IAAIzB,0BAQ/BI,QAAWA,OAAOE,QAAQmB,IAAItB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js deleted file mode 100755 index bb3c9e1d..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/plugin",["exports","editor_tiny/loader","editor_tiny/utils","./common","./commands","./configuration","./options","./autoconvert"],(function(_exports,_loader,_utils,_common,Commands,Configuration,Options,_autoconvert){function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}function _interopRequireWildcard(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}return newObj.default=obj,cache&&cache.set(obj,newObj),newObj} -/** - * Tiny Media plugin for Moodle. - * - * @module tiny_mediacms/plugin - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0,Commands=_interopRequireWildcard(Commands),Configuration=_interopRequireWildcard(Configuration),Options=_interopRequireWildcard(Options);var _default=new Promise((async resolve=>{const[tinyMCE,setupCommands,pluginMetadata]=await Promise.all([(0,_loader.getTinyMCE)(),Commands.getSetup(),(0,_utils.getPluginMetadata)(_common.component,_common.pluginName)]);tinyMCE.PluginManager.add("".concat(_common.component,"/plugin"),(editor=>(Options.register(editor),setupCommands(editor),(0,_autoconvert.setupAutoConvert)(editor),editor.on("GetContent",(e=>{if("html"===e.format){const tempDiv=document.createElement("div");tempDiv.innerHTML=e.content,tempDiv.querySelectorAll(".tiny-mediacms-edit-btn").forEach((btn=>btn.remove())),tempDiv.querySelectorAll(".tiny-mediacms-iframe-wrapper").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),tempDiv.querySelectorAll(".tiny-iframe-responsive").forEach((wrapper=>{const iframe=wrapper.querySelector("iframe");iframe&&wrapper.parentNode.insertBefore(iframe,wrapper),wrapper.remove()})),e.content=tempDiv.innerHTML}})),pluginMetadata))),resolve(["".concat(_common.component,"/plugin"),Configuration])}));return _exports.default=_default,_exports.default})); - -//# sourceMappingURL=plugin.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map deleted file mode 100755 index ad58087b..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/plugin.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"plugin.min.js","sources":["../src/plugin.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 * Tiny Media plugin for Moodle.\n *\n * @module tiny_mediacms/plugin\n * @copyright 2022 Andrew Lyons \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\nimport {getTinyMCE} from 'editor_tiny/loader';\nimport {getPluginMetadata} from 'editor_tiny/utils';\n\nimport {component, pluginName} from './common';\nimport * as Commands from './commands';\nimport * as Configuration from './configuration';\nimport * as Options from './options';\nimport {setupAutoConvert} from './autoconvert';\n\n// eslint-disable-next-line no-async-promise-executor\nexport default new Promise(async(resolve) => {\n const [\n tinyMCE,\n setupCommands,\n pluginMetadata,\n ] = await Promise.all([\n getTinyMCE(),\n Commands.getSetup(),\n getPluginMetadata(component, pluginName),\n ]);\n\n tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => {\n // Register options.\n Options.register(editor);\n\n // Setup the Commands (buttons, menu items, and so on).\n setupCommands(editor);\n\n // Setup auto-conversion of pasted MediaCMS URLs.\n setupAutoConvert(editor);\n\n // Clean up editor-only elements before content is saved.\n // Remove wrapper divs and edit buttons that are only for the editor UI.\n editor.on('GetContent', (e) => {\n if (e.format === 'html') {\n // Create a temporary container to manipulate the HTML\n const tempDiv = document.createElement('div');\n tempDiv.innerHTML = e.content;\n\n // Remove edit buttons\n tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove());\n\n // Unwrap iframes from tiny-mediacms-iframe-wrapper\n tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n // Unwrap iframes from tiny-iframe-responsive\n tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => {\n const iframe = wrapper.querySelector('iframe');\n if (iframe) {\n wrapper.parentNode.insertBefore(iframe, wrapper);\n }\n wrapper.remove();\n });\n\n e.content = tempDiv.innerHTML;\n }\n });\n\n return pluginMetadata;\n });\n\n // Resolve the Media Plugin and include configuration.\n resolve([`${component}/plugin`, Configuration]);\n});\n"],"names":["Promise","async","tinyMCE","setupCommands","pluginMetadata","all","Commands","getSetup","component","pluginName","PluginManager","add","editor","Options","register","on","e","format","tempDiv","document","createElement","innerHTML","content","querySelectorAll","forEach","btn","remove","wrapper","iframe","querySelector","parentNode","insertBefore","resolve","Configuration"],"mappings":";;;;;;;2OAgCe,IAAIA,SAAQC,MAAAA,gBAEnBC,QACAC,cACAC,sBACMJ,QAAQK,IAAI,EAClB,wBACAC,SAASC,YACT,4BAAkBC,kBAAWC,sBAGjCP,QAAQQ,cAAcC,cAAOH,8BAAqBI,SAE9CC,QAAQC,SAASF,QAGjBT,cAAcS,0CAGGA,QAIjBA,OAAOG,GAAG,cAAeC,OACJ,SAAbA,EAAEC,OAAmB,OAEfC,QAAUC,SAASC,cAAc,OACvCF,QAAQG,UAAYL,EAAEM,QAGtBJ,QAAQK,iBAAiB,2BAA2BC,SAAQC,KAAOA,IAAIC,WAGvER,QAAQK,iBAAiB,iCAAiCC,SAAQG,gBACxDC,OAASD,QAAQE,cAAc,UACjCD,QACAD,QAAQG,WAAWC,aAAaH,OAAQD,SAE5CA,QAAQD,YAIZR,QAAQK,iBAAiB,2BAA2BC,SAAQG,gBAClDC,OAASD,QAAQE,cAAc,UACjCD,QACAD,QAAQG,WAAWC,aAAaH,OAAQD,SAE5CA,QAAQD,YAGZV,EAAEM,QAAUJ,QAAQG,cAIrBjB,kBAIX4B,QAAQ,WAAIxB,6BAAoByB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js deleted file mode 100755 index 06c5ee30..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js +++ /dev/null @@ -1,3 +0,0 @@ -define("tiny_mediacms/selectors",["exports"],(function(_exports){Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.default=void 0;return _exports.default={IMAGE:{actions:{submit:".tiny_imagecms_urlentrysubmit",imageBrowser:".openimagecmsbrowser",addUrl:".tiny_imagecms_addurl",deleteImage:".tiny_imagecms_deleteicon"},elements:{form:"form.tiny_imagecms_form",alignSettings:".tiny_imagecms_button",alt:".tiny_imagecms_altentry",altWarning:".tiny_imagecms_altwarning",height:".tiny_imagecms_heightentry",width:".tiny_imagecms_widthentry",url:".tiny_imagecms_urlentry",urlWarning:".tiny_imagecms_urlwarning",size:".tiny_imagecms_size",presentation:".tiny_imagecms_presentation",constrain:".tiny_imagecms_constrain",customStyle:".tiny_imagecms_customstyle",preview:".tiny_imagecms_preview",previewBox:".tiny_imagecms_preview_box",loaderIcon:".tiny_imagecms_loader",loaderIconContainer:".tiny_imagecms_loader_container",insertImage:".tiny_imagecms_insert_image",modalFooter:".modal-footer",dropzoneContainer:".tiny_imagecms_dropzone_container",fileInput:"#tiny_imagecms_fileinput",fileNameLabel:".tiny_imagecms_filename",sizeOriginal:".tiny_imagecms_sizeoriginal",sizeCustom:".tiny_imagecms_sizecustom",properties:".tiny_imagecms_properties"},styles:{responsive:"img-fluid"}},EMBED:{actions:{submit:".tiny_mediacms_submit",mediaBrowser:".openmediacmsbrowser"},elements:{form:"form.tiny_mediacms_form",source:".tiny_mediacms_source",track:".tiny_mediacms_track",mediaSource:".tiny_mediacms_media_source",linkSource:".tiny_mediacms_link_source",linkSize:".tiny_mediacms_link_size",posterSource:".tiny_mediacms_poster_source",posterSize:".tiny_mediacms_poster_size",displayOptions:".tiny_mediacms_display_options",name:".tiny_mediacms_name_entry",title:".tiny_mediacms_title_entry",url:".tiny_mediacms_url_entry",width:".tiny_mediacms_width_entry",height:".tiny_mediacms_height_entry",trackSource:".tiny_mediacms_track_source",trackKind:".tiny_mediacms_track_kind_entry",trackLabel:".tiny_mediacms_track_label_entry",trackLang:".tiny_mediacms_track_lang_entry",trackDefault:".tiny_mediacms_track_default",mediaControl:".tiny_mediacms_controls",mediaAutoplay:".tiny_mediacms_autoplay",mediaMute:".tiny_mediacms_mute",mediaLoop:".tiny_mediacms_loop",advancedSettings:".tiny_mediacms_advancedsettings",linkTab:'li[data-medium-type="link"]',videoTab:'li[data-medium-type="video"]',audioTab:'li[data-medium-type="audio"]',linkPane:'.tab-pane[data-medium-type="link"]',videoPane:'.tab-pane[data-medium-type="video"]',audioPane:'.tab-pane[data-medium-type="audio"]',trackSubtitlesTab:'li[data-track-kind="subtitles"]',trackCaptionsTab:'li[data-track-kind="captions"]',trackDescriptionsTab:'li[data-track-kind="descriptions"]',trackChaptersTab:'li[data-track-kind="chapters"]',trackMetadataTab:'li[data-track-kind="metadata"]',trackSubtitlesPane:'.tab-pane[data-track-kind="subtitles"]',trackCaptionsPane:'.tab-pane[data-track-kind="captions"]',trackDescriptionsPane:'.tab-pane[data-track-kind="descriptions"]',trackChaptersPane:'.tab-pane[data-track-kind="chapters"]',trackMetadataPane:'.tab-pane[data-track-kind="metadata"]'},mediaTypes:{link:"LINK",video:"VIDEO",audio:"AUDIO"},trackKinds:{subtitles:"SUBTITLES",captions:"CAPTIONS",descriptions:"DESCRIPTIONS",chapters:"CHAPTERS",metadata:"METADATA"}},IFRAME:{actions:{remove:'[data-action="remove"]'},elements:{form:"form.tiny_iframecms_form",url:".tiny_iframecms_url",urlWarning:".tiny_iframecms_url_warning",showTitle:".tiny_iframecms_showtitle",linkTitle:".tiny_iframecms_linktitle",showRelated:".tiny_iframecms_showrelated",showUserAvatar:".tiny_iframecms_showuseravatar",responsive:".tiny_iframecms_responsive",startAt:".tiny_iframecms_startat",startAtEnabled:".tiny_iframecms_startat_enabled",aspectRatio:".tiny_iframecms_aspectratio",width:".tiny_iframecms_width",height:".tiny_iframecms_height",preview:".tiny_iframecms_preview",previewContainer:".tiny_iframecms_preview_container",tabs:".tiny_iframecms_tabs",tabUrlBtn:".tiny_iframecms_tab_url_btn",tabIframeLibraryBtn:".tiny_iframecms_tab_iframe_library_btn",paneUrl:".tiny_iframecms_pane_url",paneIframeLibrary:".tiny_iframecms_pane_iframe_library",iframeLibraryContainer:".tiny_iframecms_iframe_library_container",iframeLibraryPlaceholder:".tiny_iframecms_iframe_library_placeholder",iframeLibraryLoading:".tiny_iframecms_iframe_library_loading",iframeLibraryFrame:".tiny_iframecms_iframe_library_frame"},aspectRatios:{"16:9":{width:560,height:315},"4:3":{width:560,height:420},"1:1":{width:400,height:400},custom:null}}},_exports.default})); - -//# sourceMappingURL=selectors.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map deleted file mode 100755 index e90972a5..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/selectors.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"selectors.min.js","sources":["../src/selectors.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 * Tiny Media plugin helper function to build queryable data selectors.\n *\n * @module tiny_mediacms/selectors\n * @copyright 2022 Huong Nguyen \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nexport default {\n IMAGE: {\n actions: {\n submit: '.tiny_imagecms_urlentrysubmit',\n imageBrowser: '.openimagecmsbrowser',\n addUrl: '.tiny_imagecms_addurl',\n deleteImage: '.tiny_imagecms_deleteicon',\n },\n elements: {\n form: 'form.tiny_imagecms_form',\n alignSettings: '.tiny_imagecms_button',\n alt: '.tiny_imagecms_altentry',\n altWarning: '.tiny_imagecms_altwarning',\n height: '.tiny_imagecms_heightentry',\n width: '.tiny_imagecms_widthentry',\n url: '.tiny_imagecms_urlentry',\n urlWarning: '.tiny_imagecms_urlwarning',\n size: '.tiny_imagecms_size',\n presentation: '.tiny_imagecms_presentation',\n constrain: '.tiny_imagecms_constrain',\n customStyle: '.tiny_imagecms_customstyle',\n preview: '.tiny_imagecms_preview',\n previewBox: '.tiny_imagecms_preview_box',\n loaderIcon: '.tiny_imagecms_loader',\n loaderIconContainer: '.tiny_imagecms_loader_container',\n insertImage: '.tiny_imagecms_insert_image',\n modalFooter: '.modal-footer',\n dropzoneContainer: '.tiny_imagecms_dropzone_container',\n fileInput: '#tiny_imagecms_fileinput',\n fileNameLabel: '.tiny_imagecms_filename',\n sizeOriginal: '.tiny_imagecms_sizeoriginal',\n sizeCustom: '.tiny_imagecms_sizecustom',\n properties: '.tiny_imagecms_properties',\n },\n styles: {\n responsive: 'img-fluid',\n },\n },\n EMBED: {\n actions: {\n submit: '.tiny_mediacms_submit',\n mediaBrowser: '.openmediacmsbrowser',\n },\n elements: {\n form: 'form.tiny_mediacms_form',\n source: '.tiny_mediacms_source',\n track: '.tiny_mediacms_track',\n mediaSource: '.tiny_mediacms_media_source',\n linkSource: '.tiny_mediacms_link_source',\n linkSize: '.tiny_mediacms_link_size',\n posterSource: '.tiny_mediacms_poster_source',\n posterSize: '.tiny_mediacms_poster_size',\n displayOptions: '.tiny_mediacms_display_options',\n name: '.tiny_mediacms_name_entry',\n title: '.tiny_mediacms_title_entry',\n url: '.tiny_mediacms_url_entry',\n width: '.tiny_mediacms_width_entry',\n height: '.tiny_mediacms_height_entry',\n trackSource: '.tiny_mediacms_track_source',\n trackKind: '.tiny_mediacms_track_kind_entry',\n trackLabel: '.tiny_mediacms_track_label_entry',\n trackLang: '.tiny_mediacms_track_lang_entry',\n trackDefault: '.tiny_mediacms_track_default',\n mediaControl: '.tiny_mediacms_controls',\n mediaAutoplay: '.tiny_mediacms_autoplay',\n mediaMute: '.tiny_mediacms_mute',\n mediaLoop: '.tiny_mediacms_loop',\n advancedSettings: '.tiny_mediacms_advancedsettings',\n linkTab: 'li[data-medium-type=\"link\"]',\n videoTab: 'li[data-medium-type=\"video\"]',\n audioTab: 'li[data-medium-type=\"audio\"]',\n linkPane: '.tab-pane[data-medium-type=\"link\"]',\n videoPane: '.tab-pane[data-medium-type=\"video\"]',\n audioPane: '.tab-pane[data-medium-type=\"audio\"]',\n trackSubtitlesTab: 'li[data-track-kind=\"subtitles\"]',\n trackCaptionsTab: 'li[data-track-kind=\"captions\"]',\n trackDescriptionsTab: 'li[data-track-kind=\"descriptions\"]',\n trackChaptersTab: 'li[data-track-kind=\"chapters\"]',\n trackMetadataTab: 'li[data-track-kind=\"metadata\"]',\n trackSubtitlesPane: '.tab-pane[data-track-kind=\"subtitles\"]',\n trackCaptionsPane: '.tab-pane[data-track-kind=\"captions\"]',\n trackDescriptionsPane: '.tab-pane[data-track-kind=\"descriptions\"]',\n trackChaptersPane: '.tab-pane[data-track-kind=\"chapters\"]',\n trackMetadataPane: '.tab-pane[data-track-kind=\"metadata\"]',\n },\n mediaTypes: {\n link: 'LINK',\n video: 'VIDEO',\n audio: 'AUDIO',\n },\n trackKinds: {\n subtitles: 'SUBTITLES',\n captions: 'CAPTIONS',\n descriptions: 'DESCRIPTIONS',\n chapters: 'CHAPTERS',\n metadata: 'METADATA',\n },\n },\n IFRAME: {\n actions: {\n remove: '[data-action=\"remove\"]',\n },\n elements: {\n form: 'form.tiny_iframecms_form',\n url: '.tiny_iframecms_url',\n urlWarning: '.tiny_iframecms_url_warning',\n showTitle: '.tiny_iframecms_showtitle',\n linkTitle: '.tiny_iframecms_linktitle',\n showRelated: '.tiny_iframecms_showrelated',\n showUserAvatar: '.tiny_iframecms_showuseravatar',\n responsive: '.tiny_iframecms_responsive',\n startAt: '.tiny_iframecms_startat',\n startAtEnabled: '.tiny_iframecms_startat_enabled',\n aspectRatio: '.tiny_iframecms_aspectratio',\n width: '.tiny_iframecms_width',\n height: '.tiny_iframecms_height',\n preview: '.tiny_iframecms_preview',\n previewContainer: '.tiny_iframecms_preview_container',\n // Tab elements\n tabs: '.tiny_iframecms_tabs',\n tabUrlBtn: '.tiny_iframecms_tab_url_btn',\n tabIframeLibraryBtn: '.tiny_iframecms_tab_iframe_library_btn',\n paneUrl: '.tiny_iframecms_pane_url',\n paneIframeLibrary: '.tiny_iframecms_pane_iframe_library',\n // Iframe library elements\n iframeLibraryContainer: '.tiny_iframecms_iframe_library_container',\n iframeLibraryPlaceholder:\n '.tiny_iframecms_iframe_library_placeholder',\n iframeLibraryLoading: '.tiny_iframecms_iframe_library_loading',\n iframeLibraryFrame: '.tiny_iframecms_iframe_library_frame',\n },\n aspectRatios: {\n '16:9': { width: 560, height: 315 },\n '4:3': { width: 560, height: 420 },\n '1:1': { width: 400, height: 400 },\n custom: null,\n },\n },\n};\n"],"names":["IMAGE","actions","submit","imageBrowser","addUrl","deleteImage","elements","form","alignSettings","alt","altWarning","height","width","url","urlWarning","size","presentation","constrain","customStyle","preview","previewBox","loaderIcon","loaderIconContainer","insertImage","modalFooter","dropzoneContainer","fileInput","fileNameLabel","sizeOriginal","sizeCustom","properties","styles","responsive","EMBED","mediaBrowser","source","track","mediaSource","linkSource","linkSize","posterSource","posterSize","displayOptions","name","title","trackSource","trackKind","trackLabel","trackLang","trackDefault","mediaControl","mediaAutoplay","mediaMute","mediaLoop","advancedSettings","linkTab","videoTab","audioTab","linkPane","videoPane","audioPane","trackSubtitlesTab","trackCaptionsTab","trackDescriptionsTab","trackChaptersTab","trackMetadataTab","trackSubtitlesPane","trackCaptionsPane","trackDescriptionsPane","trackChaptersPane","trackMetadataPane","mediaTypes","link","video","audio","trackKinds","subtitles","captions","descriptions","chapters","metadata","IFRAME","remove","showTitle","linkTitle","showRelated","showUserAvatar","startAt","startAtEnabled","aspectRatio","previewContainer","tabs","tabUrlBtn","tabIframeLibraryBtn","paneUrl","paneIframeLibrary","iframeLibraryContainer","iframeLibraryPlaceholder","iframeLibraryLoading","iframeLibraryFrame","aspectRatios","custom"],"mappings":"yKAuBe,CACXA,MAAO,CACHC,QAAS,CACLC,OAAQ,gCACRC,aAAc,uBACdC,OAAQ,wBACRC,YAAa,6BAEjBC,SAAU,CACNC,KAAM,0BACNC,cAAe,wBACfC,IAAK,0BACLC,WAAY,4BACZC,OAAQ,6BACRC,MAAO,4BACPC,IAAK,0BACLC,WAAY,4BACZC,KAAM,sBACNC,aAAc,8BACdC,UAAW,2BACXC,YAAa,6BACbC,QAAS,yBACTC,WAAY,6BACZC,WAAY,wBACZC,oBAAqB,kCACrBC,YAAa,8BACbC,YAAa,gBACbC,kBAAmB,oCACnBC,UAAW,2BACXC,cAAe,0BACfC,aAAc,8BACdC,WAAY,4BACZC,WAAY,6BAEhBC,OAAQ,CACJC,WAAY,cAGpBC,MAAO,CACHhC,QAAS,CACLC,OAAQ,wBACRgC,aAAc,wBAElB5B,SAAU,CACNC,KAAM,0BACN4B,OAAQ,wBACRC,MAAO,uBACPC,YAAa,8BACbC,WAAY,6BACZC,SAAU,2BACVC,aAAc,+BACdC,WAAY,6BACZC,eAAgB,iCAChBC,KAAM,4BACNC,MAAO,6BACP/B,IAAK,2BACLD,MAAO,6BACPD,OAAQ,8BACRkC,YAAa,8BACbC,UAAW,kCACXC,WAAY,mCACZC,UAAW,kCACXC,aAAc,+BACdC,aAAc,0BACdC,cAAe,0BACfC,UAAW,sBACXC,UAAW,sBACXC,iBAAkB,kCAClBC,QAAS,8BACTC,SAAU,+BACVC,SAAU,+BACVC,SAAU,qCACVC,UAAW,sCACXC,UAAW,sCACXC,kBAAmB,kCACnBC,iBAAkB,iCAClBC,qBAAsB,qCACtBC,iBAAkB,iCAClBC,iBAAkB,iCAClBC,mBAAoB,yCACpBC,kBAAmB,wCACnBC,sBAAuB,4CACvBC,kBAAmB,wCACnBC,kBAAmB,yCAEvBC,WAAY,CACRC,KAAM,OACNC,MAAO,QACPC,MAAO,SAEXC,WAAY,CACRC,UAAW,YACXC,SAAU,WACVC,aAAc,eACdC,SAAU,WACVC,SAAU,aAGlBC,OAAQ,CACJhF,QAAS,CACLiF,OAAQ,0BAEZ5E,SAAU,CACNC,KAAM,2BACNM,IAAK,sBACLC,WAAY,8BACZqE,UAAW,4BACXC,UAAW,4BACXC,YAAa,8BACbC,eAAgB,iCAChBtD,WAAY,6BACZuD,QAAS,0BACTC,eAAgB,kCAChBC,YAAa,8BACb7E,MAAO,wBACPD,OAAQ,yBACRQ,QAAS,0BACTuE,iBAAkB,oCAElBC,KAAM,uBACNC,UAAW,8BACXC,oBAAqB,yCACrBC,QAAS,2BACTC,kBAAmB,sCAEnBC,uBAAwB,2CACxBC,yBACI,6CACJC,qBAAsB,yCACtBC,mBAAoB,wCAExBC,aAAc,QACF,CAAExF,MAAO,IAAKD,OAAQ,WACvB,CAAEC,MAAO,IAAKD,OAAQ,WACtB,CAAEC,MAAO,IAAKD,OAAQ,KAC7B0F,OAAQ"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js deleted file mode 100755 index 2e03a1ff..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js +++ /dev/null @@ -1,10 +0,0 @@ -define("tiny_mediacms/usedfiles",["exports","core/templates","core/config"],(function(_exports,Templates,_config){var obj;function _getRequireWildcardCache(nodeInterop){if("function"!=typeof WeakMap)return null;var cacheBabelInterop=new WeakMap,cacheNodeInterop=new WeakMap;return(_getRequireWildcardCache=function(nodeInterop){return nodeInterop?cacheNodeInterop:cacheBabelInterop})(nodeInterop)}Object.defineProperty(_exports,"__esModule",{value:!0}),_exports.init=void 0,Templates=function(obj,nodeInterop){if(!nodeInterop&&obj&&obj.__esModule)return obj;if(null===obj||"object"!=typeof obj&&"function"!=typeof obj)return{default:obj};var cache=_getRequireWildcardCache(nodeInterop);if(cache&&cache.has(obj))return cache.get(obj);var newObj={},hasPropertyDescriptor=Object.defineProperty&&Object.getOwnPropertyDescriptor;for(var key in obj)if("default"!==key&&Object.prototype.hasOwnProperty.call(obj,key)){var desc=hasPropertyDescriptor?Object.getOwnPropertyDescriptor(obj,key):null;desc&&(desc.get||desc.set)?Object.defineProperty(newObj,key,desc):newObj[key]=obj[key]}newObj.default=obj,cache&&cache.set(obj,newObj);return newObj} -/** - * Tiny Media Manager usedfiles. - * - * @module tiny_mediacms/usedfiles - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */(Templates),_config=(obj=_config)&&obj.__esModule?obj:{default:obj};class UsedFileManager{constructor(files,userContext,itemId,elementId){this.files=files,this.userContext=userContext,this.itemId=itemId,this.elementId=elementId}getElementId(){return this.elementId}getUsedFiles(){const editor=window.parent.tinymce.EditorManager.get(this.getElementId());if(!editor)return window.console.error("Editor not found for ".concat(this.getElementId())),[];const content=editor.getContent(),baseUrl="".concat(_config.default.wwwroot,"/draftfile.php/").concat(this.userContext,"/user/draft/").concat(this.itemId,"/"),pattern=new RegExp("[\"']"+baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g,"\\$&")+"(?.+?)[\\?\"']","gm");return[...content.matchAll(pattern)].map((match=>decodeURIComponent(match.groups.filename)))}findUnusedFiles(usedFiles){return Object.entries(this.files).filter((_ref=>{let[filename]=_ref;return!usedFiles.includes(filename)})).map((_ref2=>{let[filename]=_ref2;return filename}))}findMissingFiles(usedFiles){return usedFiles.filter((filename=>!this.files.hasOwnProperty(filename)))}updateFiles(){const form=document.querySelector("form"),usedFiles=this.getUsedFiles(),unusedFiles=this.findUnusedFiles(usedFiles),missingFiles=this.findMissingFiles(usedFiles);return form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox=>{unusedFiles.includes(checkbox.dataset.filename)||checkbox.closest(".fitem").remove()})),form.classList.toggle("has-missing-files",!!missingFiles.length),form.classList.toggle("has-unused-files",!!unusedFiles.length),Templates.renderForPromise("tiny_mediacms/missingfiles",{missingFiles:missingFiles}).then((_ref3=>{let{html:html,js:js}=_ref3;Templates.replaceNodeContents(form.querySelector(".missing-files"),html,js)}))}}_exports.init=(files,usercontext,itemid,elementid)=>{const manager=new UsedFileManager(files,usercontext,itemid,elementid);return manager.updateFiles(),manager}})); - -//# sourceMappingURL=usedfiles.min.js.map \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map deleted file mode 100755 index 1af09ca8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/build/usedfiles.min.js.map +++ /dev/null @@ -1 +0,0 @@ -{"version":3,"file":"usedfiles.min.js","sources":["../src/usedfiles.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 * Tiny Media Manager usedfiles.\n *\n * @module tiny_mediacms/usedfiles\n * @copyright 2022, Stevani Andolo \n * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later\n */\n\nimport * as Templates from 'core/templates';\nimport Config from 'core/config';\n\nclass UsedFileManager {\n constructor(files, userContext, itemId, elementId) {\n this.files = files;\n this.userContext = userContext;\n this.itemId = itemId;\n this.elementId = elementId;\n }\n\n getElementId() {\n return this.elementId;\n }\n\n getUsedFiles() {\n const editor = window.parent.tinymce.EditorManager.get(this.getElementId());\n if (!editor) {\n window.console.error(`Editor not found for ${this.getElementId()}`);\n return [];\n }\n const content = editor.getContent();\n const baseUrl = `${Config.wwwroot}/draftfile.php/${this.userContext}/user/draft/${this.itemId}/`;\n const pattern = new RegExp(\"[\\\"']\" + baseUrl.replace(/[-/\\\\^$*+?.()|[\\]{}]/g, '\\\\$&') + \"(?.+?)[\\\\?\\\"']\", 'gm');\n\n const usedFiles = [...content.matchAll(pattern)].map((match) => decodeURIComponent(match.groups.filename));\n\n return usedFiles;\n }\n\n // Return an array of unused files.\n findUnusedFiles(usedFiles) {\n return Object.entries(this.files)\n .filter(([filename]) => !usedFiles.includes(filename))\n .map(([filename]) => filename);\n }\n\n // Return an array of missing files.\n findMissingFiles(usedFiles) {\n return usedFiles.filter((filename) => !this.files.hasOwnProperty(filename));\n }\n\n updateFiles() {\n const form = document.querySelector('form');\n const usedFiles = this.getUsedFiles();\n const unusedFiles = this.findUnusedFiles(usedFiles);\n const missingFiles = this.findMissingFiles(usedFiles);\n\n form.querySelectorAll('input[type=checkbox][name^=\"deletefile\"]').forEach((checkbox) => {\n if (!unusedFiles.includes(checkbox.dataset.filename)) {\n checkbox.closest('.fitem').remove();\n }\n });\n\n form.classList.toggle('has-missing-files', !!missingFiles.length);\n form.classList.toggle('has-unused-files', !!unusedFiles.length);\n\n return Templates.renderForPromise('tiny_mediacms/missingfiles', {\n missingFiles,\n }).then(({html, js}) => {\n Templates.replaceNodeContents(form.querySelector('.missing-files'), html, js);\n return;\n });\n }\n}\n\nexport const init = (files, usercontext, itemid, elementid) => {\n const manager = new UsedFileManager(files, usercontext, itemid, elementid);\n manager.updateFiles();\n\n return manager;\n};\n"],"names":["UsedFileManager","constructor","files","userContext","itemId","elementId","getElementId","this","getUsedFiles","editor","window","parent","tinymce","EditorManager","get","console","error","content","getContent","baseUrl","Config","wwwroot","pattern","RegExp","replace","matchAll","map","match","decodeURIComponent","groups","filename","findUnusedFiles","usedFiles","Object","entries","filter","_ref","includes","_ref2","findMissingFiles","hasOwnProperty","updateFiles","form","document","querySelector","unusedFiles","missingFiles","querySelectorAll","forEach","checkbox","dataset","closest","remove","classList","toggle","length","Templates","renderForPromise","then","_ref3","html","js","replaceNodeContents","usercontext","itemid","elementid","manager"],"mappings":";;;;;;;+EA0BMA,gBACFC,YAAYC,MAAOC,YAAaC,OAAQC,gBAC/BH,MAAQA,WACRC,YAAcA,iBACdC,OAASA,YACTC,UAAYA,UAGrBC,sBACWC,KAAKF,UAGhBG,qBACUC,OAASC,OAAOC,OAAOC,QAAQC,cAAcC,IAAIP,KAAKD,oBACvDG,cACDC,OAAOK,QAAQC,qCAA8BT,KAAKD,iBAC3C,SAELW,QAAUR,OAAOS,aACjBC,kBAAaC,gBAAOC,kCAAyBd,KAAKJ,mCAA0BI,KAAKH,YACjFkB,QAAU,IAAIC,OAAO,QAAUJ,QAAQK,QAAQ,wBAAyB,QAAU,2BAA4B,YAElG,IAAIP,QAAQQ,SAASH,UAAUI,KAAKC,OAAUC,mBAAmBD,MAAME,OAAOC,YAMpGC,gBAAgBC,kBACLC,OAAOC,QAAQ3B,KAAKL,OACtBiC,QAAOC,WAAEN,sBAAeE,UAAUK,SAASP,aAC3CJ,KAAIY,YAAER,uBAAcA,YAI7BS,iBAAiBP,kBACNA,UAAUG,QAAQL,WAAcvB,KAAKL,MAAMsC,eAAeV,YAGrEW,oBACUC,KAAOC,SAASC,cAAc,QAC9BZ,UAAYzB,KAAKC,eACjBqC,YAActC,KAAKwB,gBAAgBC,WACnCc,aAAevC,KAAKgC,iBAAiBP,kBAE3CU,KAAKK,iBAAiB,4CAA4CC,SAASC,WAClEJ,YAAYR,SAASY,SAASC,QAAQpB,WACvCmB,SAASE,QAAQ,UAAUC,YAInCV,KAAKW,UAAUC,OAAO,sBAAuBR,aAAaS,QAC1Db,KAAKW,UAAUC,OAAO,qBAAsBT,YAAYU,QAEjDC,UAAUC,iBAAiB,6BAA8B,CAC5DX,aAAAA,eACDY,MAAKC,YAACC,KAACA,KAADC,GAAOA,UACZL,UAAUM,oBAAoBpB,KAAKE,cAAc,kBAAmBgB,KAAMC,sBAMlE,CAAC3D,MAAO6D,YAAaC,OAAQC,mBACvCC,QAAU,IAAIlE,gBAAgBE,MAAO6D,YAAaC,OAAQC,kBAChEC,QAAQzB,cAEDyB"} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js deleted file mode 100755 index 2285daee..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/autoconvert.js +++ /dev/null @@ -1,265 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny MediaCMS Auto-convert module. - * - * This module automatically converts pasted MediaCMS URLs into embedded videos. - * When a user pastes a MediaCMS video URL (e.g., https://deic.mediacms.io/view?m=JpBd1Zvdl), - * it will be automatically converted to an iframe embed. - * - * @module tiny_mediacms/autoconvert - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import {getData} from './options'; - -/** - * Regular expression patterns for MediaCMS URLs. - * Matches URLs like: - * - https://deic.mediacms.io/view?m=JpBd1Zvdl - * - https://example.mediacms.io/view?m=VIDEO_ID - * - Custom domains configured in the plugin - */ -const MEDIACMS_VIEW_URL_PATTERN = /^(https?:\/\/[^\/]+)\/view\?m=([a-zA-Z0-9_-]+)$/; - -/** - * Check if a string is a valid MediaCMS view URL. - * - * @param {string} text - The text to check - * @returns {Object|null} - Parsed URL info or null if not a valid MediaCMS URL - */ -const parseMediaCMSUrl = (text) => { - if (!text || typeof text !== 'string') { - return null; - } - - const trimmed = text.trim(); - - // Check for MediaCMS view URL pattern - const match = trimmed.match(MEDIACMS_VIEW_URL_PATTERN); - if (match) { - return { - baseUrl: match[1], - videoId: match[2], - originalUrl: trimmed, - }; - } - - return null; -}; - -/** - * Check if the pasted URL's domain is allowed based on configuration. - * - * @param {Object} parsed - Parsed URL info - * @param {Object} config - Plugin configuration - * @returns {boolean} - True if the domain is allowed - */ -const isDomainAllowed = (parsed, config) => { - // If no specific base URL is configured, allow all MediaCMS domains - const configuredBaseUrl = config.autoConvertBaseUrl || config.mediacmsBaseUrl; - if (!configuredBaseUrl) { - return true; - } - - // Check if the URL's base matches the configured base URL - try { - const configuredUrl = new URL(configuredBaseUrl); - const pastedUrl = new URL(parsed.baseUrl); - return configuredUrl.host === pastedUrl.host; - } catch (e) { - // If URL parsing fails, allow the conversion - return true; - } -}; - -/** - * Generate the iframe embed HTML for a MediaCMS video. - * - * @param {Object} parsed - Parsed URL info - * @param {Object} options - Embed options - * @returns {string} - The iframe HTML - */ -const generateEmbedHtml = (parsed, options = {}) => { - // Build the embed URL with default options - const embedUrl = new URL(`${parsed.baseUrl}/embed`); - embedUrl.searchParams.set('m', parsed.videoId); - - // Apply default options (all enabled by default for best user experience) - embedUrl.searchParams.set('showTitle', options.showTitle !== false ? '1' : '0'); - embedUrl.searchParams.set('showRelated', options.showRelated !== false ? '1' : '0'); - embedUrl.searchParams.set('showUserAvatar', options.showUserAvatar !== false ? '1' : '0'); - embedUrl.searchParams.set('linkTitle', options.linkTitle !== false ? '1' : '0'); - - // Generate clean iframe HTML (wrapper will be added by editor for UI, then stripped on save) - const html = ``; - - return html; -}; - -/** - * Set up auto-conversion for the editor. - * This registers event handlers to detect pasted MediaCMS URLs. - * - * @param {TinyMCE} editor - The TinyMCE editor instance - */ -export const setupAutoConvert = (editor) => { - const config = getData(editor) || {}; - - // Check if auto-convert is enabled (default: true) - if (config.autoConvertEnabled === false) { - return; - } - - // Handle paste events - editor.on('paste', (e) => { - handlePasteEvent(editor, e, config); - }); - - // Also handle input events for drag-and-drop text or keyboard paste - editor.on('input', (e) => { - handleInputEvent(editor, e, config); - }); -}; - -/** - * Handle paste events to detect and convert MediaCMS URLs. - * - * @param {TinyMCE} editor - The TinyMCE editor instance - * @param {Event} e - The paste event - * @param {Object} config - Plugin configuration - */ -const handlePasteEvent = (editor, e, config) => { - // Get pasted text from clipboard - const clipboardData = e.clipboardData || window.clipboardData; - if (!clipboardData) { - return; - } - - // Try to get plain text first - const text = clipboardData.getData('text/plain') || clipboardData.getData('text'); - if (!text) { - return; - } - - // Check if it's a MediaCMS URL - const parsed = parseMediaCMSUrl(text); - if (!parsed) { - return; - } - - // Check if domain is allowed - if (!isDomainAllowed(parsed, config)) { - return; - } - - // Prevent default paste behavior - e.preventDefault(); - e.stopPropagation(); - - // Generate and insert the embed HTML - const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {}); - - // Use a slight delay to ensure the editor is ready - setTimeout(() => { - editor.insertContent(embedHtml); - // Move cursor after the inserted content - editor.selection.collapse(false); - }, 0); -}; - -/** - * Handle input events to catch URLs that might have been pasted without triggering paste event. - * This is a fallback for certain browsers/scenarios. - * - * @param {TinyMCE} editor - The TinyMCE editor instance - * @param {Event} e - The input event - * @param {Object} config - Plugin configuration - */ -const handleInputEvent = (editor, e, config) => { - // Only process inputType 'insertFromPaste' if paste event didn't catch it - if (e.inputType !== 'insertFromPaste' && e.inputType !== 'insertText') { - return; - } - - // Get the current node and check if it contains just a URL - const node = editor.selection.getNode(); - if (!node || node.nodeName !== 'P') { - return; - } - - // Check if the paragraph contains only a MediaCMS URL - const text = node.textContent || ''; - const parsed = parseMediaCMSUrl(text); - - if (!parsed || !isDomainAllowed(parsed, config)) { - return; - } - - // Don't convert if there's other content in the paragraph - const trimmedHtml = node.innerHTML.trim(); - if (trimmedHtml !== text.trim() && !trimmedHtml.startsWith(text.trim())) { - return; - } - - // Generate the embed HTML - const embedHtml = generateEmbedHtml(parsed, config.autoConvertOptions || {}); - - // Replace the paragraph content with the embed - // Use a slight delay to let the input event complete - setTimeout(() => { - // Re-check that the node still contains the URL (user might have typed more) - const currentText = node.textContent || ''; - const currentParsed = parseMediaCMSUrl(currentText); - - if (currentParsed && currentParsed.originalUrl === parsed.originalUrl) { - // Select and replace the entire node - editor.selection.select(node); - editor.insertContent(embedHtml); - } - }, 100); -}; - -/** - * Check if a text is a MediaCMS URL (public helper). - * - * @param {string} text - The text to check - * @returns {boolean} - True if it's a MediaCMS URL - */ -export const isMediaCMSUrl = (text) => { - return parseMediaCMSUrl(text) !== null; -}; - -/** - * Convert a MediaCMS URL to embed HTML (public helper). - * - * @param {string} url - The MediaCMS URL - * @param {Object} options - Embed options - * @returns {string|null} - The embed HTML or null if not a valid URL - */ -export const convertToEmbed = (url, options = {}) => { - const parsed = parseMediaCMSUrl(url); - if (!parsed) { - return null; - } - return generateEmbedHtml(parsed, options); -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js deleted file mode 100755 index 27ad9b76..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/commands.js +++ /dev/null @@ -1,282 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media commands. - * - * @module tiny_mediacms/commands - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import {getStrings} from 'core/str'; -import { - component, - iframeButtonName, - iframeMenuItemName, - iframeIcon, -} from './common'; -import IframeEmbed from './iframeembed'; -import {getButtonImage} from 'editor_tiny/utils'; - -const isIframe = (node) => node.nodeName.toLowerCase() === 'iframe' || - (node.classList && node.classList.contains('tiny-iframe-responsive')) || - (node.classList && node.classList.contains('tiny-mediacms-iframe-wrapper')); - -/** - * Wrap iframes with overlay containers that allow hover detection. - * Since iframes capture mouse events, we add an invisible overlay on top - * that shows the edit button on hover. - * - * @param {TinyMCE} editor - The editor instance - * @param {Function} handleIframeAction - The action to perform when clicking the button - */ -const setupIframeOverlays = (editor, handleIframeAction) => { - /** - * Process all iframes in the editor and add overlay wrappers. - */ - const processIframes = () => { - const editorBody = editor.getBody(); - if (!editorBody) { - return; - } - - const iframes = editorBody.querySelectorAll('iframe'); - iframes.forEach((iframe) => { - // Skip if already wrapped - if (iframe.parentElement?.classList.contains('tiny-mediacms-iframe-wrapper')) { - return; - } - - // Skip TinyMCE internal iframes - if (iframe.hasAttribute('data-mce-object') || iframe.hasAttribute('data-mce-placeholder')) { - return; - } - - // Create wrapper div - const wrapper = editor.getDoc().createElement('div'); - wrapper.className = 'tiny-mediacms-iframe-wrapper'; - wrapper.setAttribute('contenteditable', 'false'); - - // Create edit button (positioned inside wrapper, over the iframe) - const editBtn = editor.getDoc().createElement('button'); - editBtn.className = 'tiny-mediacms-edit-btn'; - editBtn.setAttribute('type', 'button'); - editBtn.setAttribute('title', 'Edit video embed options'); - // Use clean inline SVG to avoid TinyMCE wrapper issues - editBtn.innerHTML = '' + - '' + - '' + - ''; - - // Wrap the iframe: insert wrapper, move iframe into it, add button - iframe.parentNode.insertBefore(wrapper, iframe); - wrapper.appendChild(iframe); - wrapper.appendChild(editBtn); - }); - }; - - /** - * Add CSS styles for hover effects to the editor's document. - */ - const addStyles = () => { - const editorDoc = editor.getDoc(); - if (!editorDoc) { - return; - } - - // Check if styles already added - if (editorDoc.getElementById('tiny-mediacms-overlay-styles')) { - return; - } - - const style = editorDoc.createElement('style'); - style.id = 'tiny-mediacms-overlay-styles'; - style.textContent = ` - .tiny-mediacms-iframe-wrapper { - display: inline-block; - position: relative; - line-height: 0; - vertical-align: top; - } - .tiny-mediacms-iframe-wrapper iframe { - display: block; - } - .tiny-mediacms-edit-btn { - position: absolute; - top: 48px; - left: 6px; - width: 28px; - height: 28px; - background: #ffffff; - border: none; - border-radius: 50%; - cursor: pointer; - z-index: 10; - padding: 0; - margin: 0; - box-shadow: 0 2px 6px rgba(0,0,0,0.35); - transition: transform 0.15s, box-shadow 0.15s; - display: flex; - align-items: center; - justify-content: center; - box-sizing: border-box; - } - .tiny-mediacms-edit-btn:hover { - transform: scale(1.15); - box-shadow: 0 3px 10px rgba(0,0,0,0.45); - } - .tiny-mediacms-edit-btn svg { - width: 18px !important; - height: 18px !important; - display: block !important; - } - `; - editorDoc.head.appendChild(style); - }; - - /** - * Handle click on the edit button. - * - * @param {Event} e - The click event - */ - const handleOverlayClick = (e) => { - const target = e.target; - - // Check if clicked on edit button or its child (svg/path) - const editBtn = target.closest('.tiny-mediacms-edit-btn'); - if (!editBtn) { - return; - } - - e.preventDefault(); - e.stopPropagation(); - - // Find the associated wrapper and iframe - const wrapper = editBtn.closest('.tiny-mediacms-iframe-wrapper'); - if (!wrapper) { - return; - } - - const iframe = wrapper.querySelector('iframe'); - if (!iframe) { - return; - } - - // Select the wrapper so TinyMCE knows which element is selected - editor.selection.select(wrapper); - - // Open the edit dialog - handleIframeAction(); - }; - - // Setup on editor init - editor.on('init', () => { - addStyles(); - processIframes(); - - // Handle clicks on the overlay - editor.getBody().addEventListener('click', handleOverlayClick); - }); - - // Re-process when content changes - editor.on('SetContent', () => { - processIframes(); - }); - - // Re-process when content is pasted - editor.on('PastePostProcess', () => { - setTimeout(processIframes, 100); - }); - - // Re-process after undo/redo - editor.on('Undo Redo', () => { - processIframes(); - }); - - // Re-process on any content change (covers modal updates) - editor.on('Change', () => { - setTimeout(processIframes, 50); - }); - - // Re-process when node changes (selection changes) - editor.on('NodeChange', () => { - processIframes(); - }); -}; - -const registerIframeCommand = (editor, iframeButtonText, iframeButtonImage) => { - const handleIframeAction = () => { - const iframeEmbed = new IframeEmbed(editor); - iframeEmbed.displayDialogue(); - }; - - // Register the iframe icon - editor.ui.registry.addIcon(iframeIcon, iframeButtonImage.html); - - // Register the Menu Button as a toggle. - // This means that when highlighted over an existing iframe element it will show as toggled on. - editor.ui.registry.addToggleButton(iframeButtonName, { - icon: iframeIcon, - tooltip: iframeButtonText, - onAction: handleIframeAction, - onSetup: api => { - return editor.selection.selectorChangedWithUnbind( - 'iframe:not([data-mce-object]):not([data-mce-placeholder]),.tiny-iframe-responsive,.tiny-mediacms-iframe-wrapper', - api.setActive - ).unbind; - } - }); - - editor.ui.registry.addMenuItem(iframeMenuItemName, { - icon: iframeIcon, - text: iframeButtonText, - onAction: handleIframeAction, - }); - - editor.ui.registry.addContextToolbar(iframeButtonName, { - predicate: isIframe, - items: iframeButtonName, - position: 'node', - scope: 'node' - }); - - editor.ui.registry.addContextMenu(iframeButtonName, { - update: isIframe, - }); - - // Setup iframe overlays with edit button on hover - setupIframeOverlays(editor, handleIframeAction); -}; - -export const getSetup = async() => { - const [ - iframeButtonText, - ] = await getStrings([ - 'iframebuttontitle', - ].map((key) => ({key, component}))); - - const [ - iframeButtonImage, - ] = await Promise.all([ - getButtonImage('icon', component), - ]); - - // Note: The function returned here must be synchronous and cannot use promises. - // All promises must be resolved prior to returning the function. - return (editor) => { - registerIframeCommand(editor, iframeButtonText, iframeButtonImage); - }; -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js deleted file mode 100755 index cf20c3c0..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/common.js +++ /dev/null @@ -1,30 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media common values. - * - * @module tiny_mediacms/common - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -export default { - pluginName: 'tiny_mediacms/plugin', - component: 'tiny_mediacms', - iframeButtonName: 'tiny_mediacms_iframe', - iframeMenuItemName: 'tiny_mediacms_iframe', - iframeIcon: 'tiny_mediacms_iframe', -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js deleted file mode 100755 index 3eb2569a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/configuration.js +++ /dev/null @@ -1,60 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media configuration. - * - * @module tiny_mediacms/configuration - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import { - iframeButtonName, - iframeMenuItemName, -} from './common'; -import { - addContextmenuItem, -} from 'editor_tiny/utils'; - -const configureMenu = (menu) => { - // Add the Iframe Embed to the insert menu. - menu.insert.items = `${iframeMenuItemName} ${menu.insert.items}`; - - return menu; -}; - -const configureToolbar = (toolbar) => { - // The toolbar contains an array of named sections. - // The Moodle integration ensures that there is a section called 'content'. - - return toolbar.map((section) => { - if (section.name === 'content') { - // Insert the iframe button at the start of it. - section.items.unshift(iframeButtonName); - } - - return section; - }); -}; - -export const configure = (instanceConfig) => { - // Update the instance configuration to add the Iframe Embed menu option to the menus and toolbars. - return { - contextmenu: addContextmenuItem(instanceConfig.contextmenu, iframeButtonName), - menu: configureMenu(instanceConfig.menu), - toolbar: configureToolbar(instanceConfig.toolbar), - }; -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js deleted file mode 100755 index c31563b3..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embed.js +++ /dev/null @@ -1,467 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin Embed class for Moodle. - * - * @module tiny_mediacms/embed - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; -import { - getString, - getStrings, -} from 'core/str'; -import * as ModalEvents from 'core/modal_events'; -import {displayFilepicker} from 'editor_tiny/utils'; -import {getCurrentLanguage, getMoodleLang} from 'editor_tiny/options'; -import {component} from "./common"; -import EmbedModal from './embedmodal'; -import Selectors from './selectors'; -import {getEmbedPermissions} from './options'; -import {getFilePicker} from 'editor_tiny/options'; - -export default class MediaEmbed { - editor = null; - canShowFilePicker = false; - canShowFilePickerPoster = false; - canShowFilePickerTrack = false; - - /** - * @property {Object} The names of the alignment options. - */ - helpStrings = null; - - /** - * @property {boolean} Indicate that the user is updating the media or not. - */ - isUpdating = false; - - /** - * @property {Object} The currently selected media. - */ - selectedMedia = null; - - constructor(editor) { - const permissions = getEmbedPermissions(editor); - - // Indicates whether the file picker can be shown. - this.canShowFilePicker = permissions.filepicker && (typeof getFilePicker(editor, 'media') !== 'undefined'); - this.canShowFilePickerPoster = permissions.filepicker && (typeof getFilePicker(editor, 'image') !== 'undefined'); - this.canShowFilePickerTrack = permissions.filepicker && (typeof getFilePicker(editor, 'subtitle') !== 'undefined'); - - this.editor = editor; - } - - async getHelpStrings() { - if (!this.helpStrings) { - const [addSource, tracks, subtitles, captions, descriptions, chapters, metadata] = await getStrings([ - 'addsource_help', - 'tracks_help', - 'subtitles_help', - 'captions_help', - 'descriptions_help', - 'chapters_help', - 'metadata_help', - ].map((key) => ({ - key, - component, - }))); - - this.helpStrings = {addSource, tracks, subtitles, captions, descriptions, chapters, metadata}; - } - - return this.helpStrings; - } - - async getTemplateContext(data) { - const languages = this.prepareMoodleLang(); - - const helpIcons = Array.from(Object.entries(await this.getHelpStrings())).forEach(([key, text]) => { - data[`${key.toLowerCase()}helpicon`] = {text}; - }); - - return Object.assign({}, { - elementid: this.editor.getElement().id, - showfilepicker: this.canShowFilePicker, - showfilepickerposter: this.canShowFilePickerPoster, - showfilepickertrack: this.canShowFilePickerTrack, - langsinstalled: languages.installed, - langsavailable: languages.available, - link: true, - video: false, - audio: false, - isupdating: this.isUpdating, - }, data, helpIcons); - } - - async displayDialogue() { - this.selectedMedia = this.getSelectedMedia(); - const data = Object.assign({}, this.getCurrentEmbedData()); - this.isUpdating = Object.keys(data).length !== 0; - - this.currentModal = await EmbedModal.create({ - title: getString('createmedia', 'tiny_mediacms'), - templateContext: await this.getTemplateContext(data), - }); - - await this.registerEventListeners(this.currentModal); - } - - getCurrentEmbedData() { - const properties = this.getMediumProperties(); - if (!properties) { - return {}; - } - - const processedProperties = {}; - processedProperties[properties.type.toLowerCase()] = properties; - processedProperties.link = false; - - return processedProperties; - } - - getSelectedMedia() { - const mediaElm = this.editor.selection.getNode(); - - if (!mediaElm) { - return null; - } - - if (mediaElm.nodeName.toLowerCase() === 'video' || mediaElm.nodeName.toLowerCase() === 'audio') { - return mediaElm; - } - - if (mediaElm.querySelector('video')) { - return mediaElm.querySelector('video'); - } - - if (mediaElm.querySelector('audio')) { - return mediaElm.querySelector('audio'); - } - - return null; - } - - getMediumProperties() { - const boolAttr = (elem, attr) => { - // As explained in MDL-64175, some OS (like Ubuntu), are removing the value for these attributes. - // So in order to check if attr="true", we need to check if the attribute exists and if the value is empty or true. - return (elem.hasAttribute(attr) && (elem.getAttribute(attr) || elem.getAttribute(attr) === '')); - }; - - const tracks = { - subtitles: [], - captions: [], - descriptions: [], - chapters: [], - metadata: [] - }; - const sources = []; - - const medium = this.selectedMedia; - if (!medium) { - return null; - } - medium.querySelectorAll('track').forEach((track) => { - tracks[track.getAttribute('kind')].push({ - src: track.getAttribute('src'), - srclang: track.getAttribute('srclang'), - label: track.getAttribute('label'), - defaultTrack: boolAttr(track, 'default') - }); - }); - - medium.querySelectorAll('source').forEach((source) => { - sources.push(source.src); - }); - - return { - type: medium.nodeName.toLowerCase() === 'video' ? Selectors.EMBED.mediaTypes.video : Selectors.EMBED.mediaTypes.audio, - sources, - poster: medium.getAttribute('poster'), - title: medium.getAttribute('title'), - width: medium.getAttribute('width'), - height: medium.getAttribute('height'), - autoplay: boolAttr(medium, 'autoplay'), - loop: boolAttr(medium, 'loop'), - muted: boolAttr(medium, 'muted'), - controls: boolAttr(medium, 'controls'), - tracks, - }; - } - - prepareMoodleLang() { - const moodleLangs = getMoodleLang(this.editor); - const currentLanguage = getCurrentLanguage(this.editor); - - const installed = Object.entries(moodleLangs.installed).map(([lang, code]) => ({ - lang, - code, - "default": lang === currentLanguage, - })); - - const available = Object.entries(moodleLangs.available).map(([lang, code]) => ({ - lang, - code, - "default": lang === currentLanguage, - })); - - return { - installed, - available, - }; - } - - getMoodleLangObj(subtitleLang) { - const {available} = getMoodleLang(this.editor); - - if (available[subtitleLang]) { - return { - lang: subtitleLang, - code: available[subtitleLang], - }; - } - - return null; - } - - filePickerCallback(params, element, fpType) { - if (params.url !== '') { - const tabPane = element.closest('.tab-pane'); - element.closest(Selectors.EMBED.elements.source).querySelector(Selectors.EMBED.elements.url).value = params.url; - - if (tabPane.id === this.editor.getElement().id + '_' + Selectors.EMBED.mediaTypes.link.toLowerCase()) { - tabPane.querySelector(Selectors.EMBED.elements.name).value = params.file; - } - - if (fpType === 'subtitle') { - // If the file is subtitle file. We need to match the language and label for that file. - const subtitleLang = params.file.split('.vtt')[0].split('-').slice(-1)[0]; - const langObj = this.getMoodleLangObj(subtitleLang); - if (langObj) { - const track = element.closest(Selectors.EMBED.elements.track); - track.querySelector(Selectors.EMBED.elements.trackLabel).value = langObj.lang.trim(); - track.querySelector(Selectors.EMBED.elements.trackLang).value = langObj.code; - } - } - } - } - - addMediaSourceComponent(element, callback) { - const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource); - const clone = sourceElement.cloneNode(true); - - sourceElement.querySelector('.removecomponent-wrapper').classList.remove('hidden'); - sourceElement.querySelector('.addcomponent-wrapper').classList.add('hidden'); - - sourceElement.parentNode.insertBefore(clone, sourceElement.nextSibling); - - if (callback) { - callback(clone); - } - } - - removeMediaSourceComponent(element) { - const sourceElement = element.closest(Selectors.EMBED.elements.source + Selectors.EMBED.elements.mediaSource); - sourceElement.remove(); - } - - addTrackComponent(element, callback) { - const trackElement = element.closest(Selectors.EMBED.elements.track); - const clone = trackElement.cloneNode(true); - - trackElement.querySelector('.removecomponent-wrapper').classList.remove('hidden'); - trackElement.querySelector('.addcomponent-wrapper').classList.add('hidden'); - - trackElement.parentNode.insertBefore(clone, trackElement.nextSibling); - - if (callback) { - callback(clone); - } - } - - removeTrackComponent(element) { - const sourceElement = element.closest(Selectors.EMBED.elements.track); - sourceElement.remove(); - } - - getMediumTypeFromTabPane(tabPane) { - return tabPane.getAttribute('data-medium-type'); - } - - getTrackTypeFromTabPane(tabPane) { - return tabPane.getAttribute('data-track-kind'); - } - - getMediaHTML(form) { - const mediumType = this.getMediumTypeFromTabPane(form.querySelector('.root.tab-content > .tab-pane.active')); - const tabContent = form.querySelector(Selectors.EMBED.elements[mediumType.toLowerCase() + 'Pane']); - - return this['getMediaHTML' + mediumType[0].toUpperCase() + mediumType.substr(1)](tabContent); - } - - getMediaHTMLLink(tab) { - const context = { - url: tab.querySelector(Selectors.EMBED.elements.url).value, - name: tab.querySelector(Selectors.EMBED.elements.name).value || false - }; - - return context.url ? Templates.renderForPromise('tiny_mediacms/embed_media_link', context) : ''; - } - - getMediaHTMLVideo(tab) { - const context = this.getContextForMediaHTML(tab); - context.width = tab.querySelector(Selectors.EMBED.elements.width).value || false; - context.height = tab.querySelector(Selectors.EMBED.elements.height).value || false; - context.poster = tab.querySelector( - `${Selectors.EMBED.elements.posterSource} ${Selectors.EMBED.elements.url}` - ).value || false; - - return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_video', context) : ''; - } - - getMediaHTMLAudio(tab) { - const context = this.getContextForMediaHTML(tab); - - return context.sources.length ? Templates.renderForPromise('tiny_mediacms/embed_media_audio', context) : ''; - } - - getContextForMediaHTML(tab) { - const tracks = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.track)).map(track => ({ - track: track.querySelector(Selectors.EMBED.elements.trackSource + ' ' + Selectors.EMBED.elements.url).value, - kind: this.getTrackTypeFromTabPane(track.closest('.tab-pane')), - label: track.querySelector(Selectors.EMBED.elements.trackLabel).value || - track.querySelector(Selectors.EMBED.elements.trackLang).value, - srclang: track.querySelector(Selectors.EMBED.elements.trackLang).value, - defaultTrack: track.querySelector(Selectors.EMBED.elements.trackDefault).checked ? "true" : null - })).filter((track) => !!track.track); - - const sources = Array.from(tab.querySelectorAll(Selectors.EMBED.elements.mediaSource + ' ' - + Selectors.EMBED.elements.url)) - .filter((source) => !!source.value) - .map((source) => source.value); - - return { - sources, - description: tab.querySelector(Selectors.EMBED.elements.mediaSource + ' ' - + Selectors.EMBED.elements.url).value || false, - tracks, - showControls: tab.querySelector(Selectors.EMBED.elements.mediaControl).checked, - autoplay: tab.querySelector(Selectors.EMBED.elements.mediaAutoplay).checked, - muted: tab.querySelector(Selectors.EMBED.elements.mediaMute).checked, - loop: tab.querySelector(Selectors.EMBED.elements.mediaLoop).checked, - title: tab.querySelector(Selectors.EMBED.elements.title).value || false - }; - } - - getFilepickerTypeFromElement(element) { - if (element.closest(Selectors.EMBED.elements.posterSource)) { - return 'image'; - } - if (element.closest(Selectors.EMBED.elements.trackSource)) { - return 'subtitle'; - } - - return 'media'; - } - - async clickHandler(e) { - const element = e.target; - - const mediaBrowser = element.closest(Selectors.EMBED.actions.mediaBrowser); - if (mediaBrowser) { - e.preventDefault(); - const fpType = this.getFilepickerTypeFromElement(element); - const params = await displayFilepicker(this.editor, fpType); - this.filePickerCallback(params, element, fpType); - } - - const addComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .addcomponent'); - if (addComponentSourceAction) { - e.preventDefault(); - this.addMediaSourceComponent(element); - } - - const removeComponentSourceAction = element.closest(Selectors.EMBED.elements.mediaSource + ' .removecomponent'); - if (removeComponentSourceAction) { - e.preventDefault(); - this.removeMediaSourceComponent(element); - } - - const addComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .addcomponent'); - if (addComponentTrackAction) { - e.preventDefault(); - this.addTrackComponent(element); - } - - const removeComponentTrackAction = element.closest(Selectors.EMBED.elements.track + ' .removecomponent'); - if (removeComponentTrackAction) { - e.preventDefault(); - this.removeTrackComponent(element); - } - - // Only allow one track per tab to be selected as "default". - const trackDefaultAction = element.closest(Selectors.EMBED.elements.trackDefault); - if (trackDefaultAction && trackDefaultAction.checked) { - const getKind = (el) => this.getTrackTypeFromTabPane(el.parentElement.closest('.tab-pane')); - - element.parentElement - .closest('.root.tab-content') - .querySelectorAll(Selectors.EMBED.elements.trackDefault) - .forEach((select) => { - if (select !== element && getKind(element) === getKind(select)) { - select.checked = false; - } - }); - } - } - - async handleDialogueSubmission(event, modal) { - const {html} = await this.getMediaHTML(modal.getRoot()[0]); - if (html) { - if (this.isUpdating) { - this.selectedMedia.outerHTML = html; - this.isUpdating = false; - } else { - this.editor.insertContent(html); - } - } - } - - async registerEventListeners(modal) { - await modal.getBody(); - const $root = modal.getRoot(); - const root = $root[0]; - if (this.canShowFilePicker || this.canShowFilePickerPoster || this.canShowFilePickerTrack) { - root.addEventListener('click', this.clickHandler.bind(this)); - } - - $root.on(ModalEvents.save, this.handleDialogueSubmission.bind(this)); - $root.on(ModalEvents.hidden, () => { - this.currentModal.destroy(); - }); - $root.on(ModalEvents.shown, () => { - root.querySelectorAll(Selectors.EMBED.elements.trackLang).forEach((dropdown) => { - const defaultVal = dropdown.getAttribute('data-value'); - if (defaultVal) { - dropdown.value = defaultVal; - } - }); - }); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js deleted file mode 100755 index 96d86853..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/embedmodal.js +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Embedded Media Management Modal for Tiny. - * - * @module tiny_mediacms/embedmodal - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Modal from 'core/modal'; -import {component} from './common'; - -export default class EmbedModal extends Modal { - static TYPE = `${component}/modal`; - static TEMPLATE = `${component}/embed_media_modal`; - - registerEventListeners() { - // Call the parent registration. - super.registerEventListeners(); - - // Register to close on save/cancel. - this.registerCloseOnSave(); - this.registerCloseOnCancel(); - } - - configure(modalConfig) { - modalConfig.large = true; - modalConfig.removeOnClose = true; - modalConfig.show = true; - - super.configure(modalConfig); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js deleted file mode 100755 index 4a3b135a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframeembed.js +++ /dev/null @@ -1,1090 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media2 Iframe Embed class. - * - * @module tiny_mediacms/iframeembed - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; -import { getString } from 'core/str'; -import * as ModalEvents from 'core/modal_events'; -import { component } from './common'; -import IframeModal from './iframemodal'; -import Selectors from './selectors'; -import { getLti } from './options'; - -export default class IframeEmbed { - editor = null; - currentModal = null; - isUpdating = false; - selectedIframe = null; - debounceTimer = null; - // Iframe library state - iframeLibraryLoaded = false; - selectedLibraryVideo = null; - iframeLibraryUrl = - 'https://temp.web357.com/mediacms/deic-mediacms-embed-videos.html'; - - constructor(editor) { - this.editor = editor; - } - - /** - * Parse input to extract video URL or iframe src. - * Handles: iframe embed code, view URL, or embed URL. - * - * @param {string} input - The user input (URL or embed code) - * @returns {Object|null} Parsed URL info or null if invalid - */ - parseInput(input) { - if (!input || !input.trim()) { - return null; - } - - input = input.trim(); - - // Check if it's an iframe embed code - const iframeMatch = input.match( - /]*src=["']([^"']+)["'][^>]*>/i, - ); - if (iframeMatch) { - return this.parseEmbedUrl(iframeMatch[1]); - } - - // Check if it's a URL - if (input.startsWith('http://') || input.startsWith('https://')) { - return this.parseVideoUrl(input); - } - - return null; - } - - /** - * Parse a video view URL and convert to embed format. - * - * @param {string} url - The video URL - * @returns {Object|null} Parsed info - */ - parseVideoUrl(url) { - try { - const urlObj = new URL(url); - const baseUrl = `${urlObj.protocol}//${urlObj.host}`; - - // MediaCMS view URL: /view?m=VIDEO_ID - if (urlObj.pathname === '/view' && urlObj.searchParams.has('m')) { - return { - baseUrl: baseUrl, - videoId: urlObj.searchParams.get('m'), - isEmbed: false, - }; - } - - // MediaCMS embed URL: /embed?m=VIDEO_ID&options - if (urlObj.pathname === '/embed' && urlObj.searchParams.has('m')) { - const tParam = urlObj.searchParams.get('t'); - return { - baseUrl: baseUrl, - videoId: urlObj.searchParams.get('m'), - isEmbed: true, - showTitle: urlObj.searchParams.get('showTitle') === '1', - linkTitle: urlObj.searchParams.get('linkTitle') === '1', - showRelated: urlObj.searchParams.get('showRelated') === '1', - showUserAvatar: - urlObj.searchParams.get('showUserAvatar') === '1', - startAt: tParam - ? this.secondsToTimeString(parseInt(tParam)) - : null, - }; - } - - // Generic URL - just use as-is - return { - baseUrl: baseUrl, - rawUrl: url, - isGeneric: true, - }; - } catch (e) { - return null; - } - } - - /** - * Parse an embed URL. - * - * @param {string} url - The embed URL - * @returns {Object|null} Parsed info - */ - parseEmbedUrl(url) { - return this.parseVideoUrl(url); - } - - /** - * Convert seconds to time string (M:SS format). - * - * @param {number} seconds - Time in seconds - * @returns {string} Time string - */ - secondsToTimeString(seconds) { - const mins = Math.floor(seconds / 60); - const secs = seconds % 60; - return `${mins}:${secs.toString().padStart(2, '0')}`; - } - - /** - * Convert time string to seconds. - * - * @param {string} timeStr - Time string (M:SS or just seconds) - * @returns {number|null} Seconds or null if invalid - */ - timeStringToSeconds(timeStr) { - if (!timeStr || !timeStr.trim()) { - return null; - } - timeStr = timeStr.trim(); - - // Handle M:SS format - if (timeStr.includes(':')) { - const parts = timeStr.split(':'); - const mins = parseInt(parts[0]) || 0; - const secs = parseInt(parts[1]) || 0; - return mins * 60 + secs; - } - - // Handle plain seconds - const secs = parseInt(timeStr); - return isNaN(secs) ? null : secs; - } - - /** - * Build the embed URL with options. - * - * @param {Object} parsed - Parsed URL info - * @param {Object} options - Embed options - * @returns {string} The complete embed URL - */ - buildEmbedUrl(parsed, options) { - if (parsed.isGeneric) { - return parsed.rawUrl; - } - - const url = new URL(`${parsed.baseUrl}/embed`); - url.searchParams.set('m', parsed.videoId); - - // Always include all options with 1 or 0 - url.searchParams.set('showTitle', options.showTitle ? '1' : '0'); - url.searchParams.set('showRelated', options.showRelated ? '1' : '0'); - url.searchParams.set( - 'showUserAvatar', - options.showUserAvatar ? '1' : '0', - ); - url.searchParams.set('linkTitle', options.linkTitle ? '1' : '0'); - - // Add start time if enabled - if (options.startAtEnabled && options.startAt) { - const seconds = this.timeStringToSeconds(options.startAt); - if (seconds !== null && seconds > 0) { - url.searchParams.set('t', seconds.toString()); - } - } - - return url.toString(); - } - - /** - * Get the template context for the modal. - * - * @param {Object} data - Existing data for updating - * @returns {Object} Template context - */ - async getTemplateContext(data = {}) { - return { - elementid: this.editor.getElement().id, - isupdating: this.isUpdating, - url: data.url || '', - showTitle: data.showTitle !== false, - linkTitle: data.linkTitle !== false, - showRelated: data.showRelated !== false, - showUserAvatar: data.showUserAvatar !== false, - responsive: data.responsive !== false, - startAtEnabled: data.startAtEnabled || false, - startAt: data.startAt || '0:00', - width: data.width || 560, - height: data.height || 315, - is16_9: !data.aspectRatio || data.aspectRatio === '16:9', - is4_3: data.aspectRatio === '4:3', - is1_1: data.aspectRatio === '1:1', - isCustom: data.aspectRatio === 'custom', - }; - } - - /** - * Display the iframe embed dialog. - */ - async displayDialogue() { - this.selectedIframe = this.getSelectedIframe(); - const data = this.getCurrentIframeData(); - this.isUpdating = data !== null; - - // Reset iframe library state for new modal - this.iframeLibraryLoaded = false; - - this.currentModal = await IframeModal.create({ - title: getString('iframemodaltitle', component), - templateContext: await this.getTemplateContext(data || {}), - }); - - await this.registerEventListeners(this.currentModal); - } - - /** - * Get the currently selected iframe in the editor. - * - * @returns {HTMLElement|null} The iframe element or null - */ - getSelectedIframe() { - const node = this.editor.selection.getNode(); - - if (node.nodeName.toLowerCase() === 'iframe') { - return node; - } - - // Check if selection contains an iframe wrapper (including overlay wrapper) - const iframe = node.querySelector('iframe'); - if (iframe) { - return iframe; - } - - // Check if we're on the overlay or wrapper and need to find the iframe - const wrapper = - node.closest('.tiny-mediacms-iframe-wrapper') || - node.closest('.tiny-iframe-responsive'); - if (wrapper) { - return wrapper.querySelector('iframe'); - } - - return null; - } - - /** - * Get current iframe data for editing. - * - * @returns {Object|null} Current iframe data or null - */ - getCurrentIframeData() { - if (!this.selectedIframe) { - return null; - } - - const src = this.selectedIframe.getAttribute('src'); - const parsed = this.parseInput(src); - - // Check if responsive by looking at style - const style = this.selectedIframe.getAttribute('style') || ''; - const isResponsive = style.includes('aspect-ratio'); - - return { - url: src, - width: this.selectedIframe.getAttribute('width') || 560, - height: this.selectedIframe.getAttribute('height') || 315, - showTitle: parsed?.showTitle ?? true, - linkTitle: parsed?.linkTitle ?? true, - showRelated: parsed?.showRelated ?? true, - showUserAvatar: parsed?.showUserAvatar ?? true, - responsive: isResponsive, - startAtEnabled: parsed?.startAt !== null, - startAt: parsed?.startAt || '0:00', - }; - } - - /** - * Get form values from the modal. - * - * @param {HTMLElement} root - Modal root element - * @returns {Object} Form values - */ - getFormValues(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - return { - url: form.querySelector(Selectors.IFRAME.elements.url).value.trim(), - showTitle: form.querySelector(Selectors.IFRAME.elements.showTitle) - .checked, - linkTitle: form.querySelector(Selectors.IFRAME.elements.linkTitle) - .checked, - showRelated: form.querySelector( - Selectors.IFRAME.elements.showRelated, - ).checked, - showUserAvatar: form.querySelector( - Selectors.IFRAME.elements.showUserAvatar, - ).checked, - responsive: form.querySelector(Selectors.IFRAME.elements.responsive) - .checked, - startAtEnabled: form.querySelector( - Selectors.IFRAME.elements.startAtEnabled, - ).checked, - startAt: form - .querySelector(Selectors.IFRAME.elements.startAt) - .value.trim(), - aspectRatio: form.querySelector( - Selectors.IFRAME.elements.aspectRatio, - ).value, - width: - parseInt( - form.querySelector(Selectors.IFRAME.elements.width).value, - ) || 560, - height: - parseInt( - form.querySelector(Selectors.IFRAME.elements.height).value, - ) || 315, - }; - } - - /** - * Generate the iframe HTML. - * - * @param {Object} values - Form values - * @returns {Promise} Generated HTML - */ - async generateIframeHtml(values) { - const parsed = this.parseInput(values.url); - if (!parsed) { - return ''; - } - - const embedUrl = this.buildEmbedUrl(parsed, values); - - // Calculate aspect ratio values for CSS - const aspectRatioCalcs = { - '16:9': '16 / 9', - '4:3': '4 / 3', - '1:1': '1 / 1', - custom: `${values.width} / ${values.height}`, - }; - - const context = { - src: embedUrl, - width: values.width, - height: values.height, - responsive: values.responsive, - aspectRatioCalc: aspectRatioCalcs[values.aspectRatio] || '16 / 9', - aspectRatioValue: aspectRatioCalcs[values.aspectRatio] || '16 / 9', - }; - - const { html } = await Templates.renderForPromise( - 'tiny_mediacms/iframe_embed_output', - context, - ); - return html; - } - - /** - * Update the preview in the modal. - * - * @param {HTMLElement} root - Modal root element - */ - async updatePreview(root) { - const values = this.getFormValues(root); - const previewContainer = root.querySelector( - Selectors.IFRAME.elements.preview, - ); - const urlWarning = root.querySelector( - Selectors.IFRAME.elements.urlWarning, - ); - - if (!values.url) { - previewContainer.innerHTML = - 'Enter a video URL to see preview'; - urlWarning.classList.add('d-none'); - return; - } - - const parsed = this.parseInput(values.url); - if (!parsed) { - previewContainer.innerHTML = - 'Invalid URL format'; - urlWarning.classList.remove('d-none'); - return; - } - - urlWarning.classList.add('d-none'); - const embedUrl = this.buildEmbedUrl(parsed, values); - - // Show a scaled preview - const previewWidth = Math.min(values.width, 400); - const scale = previewWidth / values.width; - const previewHeight = Math.round(values.height * scale); - - previewContainer.innerHTML = ` - - `; - } - - /** - * Handle form input changes with debounce. - * - * @param {HTMLElement} root - Modal root element - */ - handleInputChange(root) { - clearTimeout(this.debounceTimer); - this.debounceTimer = setTimeout(() => { - this.updatePreview(root); - }, 500); - } - - /** - * Handle aspect ratio change - update dimensions. - * - * @param {HTMLElement} root - Modal root element - */ - handleAspectRatioChange(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - const aspectRatio = form.querySelector( - Selectors.IFRAME.elements.aspectRatio, - ).value; - const dimensions = Selectors.IFRAME.aspectRatios[aspectRatio]; - - // Only update dimensions for predefined ratios, not 'custom' - if (dimensions && aspectRatio !== 'custom') { - form.querySelector(Selectors.IFRAME.elements.width).value = - dimensions.width; - form.querySelector(Selectors.IFRAME.elements.height).value = - dimensions.height; - } - - this.updatePreview(root); - } - - /** - * Handle dialog submission. - * - * @param {Object} modal - The modal instance - */ - async handleDialogueSubmission(modal) { - const root = modal.getRoot()[0]; - const values = this.getFormValues(root); - - if (!values.url) { - return; - } - - const html = await this.generateIframeHtml(values); - if (html) { - if (this.isUpdating && this.selectedIframe) { - // Replace existing iframe (including wrapper if present) - // Check for both old wrapper and new overlay wrapper - const wrapper = - this.selectedIframe.closest( - '.tiny-mediacms-iframe-wrapper', - ) || this.selectedIframe.closest('.tiny-iframe-responsive'); - if (wrapper) { - wrapper.outerHTML = html; - } else { - this.selectedIframe.outerHTML = html; - } - this.isUpdating = false; - // Fire change event to trigger overlay reprocessing - this.editor.fire('Change'); - } else { - this.editor.insertContent(html); - } - } - } - - /** - * Handle video removal from the editor. - * - * @param {Object} modal - The modal instance - */ - async handleRemove(modal) { - // Get confirmation string - const confirmMessage = await getString( - 'removeiframeconfirm', - component, - ); - - // Show confirmation dialog - // eslint-disable-next-line no-alert - if (!window.confirm(confirmMessage)) { - return; - } - - if (this.selectedIframe) { - // Remove the iframe (including wrapper if present) - const wrapper = - this.selectedIframe.closest('.tiny-mediacms-iframe-wrapper') || - this.selectedIframe.closest('.tiny-iframe-responsive'); - if (wrapper) { - wrapper.remove(); - } else { - this.selectedIframe.remove(); - } - } - - // Close the modal - this.isUpdating = false; - modal.hide(); - } - - /** - * Register event listeners for the modal. - * - * @param {Object} modal - The modal instance - */ - async registerEventListeners(modal) { - await modal.getBody(); - const $root = modal.getRoot(); - const root = $root[0]; - - // Input change handlers - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // URL input - form.querySelector(Selectors.IFRAME.elements.url).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - - // Checkbox options - [ - Selectors.IFRAME.elements.showTitle, - Selectors.IFRAME.elements.linkTitle, - Selectors.IFRAME.elements.showRelated, - Selectors.IFRAME.elements.showUserAvatar, - Selectors.IFRAME.elements.responsive, - Selectors.IFRAME.elements.startAtEnabled, - ].forEach((selector) => { - form.querySelector(selector).addEventListener('change', () => - this.handleInputChange(root), - ); - }); - - // Start at time input - form.querySelector(Selectors.IFRAME.elements.startAt).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - - // Aspect ratio change - form.querySelector( - Selectors.IFRAME.elements.aspectRatio, - ).addEventListener('change', () => this.handleAspectRatioChange(root)); - - // Dimension inputs - form.querySelector(Selectors.IFRAME.elements.width).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - form.querySelector(Selectors.IFRAME.elements.height).addEventListener( - 'input', - () => this.handleInputChange(root), - ); - - // Modal events - $root.on(ModalEvents.save, () => this.handleDialogueSubmission(modal)); - $root.on(ModalEvents.hidden, () => { - this.currentModal.destroy(); - }); - - // Remove button handler (only present when updating) - const removeBtn = root.querySelector(Selectors.IFRAME.actions.remove); - if (removeBtn) { - removeBtn.addEventListener('click', () => this.handleRemove(modal)); - } - - // Initial preview if we have a URL - const urlInput = form.querySelector(Selectors.IFRAME.elements.url); - if (urlInput.value) { - this.updatePreview(root); - } - - // Tab change handler - load iframe library when switching to iframe library tab - const iframeLibraryTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabIframeLibraryBtn, - ); - if (iframeLibraryTabBtn) { - iframeLibraryTabBtn.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - // Manually handle tab switching for Bootstrap 5 - this.switchToIframeLibraryTab(root); - - // Small delay to ensure tab pane is visible before loading iframe - setTimeout(() => this.handleIframeLibraryTabClick(root), 100); - }); - // Also handle Bootstrap tab events (if Bootstrap handles it) - iframeLibraryTabBtn.addEventListener('shown.bs.tab', () => - this.handleIframeLibraryTabClick(root), - ); - // jQuery Bootstrap 4 event - const $iframeLibraryTabBtn = window.jQuery - ? window.jQuery(iframeLibraryTabBtn) - : null; - if ($iframeLibraryTabBtn) { - $iframeLibraryTabBtn.on('shown.bs.tab', () => - this.handleIframeLibraryTabClick(root), - ); - } - } - - // Tab change handler for URL tab - const urlTabBtn = form.querySelector( - Selectors.IFRAME.elements.tabUrlBtn, - ); - if (urlTabBtn) { - urlTabBtn.addEventListener('click', (e) => { - e.preventDefault(); - e.stopPropagation(); - - // Manually handle tab switching - this.switchToUrlTab(root); - }); - } - - // Iframe library event listeners - this.registerIframeLibraryEventListeners(root); - } - - /** - * Switch to the URL tab. - * - * @param {HTMLElement} root - Modal root element - */ - switchToUrlTab(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // Get tab buttons - const urlTabBtn = form.querySelector(Selectors.IFRAME.elements.tabUrlBtn); - const iframeLibraryTabBtn = form.querySelector(Selectors.IFRAME.elements.tabIframeLibraryBtn); - - // Get tab panes - const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl); - const iframeLibraryPane = form.querySelector(Selectors.IFRAME.elements.paneIframeLibrary); - - // Update tab button states - if (urlTabBtn) { - urlTabBtn.classList.add('active'); - urlTabBtn.setAttribute('aria-selected', 'true'); - } - if (iframeLibraryTabBtn) { - iframeLibraryTabBtn.classList.remove('active'); - iframeLibraryTabBtn.setAttribute('aria-selected', 'false'); - } - - // Update tab pane visibility - if (urlPane) { - urlPane.classList.add('show', 'active'); - } - if (iframeLibraryPane) { - iframeLibraryPane.classList.remove('show', 'active'); - } - } - - /** - * Switch to the iframe library tab. - * - * @param {HTMLElement} root - Modal root element - */ - switchToIframeLibraryTab(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // Get tab buttons - const urlTabBtn = form.querySelector(Selectors.IFRAME.elements.tabUrlBtn); - const iframeLibraryTabBtn = form.querySelector(Selectors.IFRAME.elements.tabIframeLibraryBtn); - - // Get tab panes - const urlPane = form.querySelector(Selectors.IFRAME.elements.paneUrl); - const iframeLibraryPane = form.querySelector(Selectors.IFRAME.elements.paneIframeLibrary); - - // Update tab button states - if (urlTabBtn) { - urlTabBtn.classList.remove('active'); - urlTabBtn.setAttribute('aria-selected', 'false'); - } - if (iframeLibraryTabBtn) { - iframeLibraryTabBtn.classList.add('active'); - iframeLibraryTabBtn.setAttribute('aria-selected', 'true'); - } - - // Update tab pane visibility - if (urlPane) { - urlPane.classList.remove('show', 'active'); - } - if (iframeLibraryPane) { - iframeLibraryPane.classList.add('show', 'active'); - } - } - - /** - * Register event listeners for the iframe library. - * - * @param {HTMLElement} root - Modal root element - */ - registerIframeLibraryEventListeners(root) { - // Listen for messages from the iframe (for video selection) - window.addEventListener('message', (event) => { - this.handleIframeLibraryMessage(root, event); - }); - } - - /** - * Handle iframe library tab click - load iframe if not already loaded. - * - * @param {HTMLElement} root - Modal root element - */ - handleIframeLibraryTabClick(root) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - const iframeEl = pane - ? pane.querySelector(Selectors.IFRAME.elements.iframeLibraryFrame) - : null; - - // eslint-disable-next-line no-console - console.log( - 'handleIframeLibraryTabClick called, iframeLibraryLoaded:', - this.iframeLibraryLoaded, - 'iframe src:', - iframeEl ? iframeEl.src : 'no iframe', - 'pane:', - pane, - ); - - // Load if not loaded or iframe has no source or empty source - if ( - !this.iframeLibraryLoaded || - (iframeEl && (!iframeEl.src || iframeEl.src === '')) - ) { - this.loadIframeLibrary(root); - } - } - - /** - * Load the iframe library using LTI flow or fallback to static URL. - * - * @param {HTMLElement} root - Modal root element - */ - loadIframeLibrary(root) { - const ltiConfig = getLti(this.editor); - - // eslint-disable-next-line no-console - console.log('loadIframeLibrary called, LTI config:', ltiConfig); - - // Check if LTI is configured with a content item URL - if (ltiConfig?.contentItemUrl) { - this.loadIframeLibraryViaLti(root, ltiConfig); - } else { - // Fallback to static URL if LTI not configured - this.loadIframeLibraryStatic(root); - } - } - - /** - * Load the iframe library via LTI Deep Linking (Content Item Selection). - * Sets the iframe src to contentitem.php which initiates the LTI Deep Linking flow. - * This sends an LtiDeepLinkingRequest message, which will redirect to the - * tool's content selection interface (e.g., /lti/select-media/). - * - * @param {HTMLElement} root - Modal root element - * @param {Object} ltiConfig - LTI configuration - */ - loadIframeLibraryViaLti(root, ltiConfig) { - // eslint-disable-next-line no-console - console.log('loadIframeLibraryViaLti called, config:', ltiConfig); - - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - if (!pane) { - // eslint-disable-next-line no-console - console.log('paneIframeLibrary not found!'); - return; - } - - const placeholderEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryPlaceholder, - ); - const loadingEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryLoading, - ); - const iframeEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryFrame, - ); - - if (!iframeEl) { - // eslint-disable-next-line no-console - console.log('iframeEl not found!'); - return; - } - - // Hide placeholder, show loading state - if (placeholderEl) { - placeholderEl.classList.add('d-none'); - } - if (loadingEl) { - loadingEl.classList.remove('d-none'); - } - iframeEl.classList.add('d-none'); - - // Set up load listener - note: this may fire multiple times during LTI redirects - const loadHandler = () => { - // eslint-disable-next-line no-console - console.log('LTI iframe loaded'); - this.handleIframeLibraryLoad(root); - }; - iframeEl.addEventListener('load', loadHandler); - - // Set the iframe src to the content item URL - // This initiates the LTI Deep Linking flow: - // 1. contentitem.php initiates OIDC login - // 2. LTI provider authenticates - // 3. Moodle sends LtiDeepLinkingRequest - // 4. Tool provider shows content selection interface (e.g., /lti/select-media/) - // eslint-disable-next-line no-console - console.log('Setting iframe src to LTI content item URL:', ltiConfig.contentItemUrl); - iframeEl.src = ltiConfig.contentItemUrl; - } - - /** - * Load the iframe library using static URL (fallback). - * - * @param {HTMLElement} root - Modal root element - */ - loadIframeLibraryStatic(root) { - // eslint-disable-next-line no-console - console.log( - 'loadIframeLibraryStatic called, URL:', - this.iframeLibraryUrl, - ); - - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - if (!pane) { - // eslint-disable-next-line no-console - console.log('paneIframeLibrary not found!'); - return; - } - - const placeholderEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryPlaceholder, - ); - const loadingEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryLoading, - ); - const iframeEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryFrame, - ); - - // eslint-disable-next-line no-console - console.log('Elements found:', { placeholderEl, loadingEl, iframeEl }); - - if (!iframeEl) { - // eslint-disable-next-line no-console - console.log('iframeEl not found!'); - return; - } - - // Hide placeholder, show loading state - if (placeholderEl) { - placeholderEl.classList.add('d-none'); - } - if (loadingEl) { - loadingEl.classList.remove('d-none'); - } - iframeEl.classList.add('d-none'); - - // Set up load listener before setting src - const loadHandler = () => { - // eslint-disable-next-line no-console - console.log('iframe loaded, src:', iframeEl.src); - // Only handle if the src matches our target URL - if (iframeEl.src === this.iframeLibraryUrl) { - this.handleIframeLibraryLoad(root); - // Remove the listener after successful load - iframeEl.removeEventListener('load', loadHandler); - } - }; - iframeEl.addEventListener('load', loadHandler); - - // Set the iframe source - iframeEl.src = this.iframeLibraryUrl; - // eslint-disable-next-line no-console - console.log('iframe src set to:', iframeEl.src); - } - - /** - * Handle iframe library load event. - * - * @param {HTMLElement} root - Modal root element - */ - handleIframeLibraryLoad(root) { - // eslint-disable-next-line no-console - console.log('handleIframeLibraryLoad called'); - - const form = root.querySelector(Selectors.IFRAME.elements.form); - const pane = form.querySelector( - Selectors.IFRAME.elements.paneIframeLibrary, - ); - - if (!pane) { - return; - } - - const placeholderEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryPlaceholder, - ); - const loadingEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryLoading, - ); - const iframeEl = pane.querySelector( - Selectors.IFRAME.elements.iframeLibraryFrame, - ); - - // Hide placeholder and loading, show iframe - if (placeholderEl) { - placeholderEl.classList.add('d-none'); - } - if (loadingEl) { - loadingEl.classList.add('d-none'); - } - if (iframeEl) { - iframeEl.classList.remove('d-none'); - } - - this.iframeLibraryLoaded = true; - } - - /** - * Handle messages from the iframe library (for video selection). - * Supports both custom videoSelected messages and LTI Deep Linking responses. - * - * @param {HTMLElement} root - Modal root element - * @param {MessageEvent} event - The message event - */ - handleIframeLibraryMessage(root, event) { - // eslint-disable-next-line no-console - console.log( - 'handleIframeLibraryMessage received:', - event.data, - 'from origin:', - event.origin, - ); - - const data = event.data; - - if (!data) { - return; - } - - // Handle custom videoSelected message format (from static iframe or custom MediaCMS implementation) - if (data.type === 'videoSelected' && data.embedUrl) { - // eslint-disable-next-line no-console - console.log( - 'Video selected (videoSelected):', - data.embedUrl, - 'videoId:', - data.videoId, - ); - this.selectIframeLibraryVideo(root, data.embedUrl, data.videoId); - return; - } - - // Handle LTI Deep Linking response format - if ( - data.type === 'ltiDeepLinkingResponse' || - data.messageType === 'LtiDeepLinkingResponse' - ) { - // eslint-disable-next-line no-console - console.log('LTI Deep Linking response received:', data); - const contentItems = data.content_items || data.contentItems || []; - if (contentItems.length > 0) { - const item = contentItems[0]; - // Extract embed URL from the content item - const embedUrl = - item.url || item.embed_url || item.embedUrl || ''; - const videoId = item.id || item.mediaId || ''; - if (embedUrl) { - // eslint-disable-next-line no-console - console.log( - 'Video selected (LTI):', - embedUrl, - 'videoId:', - videoId, - ); - this.selectIframeLibraryVideo(root, embedUrl, videoId); - } - } - return; - } - - // Handle MediaCMS specific message format (if different from above) - if (data.action === 'selectMedia' || data.action === 'mediaSelected') { - const embedUrl = data.embedUrl || data.url || ''; - const videoId = data.mediaId || data.videoId || data.id || ''; - if (embedUrl) { - // eslint-disable-next-line no-console - console.log( - 'Video selected (mediaSelected):', - embedUrl, - 'videoId:', - videoId, - ); - this.selectIframeLibraryVideo(root, embedUrl, videoId); - } - return; - } - } - - /** - * Select a video from the iframe library and populate the URL field. - * - * @param {HTMLElement} root - Modal root element - * @param {string} embedUrl - The embed URL for the video - * @param {string} videoId - The video ID - */ - selectIframeLibraryVideo(root, embedUrl, videoId) { - const form = root.querySelector(Selectors.IFRAME.elements.form); - - // Populate the URL field - const urlInput = form.querySelector(Selectors.IFRAME.elements.url); - urlInput.value = embedUrl; - - // Switch to the URL tab using our method - this.switchToUrlTab(root); - - // Update the preview - this.updatePreview(root); - - // Store the selected video - this.selectedLibraryVideo = { embedUrl, videoId }; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js deleted file mode 100755 index f0fc1486..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/iframemodal.js +++ /dev/null @@ -1,47 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Iframe Embed Modal for Tiny Media2. - * - * @module tiny_mediacms/iframemodal - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Modal from 'core/modal'; -import {component} from './common'; - -export default class IframeModal extends Modal { - static TYPE = `${component}/iframemodal`; - static TEMPLATE = `${component}/iframe_embed_modal`; - - registerEventListeners() { - // Call the parent registration. - super.registerEventListeners(); - - // Register to close on save/cancel. - this.registerCloseOnSave(); - this.registerCloseOnCancel(); - } - - configure(modalConfig) { - modalConfig.large = true; - modalConfig.removeOnClose = true; - modalConfig.show = true; - - super.configure(modalConfig); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js deleted file mode 100755 index e3519d4c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/image.js +++ /dev/null @@ -1,273 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin Image class for Moodle. - * - * @module tiny_mediacms/image - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Selectors from './selectors'; -import ImageModal from './imagemodal'; -import {getImagePermissions} from './options'; -import {getFilePicker} from 'editor_tiny/options'; -import {ImageInsert} from 'tiny_mediacms/imageinsert'; -import {ImageDetails} from 'tiny_mediacms/imagedetails'; -import {prefetchStrings} from 'core/prefetch'; -import {getString} from 'core/str'; -import { - bodyImageInsert, - footerImageInsert, - bodyImageDetails, - footerImageDetails, - showElements, - hideElements, - isPercentageValue, -} from 'tiny_mediacms/imagehelpers'; - -prefetchStrings('tiny_mediacms', [ - 'imageurlrequired', - 'sizecustom_help', -]); - -export default class MediaImage { - canShowFilePicker = false; - editor = null; - currentModal = null; - /** - * @type {HTMLElement|null} The root element. - */ - root = null; - - constructor(editor) { - const permissions = getImagePermissions(editor); - const options = getFilePicker(editor, 'image'); - // Indicates whether the file picker can be shown. - this.canShowFilePicker = permissions.filepicker - && (typeof options !== 'undefined') - && Object.keys(options.repositories).length > 0; - // Indicates whether the drop zone area can be shown. - this.canShowDropZone = (typeof options !== 'undefined') && - Object.values(options.repositories).some(repository => repository.type === 'upload'); - - this.editor = editor; - } - - async displayDialogue() { - const currentImageData = await this.getCurrentImageData(); - this.currentModal = await ImageModal.create(); - this.root = this.currentModal.getRoot()[0]; - if (currentImageData && currentImageData.src) { - this.loadPreviewImage(currentImageData.src); - } else { - this.loadInsertImage(); - } - } - - /** - * Displays an insert image view asynchronously. - * - * @returns {Promise} - */ - loadInsertImage = async function() { - const templateContext = { - elementid: this.editor.id, - showfilepicker: this.canShowFilePicker, - showdropzone: this.canShowDropZone, - }; - - Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)]) - .then(() => { - const imageinsert = new ImageInsert( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - ); - imageinsert.init(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }; - - async getTemplateContext(data) { - return { - elementid: this.editor.id, - showfilepicker: this.canShowFilePicker, - ...data, - }; - } - - async getCurrentImageData() { - const selectedImageProperties = this.getSelectedImageProperties(); - if (!selectedImageProperties) { - return {}; - } - - const properties = {...selectedImageProperties}; - - if (properties.src) { - properties.haspreview = true; - } - - if (!properties.alt) { - properties.presentation = true; - } - - return properties; - } - - /** - * Asynchronously loads and previews an image from the provided URL. - * - * @param {string} url - The URL of the image to load and preview. - * @returns {Promise} - */ - loadPreviewImage = async function(url) { - this.startImageLoading(); - const image = new Image(); - image.src = url; - image.addEventListener('error', async() => { - const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); - urlWarningLabelEle.innerHTML = await getString('imageurlrequired', 'tiny_mediacms'); - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - this.stopImageLoading(); - }); - - image.addEventListener('load', async() => { - const currentImageData = await this.getCurrentImageData(); - let templateContext = await this.getTemplateContext(currentImageData); - templateContext.sizecustomhelpicon = {text: await getString('sizecustom_help', 'tiny_mediacms')}; - - Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)]) - .then(() => { - this.stopImageLoading(); - return; - }) - .then(() => { - const imagedetails = new ImageDetails( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - url, - image, - ); - imagedetails.init(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }); - }; - - getSelectedImageProperties() { - const image = this.getSelectedImage(); - if (!image) { - this.selectedImage = null; - return null; - } - - const properties = { - src: null, - alt: null, - width: null, - height: null, - presentation: false, - customStyle: '', // Custom CSS styles applied to the image. - }; - - const getImageHeight = (image) => { - if (!isPercentageValue(String(image.height))) { - return parseInt(image.height, 10); - } - - return image.height; - }; - - const getImageWidth = (image) => { - if (!isPercentageValue(String(image.width))) { - return parseInt(image.width, 10); - } - - return image.width; - }; - - // Get the current selection. - this.selectedImage = image; - - properties.customStyle = image.style.cssText; - - const width = getImageWidth(image); - if (width !== 0) { - properties.width = width; - } - - const height = getImageHeight(image); - if (height !== 0) { - properties.height = height; - } - - properties.src = image.getAttribute('src'); - properties.alt = image.getAttribute('alt') || ''; - properties.presentation = (image.getAttribute('role') === 'presentation'); - - return properties; - } - - getSelectedImage() { - const imgElm = this.editor.selection.getNode(); - const figureElm = this.editor.dom.getParent(imgElm, 'figure.image'); - if (figureElm) { - return this.editor.dom.select('img', figureElm)[0]; - } - - if (imgElm && (imgElm.nodeName.toUpperCase() !== 'IMG' || this.isPlaceholderImage(imgElm))) { - return null; - } - return imgElm; - } - - isPlaceholderImage(imgElm) { - if (imgElm.nodeName.toUpperCase() !== 'IMG') { - return false; - } - - return (imgElm.hasAttribute('data-mce-object') || imgElm.hasAttribute('data-mce-placeholder')); - } - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - startImageLoading() { - showElements(Selectors.IMAGE.elements.loaderIcon, this.root); - hideElements(Selectors.IMAGE.elements.insertImage, this.root); - } - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - stopImageLoading() { - hideElements(Selectors.IMAGE.elements.loaderIcon, this.root); - showElements(Selectors.IMAGE.elements.insertImage, this.root); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js deleted file mode 100755 index adf35793..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagedetails.js +++ /dev/null @@ -1,614 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny media plugin image details class for Moodle. - * - * @module tiny_mediacms/imagedetails - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Config from 'core/config'; -import ModalEvents from 'core/modal_events'; -import Notification from 'core/notification'; -import Pending from 'core/pending'; -import Selectors from './selectors'; -import Templates from 'core/templates'; -import {getString} from 'core/str'; -import {ImageInsert} from 'tiny_mediacms/imageinsert'; -import { - bodyImageInsert, - footerImageInsert, - showElements, - hideElements, - isPercentageValue, -} from 'tiny_mediacms/imagehelpers'; - -export class ImageDetails { - DEFAULTS = { - WIDTH: 160, - HEIGHT: 160, - }; - - rawImageDimensions = null; - - constructor( - root, - editor, - currentModal, - canShowFilePicker, - canShowDropZone, - currentUrl, - image, - ) { - this.root = root; - this.editor = editor; - this.currentModal = currentModal; - this.canShowFilePicker = canShowFilePicker; - this.canShowDropZone = canShowDropZone; - this.currentUrl = currentUrl; - this.image = image; - } - - init = function() { - this.currentModal.setTitle(getString('imagedetails', 'tiny_mediacms')); - this.imageTypeChecked(); - this.presentationChanged(); - this.storeImageDimensions(this.image); - this.setImageDimensions(); - this.registerEventListeners(); - }; - - /** - * Loads and displays a preview image based on the provided URL, and handles image loading events. - */ - loadInsertImage = async function() { - const templateContext = { - elementid: this.editor.id, - showfilepicker: this.canShowFilePicker, - showdropzone: this.canShowDropZone, - }; - - Promise.all([bodyImageInsert(templateContext, this.root), footerImageInsert(templateContext, this.root)]) - .then(() => { - const imageinsert = new ImageInsert( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - ); - imageinsert.init(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }; - - storeImageDimensions(image) { - // Store dimensions of the raw image, falling back to defaults for images without dimensions (e.g. SVG). - this.rawImageDimensions = { - width: image.width || this.DEFAULTS.WIDTH, - height: image.height || this.DEFAULTS.HEIGHT, - }; - - const getCurrentWidth = (element) => { - if (element.value === '') { - element.value = this.rawImageDimensions.width; - } - return element.value; - }; - - const getCurrentHeight = (element) => { - if (element.value === '') { - element.value = this.rawImageDimensions.height; - } - return element.value; - }; - - const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width); - const currentWidth = getCurrentWidth(widthInput); - - const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height); - const currentHeight = getCurrentHeight(heightInput); - - const preview = this.root.querySelector(Selectors.IMAGE.elements.preview); - preview.setAttribute('src', image.src); - preview.style.display = ''; - - // Ensure the checkbox always in unchecked status when an image loads at first. - const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain); - if (isPercentageValue(currentWidth) && isPercentageValue(currentHeight)) { - constrain.checked = currentWidth === currentHeight; - } else if (image.width === 0 || image.height === 0) { - // If we don't have both dimensions of the image, we can't auto-size it, so disable control. - constrain.disabled = 'disabled'; - } else { - // This is the same as comparing to 3 decimal places. - const widthRatio = Math.round(100 * parseInt(currentWidth, 10) / image.width); - const heightRatio = Math.round(100 * parseInt(currentHeight, 10) / image.height); - constrain.checked = widthRatio === heightRatio; - } - - /** - * Sets the selected size option based on current width and height values. - * - * @param {number} currentWidth - The current width value. - * @param {number} currentHeight - The current height value. - */ - const setSelectedSize = (currentWidth, currentHeight) => { - if (this.rawImageDimensions.width === currentWidth && - this.rawImageDimensions.height === currentHeight - ) { - this.currentWidth = this.rawImageDimensions.width; - this.currentHeight = this.rawImageDimensions.height; - this.sizeChecked('original'); - } else { - this.currentWidth = currentWidth; - this.currentHeight = currentHeight; - this.sizeChecked('custom'); - } - }; - - setSelectedSize(Number(currentWidth), Number(currentHeight)); - } - - /** - * Handles the selection of image size options and updates the form inputs accordingly. - * - * @param {string} option - The selected image size option ("original" or "custom"). - */ - sizeChecked(option) { - const widthInput = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightInput = this.root.querySelector(Selectors.IMAGE.elements.height); - if (option === "original") { - this.sizeOriginalChecked(); - widthInput.value = this.rawImageDimensions.width; - heightInput.value = this.rawImageDimensions.height; - } else if (option === "custom") { - this.sizeCustomChecked(); - widthInput.value = this.currentWidth; - heightInput.value = this.currentHeight; - - // If the current size is equal to the original size, then check the Keep proportion checkbox. - if (this.currentWidth === this.rawImageDimensions.width && this.currentHeight === this.rawImageDimensions.height) { - const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain); - constrainField.checked = true; - } - } - this.autoAdjustSize(); - } - - autoAdjustSize(forceHeight = false) { - // If we do not know the image size, do not do anything. - if (!this.rawImageDimensions) { - return; - } - - const widthField = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightField = this.root.querySelector(Selectors.IMAGE.elements.height); - - const normalizeFieldData = (fieldData) => { - fieldData.isPercentageValue = !!isPercentageValue(fieldData.field.value); - if (fieldData.isPercentageValue) { - fieldData.percentValue = parseInt(fieldData.field.value, 10); - fieldData.pixelSize = this.rawImageDimensions[fieldData.type] / 100 * fieldData.percentValue; - } else { - fieldData.pixelSize = parseInt(fieldData.field.value, 10); - fieldData.percentValue = fieldData.pixelSize / this.rawImageDimensions[fieldData.type] * 100; - } - - return fieldData; - }; - - const getKeyField = () => { - const getValue = () => { - if (forceHeight) { - return { - field: heightField, - type: 'height', - }; - } else { - return { - field: widthField, - type: 'width', - }; - } - }; - - const currentValue = getValue(); - if (currentValue.field.value === '') { - currentValue.field.value = this.rawImageDimensions[currentValue.type]; - } - - return normalizeFieldData(currentValue); - }; - - const getRelativeField = () => { - if (forceHeight) { - return normalizeFieldData({ - field: widthField, - type: 'width', - }); - } else { - return normalizeFieldData({ - field: heightField, - type: 'height', - }); - } - }; - - // Now update with the new values. - const constrainField = this.root.querySelector(Selectors.IMAGE.elements.constrain); - if (constrainField.checked) { - const keyField = getKeyField(); - const relativeField = getRelativeField(); - // We are keeping the image in proportion. - // Calculate the size for the relative field. - if (keyField.isPercentageValue) { - // In proportion, so the percentages are the same. - relativeField.field.value = keyField.field.value; - relativeField.percentValue = keyField.percentValue; - } else { - relativeField.pixelSize = Math.round( - keyField.pixelSize / this.rawImageDimensions[keyField.type] * this.rawImageDimensions[relativeField.type] - ); - relativeField.field.value = relativeField.pixelSize; - } - } - - // Store the custom width and height to reuse. - this.currentWidth = Number(widthField.value) !== this.rawImageDimensions.width ? widthField.value : this.currentWidth; - this.currentHeight = Number(heightField.value) !== this.rawImageDimensions.height ? heightField.value : this.currentHeight; - } - - /** - * Sets the dimensions of the image preview element based on user input and constraints. - */ - setImageDimensions = () => { - const imagePreviewBox = this.root.querySelector(Selectors.IMAGE.elements.previewBox); - const image = this.root.querySelector(Selectors.IMAGE.elements.preview); - const widthField = this.root.querySelector(Selectors.IMAGE.elements.width); - const heightField = this.root.querySelector(Selectors.IMAGE.elements.height); - - const updateImageDimensions = () => { - // Get the latest dimensions of the preview box for responsiveness. - const boxWidth = imagePreviewBox.clientWidth; - const boxHeight = imagePreviewBox.clientHeight; - // Get the new width and height for the image. - const dimensions = this.fitSquareIntoBox(widthField.value, heightField.value, boxWidth, boxHeight); - image.style.width = `${dimensions.width}px`; - image.style.height = `${dimensions.height}px`; - }; - // If the client size is zero, then get the new dimensions once the modal is shown. - if (imagePreviewBox.clientWidth === 0) { - // Call the shown event. - this.currentModal.getRoot().on(ModalEvents.shown, () => { - updateImageDimensions(); - }); - } else { - updateImageDimensions(); - } - }; - - /** - * Handles the selection of the "Original Size" option and updates the form elements accordingly. - */ - sizeOriginalChecked() { - this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = true; - this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = false; - hideElements(Selectors.IMAGE.elements.properties, this.root); - } - - /** - * Handles the selection of the "Custom Size" option and updates the form elements accordingly. - */ - sizeCustomChecked() { - this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked = false; - this.root.querySelector(Selectors.IMAGE.elements.sizeCustom).checked = true; - showElements(Selectors.IMAGE.elements.properties, this.root); - } - - /** - * Handles changes in the image presentation checkbox and enables/disables the image alt text input accordingly. - */ - presentationChanged() { - const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation); - const alt = this.root.querySelector(Selectors.IMAGE.elements.alt); - alt.disabled = presentation.checked; - - // Counting the image description characters. - this.handleKeyupCharacterCount(); - } - - /** - * This function checks whether an image URL is local (within the same website's domain) or external (from an external source). - * Depending on the result, it dynamically updates the visibility and content of HTML elements in a user interface. - * If the image is local then we only show it's filename. - * If the image is external then it will show full URL and it can be updated. - */ - imageTypeChecked() { - const regex = new RegExp(`${Config.wwwroot}`); - - // True if the URL is from external, otherwise false. - const isExternalUrl = regex.test(this.currentUrl) === false; - - // Hide the URL input. - hideElements(Selectors.IMAGE.elements.url, this.root); - - if (!isExternalUrl) { - // Split the URL by '/' to get an array of segments. - const segments = this.currentUrl.split('/'); - // Get the last segment, which should be the filename. - const filename = segments.pop().split('?')[0]; - // Show the file name. - this.setFilenameLabel(decodeURI(filename)); - } else { - - this.setFilenameLabel(decodeURI(this.currentUrl)); - } - } - - /** - * Set the string for the URL label element. - * - * @param {string} label - The label text to set. - */ - setFilenameLabel(label) { - const urlLabelEle = this.root.querySelector(Selectors.IMAGE.elements.fileNameLabel); - if (urlLabelEle) { - urlLabelEle.innerHTML = label; - urlLabelEle.setAttribute("title", label); - } - } - - toggleAriaInvalid(selectors, predicate) { - selectors.forEach((selector) => { - const elements = this.root.querySelectorAll(selector); - elements.forEach((element) => element.setAttribute('aria-invalid', predicate)); - }); - } - - hasErrorUrlField() { - const urlError = this.currentUrl === ''; - if (urlError) { - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - } else { - hideElements(Selectors.IMAGE.elements.urlWarning, this.root); - } - this.toggleAriaInvalid([Selectors.IMAGE.elements.url], urlError); - - return urlError; - } - - hasErrorAltField() { - const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value; - const presentation = this.root.querySelector(Selectors.IMAGE.elements.presentation).checked; - const imageAltError = alt === '' && !presentation; - if (imageAltError) { - showElements(Selectors.IMAGE.elements.altWarning, this.root); - } else { - hideElements(Selectors.IMAGE.elements.urlWaaltWarningrning, this.root); - } - this.toggleAriaInvalid([Selectors.IMAGE.elements.alt, Selectors.IMAGE.elements.presentation], imageAltError); - - return imageAltError; - } - - updateWarning() { - const urlError = this.hasErrorUrlField(); - const imageAltError = this.hasErrorAltField(); - - return urlError || imageAltError; - } - - getImageContext() { - // Check if there are any accessibility issues. - if (this.updateWarning()) { - return null; - } - - const classList = []; - const constrain = this.root.querySelector(Selectors.IMAGE.elements.constrain).checked; - const sizeOriginal = this.root.querySelector(Selectors.IMAGE.elements.sizeOriginal).checked; - if (constrain || sizeOriginal) { - // If the Auto size checkbox is checked or the Original size is checked, then apply the responsive class. - classList.push(Selectors.IMAGE.styles.responsive); - } else { - // Otherwise, remove it. - classList.pop(Selectors.IMAGE.styles.responsive); - } - - return { - url: this.currentUrl, - alt: this.root.querySelector(Selectors.IMAGE.elements.alt).value, - width: this.root.querySelector(Selectors.IMAGE.elements.width).value, - height: this.root.querySelector(Selectors.IMAGE.elements.height).value, - presentation: this.root.querySelector(Selectors.IMAGE.elements.presentation).checked, - customStyle: this.root.querySelector(Selectors.IMAGE.elements.customStyle).value, - classlist: classList.join(' '), - }; - } - - setImage() { - const pendingPromise = new Pending('tiny_mediacms:setImage'); - const url = this.currentUrl; - if (url === '') { - return; - } - - // Check if there are any accessibility issues. - if (this.updateWarning()) { - pendingPromise.resolve(); - return; - } - - // Check for invalid width or height. - const width = this.root.querySelector(Selectors.IMAGE.elements.width).value; - if (!isPercentageValue(width) && isNaN(parseInt(width, 10))) { - this.root.querySelector(Selectors.IMAGE.elements.width).focus(); - pendingPromise.resolve(); - return; - } - - const height = this.root.querySelector(Selectors.IMAGE.elements.height).value; - if (!isPercentageValue(height) && isNaN(parseInt(height, 10))) { - this.root.querySelector(Selectors.IMAGE.elements.height).focus(); - pendingPromise.resolve(); - return; - } - - Templates.render('tiny_mediacms/image', this.getImageContext()) - .then((html) => { - this.editor.insertContent(html); - this.currentModal.destroy(); - pendingPromise.resolve(); - - return html; - }) - .catch(error => { - window.console.log(error); - }); - } - - /** - * Deletes the image after confirming with the user and loads the insert image page. - */ - deleteImage() { - Notification.deleteCancelPromise( - getString('deleteimage', 'tiny_mediacms'), - getString('deleteimagewarning', 'tiny_mediacms'), - ).then(() => { - hideElements(Selectors.IMAGE.elements.altWarning, this.root); - // Removing the image in the preview will bring the user to the insert page. - this.loadInsertImage(); - return; - }).catch(error => { - window.console.log(error); - }); - } - - registerEventListeners() { - const submitAction = this.root.querySelector(Selectors.IMAGE.actions.submit); - submitAction.addEventListener('click', (e) => { - e.preventDefault(); - this.setImage(); - }); - - const deleteImageEle = this.root.querySelector(Selectors.IMAGE.actions.deleteImage); - deleteImageEle.addEventListener('click', () => { - this.deleteImage(); - }); - deleteImageEle.addEventListener("keydown", (e) => { - if (e.key === "Enter") { - this.deleteImage(); - } - }); - - this.root.addEventListener('change', (e) => { - const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation); - if (presentationEle) { - this.presentationChanged(); - } - - const constrainEle = e.target.closest(Selectors.IMAGE.elements.constrain); - if (constrainEle) { - this.autoAdjustSize(); - } - - const sizeOriginalEle = e.target.closest(Selectors.IMAGE.elements.sizeOriginal); - if (sizeOriginalEle) { - this.sizeChecked('original'); - } - - const sizeCustomEle = e.target.closest(Selectors.IMAGE.elements.sizeCustom); - if (sizeCustomEle) { - this.sizeChecked('custom'); - } - }); - - this.root.addEventListener('blur', (e) => { - if (e.target.nodeType === Node.ELEMENT_NODE) { - - const presentationEle = e.target.closest(Selectors.IMAGE.elements.presentation); - if (presentationEle) { - this.presentationChanged(); - } - } - }, true); - - // Character count. - this.root.addEventListener('keyup', (e) => { - const altEle = e.target.closest(Selectors.IMAGE.elements.alt); - if (altEle) { - this.handleKeyupCharacterCount(); - } - }); - - this.root.addEventListener('input', (e) => { - const widthEle = e.target.closest(Selectors.IMAGE.elements.width); - if (widthEle) { - // Avoid empty value. - widthEle.value = widthEle.value === "" ? 0 : Number(widthEle.value); - this.autoAdjustSize(); - } - - const heightEle = e.target.closest(Selectors.IMAGE.elements.height); - if (heightEle) { - // Avoid empty value. - heightEle.value = heightEle.value === "" ? 0 : Number(heightEle.value); - this.autoAdjustSize(true); - } - }); - } - - handleKeyupCharacterCount() { - const alt = this.root.querySelector(Selectors.IMAGE.elements.alt).value; - const current = this.root.querySelector('#currentcount'); - current.innerHTML = alt.length; - } - - /** - * Calculates the dimensions to fit a square into a specified box while maintaining aspect ratio. - * - * @param {number} squareWidth - The width of the square. - * @param {number} squareHeight - The height of the square. - * @param {number} boxWidth - The width of the box. - * @param {number} boxHeight - The height of the box. - * @returns {Object} An object with the new width and height of the square to fit in the box. - */ - fitSquareIntoBox = (squareWidth, squareHeight, boxWidth, boxHeight) => { - if (squareWidth < boxWidth && squareHeight < boxHeight) { - // If the square is smaller than the box, keep its dimensions. - return { - width: squareWidth, - height: squareHeight, - }; - } - // Calculate the scaling factor based on the minimum scaling required to fit in the box. - const widthScaleFactor = boxWidth / squareWidth; - const heightScaleFactor = boxHeight / squareHeight; - const minScaleFactor = Math.min(widthScaleFactor, heightScaleFactor); - // Scale the square's dimensions based on the aspect ratio and the minimum scaling factor. - const newWidth = squareWidth * minScaleFactor; - const newHeight = squareHeight * minScaleFactor; - return { - width: newWidth, - height: newHeight, - }; - }; -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js deleted file mode 100755 index a59367b9..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagehelpers.js +++ /dev/null @@ -1,149 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny media plugin image helpers. - * - * @module tiny_mediacms/imagehelpers - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; - -/** - * Renders and inserts the body template for inserting an image into the modal. - * - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const bodyImageInsert = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Renders and inserts the footer template for inserting an image into the modal. - * - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const footerImageInsert = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_insert_footer', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Renders and inserts the body template for displaying image details in the modal. - * - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const bodyImageDetails = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_body_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Renders and inserts the footer template for displaying image details in the modal. - * @param {object} templateContext - The context for rendering the template. - * @param {HTMLElement} root - The root element where the template will be inserted. - * @returns {Promise} - */ -export const footerImageDetails = async(templateContext, root) => { - return Templates.renderForPromise('tiny_mediacms/insert_image_modal_details_footer', {...templateContext}) - .then(({html, js}) => { - Templates.replaceNodeContents(root.querySelector('.tiny_imagecms_footer_template'), html, js); - return; - }) - .catch(error => { - window.console.log(error); - }); -}; - -/** - * Show the element(s). - * - * @param {string|string[]} elements - The CSS selector for the elements to toggle. - * @param {object} root - The CSS selector for the elements to toggle. - */ -export const showElements = (elements, root) => { - if (elements instanceof Array) { - elements.forEach((elementSelector) => { - const element = root.querySelector(elementSelector); - if (element) { - element.classList.remove('d-none'); - } - }); - } else { - const element = root.querySelector(elements); - if (element) { - element.classList.remove('d-none'); - } - } -}; - -/** - * Hide the element(s). - * - * @param {string|string[]} elements - The CSS selector for the elements to toggle. - * @param {object} root - The CSS selector for the elements to toggle. - */ -export const hideElements = (elements, root) => { - if (elements instanceof Array) { - elements.forEach((elementSelector) => { - const element = root.querySelector(elementSelector); - if (element) { - element.classList.add('d-none'); - } - }); - } else { - const element = root.querySelector(elements); - if (element) { - element.classList.add('d-none'); - } - } -}; - -/** - * Checks if the given value is a percentage value. - * - * @param {string} value - The value to check. - * @returns {boolean} True if the value is a percentage value, false otherwise. - */ -export const isPercentageValue = (value) => { - return value.match(/\d+%/); -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js deleted file mode 100755 index 5941bc0e..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imageinsert.js +++ /dev/null @@ -1,282 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny media plugin image insertion class for Moodle. - * - * @module tiny_mediacms/imageinsert - * @copyright 2024 Meirza - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Selectors from './selectors'; -import Dropzone from 'core/dropzone'; -import uploadFile from 'editor_tiny/uploader'; -import {prefetchStrings} from 'core/prefetch'; -import {getStrings} from 'core/str'; -import {component} from "./common"; -import {getFilePicker} from 'editor_tiny/options'; -import {displayFilepicker} from 'editor_tiny/utils'; -import {ImageDetails} from 'tiny_mediacms/imagedetails'; -import { - showElements, - hideElements, - bodyImageDetails, - footerImageDetails, -} from 'tiny_mediacms/imagehelpers'; - -prefetchStrings('tiny_mediacms', [ - 'insertimage', - 'enterurl', - 'enterurlor', - 'imageurlrequired', - 'uploading', - 'loading', - 'addfilesdrop', - 'sizecustom_help', -]); - -export class ImageInsert { - - constructor( - root, - editor, - currentModal, - canShowFilePicker, - canShowDropZone, - ) { - this.root = root; - this.editor = editor; - this.currentModal = currentModal; - this.canShowFilePicker = canShowFilePicker; - this.canShowDropZone = canShowDropZone; - } - - init = async function() { - // Get the localization lang strings and turn them into object. - const langStringKeys = [ - 'insertimage', - 'enterurl', - 'enterurlor', - 'imageurlrequired', - 'uploading', - 'loading', - 'addfilesdrop', - 'sizecustom_help', - ]; - const langStringvalues = await getStrings([...langStringKeys].map((key) => ({key, component}))); - - // Convert array to object. - this.langStrings = Object.fromEntries(langStringKeys.map((key, index) => [key, langStringvalues[index]])); - this.currentModal.setTitle(this.langStrings.insertimage); - if (this.canShowDropZone) { - const dropZoneEle = document.querySelector(Selectors.IMAGE.elements.dropzoneContainer); - - // Accepted types can be either a string or an array. - let acceptedTypes = getFilePicker(this.editor, 'image').accepted_types; - if (Array.isArray(acceptedTypes)) { - acceptedTypes = acceptedTypes.join(','); - } - - const dropZone = new Dropzone( - dropZoneEle, - acceptedTypes, - files => { - this.handleUploadedFile(files); - } - ); - dropZone.setLabel(this.langStrings.addfilesdrop); - dropZone.init(); - } - await this.registerEventListeners(); - }; - - /** - * Enables or disables the URL-related buttons in the footer based on the current URL and input value. - */ - toggleUrlButton() { - const urlInput = this.root.querySelector(Selectors.IMAGE.elements.url); - const url = urlInput.value; - const addUrl = this.root.querySelector(Selectors.IMAGE.actions.addUrl); - addUrl.disabled = !(url !== "" && this.isValidUrl(url)); - } - - /** - * Check if given string is a valid URL. - * - * @param {String} urlString URL the link will point to. - * @returns {boolean} True is valid, otherwise false. - */ - isValidUrl = urlString => { - const urlPattern = new RegExp('^(https?:\\/\\/)?' + // Protocol. - '((([a-z\\d]([a-z\\d-]*[a-z\\d])*)\\.)+[a-z]{2,}|' + // Domain name. - '((\\d{1,3}\\.){3}\\d{1,3})|localhost)' + // OR ip (v4) address, localhost. - '(\\:\\d+)?(\\/[-a-z\\d%_.~+]*)*'); // Port and path. - return !!urlPattern.test(urlString); - }; - - /** - * Handles changes in the image URL input field and loads a preview of the image if the URL has changed. - */ - urlChanged() { - hideElements(Selectors.IMAGE.elements.urlWarning, this.root); - const input = this.root.querySelector(Selectors.IMAGE.elements.url); - if (input.value && input.value !== this.currentUrl) { - this.loadPreviewImage(input.value); - } - } - - /** - * Loads and displays a preview image based on the provided URL, and handles image loading events. - * - * @param {string} url - The URL of the image to load and display. - */ - loadPreviewImage = function(url) { - this.startImageLoading(); - this.currentUrl = url; - const image = new Image(); - image.src = url; - image.addEventListener('error', () => { - const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); - urlWarningLabelEle.innerHTML = this.langStrings.imageurlrequired; - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - this.currentUrl = ""; - this.stopImageLoading(); - }); - - image.addEventListener('load', () => { - let templateContext = {}; - templateContext.sizecustomhelpicon = {text: this.langStrings.sizecustom_help}; - Promise.all([bodyImageDetails(templateContext, this.root), footerImageDetails(templateContext, this.root)]) - .then(() => { - const imagedetails = new ImageDetails( - this.root, - this.editor, - this.currentModal, - this.canShowFilePicker, - this.canShowDropZone, - this.currentUrl, - image, - ); - imagedetails.init(); - return; - }).then(() => { - this.stopImageLoading(); - return; - }) - .catch(error => { - window.console.log(error); - }); - }); - }; - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - startImageLoading() { - showElements(Selectors.IMAGE.elements.loaderIcon, this.root); - const elementsToHide = [ - Selectors.IMAGE.elements.insertImage, - Selectors.IMAGE.elements.urlWarning, - Selectors.IMAGE.elements.modalFooter, - ]; - hideElements(elementsToHide, this.root); - } - - /** - * Displays the upload loader and disables UI elements while loading a file. - */ - stopImageLoading() { - hideElements(Selectors.IMAGE.elements.loaderIcon, this.root); - const elementsToShow = [ - Selectors.IMAGE.elements.insertImage, - Selectors.IMAGE.elements.modalFooter, - ]; - showElements(elementsToShow, this.root); - } - - filePickerCallback(params) { - if (params.url) { - this.loadPreviewImage(params.url); - } - } - - /** - * Updates the content of the loader icon. - * - * @param {HTMLElement} root - The root element containing the loader icon. - * @param {object} langStrings - An object containing language strings. - * @param {number|null} progress - The progress percentage (optional). - * @returns {void} - */ - updateLoaderIcon = (root, langStrings, progress = null) => { - const loaderIcon = root.querySelector(Selectors.IMAGE.elements.loaderIconContainer + ' div'); - loaderIcon.innerHTML = progress !== null ? `${langStrings.uploading} ${Math.round(progress)}%` : langStrings.loading; - }; - - /** - * Handles the uploaded file, initiates the upload process, and updates the UI during the upload. - * - * @param {FileList} files - The list of files to upload (usually from a file input field). - * @returns {Promise} A promise that resolves when the file is uploaded and processed. - */ - handleUploadedFile = async(files) => { - try { - this.startImageLoading(); - const fileURL = await uploadFile(this.editor, 'image', files[0], files[0].name, (progress) => { - this.updateLoaderIcon(this.root, this.langStrings, progress); - }); - // Set the loader icon content to "loading" after the file upload completes. - this.updateLoaderIcon(this.root, this.langStrings); - this.filePickerCallback({url: fileURL}); - } catch (error) { - // Handle the error. - const urlWarningLabelEle = this.root.querySelector(Selectors.IMAGE.elements.urlWarning); - urlWarningLabelEle.innerHTML = error.error !== undefined ? error.error : error; - showElements(Selectors.IMAGE.elements.urlWarning, this.root); - this.stopImageLoading(); - } - }; - - registerEventListeners() { - this.root.addEventListener('click', async(e) => { - const addUrlEle = e.target.closest(Selectors.IMAGE.actions.addUrl); - if (addUrlEle) { - this.urlChanged(); - } - - const imageBrowserAction = e.target.closest(Selectors.IMAGE.actions.imageBrowser); - if (imageBrowserAction && this.canShowFilePicker) { - e.preventDefault(); - const params = await displayFilepicker(this.editor, 'image'); - this.filePickerCallback(params); - } - }); - - this.root.addEventListener('input', (e) => { - const urlEle = e.target.closest(Selectors.IMAGE.elements.url); - if (urlEle) { - this.toggleUrlButton(); - } - }); - - const fileInput = this.root.querySelector(Selectors.IMAGE.elements.fileInput); - if (fileInput) { - fileInput.addEventListener('change', () => { - this.handleUploadedFile(fileInput.files); - }); - } - } -} \ No newline at end of file diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js deleted file mode 100755 index 0f38b6d8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/imagemodal.js +++ /dev/null @@ -1,49 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Image Modal for Tiny. - * - * @module tiny_mediacms/imagemodal - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Modal from 'core/modal'; -import {component} from './common'; - -export default class ImageModal extends Modal { - static TYPE = `${component}/imagemodal`; - static TEMPLATE = `${component}/insert_image_modal`; - - registerEventListeners() { - // Call the parent registration. - super.registerEventListeners(); - - // Register to close on save/cancel. - this.registerCloseOnSave(); - this.registerCloseOnCancel(); - } - - configure(modalConfig) { - modalConfig.large = true; - modalConfig.removeOnClose = true; - modalConfig.show = true; - - super.configure(modalConfig); - } -} - -ImageModal.registerModalType(); diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js deleted file mode 100755 index 0f4c5f4f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/manager.js +++ /dev/null @@ -1,86 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media Manager plugin class for Moodle. - * - * @module tiny_mediacms/manager - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import Templates from 'core/templates'; -import {getString} from 'core/str'; -import Modal from 'core/modal'; -import * as ModalEvents from 'core/modal_events'; -import {getData} from './options'; -import Config from 'core/config'; - -export default class MediaManager { - - editor = null; - area = null; - - constructor(editor) { - this.editor = editor; - const data = getData(editor); - this.area = data.params.area; - this.area.itemid = data.fpoptions.image.itemid; - } - - async displayDialogue() { - const modal = await Modal.create({ - large: true, - title: getString('mediamanagerproperties', 'tiny_mediacms'), - body: Templates.render('tiny_mediacms/mm2_iframe', { - src: this.getIframeURL() - }), - removeOnClose: true, - show: true, - }); - modal.getRoot().on(ModalEvents.bodyRendered, () => { - this.selectFirstElement(); - }); - - document.querySelector('.modal-lg').style.cssText = `max-width: 850px`; - return modal; - } - - // It will select the first element in the file manager. - selectFirstElement() { - const iframe = document.getElementById('mm2-iframe'); - iframe.addEventListener('load', function() { - let intervalId = setInterval(function() { - const iDocument = iframe.contentWindow.document; - if (iDocument.querySelector('.filemanager')) { - const firstFocusableElement = iDocument.querySelector('.fp-navbar a:not([disabled])'); - if (firstFocusableElement) { - firstFocusableElement.focus(); - } - clearInterval(intervalId); - } - }, 200); - }); - } - - getIframeURL() { - const url = new URL(`${Config.wwwroot}/lib/editor/tiny/plugins/mediacms/manage.php`); - url.searchParams.append('elementid', this.editor.getElement().id); - for (const key in this.area) { - url.searchParams.append(key, this.area[key]); - } - return url.toString(); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js deleted file mode 100755 index 7c5446f7..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/options.js +++ /dev/null @@ -1,117 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Options helper for Tiny Media plugin. - * - * @module tiny_mediacms/options - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import {getPluginOptionName} from 'editor_tiny/options'; -import {pluginName} from './common'; - -const dataName = getPluginOptionName(pluginName, 'data'); -const permissionsName = getPluginOptionName(pluginName, 'permissions'); -const ltiName = getPluginOptionName(pluginName, 'lti'); - -/** - * Register the options for the Tiny Media plugin. - * - * @param {TinyMCE} editor - */ -export const register = (editor) => { - const registerOption = editor.options.register; - - registerOption(permissionsName, { - processor: 'object', - "default": { - image: { - filepicker: false, - } - }, - }); - - registerOption(dataName, { - processor: 'object', - "default": { - // MediaCMS video library configuration - mediacmsApiUrl: '', // e.g., 'https://deic.mediacms.io/api/v1/media' - mediacmsBaseUrl: '', // e.g., 'https://deic.mediacms.io' - mediacmsPageSize: 12, - // Auto-conversion settings - autoConvertEnabled: true, // Enable/disable auto-conversion of pasted MediaCMS URLs - autoConvertBaseUrl: '', // Base URL to restrict auto-conversion (empty = allow all MediaCMS domains) - autoConvertOptions: { - // Default embed options for auto-converted videos - showTitle: true, - linkTitle: true, - showRelated: true, - showUserAvatar: true, - }, - }, - }); - - registerOption(ltiName, { - processor: 'object', - "default": { - // LTI configuration for MediaCMS iframe library - toolId: 0, // LTI external tool ID - courseId: 0, // Current course ID - contentItemUrl: '', // URL to /mod/lti/contentitem.php for Deep Linking - }, - }); -}; - -/** - * Get the permissions configuration for the Tiny Media plugin. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getPermissions = (editor) => editor.options.get(permissionsName); - -/** - * Get the permissions configuration for the Tiny Media plugin. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getImagePermissions = (editor) => getPermissions(editor).image; - -/** - * Get the permissions configuration for the Tiny Media plugin. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getEmbedPermissions = (editor) => getPermissions(editor).embed; - -/** - * Get the data configuration for the Media Manager. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getData = (editor) => editor.options.get(dataName); - -/** - * Get the LTI configuration for the MediaCMS iframe library. - * - * @param {TinyMCE} editor - * @returns {object} - */ -export const getLti = (editor) => editor.options.get(ltiName); diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js deleted file mode 100755 index ab668f8b..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/plugin.js +++ /dev/null @@ -1,92 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin for Moodle. - * - * @module tiny_mediacms/plugin - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -import {getTinyMCE} from 'editor_tiny/loader'; -import {getPluginMetadata} from 'editor_tiny/utils'; - -import {component, pluginName} from './common'; -import * as Commands from './commands'; -import * as Configuration from './configuration'; -import * as Options from './options'; -import {setupAutoConvert} from './autoconvert'; - -// eslint-disable-next-line no-async-promise-executor -export default new Promise(async(resolve) => { - const [ - tinyMCE, - setupCommands, - pluginMetadata, - ] = await Promise.all([ - getTinyMCE(), - Commands.getSetup(), - getPluginMetadata(component, pluginName), - ]); - - tinyMCE.PluginManager.add(`${component}/plugin`, (editor) => { - // Register options. - Options.register(editor); - - // Setup the Commands (buttons, menu items, and so on). - setupCommands(editor); - - // Setup auto-conversion of pasted MediaCMS URLs. - setupAutoConvert(editor); - - // Clean up editor-only elements before content is saved. - // Remove wrapper divs and edit buttons that are only for the editor UI. - editor.on('GetContent', (e) => { - if (e.format === 'html') { - // Create a temporary container to manipulate the HTML - const tempDiv = document.createElement('div'); - tempDiv.innerHTML = e.content; - - // Remove edit buttons - tempDiv.querySelectorAll('.tiny-mediacms-edit-btn').forEach(btn => btn.remove()); - - // Unwrap iframes from tiny-mediacms-iframe-wrapper - tempDiv.querySelectorAll('.tiny-mediacms-iframe-wrapper').forEach(wrapper => { - const iframe = wrapper.querySelector('iframe'); - if (iframe) { - wrapper.parentNode.insertBefore(iframe, wrapper); - } - wrapper.remove(); - }); - - // Unwrap iframes from tiny-iframe-responsive - tempDiv.querySelectorAll('.tiny-iframe-responsive').forEach(wrapper => { - const iframe = wrapper.querySelector('iframe'); - if (iframe) { - wrapper.parentNode.insertBefore(iframe, wrapper); - } - wrapper.remove(); - }); - - e.content = tempDiv.innerHTML; - } - }); - - return pluginMetadata; - }); - - // Resolve the Media Plugin and include configuration. - resolve([`${component}/plugin`, Configuration]); -}); diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js deleted file mode 100755 index e5149c16..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/selectors.js +++ /dev/null @@ -1,162 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media plugin helper function to build queryable data selectors. - * - * @module tiny_mediacms/selectors - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -export default { - IMAGE: { - actions: { - submit: '.tiny_imagecms_urlentrysubmit', - imageBrowser: '.openimagecmsbrowser', - addUrl: '.tiny_imagecms_addurl', - deleteImage: '.tiny_imagecms_deleteicon', - }, - elements: { - form: 'form.tiny_imagecms_form', - alignSettings: '.tiny_imagecms_button', - alt: '.tiny_imagecms_altentry', - altWarning: '.tiny_imagecms_altwarning', - height: '.tiny_imagecms_heightentry', - width: '.tiny_imagecms_widthentry', - url: '.tiny_imagecms_urlentry', - urlWarning: '.tiny_imagecms_urlwarning', - size: '.tiny_imagecms_size', - presentation: '.tiny_imagecms_presentation', - constrain: '.tiny_imagecms_constrain', - customStyle: '.tiny_imagecms_customstyle', - preview: '.tiny_imagecms_preview', - previewBox: '.tiny_imagecms_preview_box', - loaderIcon: '.tiny_imagecms_loader', - loaderIconContainer: '.tiny_imagecms_loader_container', - insertImage: '.tiny_imagecms_insert_image', - modalFooter: '.modal-footer', - dropzoneContainer: '.tiny_imagecms_dropzone_container', - fileInput: '#tiny_imagecms_fileinput', - fileNameLabel: '.tiny_imagecms_filename', - sizeOriginal: '.tiny_imagecms_sizeoriginal', - sizeCustom: '.tiny_imagecms_sizecustom', - properties: '.tiny_imagecms_properties', - }, - styles: { - responsive: 'img-fluid', - }, - }, - EMBED: { - actions: { - submit: '.tiny_mediacms_submit', - mediaBrowser: '.openmediacmsbrowser', - }, - elements: { - form: 'form.tiny_mediacms_form', - source: '.tiny_mediacms_source', - track: '.tiny_mediacms_track', - mediaSource: '.tiny_mediacms_media_source', - linkSource: '.tiny_mediacms_link_source', - linkSize: '.tiny_mediacms_link_size', - posterSource: '.tiny_mediacms_poster_source', - posterSize: '.tiny_mediacms_poster_size', - displayOptions: '.tiny_mediacms_display_options', - name: '.tiny_mediacms_name_entry', - title: '.tiny_mediacms_title_entry', - url: '.tiny_mediacms_url_entry', - width: '.tiny_mediacms_width_entry', - height: '.tiny_mediacms_height_entry', - trackSource: '.tiny_mediacms_track_source', - trackKind: '.tiny_mediacms_track_kind_entry', - trackLabel: '.tiny_mediacms_track_label_entry', - trackLang: '.tiny_mediacms_track_lang_entry', - trackDefault: '.tiny_mediacms_track_default', - mediaControl: '.tiny_mediacms_controls', - mediaAutoplay: '.tiny_mediacms_autoplay', - mediaMute: '.tiny_mediacms_mute', - mediaLoop: '.tiny_mediacms_loop', - advancedSettings: '.tiny_mediacms_advancedsettings', - linkTab: 'li[data-medium-type="link"]', - videoTab: 'li[data-medium-type="video"]', - audioTab: 'li[data-medium-type="audio"]', - linkPane: '.tab-pane[data-medium-type="link"]', - videoPane: '.tab-pane[data-medium-type="video"]', - audioPane: '.tab-pane[data-medium-type="audio"]', - trackSubtitlesTab: 'li[data-track-kind="subtitles"]', - trackCaptionsTab: 'li[data-track-kind="captions"]', - trackDescriptionsTab: 'li[data-track-kind="descriptions"]', - trackChaptersTab: 'li[data-track-kind="chapters"]', - trackMetadataTab: 'li[data-track-kind="metadata"]', - trackSubtitlesPane: '.tab-pane[data-track-kind="subtitles"]', - trackCaptionsPane: '.tab-pane[data-track-kind="captions"]', - trackDescriptionsPane: '.tab-pane[data-track-kind="descriptions"]', - trackChaptersPane: '.tab-pane[data-track-kind="chapters"]', - trackMetadataPane: '.tab-pane[data-track-kind="metadata"]', - }, - mediaTypes: { - link: 'LINK', - video: 'VIDEO', - audio: 'AUDIO', - }, - trackKinds: { - subtitles: 'SUBTITLES', - captions: 'CAPTIONS', - descriptions: 'DESCRIPTIONS', - chapters: 'CHAPTERS', - metadata: 'METADATA', - }, - }, - IFRAME: { - actions: { - remove: '[data-action="remove"]', - }, - elements: { - form: 'form.tiny_iframecms_form', - url: '.tiny_iframecms_url', - urlWarning: '.tiny_iframecms_url_warning', - showTitle: '.tiny_iframecms_showtitle', - linkTitle: '.tiny_iframecms_linktitle', - showRelated: '.tiny_iframecms_showrelated', - showUserAvatar: '.tiny_iframecms_showuseravatar', - responsive: '.tiny_iframecms_responsive', - startAt: '.tiny_iframecms_startat', - startAtEnabled: '.tiny_iframecms_startat_enabled', - aspectRatio: '.tiny_iframecms_aspectratio', - width: '.tiny_iframecms_width', - height: '.tiny_iframecms_height', - preview: '.tiny_iframecms_preview', - previewContainer: '.tiny_iframecms_preview_container', - // Tab elements - tabs: '.tiny_iframecms_tabs', - tabUrlBtn: '.tiny_iframecms_tab_url_btn', - tabIframeLibraryBtn: '.tiny_iframecms_tab_iframe_library_btn', - paneUrl: '.tiny_iframecms_pane_url', - paneIframeLibrary: '.tiny_iframecms_pane_iframe_library', - // Iframe library elements - iframeLibraryContainer: '.tiny_iframecms_iframe_library_container', - iframeLibraryPlaceholder: - '.tiny_iframecms_iframe_library_placeholder', - iframeLibraryLoading: '.tiny_iframecms_iframe_library_loading', - iframeLibraryFrame: '.tiny_iframecms_iframe_library_frame', - }, - aspectRatios: { - '16:9': { width: 560, height: 315 }, - '4:3': { width: 560, height: 420 }, - '1:1': { width: 400, height: 400 }, - custom: null, - }, - }, -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js b/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js deleted file mode 100755 index 1a418046..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/amd/src/usedfiles.js +++ /dev/null @@ -1,95 +0,0 @@ -// This file is part of Moodle - http://moodle.org/ -// -// Moodle is free software: you can redistribute it and/or modify -// it under the terms of the GNU General Public License as published by -// the Free Software Foundation, either version 3 of the License, or -// (at your option) any later version. -// -// Moodle is distributed in the hope that it will be useful, -// but WITHOUT ANY WARRANTY; without even the implied warranty of -// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the -// GNU General Public License for more details. -// -// You should have received a copy of the GNU General Public License -// along with Moodle. If not, see . - -/** - * Tiny Media Manager usedfiles. - * - * @module tiny_mediacms/usedfiles - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -import * as Templates from 'core/templates'; -import Config from 'core/config'; - -class UsedFileManager { - constructor(files, userContext, itemId, elementId) { - this.files = files; - this.userContext = userContext; - this.itemId = itemId; - this.elementId = elementId; - } - - getElementId() { - return this.elementId; - } - - getUsedFiles() { - const editor = window.parent.tinymce.EditorManager.get(this.getElementId()); - if (!editor) { - window.console.error(`Editor not found for ${this.getElementId()}`); - return []; - } - const content = editor.getContent(); - const baseUrl = `${Config.wwwroot}/draftfile.php/${this.userContext}/user/draft/${this.itemId}/`; - const pattern = new RegExp("[\"']" + baseUrl.replace(/[-/\\^$*+?.()|[\]{}]/g, '\\$&') + "(?.+?)[\\?\"']", 'gm'); - - const usedFiles = [...content.matchAll(pattern)].map((match) => decodeURIComponent(match.groups.filename)); - - return usedFiles; - } - - // Return an array of unused files. - findUnusedFiles(usedFiles) { - return Object.entries(this.files) - .filter(([filename]) => !usedFiles.includes(filename)) - .map(([filename]) => filename); - } - - // Return an array of missing files. - findMissingFiles(usedFiles) { - return usedFiles.filter((filename) => !this.files.hasOwnProperty(filename)); - } - - updateFiles() { - const form = document.querySelector('form'); - const usedFiles = this.getUsedFiles(); - const unusedFiles = this.findUnusedFiles(usedFiles); - const missingFiles = this.findMissingFiles(usedFiles); - - form.querySelectorAll('input[type=checkbox][name^="deletefile"]').forEach((checkbox) => { - if (!unusedFiles.includes(checkbox.dataset.filename)) { - checkbox.closest('.fitem').remove(); - } - }); - - form.classList.toggle('has-missing-files', !!missingFiles.length); - form.classList.toggle('has-unused-files', !!unusedFiles.length); - - return Templates.renderForPromise('tiny_mediacms/missingfiles', { - missingFiles, - }).then(({html, js}) => { - Templates.replaceNodeContents(form.querySelector('.missing-files'), html, js); - return; - }); - } -} - -export const init = (files, usercontext, itemid, elementid) => { - const manager = new UsedFileManager(files, usercontext, itemid, elementid); - manager.updateFiles(); - - return manager; -}; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php b/lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php deleted file mode 100755 index 00d021a1..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/classes/form/manage_files_form.php +++ /dev/null @@ -1,118 +0,0 @@ -. - -/** - * Atto text editor manage files plugin form. - * - * @package tiny_mediacms - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -namespace tiny_mediacms\form; - -use html_writer; - -defined('MOODLE_INTERNAL') || die(); - -require_once("{$CFG->libdir}/formslib.php"); - -/** - * Form allowing to edit files in one draft area. - * - * @package tiny_mediacms - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class manage_files_form extends \moodleform { - - public function definition() { - global $PAGE, $USER; - $mform = $this->_form; - - $mform->setDisableShortforms(true); - - $itemid = $this->_customdata['draftitemid']; - $elementid = $this->_customdata['elementid']; - $options = $this->_customdata['options']; - $files = $this->_customdata['files']; - $usercontext = $this->_customdata['context']; - $removeorphaneddrafts = $this->_customdata['removeorphaneddrafts'] ?? false; - - $mform->addElement('header', 'filemanagerhdr', get_string('filemanager', 'tiny_mediacms')); - - $mform->addElement('hidden', 'itemid'); - $mform->setType('itemid', PARAM_INT); - - $mform->addElement('hidden', 'maxbytes'); - $mform->setType('maxbytes', PARAM_INT); - - $mform->addElement('hidden', 'subdirs'); - $mform->setType('subdirs', PARAM_INT); - - $mform->addElement('hidden', 'accepted_types'); - $mform->setType('accepted_types', PARAM_RAW); - - $mform->addElement('hidden', 'return_types'); - $mform->setType('return_types', PARAM_INT); - - $mform->addElement('hidden', 'context'); - $mform->setType('context', PARAM_INT); - - $mform->addElement('hidden', 'areamaxbytes'); - $mform->setType('areamaxbytes', PARAM_INT); - - $mform->addElement('hidden', 'elementid'); - $mform->setType('elementid', PARAM_TEXT); - - $mform->addElement('filemanager', 'files_filemanager', '', null, $options); - - // Let the user know that any drafts not referenced in the text will be removed automatically. - if ($removeorphaneddrafts) { - $mform->addElement('static', '', '', html_writer::tag( - 'div', - get_string('unusedfilesremovalnotice', 'tiny_mediacms') - )); - } - - $mform->addElement('header', 'missingfileshdr', get_string('missingfiles', 'tiny_mediacms')); - $mform->addElement('static', '', '', - html_writer::tag( - 'div', - html_writer::tag('div', get_string('hasmissingfiles', 'tiny_mediacms')) . - html_writer::tag('div', '', ['class' => 'missing-files'] - ), - ['class' => 'file-status']) - ); - - $mform->addElement('header', 'deletefileshdr', get_string('unusedfilesheader', 'tiny_mediacms')); - $mform->addElement('static', '', '', html_writer::tag('div', get_string('unusedfilesdesc', 'tiny_mediacms'))); - - foreach ($files as $hash => $file) { - $mform->addElement('checkbox', "deletefile[{$hash}]", '', $file, ['data-filename' => $file]); - $mform->setType("deletefile[{$hash}]", PARAM_INT); - } - - $mform->addElement('submit', 'delete', get_string('deleteselected', 'tiny_mediacms')); - - $PAGE->requires->js_call_amd('tiny_mediacms/usedfiles', 'init', [ - 'files' => array_flip($files), - 'usercontext' => $usercontext->id, - 'itemid' => $itemid, - 'elementid' => $elementid, - ]); - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php b/lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php deleted file mode 100755 index 32c6c9df..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/classes/plugininfo.php +++ /dev/null @@ -1,226 +0,0 @@ -. - -namespace tiny_mediacms; - -use context; -use context_course; -use context_module; -use editor_tiny\editor; -use editor_tiny\plugin; -use editor_tiny\plugin_with_buttons; -use editor_tiny\plugin_with_configuration; -use editor_tiny\plugin_with_menuitems; -use moodle_url; - -/** - * Tiny media plugin. - * - * @package tiny_media - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class plugininfo extends plugin implements plugin_with_buttons, plugin_with_menuitems, plugin_with_configuration { - /** - * Whether the plugin is enabled - * - * @param context $context The context that the editor is used within - * @param array $options The options passed in when requesting the editor - * @param array $fpoptions The filepicker options passed in when requesting the editor - * @param editor $editor The editor instance in which the plugin is initialised - * @return boolean - */ - public static function is_enabled( - context $context, - array $options, - array $fpoptions, - ?editor $editor = null - ): bool { - // Disabled if: - // - Not logged in or guest. - // - Files are not allowed. - // - Only URL are supported. - $canhavefiles = !empty($options['maxfiles']); - $canhaveexternalfiles = !empty($options['return_types']) && ($options['return_types'] & FILE_EXTERNAL); - - return isloggedin() && !isguestuser() && ($canhavefiles || $canhaveexternalfiles); - } - - public static function get_available_buttons(): array { - return [ - 'tiny_mediacms/tiny_mediacms_image', - 'tiny_mediacms/tiny_mediacms_video', - 'tiny_mediacms/tiny_mediacms_iframe', - ]; - } - - public static function get_available_menuitems(): array { - return [ - 'tiny_mediacms/tiny_mediacms_image', - 'tiny_mediacms/tiny_mediacms_video', - 'tiny_mediacms/tiny_mediacms_iframe', - ]; - } - - public static function get_plugin_configuration_for_context( - context $context, - array $options, - array $fpoptions, - ?editor $editor = null - ): array { - - // TODO Fetch the actual permissions. - $permissions = [ - 'image' => [ - 'filepicker' => true, - ], - 'embed' => [ - 'filepicker' => true, - ] - ]; - - // Get LTI configuration for MediaCMS iframe library. - $lticonfig = self::get_lti_configuration($context); - - // Get auto-convert configuration. - $autoconvertconfig = self::get_autoconvert_configuration(); - - return array_merge([ - 'permissions' => $permissions, - ], self::get_file_manager_configuration($context, $options, $fpoptions), $lticonfig, $autoconvertconfig); - } - - /** - * Get the auto-convert configuration for pasted MediaCMS URLs. - * - * @return array Auto-convert configuration data - */ - protected static function get_autoconvert_configuration(): array { - $baseurl = get_config('tiny_mediacms', 'autoconvert_baseurl'); - - // Helper function to get config with default value of true. - $getboolconfig = function($name) { - $value = get_config('tiny_mediacms', $name); - // If the setting hasn't been saved yet (false/empty), default to true. - // Only return false if explicitly set to '0'. - return $value !== '0' && $value !== 0; - }; - - return [ - 'data' => [ - 'autoConvertEnabled' => true, // Always enabled - 'autoConvertBaseUrl' => !empty($baseurl) ? $baseurl : '', - 'autoConvertOptions' => [ - 'showTitle' => $getboolconfig('autoconvert_showtitle'), - 'linkTitle' => $getboolconfig('autoconvert_linktitle'), - 'showRelated' => $getboolconfig('autoconvert_showrelated'), - 'showUserAvatar' => $getboolconfig('autoconvert_showuseravatar'), - ], - ], - ]; - } - - /** - * Get the LTI configuration for the MediaCMS iframe library. - * - * @param context $context The context that the editor is used within - * @return array LTI configuration data - */ - protected static function get_lti_configuration(context $context): array { - global $COURSE; - - // Get the configured LTI tool ID from plugin settings. - $ltitoolid = get_config('tiny_mediacms', 'ltitoolid'); - - // Determine the course ID from context. - $courseid = 0; - if ($context instanceof context_course) { - $courseid = $context->instanceid; - } else if ($context instanceof context_module) { - // Get the course from the module context. - $coursecontext = $context->get_course_context(false); - if ($coursecontext) { - $courseid = $coursecontext->instanceid; - } - } else if (!empty($COURSE->id) && $COURSE->id != SITEID) { - // Fall back to the global $COURSE if available and not the site. - $courseid = $COURSE->id; - } - - // Build the content item URL for LTI Deep Linking. - // This URL initiates the LTI Deep Linking flow which allows users - // to select content (like videos) from the tool provider. - $contentitemurl = ''; - if (!empty($ltitoolid) && $courseid > 0) { - $contentitemurl = (new moodle_url('/mod/lti/contentitem.php', [ - 'id' => $ltitoolid, - 'course' => $courseid, - 'title' => 'MediaCMS Library', - 'return_types' => 1 // LTI_DEEPLINKING_RETURN_TYPE_LTI_LINK - ]))->out(false); - } - - return [ - 'lti' => [ - 'toolId' => !empty($ltitoolid) ? (int) $ltitoolid : 0, - 'courseId' => $courseid, - 'contentItemUrl' => $contentitemurl, - ], - ]; - } - - protected static function get_file_manager_configuration( - context $context, - array $options, - array $fpoptions - ): array { - global $USER; - - $params = [ - 'area' => [], - 'usercontext' => \context_user::instance($USER->id)->id, - ]; - - $keys = [ - 'itemid', - 'areamaxbytes', - 'maxbytes', - 'subdirs', - 'return_types', - 'removeorphaneddrafts', - ]; - if (isset($options['context'])) { - if (is_object($options['context'])) { - $params['area']['context'] = $options['context']->id; - } else { - $params['area']['context'] = $options['context']; - } - } - foreach ($keys as $key) { - if (isset($options[$key])) { - $params['area'][$key] = $options[$key]; - } - } - - return [ - 'storeinrepo' => true, - 'data' => [ - 'params' => $params, - 'fpoptions' => $fpoptions, - ], - ]; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php b/lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php deleted file mode 100755 index 14eeaf6f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/classes/privacy/provider.php +++ /dev/null @@ -1,30 +0,0 @@ -. - -namespace tiny_mediacms\privacy; - -/** - * Privacy Subsystem implementation for the media plugin for TinyMCE. - * - * @package tiny_media - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ -class provider implements \core_privacy\local\metadata\null_provider { - public static function get_reason(): string { - return 'privacy:metadata'; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt b/lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt deleted file mode 100755 index 431de560..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/deprecated.txt +++ /dev/null @@ -1,7 +0,0 @@ -alignment,tiny_media -alignment_bottom,tiny_media -alignment_left,tiny_media -alignment_middle,tiny_media -alignment_right,tiny_media -alignment_top,tiny_media -helplinktext,tiny_media diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php b/lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php deleted file mode 100755 index 0463f9eb..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/lang/en/tiny_mediacms.php +++ /dev/null @@ -1,199 +0,0 @@ -. - -/** - * Strings for component 'tiny_mediacms', language 'en'. - * - * @package tiny_mediacms - * @copyright 2022 Andrew Lyons - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -$string['addcaptionstrack'] = 'Add caption track'; -$string['addchapterstrack'] = 'Add chapter track'; -$string['adddescriptionstrack'] = 'Add description track'; -$string['addfilesdrop'] = 'Drag and drop an image to upload, or click to select'; -$string['addmetadatatrack'] = 'Add metadata track'; -$string['addsource_help'] = 'You are recommended to provide an alternative media source, as desktop and mobile browsers support different file formats.'; -$string['addsource'] = 'Add alternative source'; -$string['addsubtitlestrack'] = 'Add subtitle track'; -$string['addurl'] = 'Add'; -$string['advancedsettings'] = 'Advanced settings'; -$string['audio'] = 'Audio'; -$string['audiosourcelabel'] = 'Audio source URL'; -$string['autoplay'] = 'Play automatically'; -$string['browserepositories'] = 'Browse repositories...'; -$string['browserepositoriesimage'] = 'Browse repositories'; -$string['captions_help'] = 'Captions may be used to describe everything happening in the track, including non-verbal sounds such as a phone ringing.'; -$string['captions'] = 'Captions'; -$string['captionssourcelabel'] = 'Caption track URL'; -$string['chapters_help'] = 'Chapter titles may be provided for use in navigating the media resource.'; -$string['chapters'] = 'Chapters'; -$string['chapterssourcelabel'] = 'Chapter track URL'; -$string['constrain'] = 'Keep proportion'; -$string['controls'] = 'Show controls'; -$string['createmedia'] = 'Insert favorite media'; -$string['default'] = 'Default'; -$string['deleteimage'] = 'Delete image'; -$string['deleteimagewarning'] = 'Are you sure you want to remove the image?'; -$string['deleteselected'] = 'Delete selected files'; -$string['descriptions_help'] = 'Audio descriptions may be used to provide a narration which explains visual details not apparent from the audio alone.'; -$string['descriptions'] = 'Descriptions'; -$string['descriptionssourcelabel'] = 'Description track URL'; -$string['displayoptions'] = 'Display options'; -$string['enteralt'] = 'How would you describe this image to someone who can\'t see it?'; -$string['entername'] = 'Name'; -$string['entersource'] = 'Source URL'; -$string['entertitle'] = 'Title'; -$string['enterurl'] = 'Add via URL'; -$string['enterurlor'] = 'Or add via URL'; -$string['filemanager'] = 'Favorite file manager'; -$string['hasmissingfiles'] = 'Warning! The following files that are referenced in the text area appear to be missing:'; -$string['height'] = 'Height'; -$string['imagebuttontitle'] = 'Favorite Image'; -$string['imagedetails'] = 'Image details'; -$string['imageproperties'] = 'Image properties'; -$string['imageurlrequired'] = 'An image must have a valid URL.'; -$string['insertimage'] = 'Insert favorite image'; -$string['label'] = 'Label'; -$string['languagesavailable'] = 'Languages available'; -$string['languagesinstalled'] = 'Languages installed'; -$string['link'] = 'Link'; -$string['loading'] = 'Preparing the image'; -$string['loop'] = 'Loop'; -$string['managefiles'] = 'Manage favorite files'; -$string['mediabuttontitle'] = 'Favorite Multimedia'; -$string['mediamanagerbuttontitle'] = 'Favorite media manager'; -$string['mediamanagerproperties'] = 'Favorite media manager'; -$string['metadata_help'] = 'Metadata tracks, for use from a script, may be used only if the player supports metadata.'; -$string['metadata'] = 'Metadata'; -$string['metadatasourcelabel'] = 'Metadata track URL'; -$string['missingfiles'] = 'Missing files'; -$string['mute'] = 'Muted'; -$string['pluginname'] = 'MediaCMS'; -$string['presentation'] = 'This image is decorative only'; -$string['presentationoraltrequired'] = 'An image must have a description, unless it is marked as decorative only.'; -$string['privacy:metadata'] = 'The favorite media plugin for TinyMCE does not store any personal data.'; -$string['remove'] = 'Remove'; -$string['repositorynotpermitted'] = 'Paste an image link in the field below.'; -$string['repositoryuploadnotpermitted'] = 'Paste an image link in the field below or
click the Browse Repositories button.'; -$string['saveimage'] = 'Save'; -$string['size'] = 'Width x height (in pixels)'; -$string['sizecustom'] = 'Custom size'; -$string['sizecustom_help'] = 'This image is just a preview. Changes to its size will be visible after you save it.'; -$string['sizeoriginal'] = 'Original size'; -$string['srclang'] = 'Language'; -$string['subtitles_help'] = 'Subtitles may be used to provide a transcription or translation of the dialogue.'; -$string['subtitles'] = 'Subtitles'; -$string['subtitlessourcelabel'] = 'Subtitle track URL'; -$string['tracks_help'] = 'Subtitles, captions, chapters and descriptions can be added via a WebVTT (Web Video Text Tracks) format file. Track labels will be shown in the selection drop-down menu. For each type of track, any track set as default will be pre-selected at the start of the video.'; -$string['tracks'] = 'Subtitles and captions'; -$string['unusedfilesdesc'] = 'The following embedded files are not used in the text area:'; -$string['unusedfilesheader'] = 'Unused files'; -$string['unusedfilesremovalnotice'] = 'Any unused files will be automatically deleted when saving changes.'; -$string['updatemedia'] = 'Update favorite media'; -$string['uploading'] = 'Uploading'; -$string['video'] = 'Video'; -$string['videoheight'] = 'Video height'; -$string['videosourcelabel'] = 'Video source URL'; -$string['videowidth'] = 'Video width'; -$string['width'] = 'Width'; - -// Iframe embed strings. -$string['iframebuttontitle'] = 'Insert MediaCMS Media'; -$string['iframemodaltitle'] = 'Insert MediaCMS Media'; -$string['iframeurl'] = 'MediaCMS Video URL or embed code'; -$string['iframeurlplaceholder'] = 'Paste MediaCMS Video URL or iframe embed code'; -$string['iframeurlinvalid'] = 'Please enter a valid MediaCMS Video URL or embed code'; -$string['embedoptions'] = 'Embed Options'; -$string['showtitle'] = 'Show title'; -$string['linktitle'] = 'Link title'; -$string['showrelated'] = 'Show related'; -$string['showuseravatar'] = 'Show user avatar'; -$string['responsive'] = 'Responsive'; -$string['startat'] = 'Start at'; -$string['aspectratio'] = 'Aspect Ratio'; -$string['aspectratio_16_9'] = '16:9'; -$string['aspectratio_4_3'] = '4:3'; -$string['aspectratio_1_1'] = '1:1'; -$string['aspectratio_custom'] = 'Custom'; -$string['dimensions'] = 'Dimensions'; -$string['preview'] = 'Preview'; -$string['insertiframe'] = 'Insert video'; -$string['updateiframe'] = 'Update video'; -$string['removeiframe'] = 'Remove video'; -$string['removeiframeconfirm'] = 'Are you sure you want to remove this video from the editor?'; - -// Iframe modal tabs. -$string['tabembedurl'] = 'Embed URL'; -$string['tabvideolibrary'] = 'Video Library'; -$string['tabvideolibraryiframe'] = 'Media Library'; - -// Video library strings. -$string['librarysearchplaceholder'] = 'Search videos...'; -$string['librarysortnewest'] = 'Newest first'; -$string['librarysortoldest'] = 'Oldest first'; -$string['librarysorttitle'] = 'Title A-Z'; -$string['librarysortviews'] = 'Most views'; -$string['libraryloading'] = 'Loading videos...'; -$string['libraryempty'] = 'No videos found'; -$string['libraryerror'] = 'Failed to load videos'; -$string['libraryretry'] = 'Retry'; -$string['libraryprev'] = 'Previous'; -$string['librarynext'] = 'Next'; -$string['libraryselect'] = 'Select'; -$string['librarypage'] = 'Page {$a->current} of {$a->total}'; -$string['libraryvideoselected'] = 'Video selected. Configure embed options below.'; - -// LTI settings strings. -$string['ltitoolid'] = 'LTI Tool'; -$string['ltitoolid_desc'] = 'Select the External Tool configuration for MediaCMS. This enables the authenticated video library in the editor.'; -$string['noltitoolsfound'] = 'No LTI tools found'; -$string['choose'] = 'Choose...'; -$string['ltitoolid_help'] = 'To find the LTI tool ID, go to Site administration > Plugins > Activity modules > External tool > Manage tools. The ID is shown in the URL when editing a tool (e.g., id=2).'; - -// Iframe library from LTI strings. -$string['iframelibraryloading'] = 'Loading MediaCMS video library...'; -$string['iframelibraryplaceholder'] = 'Click here to load the video library'; -$string['iframelibraryerror'] = 'Failed to load the video library. Please check your LTI configuration.'; -$string['iframelibrarynotconfigured'] = 'The MediaCMS LTI tool has not been configured. Please contact your administrator.'; - -// Auto-convert settings strings. -$string['autoconvertheading'] = 'Auto-convert MediaCMS URLs'; -$string['autoconvertheading_desc'] = 'Configure automatic conversion of pasted MediaCMS URLs to embedded videos.'; -$string['autoconvertenabled'] = 'Enable auto-convert'; -$string['autoconvertenabled_desc'] = 'When enabled, pasting a MediaCMS video URL (e.g., https://lti.mediacms.io/view?m=VIDEO_ID) into the editor will automatically convert it to an embedded video player.'; -$string['autoconvert_baseurl'] = 'MediaCMS URL'; -$string['autoconvert_baseurl_desc'] = 'The base URL of your MediaCMS instance (e.g., https://lti.mediacms.io). If specified, only URLs from this domain will be auto-converted. Leave empty to allow any MediaCMS URL.'; -$string['autoconvert_showtitle'] = 'Show video title'; -$string['autoconvert_showtitle_desc'] = 'Display the video title in the embedded player.'; -$string['autoconvert_linktitle'] = 'Link video title'; -$string['autoconvert_linktitle_desc'] = 'Make the video title clickable, linking to the original video page.'; -$string['autoconvert_showrelated'] = 'Show related videos'; -$string['autoconvert_showrelated_desc'] = 'Display related videos after the current video ends.'; -$string['autoconvert_showuseravatar'] = 'Show user avatar'; -$string['autoconvert_showuseravatar_desc'] = 'Display the uploader\'s avatar in the embedded player.'; - -// Deprecated since Moodle 4.4. -$string['alignment'] = 'Alignment'; -$string['alignment_bottom'] = 'Bottom'; -$string['alignment_left'] = 'Left'; -$string['alignment_middle'] = 'Middle'; -$string['alignment_right'] = 'Right'; -$string['alignment_top'] = 'Top'; - -// Deprecated since Moodle 4.5. -$string['helplinktext'] = 'Favorite media helper'; diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/manage.php b/lms-plugins/mediacms-moodle/tiny/mediacms/manage.php deleted file mode 100755 index 844953cc..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/manage.php +++ /dev/null @@ -1,148 +0,0 @@ -. - -/** - * Manage files in user draft area attached to texteditor. - * - * @package tiny_mediacms - * @copyright 2022, Stevani Andolo - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -require(__DIR__ . '/../../../../../config.php'); -require_once($CFG->libdir . '/filestorage/file_storage.php'); -require_once($CFG->dirroot . '/repository/lib.php'); - -$itemid = required_param('itemid', PARAM_INT) ?? 0; -$maxbytes = optional_param('maxbytes', 0, PARAM_INT); -$subdirs = optional_param('subdirs', 0, PARAM_INT); -$acceptedtypes = optional_param('accepted_types', '*', PARAM_RAW); // TODO Not yet passed to this script. -$returntypes = optional_param('return_types', null, PARAM_INT); -$areamaxbytes = optional_param('areamaxbytes', FILE_AREA_MAX_BYTES_UNLIMITED, PARAM_INT); -$contextid = optional_param('context', SYSCONTEXTID, PARAM_INT); -$elementid = optional_param('elementid', '', PARAM_TEXT); -$removeorphaneddrafts = optional_param('removeorphaneddrafts', 0, PARAM_INT); - -$context = context::instance_by_id($contextid); -if ($context->contextlevel == CONTEXT_MODULE) { - // Module context. - $cm = $DB->get_record('course_modules', ['id' => $context->instanceid]); - require_login($cm->course, true, $cm); -} else if (($coursecontext = $context->get_course_context(false)) && $coursecontext->id != SITEID) { - // Course context or block inside the course. - require_login($coursecontext->instanceid); - $PAGE->set_context($context); -} else { - // Block that is not inside the course, user or system context. - require_login(); - $PAGE->set_context($context); -} - -// Guests can never manage files. -if (isguestuser()) { - throw new \moodle_exception('noguest'); -} - -$title = get_string('managefiles', 'tiny_mediacms'); - -$url = new moodle_url('/lib/editor/tiny/plugins/mediacms/manage.php', [ - 'itemid' => $itemid, - 'maxbytes' => $maxbytes, - 'subdirs' => $subdirs, - 'accepted_types' => $acceptedtypes, - 'return_types' => $returntypes, - 'areamaxbytes' => $areamaxbytes, - 'context' => $contextid, - 'elementid' => $elementid, - 'removeorphaneddrafts' => $removeorphaneddrafts, -]); - -$PAGE->set_url($url); -$PAGE->set_title($title); -$PAGE->set_heading($title); -$PAGE->set_pagelayout('popup'); - -if ($returntypes !== null) { - // Links are allowed in textarea but never allowed in filemanager. - $returntypes = $returntypes & ~FILE_EXTERNAL; -} - -// These are the options required for the filepicker. -$options = [ - 'subdirs' => $subdirs, - 'maxbytes' => $maxbytes, - 'maxfiles' => -1, - 'accepted_types' => $acceptedtypes, - 'areamaxbytes' => $areamaxbytes, - 'return_types' => $returntypes, - 'context' => $context -]; - -$usercontext = context_user::instance($USER->id); -$fs = get_file_storage(); -$files = $fs->get_directory_files($usercontext->id, 'user', 'draft', $itemid, '/', !empty($subdirs), false); -$filenames = []; -foreach ($files as $file) { - $filenames[$file->get_pathnamehash()] = ltrim($file->get_filepath(), '/') . $file->get_filename(); -} - -$mform = new tiny_mediacms\form\manage_files_form(null, [ - 'context' => $usercontext, - 'options' => $options, - 'draftitemid' => $itemid, - 'files' => $filenames, - 'elementid' => $elementid, - 'removeorphaneddrafts' => $removeorphaneddrafts, - ], 'post', '', [ - 'id' => 'tiny_mediacms_form', - ] -); - -if ($data = $mform->get_data()) { - if (!empty($data->deletefile)) { - foreach (array_keys($data->deletefile) as $filehash) { - if ($file = $fs->get_file_by_hash($filehash)) { - // Make sure the user didn't modify the filehash to delete another file. - if ($file->get_component() !== 'user' || $file->get_filearea() !== 'draft') { - // The file must belong to the user/draft area. - continue; - } - if ($file->get_contextid() != $usercontext->id) { - // The user must own the file - that is it must be in their user draft file area. - continue; - } - if ($file->get_itemid() != $itemid) { - // It must be the file they requested be deleted. - continue; - } - $file->delete(); - } - } - } - // Redirect to prevent re-posting the form. - redirect($url); -} - -$mform->set_data(array_merge($options, [ - 'files_filemanager' => $itemid, - 'itemid' => $itemid, - 'elementid' => $elementid, - 'context' => $context->id, -])); - -echo $OUTPUT->header(); -$mform->display(); -echo $OUTPUT->footer(); diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/pix/filemanager.svg b/lms-plugins/mediacms-moodle/tiny/mediacms/pix/filemanager.svg deleted file mode 100755 index a210b89a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/pix/filemanager.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/pix/icon.svg b/lms-plugins/mediacms-moodle/tiny/mediacms/pix/icon.svg deleted file mode 100755 index 6ee4edd2..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/pix/icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/settings.php b/lms-plugins/mediacms-moodle/tiny/mediacms/settings.php deleted file mode 100755 index 5eb78f5b..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/settings.php +++ /dev/null @@ -1,97 +0,0 @@ -. - -/** - * Settings for the tiny_mediacms plugin. - * - * @package tiny_mediacms - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -if ($ADMIN->fulltree) { - global $DB; - - // LTI Tool ID setting (Dropdown). - $ltioptions = [0 => get_string('noltitoolsfound', 'tiny_mediacms')]; - try { - $tools = $DB->get_records('lti_types', null, 'name ASC', 'id, name, baseurl'); - if (!empty($tools)) { - $ltioptions = [0 => get_string('choose', 'tiny_mediacms')]; - foreach ($tools as $tool) { - $ltioptions[$tool->id] = $tool->name . ' (' . $tool->baseurl . ')'; - } - } - } catch (Exception $e) { - // Database might not be ready during install - } - - $setting = new admin_setting_configselect( - 'tiny_mediacms/ltitoolid', - new lang_string('ltitoolid', 'tiny_mediacms'), - new lang_string('ltitoolid_desc', 'tiny_mediacms'), - 0, - $ltioptions - ); - $settings->add($setting); - - // Auto-convert is enabled by default in plugininfo.php (data.autoConvertEnabled = true). - - // MediaCMS base URL for auto-convert. - $setting = new admin_setting_configtext( - 'tiny_mediacms/autoconvert_baseurl', - new lang_string('autoconvert_baseurl', 'tiny_mediacms'), - new lang_string('autoconvert_baseurl_desc', 'tiny_mediacms'), - 'https://lti.mediacms.io', // Default matching filter - PARAM_URL - ); - $settings->add($setting); - - // Auto-convert embed options. - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_showtitle', - new lang_string('autoconvert_showtitle', 'tiny_mediacms'), - new lang_string('autoconvert_showtitle_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); - - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_linktitle', - new lang_string('autoconvert_linktitle', 'tiny_mediacms'), - new lang_string('autoconvert_linktitle_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); - - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_showrelated', - new lang_string('autoconvert_showrelated', 'tiny_mediacms'), - new lang_string('autoconvert_showrelated_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); - - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_showuseravatar', - new lang_string('autoconvert_showuseravatar', 'tiny_mediacms'), - new lang_string('autoconvert_showuseravatar_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/styles.css b/lms-plugins/mediacms-moodle/tiny/mediacms/styles.css deleted file mode 100755 index 211dea40..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/styles.css +++ /dev/null @@ -1,79 +0,0 @@ -#tiny_mediacms_form { - padding: 1rem; -} - -#tiny_mediacms_form #id_deletefileshdr { - display: none; -} - -#tiny_mediacms_form.has-unused-files #id_deletefileshdr { - display: block; -} - -#tiny_mediacms_form #id_missingfileshdr { - display: none; -} - -#tiny_mediacms_form.has-missing-files #id_missingfileshdr { - display: block; -} - -iframe.mmcms_iframe { - height: 650px; - border: none; - width: 100%; -} - -.missing-files ol { - padding-left: 15px; -} - -.missing-files ol li { - font-style: italic; - font-weight: 600; - color: red; -} - -.tiny_imagecms_form .tiny_imagecms_dropzone_container { - height: 200px; -} - -.tiny_imagecms_form .tiny_imagecms_dropzone_container .dropzone-label { - font-size: 1.25rem; -} - -.tiny_imagecms_form .tiny_imagecms_loader_container { - height: 200px; -} - -.tiny_imagecms_form .tiny_imagecms_preview_box { - height: 300px; - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; -} - -.tiny_imagecms_form .tiny_imagecms_deleteicon { - position: absolute; - top: 5px; - right: 5px; - cursor: pointer; - z-index: 1; - width: 30px; - height: 30px; - background: rgba(255, 255, 255, 1); - border-radius: 50%; - padding: 4px 5px 5px 9px; -} - -.tiny_imagecms_form .tiny_imagecms_deleteicon .fa-trash { - color: #1d2125; -} - - -@media (max-width: 767px) { - .tiny_imagecms_form .tiny_imagecms_properties_col { - padding: 0; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_audio.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_audio.mustache deleted file mode 100755 index 8e596063..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_audio.mustache +++ /dev/null @@ -1,41 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_audio - - Embed media audio template. - - Example context (json): - { - - } -}} -  - {{#sources}}{{/sources}} - {{#tracks}} - - {{/tracks}} - {{#description}}{{.}}{{/description}} -  diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_link.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_link.mustache deleted file mode 100755 index d87538a6..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_link.mustache +++ /dev/null @@ -1,33 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_link - - Embed media link template. - - Example context (json): - { - - } -}} -{{! - }}{{#name}}{{.}}{{/name}}{{! - }}{{^name}}{{url}}{{/name}}{{! -}} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal.mustache deleted file mode 100755 index 6ae910f5..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal.mustache +++ /dev/null @@ -1,78 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal - - Embed media modal template. - - Example context (json): - { - - } -}} -{{< core/modal }} - - {{$title}} - {{#str}} modaltitle, tiny_h5p {{/str}} - {{/title}} - - {{$body}} -
- -
- -
- {{> tiny_mediacms/embed_media_modal_video}} -
-
- {{> tiny_mediacms/embed_media_modal_audio}} -
-
-
- {{/body}} - - {{$footer}} - - - {{/footer}} - -{{/ core/modal }} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_audio.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_audio.mustache deleted file mode 100755 index 2c350cec..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_audio.mustache +++ /dev/null @@ -1,802 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal_audio - - Embed media audio modal template. - - Example context (json): - { - - } -}} -{{#audio.sources}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/audio.sources}} -{{^audio}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/audio}} - - - diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_link.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_link.mustache deleted file mode 100755 index b5fe580b..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_link.mustache +++ /dev/null @@ -1,43 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal_link - - Embed media link modal template. - - Example context (json): - { - - } -}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_video.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_video.mustache deleted file mode 100755 index 0aa73e3f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_modal_video.mustache +++ /dev/null @@ -1,832 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal_video - - Embed media video modal template. - - Example context (json): - { - - } -}} -{{#video.sources}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/video.sources}} -{{^video}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/video}} - - - diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_video.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_video.mustache deleted file mode 100755 index 756c3e8f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/embed_media_video.mustache +++ /dev/null @@ -1,50 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_video - - Embed media video template. - - Example context (json): - { - - } -}} -  - {{#sources}} - - {{/sources}} - {{#tracks}} - - {{/tracks}} - {{#description}}{{.}}{{/description}} -  diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_modal.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_modal.mustache deleted file mode 100755 index 6726bc99..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_modal.mustache +++ /dev/null @@ -1,134 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_embed_modal - - Iframe embed modal template. - - Example context (json): - { - "elementid": "editor1", - "isupdating": false - } -}} -{{< core/modal }} - - {{$body}} -
- - - - -
- -
-
-
- -
- -
- - -
- {{#str}} iframeurlinvalid, tiny_mediacms {{/str}} -
-
- - {{> tiny_mediacms/iframe_embed_options }} -
- - -
- -
-
- {{#str}} iframeurlplaceholder, tiny_mediacms {{/str}} -
-
-
-
-
-
- - -
-
-
-

{{#str}} libraryloading, tiny_mediacms {{/str}}

-
-
-
- {{#str}} libraryloading, tiny_mediacms {{/str}} -
-

{{#str}} libraryloading, tiny_mediacms {{/str}}

-
- -
-
-
-
- {{/body}} - - {{$footer}} - - {{#isupdating}} - - {{/isupdating}} - - {{/footer}} - -{{/ core/modal }} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_options.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_options.mustache deleted file mode 100755 index 7ea36182..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_options.mustache +++ /dev/null @@ -1,127 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_embed_options - - Embed options partial for iframe modal. - - Example context (json): - { - "elementid": "editor1", - "showTitle": true, - "linkTitle": true, - "showRelated": true, - "showUserAvatar": true, - "responsive": true, - "startAtEnabled": false, - "startAt": "0:00" - } -}} - -
- -
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - - -
-
-
-
- - -
- - -
- - -
- -
-
-
- - px -
- {{#str}} width, tiny_mediacms {{/str}} -
-
-
- - px -
- {{#str}} height, tiny_mediacms {{/str}} -
-
-
diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_output.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_output.mustache deleted file mode 100755 index 1c9edfad..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_embed_output.mustache +++ /dev/null @@ -1,36 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_embed_output - - Iframe embed output template. - - Example context (json): - { - "src": "https://example.com/embed?m=abc123", - "width": 560, - "height": 315, - "responsive": true, - "aspectRatioClass": "ratio-16-9" - } -}} -{{#responsive}} - -{{/responsive}} -{{^responsive}} - -{{/responsive}} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library.mustache deleted file mode 100755 index 404af4dd..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library.mustache +++ /dev/null @@ -1,91 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_video_library - - Video library browser for iframe modal. - - Example context (json): - { - "elementid": "editor1" - } -}} -
- -
-
-
- - -
-
-
- -
-
- - -
- -
-
- {{#str}} loading, tiny_mediacms {{/str}} -
-

{{#str}} libraryloading, tiny_mediacms {{/str}}

-
- - -
- -
- - -
- -

{{#str}} libraryempty, tiny_mediacms {{/str}}

-
- - -
- -

{{#str}} libraryerror, tiny_mediacms {{/str}}

- -
-
- - -
- - - -
-
diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library_item.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library_item.mustache deleted file mode 100755 index 6cb41e30..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/iframe_video_library_item.mustache +++ /dev/null @@ -1,73 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_video_library_item - - Single video item in the library grid. - - Example context (json): - { - "id": "abc123", - "title": "My Video", - "thumbnail": "https://example.com/thumb.jpg", - "duration": "2:30", - "views": "150 views", - "date": "2 weeks ago", - "embedUrl": "https://example.com/embed?m=abc123" - } -}} -
-
-
- {{title}} - {{#duration}} - - {{duration}} - - {{/duration}} - -
- -
-
-
-
- {{title}} -
-

- {{#views}}{{views}} • {{/views}}{{date}} -

-
-
-
- diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/image.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/image.mustache deleted file mode 100755 index 99be5816..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/image.mustache +++ /dev/null @@ -1,34 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/image - - Image template. - - Example context (json): - { - - } -}} -{{alt}} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal.mustache deleted file mode 100755 index bf5a78d2..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal.mustache +++ /dev/null @@ -1,61 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal - - Insert image template. - - Example context (json): - { - "uniqid": 0, - "elementid": "exampleId", - "loading": {}, - "title": "Insert image" - } -}} -{{< core/modal }} - {{$body}} -
- - - - - -
-
- - {{> core/loading }} - -
{{#str}} loading, tiny_mediacms {{/str}}
-
-
-
-
- {{/body}} - - {{$footer}} - - {{/footer}} -{{/ core/modal }} diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details.mustache deleted file mode 100755 index 36d4f608..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details.mustache +++ /dev/null @@ -1,112 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_details - - Insert image details body template. - - Example context (json): - { - "elementid": "exampleId", - "alt": "Image description", - "presentation": true, - "width": 600, - "height": 400, - "customStyle": "", - "sizecustomhelpicon": { - "text": "Help text" - } - } - -}} -
-
-
- -
- - -
- -
- -
- - -
- -
- - - -
- 0 - / 125 -
-
-
- -
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
- -
-
- - -
-
- -
X
- -
-
- - -
-
-
{{#sizecustomhelpicon}}{{> core/help_icon }}{{/sizecustomhelpicon}}
-
-
- -
- - -
-
-
-
-
-
diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details_footer.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details_footer.mustache deleted file mode 100755 index 9fd5fd71..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_details_footer.mustache +++ /dev/null @@ -1,42 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_details_footer - - Insert image details footer template. - - Example context (json): - { - "elementid": "exampleId" - } - -}} -
- -
- - -
- -
- - - - -
-
- diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert.mustache deleted file mode 100755 index 8a0b091c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert.mustache +++ /dev/null @@ -1,53 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_insert - - Insert image body template. - - Example context (json): - { - "showdropzone": true, - "showfilepicker": true - } - -}} -
-
- {{#showdropzone}} -
- - - {{/showdropzone}} - {{^showdropzone}} -
- - - -
-

- {{#showfilepicker}} - {{#str}} repositoryuploadnotpermitted, tiny_mediacms {{/str}}

- {{/showfilepicker}} - {{^showfilepicker}} - {{#str}} repositorynotpermitted, tiny_mediacms {{/str}} - {{/showfilepicker}} -
-
- {{/showdropzone}} -
-
diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert_footer.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert_footer.mustache deleted file mode 100755 index bda98412..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/insert_image_modal_insert_footer.mustache +++ /dev/null @@ -1,56 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_insert_footer - - Insert image footer template. - - Example context (json): - { - "showdropzone": true, - "elementid": "exampleId", - "src": "https://moodle.org/logo.png", - "showfilepicker": true - } - -}} -
- -
- - {{#showdropzone}} - - {{/showdropzone}} - {{^showdropzone}} - - {{/showdropzone}} - - - - -
- -
- - - {{#showfilepicker}} - - - {{/showfilepicker}} -
-
- diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/missingfiles.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/missingfiles.mustache deleted file mode 100755 index cbde9013..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/missingfiles.mustache +++ /dev/null @@ -1,34 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/missingfiles - - Insert image template. - - Example context (json): - { - "missingFiles": [ - "Example.png", - "Another.mov" - ] - } -}} -
    -{{#missingFiles}} -
  1. {{.}}
  2. -{{/missingFiles}} -
diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/mm2_iframe.mustache b/lms-plugins/mediacms-moodle/tiny/mediacms/templates/mm2_iframe.mustache deleted file mode 100755 index 5aee7a68..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/templates/mm2_iframe.mustache +++ /dev/null @@ -1,28 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/mm2_iframe - - Insert image template. - - Example context (json): - { - - } -}} - diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/image.feature b/lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/image.feature deleted file mode 100755 index 1ec162c8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/image.feature +++ /dev/null @@ -1,74 +0,0 @@ -@editor @editor_tiny @tiny_mediacms @javascript -Feature: Use the TinyMCE editor to upload an image - In order to work with images - As a user - I need to be able to upload and manipulate images - - Scenario: Clicking on the Image button in the TinyMCE editor opens the image dialog - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - Then "Insert image" "dialogue" should exist - - Scenario: Browsing repositories in the TinyMCE editor opens the image dialog and shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - Then "File picker" "dialogue" should exist - - Scenario: Focus returns to the correct location after closing a nested FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - And I press "Browse repositories" - When I press the escape key - Then the focused element is "Browse repositories" "button" - - @_file_upload @test_tiny - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker and upload url image - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - And I upload "/lib/editor/tiny/tests/behat/fixtures/tinyscreenshot.png" to the file picker for TinyMCE - # Note: This needs to be replaced with a label. - Then ".tiny_image_preview" "css_element" should be visible - - @_file_upload - Scenario: Insert image to the TinyMCE editor - Given I log in as "admin" - And I open my profile in edit mode - And I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE - And I set the field "How would you describe this image to someone who can't see it?" to "It's the Moodle" - And I click on "Save" "button" in the "Image details" "dialogue" - When I select the "img" element in position "0" of the "Description" TinyMCE editor - And I click on the "Image" button for the "Description" TinyMCE editor - Then the field "How would you describe this image to someone who can't see it?" matches value "It's the Moodle" - # Note: This needs to be replaced with a label. - And ".tiny_image_preview" "css_element" should be visible - - @_file_upload - Scenario: Resizing the image uses the original and custom sizes and the keep proportion checkbox - Given I log in as "admin" - And I open my profile in edit mode - And I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE - And I click on "This image is decorative only" "checkbox" - And I click on "Save" "button" in the "Image details" "dialogue" - When I select the "img" element in position "0" of the "Description" TinyMCE editor - And I click on the "Image" button for the "Description" TinyMCE editor - Then the field "Original size" matches value "1" - And I click on "Custom size" "radio" - Then the field "Keep proportion" matches value "1" - And I click on "Keep proportion" "checkbox" - And I set the field "Width" to "102" - And I click on "Save" "button" in the "Image details" "dialogue" - When I select the "img" element in position "0" of the "Description" TinyMCE editor - And I click on the "Image" button for the "Description" TinyMCE editor - Then the field "Custom size" matches value "1" - And the field "Width" matches value "102" - And the field "Keep proportion" matches value "0" diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/video.feature b/lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/video.feature deleted file mode 100755 index f8d72890..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/tests/behat/video.feature +++ /dev/null @@ -1,58 +0,0 @@ -@editor @editor_tiny @tiny_mediacms @javascript -Feature: Use the TinyMCE editor to upload a video - In order to work with videos - As a user - I need to be able to upload and manipulate videos - - Scenario: Clicking on the Video button in the TinyMCE editor opens the video dialog - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - Then "File picker" "dialogue" should exist - - @_file_upload - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I follow "Video" - And I click on "Browse repositories..." "button" in the "#id_description_editor_video .tiny_media_source.tiny_media_media_source" "css_element" - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - When I click on "Insert media" "button" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - - @_file_upload - Scenario: Insert and update video in the TinyMCE editor - Given I log in as "admin" - And I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And I follow "Video" - And I click on "Browse repositories..." "button" in the "#id_description_editor_video .tiny_media_source.tiny_media_media_source" "css_element" - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - And I click on "Insert media" "button" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Display options" "link" - And I set the field "Title" to "Test title" - And I click on "Advanced settings" "link" - And I click on "Play automatically" "checkbox" - And I click on "Muted" "checkbox" - And I click on "Loop" "checkbox" - Then "Insert media" "button" should not exist in the "Insert media" "dialogue" - And "Update media" "button" should exist in the "Insert media" "dialogue" - And I click on "Update media" "button" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Display options" "link" - And the field "Title" matches value "Test title" - And I click on "Advanced settings" "link" - And the field "Play automatically" matches value "1" - And the field "Muted" matches value "1" - And the field "Loop" matches value "1" diff --git a/lms-plugins/mediacms-moodle/tiny/mediacms/version.php b/lms-plugins/mediacms-moodle/tiny/mediacms/version.php deleted file mode 100755 index 746c7a5c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/mediacms/version.php +++ /dev/null @@ -1,30 +0,0 @@ -. - -/** - * Tiny media plugin version details. - * - * @package tiny_media - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -$plugin->version = 2026020103; // Bumped version to ensure upgrade -$plugin->requires = 2024100100; -$plugin->component = 'tiny_mediacms'; -$plugin->dependencies = ['filter_mediacms' => 2026020100]; // Keep dependency on our filter diff --git a/lms-plugins/mediacms-moodle/tiny/pix/filemanager.svg b/lms-plugins/mediacms-moodle/tiny/pix/filemanager.svg deleted file mode 100755 index a210b89a..00000000 --- a/lms-plugins/mediacms-moodle/tiny/pix/filemanager.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lms-plugins/mediacms-moodle/tiny/pix/icon.svg b/lms-plugins/mediacms-moodle/tiny/pix/icon.svg deleted file mode 100755 index 6ee4edd2..00000000 --- a/lms-plugins/mediacms-moodle/tiny/pix/icon.svg +++ /dev/null @@ -1,4 +0,0 @@ - - - - diff --git a/lms-plugins/mediacms-moodle/tiny/settings.php b/lms-plugins/mediacms-moodle/tiny/settings.php deleted file mode 100755 index 5eb78f5b..00000000 --- a/lms-plugins/mediacms-moodle/tiny/settings.php +++ /dev/null @@ -1,97 +0,0 @@ -. - -/** - * Settings for the tiny_mediacms plugin. - * - * @package tiny_mediacms - * @copyright 2024 - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -if ($ADMIN->fulltree) { - global $DB; - - // LTI Tool ID setting (Dropdown). - $ltioptions = [0 => get_string('noltitoolsfound', 'tiny_mediacms')]; - try { - $tools = $DB->get_records('lti_types', null, 'name ASC', 'id, name, baseurl'); - if (!empty($tools)) { - $ltioptions = [0 => get_string('choose', 'tiny_mediacms')]; - foreach ($tools as $tool) { - $ltioptions[$tool->id] = $tool->name . ' (' . $tool->baseurl . ')'; - } - } - } catch (Exception $e) { - // Database might not be ready during install - } - - $setting = new admin_setting_configselect( - 'tiny_mediacms/ltitoolid', - new lang_string('ltitoolid', 'tiny_mediacms'), - new lang_string('ltitoolid_desc', 'tiny_mediacms'), - 0, - $ltioptions - ); - $settings->add($setting); - - // Auto-convert is enabled by default in plugininfo.php (data.autoConvertEnabled = true). - - // MediaCMS base URL for auto-convert. - $setting = new admin_setting_configtext( - 'tiny_mediacms/autoconvert_baseurl', - new lang_string('autoconvert_baseurl', 'tiny_mediacms'), - new lang_string('autoconvert_baseurl_desc', 'tiny_mediacms'), - 'https://lti.mediacms.io', // Default matching filter - PARAM_URL - ); - $settings->add($setting); - - // Auto-convert embed options. - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_showtitle', - new lang_string('autoconvert_showtitle', 'tiny_mediacms'), - new lang_string('autoconvert_showtitle_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); - - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_linktitle', - new lang_string('autoconvert_linktitle', 'tiny_mediacms'), - new lang_string('autoconvert_linktitle_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); - - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_showrelated', - new lang_string('autoconvert_showrelated', 'tiny_mediacms'), - new lang_string('autoconvert_showrelated_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); - - $setting = new admin_setting_configcheckbox( - 'tiny_mediacms/autoconvert_showuseravatar', - new lang_string('autoconvert_showuseravatar', 'tiny_mediacms'), - new lang_string('autoconvert_showuseravatar_desc', 'tiny_mediacms'), - 1 - ); - $settings->add($setting); -} diff --git a/lms-plugins/mediacms-moodle/tiny/styles.css b/lms-plugins/mediacms-moodle/tiny/styles.css deleted file mode 100755 index 56eed0b7..00000000 --- a/lms-plugins/mediacms-moodle/tiny/styles.css +++ /dev/null @@ -1,83 +0,0 @@ -#tiny_mediacms_form { - padding: 1rem; -} - -#tiny_mediacms_form #id_deletefileshdr { - display: none; -} - -#tiny_mediacms_form.has-unused-files #id_deletefileshdr { - display: block; -} - -#tiny_mediacms_form #id_missingfileshdr { - display: none; -} - -#tiny_mediacms_form.has-missing-files #id_missingfileshdr { - display: block; -} - -iframe.mmcms_iframe { - height: 650px; - border: none; - width: 100%; -} - -.missing-files ol { - padding-left: 15px; -} - -.missing-files ol li { - font-style: italic; - font-weight: 600; - color: red; -} - -.tiny_imagecms_form .tiny_imagecms_dropzone_container { - height: 200px; -} - -.tiny_imagecms_form .tiny_imagecms_dropzone_container .dropzone-label { - font-size: 1.25rem; -} - -.tiny_imagecms_form .tiny_imagecms_loader_container { - height: 200px; -} - -.tiny_imagecms_form .tiny_imagecms_preview_box { - height: 300px; - display: flex; - justify-content: center; - align-items: center; - overflow: hidden; -} - -.tiny_imagecms_form .tiny_imagecms_deleteicon { - position: absolute; - top: 5px; - right: 5px; - cursor: pointer; - z-index: 1; - width: 30px; - height: 30px; - background: rgba(255, 255, 255, 1); - border-radius: 50%; - padding: 4px 5px 5px 9px; -} - -.tiny_imagecms_form .tiny_imagecms_deleteicon .fa-trash { - color: #1d2125; -} - - -label.form-check-label { - margin-left: 8px; -} - -@media (max-width: 767px) { - .tiny_imagecms_form .tiny_imagecms_properties_col { - padding: 0; - } -} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_audio.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_audio.mustache deleted file mode 100755 index 8e596063..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_audio.mustache +++ /dev/null @@ -1,41 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_audio - - Embed media audio template. - - Example context (json): - { - - } -}} -  - {{#sources}}{{/sources}} - {{#tracks}} - - {{/tracks}} - {{#description}}{{.}}{{/description}} -  diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_link.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_link.mustache deleted file mode 100755 index d87538a6..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_link.mustache +++ /dev/null @@ -1,33 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_link - - Embed media link template. - - Example context (json): - { - - } -}} -{{! - }}{{#name}}{{.}}{{/name}}{{! - }}{{^name}}{{url}}{{/name}}{{! -}} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal.mustache deleted file mode 100755 index 6ae910f5..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal.mustache +++ /dev/null @@ -1,78 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal - - Embed media modal template. - - Example context (json): - { - - } -}} -{{< core/modal }} - - {{$title}} - {{#str}} modaltitle, tiny_h5p {{/str}} - {{/title}} - - {{$body}} -
- -
- -
- {{> tiny_mediacms/embed_media_modal_video}} -
-
- {{> tiny_mediacms/embed_media_modal_audio}} -
-
-
- {{/body}} - - {{$footer}} - - - {{/footer}} - -{{/ core/modal }} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_audio.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_audio.mustache deleted file mode 100755 index 2c350cec..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_audio.mustache +++ /dev/null @@ -1,802 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal_audio - - Embed media audio modal template. - - Example context (json): - { - - } -}} -{{#audio.sources}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/audio.sources}} -{{^audio}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/audio}} - - - diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_link.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_link.mustache deleted file mode 100755 index b5fe580b..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_link.mustache +++ /dev/null @@ -1,43 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal_link - - Embed media link modal template. - - Example context (json): - { - - } -}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_video.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_video.mustache deleted file mode 100755 index 0aa73e3f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_modal_video.mustache +++ /dev/null @@ -1,832 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_modal_video - - Embed media video modal template. - - Example context (json): - { - - } -}} -{{#video.sources}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/video.sources}} -{{^video}} -
-
- -
- - {{#showfilepicker}} - - - - {{/showfilepicker}} -
-
-
- - {{#str}} addsource, tiny_mediacms {{/str}} - - {{#addsourcehelpicon}} - {{> core/help_icon }} - {{/addsourcehelpicon}} -
- -
-{{/video}} - - - diff --git a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_video.mustache b/lms-plugins/mediacms-moodle/tiny/templates/embed_media_video.mustache deleted file mode 100755 index 756c3e8f..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/embed_media_video.mustache +++ /dev/null @@ -1,50 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/embed_media_video - - Embed media video template. - - Example context (json): - { - - } -}} -  - {{#sources}} - - {{/sources}} - {{#tracks}} - - {{/tracks}} - {{#description}}{{.}}{{/description}} -  diff --git a/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_modal.mustache b/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_modal.mustache deleted file mode 100755 index 6726bc99..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_modal.mustache +++ /dev/null @@ -1,134 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_embed_modal - - Iframe embed modal template. - - Example context (json): - { - "elementid": "editor1", - "isupdating": false - } -}} -{{< core/modal }} - - {{$body}} -
- - - - -
- -
-
-
- -
- -
- - -
- {{#str}} iframeurlinvalid, tiny_mediacms {{/str}} -
-
- - {{> tiny_mediacms/iframe_embed_options }} -
- - -
- -
-
- {{#str}} iframeurlplaceholder, tiny_mediacms {{/str}} -
-
-
-
-
-
- - -
-
-
-

{{#str}} libraryloading, tiny_mediacms {{/str}}

-
-
-
- {{#str}} libraryloading, tiny_mediacms {{/str}} -
-

{{#str}} libraryloading, tiny_mediacms {{/str}}

-
- -
-
-
-
- {{/body}} - - {{$footer}} - - {{#isupdating}} - - {{/isupdating}} - - {{/footer}} - -{{/ core/modal }} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_options.mustache b/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_options.mustache deleted file mode 100755 index ac7ddfd8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_options.mustache +++ /dev/null @@ -1,127 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_embed_options - - Embed options partial for iframe modal. - - Example context (json): - { - "elementid": "editor1", - "showTitle": true, - "linkTitle": true, - "showRelated": true, - "showUserAvatar": true, - "responsive": true, - "startAtEnabled": false, - "startAt": "0:00" - } -}} - -
- -
-
-
- - -
-
- - -
-
- - -
-
-
-
- - -
-
- - -
-
- - - -
-
-
-
- - -
- - -
- - -
- -
-
-
- - px -
- {{#str}} width, tiny_mediacms {{/str}} -
-
-
- - px -
- {{#str}} height, tiny_mediacms {{/str}} -
-
-
diff --git a/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_output.mustache b/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_output.mustache deleted file mode 100755 index 1c9edfad..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/iframe_embed_output.mustache +++ /dev/null @@ -1,36 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_embed_output - - Iframe embed output template. - - Example context (json): - { - "src": "https://example.com/embed?m=abc123", - "width": 560, - "height": 315, - "responsive": true, - "aspectRatioClass": "ratio-16-9" - } -}} -{{#responsive}} - -{{/responsive}} -{{^responsive}} - -{{/responsive}} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library.mustache b/lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library.mustache deleted file mode 100755 index 404af4dd..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library.mustache +++ /dev/null @@ -1,91 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_video_library - - Video library browser for iframe modal. - - Example context (json): - { - "elementid": "editor1" - } -}} -
- -
-
-
- - -
-
-
- -
-
- - -
- -
-
- {{#str}} loading, tiny_mediacms {{/str}} -
-

{{#str}} libraryloading, tiny_mediacms {{/str}}

-
- - -
- -
- - -
- -

{{#str}} libraryempty, tiny_mediacms {{/str}}

-
- - -
- -

{{#str}} libraryerror, tiny_mediacms {{/str}}

- -
-
- - -
- - - -
-
diff --git a/lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library_item.mustache b/lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library_item.mustache deleted file mode 100755 index 6cb41e30..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/iframe_video_library_item.mustache +++ /dev/null @@ -1,73 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/iframe_video_library_item - - Single video item in the library grid. - - Example context (json): - { - "id": "abc123", - "title": "My Video", - "thumbnail": "https://example.com/thumb.jpg", - "duration": "2:30", - "views": "150 views", - "date": "2 weeks ago", - "embedUrl": "https://example.com/embed?m=abc123" - } -}} -
-
-
- {{title}} - {{#duration}} - - {{duration}} - - {{/duration}} - -
- -
-
-
-
- {{title}} -
-

- {{#views}}{{views}} • {{/views}}{{date}} -

-
-
-
- diff --git a/lms-plugins/mediacms-moodle/tiny/templates/image.mustache b/lms-plugins/mediacms-moodle/tiny/templates/image.mustache deleted file mode 100755 index 99be5816..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/image.mustache +++ /dev/null @@ -1,34 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/image - - Image template. - - Example context (json): - { - - } -}} -{{alt}} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal.mustache b/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal.mustache deleted file mode 100755 index bf5a78d2..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal.mustache +++ /dev/null @@ -1,61 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal - - Insert image template. - - Example context (json): - { - "uniqid": 0, - "elementid": "exampleId", - "loading": {}, - "title": "Insert image" - } -}} -{{< core/modal }} - {{$body}} -
- - - - - -
-
- - {{> core/loading }} - -
{{#str}} loading, tiny_mediacms {{/str}}
-
-
-
-
- {{/body}} - - {{$footer}} - - {{/footer}} -{{/ core/modal }} diff --git a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details.mustache b/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details.mustache deleted file mode 100755 index 36d4f608..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details.mustache +++ /dev/null @@ -1,112 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_details - - Insert image details body template. - - Example context (json): - { - "elementid": "exampleId", - "alt": "Image description", - "presentation": true, - "width": 600, - "height": 400, - "customStyle": "", - "sizecustomhelpicon": { - "text": "Help text" - } - } - -}} -
-
-
- -
- - -
- -
- -
- - -
- -
- - - -
- 0 - / 125 -
-
-
- -
- -
- - -
- -
- - -
- -
- - -
- -
- -
-
- -
-
- - -
-
- -
X
- -
-
- - -
-
-
{{#sizecustomhelpicon}}{{> core/help_icon }}{{/sizecustomhelpicon}}
-
-
- -
- - -
-
-
-
-
-
diff --git a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details_footer.mustache b/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details_footer.mustache deleted file mode 100755 index 9fd5fd71..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_details_footer.mustache +++ /dev/null @@ -1,42 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_details_footer - - Insert image details footer template. - - Example context (json): - { - "elementid": "exampleId" - } - -}} -
- -
- - -
- -
- - - - -
-
- diff --git a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert.mustache b/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert.mustache deleted file mode 100755 index 8a0b091c..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert.mustache +++ /dev/null @@ -1,53 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_insert - - Insert image body template. - - Example context (json): - { - "showdropzone": true, - "showfilepicker": true - } - -}} -
-
- {{#showdropzone}} -
- - - {{/showdropzone}} - {{^showdropzone}} -
- - - -
-

- {{#showfilepicker}} - {{#str}} repositoryuploadnotpermitted, tiny_mediacms {{/str}}

- {{/showfilepicker}} - {{^showfilepicker}} - {{#str}} repositorynotpermitted, tiny_mediacms {{/str}} - {{/showfilepicker}} -
-
- {{/showdropzone}} -
-
diff --git a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert_footer.mustache b/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert_footer.mustache deleted file mode 100755 index bda98412..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/insert_image_modal_insert_footer.mustache +++ /dev/null @@ -1,56 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/insert_image_modal_insert_footer - - Insert image footer template. - - Example context (json): - { - "showdropzone": true, - "elementid": "exampleId", - "src": "https://moodle.org/logo.png", - "showfilepicker": true - } - -}} -
- -
- - {{#showdropzone}} - - {{/showdropzone}} - {{^showdropzone}} - - {{/showdropzone}} - - - - -
- -
- - - {{#showfilepicker}} - - - {{/showfilepicker}} -
-
- diff --git a/lms-plugins/mediacms-moodle/tiny/templates/missingfiles.mustache b/lms-plugins/mediacms-moodle/tiny/templates/missingfiles.mustache deleted file mode 100755 index cbde9013..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/missingfiles.mustache +++ /dev/null @@ -1,34 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/missingfiles - - Insert image template. - - Example context (json): - { - "missingFiles": [ - "Example.png", - "Another.mov" - ] - } -}} -
    -{{#missingFiles}} -
  1. {{.}}
  2. -{{/missingFiles}} -
diff --git a/lms-plugins/mediacms-moodle/tiny/templates/mm2_iframe.mustache b/lms-plugins/mediacms-moodle/tiny/templates/mm2_iframe.mustache deleted file mode 100755 index 5aee7a68..00000000 --- a/lms-plugins/mediacms-moodle/tiny/templates/mm2_iframe.mustache +++ /dev/null @@ -1,28 +0,0 @@ -{{! - This file is part of Moodle - http://moodle.org/ - - Moodle is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 3 of the License, or - (at your option) any later version. - - Moodle is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with Moodle. If not, see . -}} -{{! - @template tiny_mediacms/mm2_iframe - - Insert image template. - - Example context (json): - { - - } -}} - diff --git a/lms-plugins/mediacms-moodle/tiny/tests/behat/image.feature b/lms-plugins/mediacms-moodle/tiny/tests/behat/image.feature deleted file mode 100755 index 1ec162c8..00000000 --- a/lms-plugins/mediacms-moodle/tiny/tests/behat/image.feature +++ /dev/null @@ -1,74 +0,0 @@ -@editor @editor_tiny @tiny_mediacms @javascript -Feature: Use the TinyMCE editor to upload an image - In order to work with images - As a user - I need to be able to upload and manipulate images - - Scenario: Clicking on the Image button in the TinyMCE editor opens the image dialog - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - Then "Insert image" "dialogue" should exist - - Scenario: Browsing repositories in the TinyMCE editor opens the image dialog and shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - Then "File picker" "dialogue" should exist - - Scenario: Focus returns to the correct location after closing a nested FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - And I press "Browse repositories" - When I press the escape key - Then the focused element is "Browse repositories" "button" - - @_file_upload @test_tiny - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker and upload url image - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - And I upload "/lib/editor/tiny/tests/behat/fixtures/tinyscreenshot.png" to the file picker for TinyMCE - # Note: This needs to be replaced with a label. - Then ".tiny_image_preview" "css_element" should be visible - - @_file_upload - Scenario: Insert image to the TinyMCE editor - Given I log in as "admin" - And I open my profile in edit mode - And I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE - And I set the field "How would you describe this image to someone who can't see it?" to "It's the Moodle" - And I click on "Save" "button" in the "Image details" "dialogue" - When I select the "img" element in position "0" of the "Description" TinyMCE editor - And I click on the "Image" button for the "Description" TinyMCE editor - Then the field "How would you describe this image to someone who can't see it?" matches value "It's the Moodle" - # Note: This needs to be replaced with a label. - And ".tiny_image_preview" "css_element" should be visible - - @_file_upload - Scenario: Resizing the image uses the original and custom sizes and the keep proportion checkbox - Given I log in as "admin" - And I open my profile in edit mode - And I click on the "Image" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert image" "dialogue" - And I upload "lib/editor/tiny/tests/behat/fixtures/moodle-logo.png" to the file picker for TinyMCE - And I click on "This image is decorative only" "checkbox" - And I click on "Save" "button" in the "Image details" "dialogue" - When I select the "img" element in position "0" of the "Description" TinyMCE editor - And I click on the "Image" button for the "Description" TinyMCE editor - Then the field "Original size" matches value "1" - And I click on "Custom size" "radio" - Then the field "Keep proportion" matches value "1" - And I click on "Keep proportion" "checkbox" - And I set the field "Width" to "102" - And I click on "Save" "button" in the "Image details" "dialogue" - When I select the "img" element in position "0" of the "Description" TinyMCE editor - And I click on the "Image" button for the "Description" TinyMCE editor - Then the field "Custom size" matches value "1" - And the field "Width" matches value "102" - And the field "Keep proportion" matches value "0" diff --git a/lms-plugins/mediacms-moodle/tiny/tests/behat/video.feature b/lms-plugins/mediacms-moodle/tiny/tests/behat/video.feature deleted file mode 100755 index f8d72890..00000000 --- a/lms-plugins/mediacms-moodle/tiny/tests/behat/video.feature +++ /dev/null @@ -1,58 +0,0 @@ -@editor @editor_tiny @tiny_mediacms @javascript -Feature: Use the TinyMCE editor to upload a video - In order to work with videos - As a user - I need to be able to upload and manipulate videos - - Scenario: Clicking on the Video button in the TinyMCE editor opens the video dialog - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - Then "Insert media" "dialogue" should exist - - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Browse repositories" "button" in the "Insert media" "dialogue" - Then "File picker" "dialogue" should exist - - @_file_upload - Scenario: Browsing repositories in the TinyMCE editor shows the FilePicker - Given I log in as "admin" - And I open my profile in edit mode - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I follow "Video" - And I click on "Browse repositories..." "button" in the "#id_description_editor_video .tiny_media_source.tiny_media_media_source" "css_element" - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - When I click on "Insert media" "button" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - - @_file_upload - Scenario: Insert and update video in the TinyMCE editor - Given I log in as "admin" - And I open my profile in edit mode - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And I follow "Video" - And I click on "Browse repositories..." "button" in the "#id_description_editor_video .tiny_media_source.tiny_media_media_source" "css_element" - And I upload "/lib/editor/tiny/tests/behat/fixtures/moodle-logo.mp4" to the file picker for TinyMCE - And I click on "Insert media" "button" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - When I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Display options" "link" - And I set the field "Title" to "Test title" - And I click on "Advanced settings" "link" - And I click on "Play automatically" "checkbox" - And I click on "Muted" "checkbox" - And I click on "Loop" "checkbox" - Then "Insert media" "button" should not exist in the "Insert media" "dialogue" - And "Update media" "button" should exist in the "Insert media" "dialogue" - And I click on "Update media" "button" - And I select the "video" element in position "1" of the "Description" TinyMCE editor - And I click on the "Multimedia" button for the "Description" TinyMCE editor - And I click on "Display options" "link" - And the field "Title" matches value "Test title" - And I click on "Advanced settings" "link" - And the field "Play automatically" matches value "1" - And the field "Muted" matches value "1" - And the field "Loop" matches value "1" diff --git a/lms-plugins/mediacms-moodle/tiny/version.php b/lms-plugins/mediacms-moodle/tiny/version.php deleted file mode 100755 index e2eae3ca..00000000 --- a/lms-plugins/mediacms-moodle/tiny/version.php +++ /dev/null @@ -1,30 +0,0 @@ -. - -/** - * Tiny media plugin version details. - * - * @package tiny_media - * @copyright 2022 Huong Nguyen - * @license http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later - */ - -defined('MOODLE_INTERNAL') || die(); - -$plugin->version = 2026020200; // Updated 2026-02-02 -$plugin->requires = 2024100100; -$plugin->component = 'tiny_mediacms'; -$plugin->dependencies = ['filter_mediacms' => 2026020100]; // Keep dependency on our filter