Proyectos de Subversion Moodle

Rev

| Ultima modificación | Ver Log |

Rev Autor Línea Nro. Línea
1441 ariadna 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
/**
18
 * Backward compatibility for Bootstrap 5.
19
 *
20
 * This module silently adapts the current page to Bootstrap 5.
21
 * When the Boostrap 4 backward compatibility period ends in MDL-84465,
22
 * this module will be removed.
23
 *
24
 * @module     theme_boost/bs4-compat
25
 * @copyright  2025 Mikel Martín <mikel@moodle.com>
26
 * @license    http://www.gnu.org/copyleft/gpl.html GNU GPL v3 or later
27
 * @deprecated since Moodle 5.0
28
 * @todo       Final deprecation in Moodle 6.0. See MDL-84465.
29
 */
30
 
31
import {DefaultAllowlist} from './bootstrap/util/sanitizer';
32
import Popover from 'theme_boost/bootstrap/popover';
33
import Tooltip from 'theme_boost/bootstrap/tooltip';
34
import log from 'core/log';
35
 
36
/**
37
 * List of Bootstrap 4 elements to replace with Bootstrap 5 elements.
38
 * This list is based on the Bootstrap 4 to 5 migration guide:
39
 * https://getbootstrap.com/docs/5.0/migration/
40
 *
41
 * The list is not exhaustive and it will be updated as needed.
42
 */
43
const bootstrapElements = [
44
    {
45
        selector: '.alert button.close',
46
        replacements: [
47
            {bs4: 'data-dismiss', bs5: 'data-bs-dismiss'},
48
        ],
49
    },
50
    {
51
        selector: '[data-toggle="modal"]',
52
        replacements: [
53
            {bs4: 'data-toggle', bs5: 'data-bs-toggle'},
54
            {bs4: 'data-target', bs5: 'data-bs-target'},
55
        ],
56
    },
57
    {
58
        selector: '.modal .modal-header button.close',
59
        replacements: [
60
            {bs4: 'data-dismiss', bs5: 'data-bs-dismiss'},
61
        ],
62
    },
63
    {
64
        selector: '[data-toggle="dropdown"]',
65
        replacements: [
66
            {bs4: 'data-toggle', bs5: 'data-bs-toggle'},
67
        ],
68
    },
69
    {
70
        selector: '[data-toggle="collapse"]',
71
        replacements: [
72
            {bs4: 'data-toggle', bs5: 'data-bs-toggle'},
73
            {bs4: 'data-target', bs5: 'data-bs-target'},
74
            {bs4: 'data-parent', bs5: 'data-bs-parent'},
75
        ],
76
    },
77
    {
78
        selector: '.carousel [data-slide]',
79
        replacements: [
80
            {bs4: 'data-slide', bs5: 'data-bs-slide'},
81
            {bs4: 'data-target', bs5: 'data-bs-target'},
82
        ],
83
    },
84
    {
85
        selector: '[data-toggle="tooltip"]',
86
        replacements: [
87
            {bs4: 'data-toggle', bs5: 'data-bs-toggle'},
88
            {bs4: 'data-placement', bs5: 'data-bs-placement'},
89
            {bs4: 'data-animation', bs5: 'data-bs-animation'},
90
            {bs4: 'data-delay', bs5: 'data-bs-delay'},
91
            {bs4: 'data-title', bs5: 'data-bs-title'},
92
            {bs4: 'data-html', bs5: 'data-bs-html'},
93
            {bs4: 'data-trigger', bs5: 'data-bs-trigger'},
94
            {bs4: 'data-selector', bs5: 'data-bs-selector'},
95
            {bs4: 'data-container', bs5: 'data-bs-container'},
96
        ],
97
    },
98
    {
99
        selector: '[data-toggle="popover"]',
100
        replacements: [
101
            {bs4: 'data-toggle', bs5: 'data-bs-toggle'},
102
            {bs4: 'data-content', bs5: 'data-bs-content'},
103
            {bs4: 'data-placement', bs5: 'data-bs-placement'},
104
            {bs4: 'data-animation', bs5: 'data-bs-animation'},
105
            {bs4: 'data-delay', bs5: 'data-bs-delay'},
106
            {bs4: 'data-title', bs5: 'data-bs-title'},
107
            {bs4: 'data-html', bs5: 'data-bs-html'},
108
            {bs4: 'data-trigger', bs5: 'data-bs-trigger'},
109
            {bs4: 'data-selector', bs5: 'data-bs-selector'},
110
            {bs4: 'data-container', bs5: 'data-bs-container'},
111
        ],
112
    },
113
    {
114
        selector: '[data-toggle="tab"]',
115
        replacements: [
116
            {bs4: 'data-toggle', bs5: 'data-bs-toggle'},
117
            {bs4: 'data-target', bs5: 'data-bs-target'},
118
        ],
119
    },
120
];
121
 
122
/**
123
 * Replace Bootstrap 4 attributes with Bootstrap 5 attributes.
124
 *
125
 * @param {HTMLElement} container The element to search for Bootstrap 4 elements.
126
 */
127
const replaceBootstrap4Attributes = (container) => {
128
    for (const bootstrapElement of bootstrapElements) {
129
        const elements = container.querySelectorAll(bootstrapElement.selector);
130
        for (const element of elements) {
131
            for (const replacement of bootstrapElement.replacements) {
132
                if (element.hasAttribute(replacement.bs4)) {
133
                    element.setAttribute(replacement.bs5, element.getAttribute(replacement.bs4));
134
                    element.removeAttribute(replacement.bs4);
135
                    log.debug(`Silent Bootstrap 4 to 5 compatibility: ${replacement.bs4} replaced by ${replacement.bs5}`);
136
                    log.debug(element);
137
                }
138
            }
139
        }
140
    }
141
};
142
 
143
/**
144
 * Ensure Bootstrap 4 components are initialized.
145
 *
146
 * Some elements (tooltip and popovers) needs to be initialized manually after adding the data attributes.
147
 *
148
 * @param {HTMLElement} container The element to search for Bootstrap 4 elements.
149
 */
150
const initializeBootsrap4Components = (container) => {
151
    const popoverConfig = {
152
        container: 'body',
153
        trigger: 'focus',
154
        allowList: Object.assign(DefaultAllowlist, {table: [], thead: [], tbody: [], tr: [], th: [], td: []}),
155
    };
156
    container.querySelectorAll('[data-bs-toggle="popover"]').forEach((tooltipTriggerEl) => {
157
        const popOverInstance = Popover.getInstance(tooltipTriggerEl);
158
        if (!popOverInstance) {
159
            new Popover(tooltipTriggerEl, popoverConfig);
160
        }
161
    });
162
 
163
    container.querySelectorAll('[data-bs-toggle="tooltip"]').forEach((tooltipTriggerEl) => {
164
        const tooltipInstance = Tooltip.getInstance(tooltipTriggerEl);
165
        if (!tooltipInstance) {
166
            new Tooltip(tooltipTriggerEl);
167
        }
168
    });
169
};
170
 
171
/**
172
 * Init Bootstrap 4 compatibility.
173
 *
174
 * @deprecated since Moodle 5.0
175
 * @param {HTMLElement} element The element to search for Bootstrap 4 elements.
176
 */
177
export const init = (element) => {
178
    if (!element) {
179
        element = document;
180
    }
181
    replaceBootstrap4Attributes(element);
182
    initializeBootsrap4Components(element);
183
};