Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1 efrain 1
// This file is part of Moodle - https://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 <https://www.gnu.org/licenses/>.
15
 
16
/**
17
 * Tiny tiny_html for Moodle.
18
 *
19
 * @module      tiny_html/plugin
20
 * @copyright   2023 Matt Porritt <matt.porritt@moodle.com>
21
 * @license     http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
22
 */
23
 
24
import {getTinyMCE} from 'editor_tiny/loader';
25
import {getPluginMetadata} from 'editor_tiny/utils';
26
 
27
import {component, pluginName, codeMirrorStyle} from './common';
28
 
29
/* eslint-disable camelcase */
30
import {html_beautify} from './beautify/beautify-html';
31
import {get_strings} from 'core/str';
32
/* eslint-enable camelcase */
33
import {
34
    EditorState,
35
    EditorView,
36
    basicSetup,
37
    lang,
38
} from './codemirror-lazy';
39
 
40
/**
41
 * Options for the html_beautify function.
42
 * We disable the camelCase check here as these are
43
 * variables that we are passing to the js-beautify library.
44
 */
45
/* eslint-disable camelcase */
46
const beautifyOptions = {
47
    indent_size: 2,
48
    wrap_line_length: 80,
49
    unformatted: [],
50
};
51
/* eslint-enable camelcase */
52
 
53
// Set up the tiny_html Plugin.
54
// eslint-disable-next-line no-async-promise-executor
55
export default new Promise(async(resolve) => {
56
    // Note: The PluginManager.add function does not support asynchronous configuration.
57
    // Perform any asynchronous configuration here, and then call the PluginManager.add function.
58
    const [
59
        tinyMCE,
60
        pluginMetadata,
61
        buttonStrings,
62
    ] = await Promise.all([
63
        getTinyMCE(),
64
        getPluginMetadata(component, pluginName),
65
        get_strings([
66
            {key: 'cancel', component: 'moodle'},
67
            {key: 'save', component: 'moodle'},
68
        ])
69
    ]);
70
 
71
    // Reminder: Any asynchronous code must be run before this point.
72
    tinyMCE.PluginManager.add(pluginName, (editor) => {
73
        // Initial configuration for TinyMCE editor the windowManager.
74
        const windowManagerConfig = {
75
            title: 'Source code',
76
            size: 'large',
77
            body: {
78
                type: 'panel',
79
                items: [
80
                    {
81
                        type: 'htmlpanel',
82
                        html: '<div id="' + editor.id + '_codeMirrorContainer" style="height: 100%;"></div>',
83
                    },
84
                ],
85
            },
86
            buttons: null,
87
            initialData: null,
88
            onSubmit: null,
89
        };
90
 
91
        // Overriding the default 'mceCodeEditor' command
92
        editor.addCommand('mceCodeEditor', () => {
93
            // Get the current content of the editor
94
            // eslint-disable-next-line camelcase
95
            const content = editor.getContent({source_view: true});
96
 
97
            // Beautify the content using html_beautify
98
            const beautifiedContent = html_beautify(content, beautifyOptions);
99
 
100
            // Create the CodeMirror instance
101
            let cmInstance;
102
 
103
            let state = EditorState.create({
104
                doc: beautifiedContent,
105
                // This is where basicSetup should go as [basicSetup, ...].
106
                extensions: [
107
                    basicSetup,
108
                    EditorState.tabSize.of(2),
109
                    // Bring in all language extensions.
110
                    ...Object.entries(lang).map(([, languagePlugin]) => languagePlugin()),
111
                ],
112
            });
113
 
114
            // Create a new window to display the beautified code
115
            editor.windowManager.open({
116
                ...windowManagerConfig,
117
                onSubmit: (api) => {
118
                    const cmContent = cmInstance.state.doc.toString();
119
                    // eslint-disable-next-line camelcase
120
                    editor.setContent(cmContent, {source_view: true});
121
                    api.close();
122
                },
123
                buttons: [
124
                    {
125
                        type: 'cancel',
126
                        text: buttonStrings[0],
127
                    },
128
                    {
129
                        type: 'submit',
130
                        text: buttonStrings[1],
131
                        primary: true,
132
                    },
133
                ]
134
            });
135
 
136
            const container = document.getElementById(editor.id + '_codeMirrorContainer');
137
            // Create a shadow root for the CodeMirror instance.
138
            // This is required to prevent the TinyMCE editor styles from overriding the CodeMirror ones.
139
            const shadowRoot = container.attachShadow({mode: "open"});
140
 
141
            // Add the styles to the shadow root
142
            const style = document.createElement('style');
143
            style.textContent = codeMirrorStyle;
144
            shadowRoot.appendChild(style);
145
 
146
            // Create a new div and add the class 'my-codemirror-container'
147
            const div = document.createElement('div');
148
            div.classList.add('modal-codemirror-container');
149
            shadowRoot.appendChild(div);
150
 
151
            // Create the CodeMirror instance
152
            cmInstance = new EditorView({
153
                state,
154
                parent: div,
155
            });
156
 
157
            // Add an event listener to the shadow root to listen for the tab key press.
158
            shadowRoot.addEventListener('keydown', (event) => {
159
                // If the tab key is pressed, prevent the default action and select the save button.
160
                // We need to do this as the shadow root is not part of the DOM, so the tab key will not
161
                // be caught by the TinyMCE dialog.
162
                if (event.key === 'Tab') {
163
                    event.preventDefault();
164
                    const codeMirrorContainer = document.getElementById(editor.id + '_codeMirrorContainer');
165
                    const dialogElement = codeMirrorContainer.closest('.tox-dialog');
166
                    const cancelButton = dialogElement.querySelector('button[title="' + buttonStrings[1] + '"]');
167
                    cancelButton.focus();
168
                }
169
            });
170
 
171
        });
172
        // Return the pluginMetadata object. This is used by TinyMCE to display a help link for your plugin.
173
        return pluginMetadata;
174
    });
175
 
176
    resolve(pluginName);
177
});