| 1 | efrain | 1 | // This file is part of Moodle - http://moodle.org/
 | 
        
           |  |  | 2 | //
 | 
        
           |  |  | 3 | // Moodle is free software: you can redistribute it and/or modify
 | 
        
           |  |  | 4 | // it under the terms of the GNU General Public License as published by
 | 
        
           |  |  | 5 | // the Free Software Foundation, either version 3 of the License, or
 | 
        
           |  |  | 6 | // (at your option) any later version.
 | 
        
           |  |  | 7 | //
 | 
        
           |  |  | 8 | // Moodle is distributed in the hope that it will be useful,
 | 
        
           |  |  | 9 | // but WITHOUT ANY WARRANTY; without even the implied warranty of
 | 
        
           |  |  | 10 | // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 | 
        
           |  |  | 11 | // GNU General Public License for more details.
 | 
        
           |  |  | 12 | //
 | 
        
           |  |  | 13 | // You should have received a copy of the GNU General Public License
 | 
        
           |  |  | 14 | // along with Moodle.  If not, see <http://www.gnu.org/licenses/>.
 | 
        
           |  |  | 15 |   | 
        
           |  |  | 16 | /**
 | 
        
           |  |  | 17 |  * Tiny Media commands.
 | 
        
           |  |  | 18 |  *
 | 
        
           |  |  | 19 |  * @module      tiny_media/commands
 | 
        
           |  |  | 20 |  * @copyright   2022 Huong Nguyen <huongnv13@gmail.com>
 | 
        
           |  |  | 21 |  * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
 | 
        
           |  |  | 22 |  */
 | 
        
           |  |  | 23 |   | 
        
           |  |  | 24 | import {getStrings} from 'core/str';
 | 
        
           |  |  | 25 | import {
 | 
        
           |  |  | 26 |     component,
 | 
        
           |  |  | 27 |     imageButtonName,
 | 
        
           |  |  | 28 |     videoButtonName,
 | 
        
           |  |  | 29 |     mediaManagerButtonName
 | 
        
           |  |  | 30 | } from './common';
 | 
        
           |  |  | 31 | import MediaImage from './image';
 | 
        
           |  |  | 32 | import MediaEmbed from './embed';
 | 
        
           |  |  | 33 | import MediaManager from './manager';
 | 
        
           |  |  | 34 | import {getButtonImage} from 'editor_tiny/utils';
 | 
        
           | 1441 | ariadna | 35 | import Selectors from './selectors';
 | 
        
           | 1 | efrain | 36 |   | 
        
           |  |  | 37 | const isImage = (node) => node.nodeName.toLowerCase() === 'img';
 | 
        
           |  |  | 38 | const isVideo = (node) => node.nodeName.toLowerCase() === 'video' || node.nodeName.toLowerCase() === 'audio';
 | 
        
           |  |  | 39 |   | 
        
           |  |  | 40 | const registerImageCommand = (editor, imageButtonText) => {
 | 
        
           |  |  | 41 |     const imageIcon = 'image';
 | 
        
           |  |  | 42 |     const handleImageAction = () => {
 | 
        
           |  |  | 43 |         const mediaImage = new MediaImage(editor);
 | 
        
           |  |  | 44 |         mediaImage.displayDialogue();
 | 
        
           |  |  | 45 |     };
 | 
        
           |  |  | 46 |   | 
        
           |  |  | 47 |     // Register the Menu Button as a toggle.
 | 
        
           |  |  | 48 |     // This means that when highlighted over an existing Media Image element it will show as toggled on.
 | 
        
           |  |  | 49 |     editor.ui.registry.addToggleButton(imageButtonName, {
 | 
        
           |  |  | 50 |         icon: imageIcon,
 | 
        
           |  |  | 51 |         tooltip: imageButtonText,
 | 
        
           |  |  | 52 |         onAction: handleImageAction,
 | 
        
           |  |  | 53 |         onSetup: api => {
 | 
        
           |  |  | 54 |             return editor.selection.selectorChangedWithUnbind(
 | 
        
           |  |  | 55 |                 'img:not([data-mce-object]):not([data-mce-placeholder]),figure.image',
 | 
        
           |  |  | 56 |                 api.setActive
 | 
        
           |  |  | 57 |             ).unbind;
 | 
        
           |  |  | 58 |         }
 | 
        
           |  |  | 59 |     });
 | 
        
           |  |  | 60 |   | 
        
           |  |  | 61 |     editor.ui.registry.addMenuItem(imageButtonName, {
 | 
        
           |  |  | 62 |         icon: imageIcon,
 | 
        
           |  |  | 63 |         text: imageButtonText,
 | 
        
           |  |  | 64 |         onAction: handleImageAction,
 | 
        
           |  |  | 65 |     });
 | 
        
           |  |  | 66 |   | 
        
           |  |  | 67 |     editor.ui.registry.addContextToolbar(imageButtonName, {
 | 
        
           |  |  | 68 |         predicate: isImage,
 | 
        
           |  |  | 69 |         items: imageButtonName,
 | 
        
           |  |  | 70 |         position: 'node',
 | 
        
           |  |  | 71 |         scope: 'node'
 | 
        
           |  |  | 72 |     });
 | 
        
           |  |  | 73 |   | 
        
           |  |  | 74 |     editor.ui.registry.addContextMenu(imageButtonName, {
 | 
        
           |  |  | 75 |         update: isImage,
 | 
        
           |  |  | 76 |     });
 | 
        
           | 1441 | ariadna | 77 |   | 
        
           |  |  | 78 |     // Let's check for image file at dragged and dropped and add img-fluid class if it does't have it yet.
 | 
        
           |  |  | 79 |     editor.on('SetContent', function() {
 | 
        
           |  |  | 80 |         const imgs = editor.getBody().querySelectorAll(`img:not(.${Selectors.IMAGE.styles.responsive})`);
 | 
        
           |  |  | 81 |         imgs.forEach(img => img.classList.add(`${Selectors.IMAGE.styles.responsive}`));
 | 
        
           |  |  | 82 |     });
 | 
        
           | 1 | efrain | 83 | };
 | 
        
           |  |  | 84 |   | 
        
           |  |  | 85 | const registerEmbedCommand = (editor, videoButtonText) => {
 | 
        
           |  |  | 86 |     const videoIcon = 'embed';
 | 
        
           |  |  | 87 |     const handleEmbedAction = () => {
 | 
        
           |  |  | 88 |         const mediaImage = new MediaEmbed(editor);
 | 
        
           |  |  | 89 |         mediaImage.displayDialogue();
 | 
        
           |  |  | 90 |     };
 | 
        
           |  |  | 91 |   | 
        
           |  |  | 92 |     // Register the Menu Button as a toggle.
 | 
        
           |  |  | 93 |     // This means that when highlighted over an existing Media Video element it will show as toggled on.
 | 
        
           |  |  | 94 |     editor.ui.registry.addToggleButton(videoButtonName, {
 | 
        
           |  |  | 95 |         icon: videoIcon,
 | 
        
           |  |  | 96 |         tooltip: videoButtonText,
 | 
        
           |  |  | 97 |         onAction: handleEmbedAction,
 | 
        
           |  |  | 98 |         onSetup: api => {
 | 
        
           |  |  | 99 |             return editor.selection.selectorChangedWithUnbind(
 | 
        
           |  |  | 100 |                 'video:not([data-mce-object]):not([data-mce-placeholder]),' +
 | 
        
           | 1441 | ariadna | 101 |                 'audio:not([data-mce-object]):not([data-mce-placeholder]),' +
 | 
        
           |  |  | 102 |                 'a:not([data-mce-object]):not([data-mce-placeholder])[class=external-media-provider]',
 | 
        
           | 1 | efrain | 103 |                 api.setActive
 | 
        
           |  |  | 104 |             ).unbind;
 | 
        
           |  |  | 105 |         }
 | 
        
           |  |  | 106 |     });
 | 
        
           |  |  | 107 |   | 
        
           |  |  | 108 |     editor.ui.registry.addMenuItem(videoButtonName, {
 | 
        
           |  |  | 109 |         icon: videoIcon,
 | 
        
           |  |  | 110 |         text: videoButtonText,
 | 
        
           |  |  | 111 |         onAction: handleEmbedAction,
 | 
        
           |  |  | 112 |     });
 | 
        
           |  |  | 113 |   | 
        
           |  |  | 114 |     editor.ui.registry.addContextMenu(videoButtonName, {
 | 
        
           |  |  | 115 |         update: isVideo,
 | 
        
           |  |  | 116 |     });
 | 
        
           |  |  | 117 |   | 
        
           |  |  | 118 |     editor.ui.registry.addContextToolbar(videoButtonName, {
 | 
        
           |  |  | 119 |         predicate: isVideo,
 | 
        
           |  |  | 120 |         items: videoButtonName,
 | 
        
           |  |  | 121 |         position: 'node',
 | 
        
           |  |  | 122 |         scope: 'node'
 | 
        
           |  |  | 123 |     });
 | 
        
           |  |  | 124 |   | 
        
           |  |  | 125 | };
 | 
        
           |  |  | 126 |   | 
        
           |  |  | 127 | const registerManagerCommand = (editor, mediaManagerButtonText, mediaManagerButtonImage) => {
 | 
        
           |  |  | 128 |     const mediaManagerIcon = 'filemanager';
 | 
        
           |  |  | 129 |     const handleMediaManager = () => {
 | 
        
           |  |  | 130 |         const mediaManager = new MediaManager(editor);
 | 
        
           |  |  | 131 |         mediaManager.displayDialogue();
 | 
        
           |  |  | 132 |     };
 | 
        
           |  |  | 133 |   | 
        
           |  |  | 134 |     // Register the Menu Button as a toggle.
 | 
        
           |  |  | 135 |     editor.ui.registry.addIcon(mediaManagerIcon, mediaManagerButtonImage.html);
 | 
        
           |  |  | 136 |     editor.ui.registry.addButton(mediaManagerButtonName, {
 | 
        
           |  |  | 137 |         icon: mediaManagerIcon,
 | 
        
           |  |  | 138 |         tooltip: mediaManagerButtonText,
 | 
        
           |  |  | 139 |         onAction: () => {
 | 
        
           |  |  | 140 |             handleMediaManager();
 | 
        
           |  |  | 141 |         }
 | 
        
           |  |  | 142 |     });
 | 
        
           |  |  | 143 |   | 
        
           |  |  | 144 |     editor.ui.registry.addMenuItem(mediaManagerButtonName, {
 | 
        
           |  |  | 145 |         icon: mediaManagerIcon,
 | 
        
           |  |  | 146 |         text: mediaManagerButtonText,
 | 
        
           |  |  | 147 |         onAction: () => {
 | 
        
           |  |  | 148 |             handleMediaManager();
 | 
        
           |  |  | 149 |         }
 | 
        
           |  |  | 150 |     });
 | 
        
           |  |  | 151 | };
 | 
        
           |  |  | 152 |   | 
        
           |  |  | 153 | export const getSetup = async() => {
 | 
        
           |  |  | 154 |     const [
 | 
        
           |  |  | 155 |         imageButtonText,
 | 
        
           |  |  | 156 |         mediaButtonText,
 | 
        
           |  |  | 157 |         mediaManagerButtonText
 | 
        
           |  |  | 158 |     ] = await getStrings(['imagebuttontitle', 'mediabuttontitle', 'mediamanagerbuttontitle'].map((key) => ({key, component})));
 | 
        
           |  |  | 159 |   | 
        
           |  |  | 160 |     const [
 | 
        
           |  |  | 161 |         mediaManagerButtonImage,
 | 
        
           |  |  | 162 |     ] = await Promise.all([
 | 
        
           |  |  | 163 |         getButtonImage('filemanager', component)
 | 
        
           |  |  | 164 |     ]);
 | 
        
           |  |  | 165 |   | 
        
           |  |  | 166 |     // Note: The function returned here must be synchronous and cannot use promises.
 | 
        
           |  |  | 167 |     // All promises must be resolved prior to returning the function.
 | 
        
           |  |  | 168 |     return (editor) => {
 | 
        
           |  |  | 169 |         registerImageCommand(editor, imageButtonText);
 | 
        
           |  |  | 170 |         registerEmbedCommand(editor, mediaButtonText);
 | 
        
           |  |  | 171 |         registerManagerCommand(editor, mediaManagerButtonText, mediaManagerButtonImage);
 | 
        
           |  |  | 172 |     };
 | 
        
           |  |  | 173 | };
 |