29
0
mirror of https://github.com/joomla/joomla-cms.git synced 2024-06-25 14:53:01 +00:00

[4.0] BS5 JS (#31990)

This commit is contained in:
Dimitris Grammatikogiannis 2021-01-23 23:15:35 +01:00 committed by GitHub
parent 6e8f744d93
commit 88c498b83f
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
43 changed files with 1604 additions and 761 deletions

View File

@ -26,6 +26,9 @@ use Joomla\Utilities\ArrayHelper;
HTMLHelper::_('behavior.multiselect');
// Just for the tests :(
HTMLHelper::_('jquery.framework');
$app = Factory::getApplication();
$user = Factory::getUser();
$userId = $user->get('id');

View File

@ -20,6 +20,9 @@ use Joomla\Component\Fields\Administrator\Helper\FieldsHelper;
HTMLHelper::_('behavior.multiselect');
// Just for the tests :(
HTMLHelper::_('jquery.framework');
$app = Factory::getApplication();
$user = Factory::getUser();
$userId = $user->get('id');

View File

@ -16,6 +16,7 @@ use Joomla\CMS\Uri\Uri;
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('core')
->useScript('jquery')
->useScript('com_joomlaupdate.encryption')
->useScript('com_joomlaupdate.update')
->useScript('com_joomlaupdate.admin-update');

View File

@ -20,6 +20,7 @@ $twofactormethods = AuthenticationHelper::getTwoFactorMethods();
/** @var Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = $this->document->getWebAssetManager();
$wa->useScript('core')
->useScript('jquery')
->useScript('form.validate')
->useScript('keepalive')
->useScript('field.passwordview');

View File

@ -14,6 +14,8 @@ use Joomla\CMS\Language\Text;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Router\Route;
HTMLHelper::_('bootstrap.dropdown');
$listOrder = $this->escape($this->state->get('list.ordering'));
$listDirn = $this->escape($this->state->get('list.direction'));
?>

View File

@ -376,7 +376,7 @@ $copyModalData = array(
);
?>
<form action="<?php echo Route::_('index.php?option=com_templates&task=template.copy&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" method="post" name="adminForm" id="adminForm">
<?php echo LayoutHelper::render('joomla.modal.main', $copyModalData); ?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $copyModalData); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php if ($this->type != 'home') : ?>
@ -391,7 +391,7 @@ $copyModalData = array(
);
?>
<form action="<?php echo Route::_('index.php?option=com_templates&task=template.renameFile&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" method="post">
<?php echo LayoutHelper::render('joomla.modal.main', $renameModalData); ?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $renameModalData); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php endif; ?>
@ -406,7 +406,7 @@ $copyModalData = array(
'body' => $this->loadTemplate('modal_delete_body')
);
?>
<?php echo LayoutHelper::render('joomla.modal.main', $deleteModalData); ?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $deleteModalData); ?>
<?php endif; ?>
<?php // File Modal
$fileModalData = array(
@ -422,7 +422,7 @@ $fileModalData = array(
'body' => $this->loadTemplate('modal_file_body')
);
?>
<?php echo LayoutHelper::render('joomla.modal.main', $fileModalData); ?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $fileModalData); ?>
<?php // Folder Modal
$folderModalData = array(
'selector' => 'folderModal',
@ -437,7 +437,7 @@ $folderModalData = array(
'body' => $this->loadTemplate('modal_folder_body')
);
?>
<?php echo LayoutHelper::render('joomla.modal.main', $folderModalData); ?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $folderModalData); ?>
<?php if ($this->type == 'image') : ?>
<?php // Resize Modal
$resizeModalData = array(
@ -450,7 +450,7 @@ $folderModalData = array(
);
?>
<form action="<?php echo Route::_('index.php?option=com_templates&task=template.resizeImage&id=' . $input->getInt('id') . '&file=' . $this->file); ?>" method="post">
<?php echo LayoutHelper::render('joomla.modal.main', $resizeModalData); ?>
<?php echo LayoutHelper::render('libraries.html.bootstrap.modal.main', $resizeModalData); ?>
<?php echo HTMLHelper::_('form.token'); ?>
</form>
<?php endif; ?>

View File

@ -0,0 +1,155 @@
const {
readdir, readFile, rename, writeFile, rm,
} = require('fs').promises;
const { resolve } = require('path');
const { minify } = require('terser');
const rimraf = require('rimraf');
const rollup = require('rollup');
const { nodeResolve } = require('@rollup/plugin-node-resolve');
const replace = require('@rollup/plugin-replace');
const { babel } = require('@rollup/plugin-babel');
const tasks = [];
const inputFolder = 'build/media_source/vendor/bootstrap/js';
const outputFolder = 'media/vendor/bootstrap/js';
const createMinified = async (file) => {
const initial = await readFile(resolve(outputFolder, file), { encoding: 'utf8' });
const mini = await minify(initial);
await rename(resolve(outputFolder, file), resolve(outputFolder, `${file.split('-')[0]}.es6.js`));
await writeFile(resolve(outputFolder, `${file.split('-')[0]}.es6.min.js`), mini.code, { encoding: 'utf8' });
};
const build = async () => {
// eslint-disable-next-line no-console
console.log('Building ES6 Components...');
const bundle = await rollup.rollup({
input: resolve(inputFolder, 'index.es6.js'),
plugins: [
nodeResolve(),
replace({
'process.env.NODE_ENV': '\'production\'',
}),
],
external: [
'./base-component.js',
'./dom/data.js',
'./event-handler.js',
'./dom/manipulator.js',
'./selector-engine.js',
'./util/index.js',
],
manualChunks: {
alert: ['build/media_source/vendor/bootstrap/js/alert.es6.js'],
button: ['build/media_source/vendor/bootstrap/js/button.es6.js'],
carousel: ['build/media_source/vendor/bootstrap/js/carousel.es6.js'],
collapse: ['build/media_source/vendor/bootstrap/js/collapse.es6.js'],
dropdown: ['build/media_source/vendor/bootstrap/js/dropdown.es6.js'],
modal: ['build/media_source/vendor/bootstrap/js/modal.es6.js'],
popover: ['build/media_source/vendor/bootstrap/js/popover.es6.js'],
scrollspy: ['build/media_source/vendor/bootstrap/js/scrollspy.es6.js'],
tab: ['build/media_source/vendor/bootstrap/js/tab.es6.js'],
toast: ['build/media_source/vendor/bootstrap/js/toast.es6.js'],
popper: ['@popperjs/core'],
dom: [
'node_modules/bootstrap/js/src/base-component.js',
'node_modules/bootstrap/js/src/dom/data.js',
'node_modules/bootstrap/js/src/dom/event-handler.js',
'node_modules/bootstrap/js/src/dom/manipulator.js',
'node_modules/bootstrap/js/src/dom/selector-engine.js',
'node_modules/bootstrap/js/src/util/index.js',
],
},
});
await bundle.write({
format: 'es',
sourcemap: false,
dir: outputFolder,
});
};
const buildLegacy = async () => {
// eslint-disable-next-line no-console
console.log('Building Legacy...');
const bundle = await rollup.rollup({
input: resolve(inputFolder, 'index.es6.js'),
plugins: [
nodeResolve(),
replace({
'process.env.NODE_ENV': '\'production\'',
}),
babel({
exclude: 'node_modules/core-js/**',
babelHelpers: 'bundled',
babelrc: false,
presets: [
[
'@babel/preset-env',
{
corejs: '3.8',
useBuiltIns: 'usage',
targets: {
chrome: '58',
ie: '11',
},
loose: true,
bugfixes: true,
modules: false,
},
],
],
}),
],
external: [],
});
await bundle.write({
format: 'iife',
sourcemap: false,
name: 'Bootstrap',
file: resolve(outputFolder, 'bootstrap.es5.js'),
});
};
(async () => {
rimraf.sync(resolve(outputFolder));
try {
await build(resolve(inputFolder, 'index.es6.js'));
await rm(resolve(outputFolder, 'index.es6.js'));
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
}
(await readdir(outputFolder)).forEach((file) => {
if (!(file.startsWith('dom-') || file.startsWith('popper-'))) {
tasks.push(createMinified(file));
}
});
await Promise.all(tasks).catch((er) => {
// eslint-disable-next-line no-console
console.log(er);
process.exit(1);
});
// eslint-disable-next-line no-console
console.log('ES6 components ready ✅');
try {
await buildLegacy(inputFolder, 'index.es6.js');
const es5File = await readFile(resolve(outputFolder, 'bootstrap.es5.js'), { encoding: 'utf8' });
const mini = await minify(es5File);
await writeFile(resolve(outputFolder, 'bootstrap.es5.min.js'), mini.code, { encoding: 'utf8' });
// eslint-disable-next-line no-console
console.log('Legacy done! ✅');
} catch (error) {
// eslint-disable-next-line no-console
console.error(error);
process.exit(1);
}
})();

View File

@ -1,4 +1,5 @@
const Fs = require('fs');
const { sep } = require('path');
const Recurs = require('recursive-readdir');
const HandleFile = require('./javascript/handle-file.es6.js');
@ -45,6 +46,9 @@ module.exports.compileJS = (options, path) => {
(files) => {
files.forEach(
(file) => {
if (file.includes(`build${sep}media_source${sep}vendor${sep}bootstrap${sep}js`)) {
return;
}
HandleFile.run(file);
},
(error) => {

View File

@ -39,13 +39,6 @@
},
"bootstrap": {
"name": "bootstrap",
"js": {
"dist/js/bootstrap.js": "js/bootstrap.js",
"dist/js/bootstrap.min.js": "js/bootstrap.min.js",
"dist/js/bootstrap.bundle.js": "js/bootstrap.bundle.js",
"dist/js/bootstrap.bundle.min.js": "js/bootstrap.bundle.min.js",
"dist/js/bootstrap.bundle.min.js.map": "js/bootstrap.bundle.min.js.map"
},
"css": {
"dist/css/bootstrap.css": "css/bootstrap.css",
"dist/css/bootstrap.min.css": "css/bootstrap.min.css",
@ -76,22 +69,6 @@
"dependencies": [
"bootstrap.css"
]
},
{
"name": "bootstrap.js",
"type": "script",
"uri": "bootstrap.min.js",
"dependencies": [
"jquery"
]
},
{
"name": "bootstrap.js.bundle",
"type": "script",
"uri": "bootstrap.bundle.min.js",
"dependencies": [
"jquery"
]
}
],
"dependencies": [],

View File

@ -10,7 +10,8 @@
"type": "script",
"uri": "com_joomlaupdate/admin-update-default.min.js",
"dependencies": [
"core"
"core",
"jquery"
],
"attributes": {
"defer": true
@ -21,7 +22,8 @@
"type": "script",
"uri": "com_joomlaupdate/default.min.js",
"dependencies": [
"core"
"core",
"jquery"
],
"attributes": {
"defer": true

View File

@ -38,7 +38,7 @@ Joomla.toggleAllNextElements = (element, className) => {
'use strict';
document.addEventListener('DOMContentLoaded', () => {
const dropDownBtn = document.getElementById('toolbar-dropdown-status-group');
const dropDownBtn = document.getElementById('toolbar-status-group');
const transitions = [].slice.call(dropDownBtn.querySelectorAll('.button-transition'));
const headline = dropDownBtn.querySelector('.button-transition-headline');
const separator = dropDownBtn.querySelector('.button-transition-separator');

View File

@ -1,219 +0,0 @@
Joomla = window.Joomla || {};
(function(Joomla) {
"use strict";
/**
* Method to invoke a click on button inside an iframe
*
* @param {object} options Object with the css selector for the parent element of an iframe
* and the selector of the button in the iframe that will be clicked
* { iframeSelector: '', buttonSelector: '' }
* @returns {boolean}
*
* @since 4.0
*/
Joomla.iframeButtonClick = function(options) {
if (!options.iframeSelector || !options.buttonSelector) {
throw new Error('Selector is missing');
}
var iframe = document.querySelector(options.iframeSelector + ' iframe');
if (iframe) {
var button = iframe.contentWindow.document.querySelector(options.buttonSelector);
if (button) {
button.click();
}
}
};
jQuery(document).ready(function($) {
Joomla.Bootstrap = {};
// Initialize some variables
var accordion = Joomla.getOptions('bootstrap.accordion'),
alert = Joomla.getOptions('bootstrap.alert'),
button = Joomla.getOptions('bootstrap.button'),
carousel = Joomla.getOptions('bootstrap.carousel'),
dropdown = Joomla.getOptions('bootstrap.dropdown'),
modals = [].slice.call(document.querySelectorAll('.joomla-modal')),
popover = Joomla.getOptions('bootstrap.popover'),
scrollspy = Joomla.getOptions('bootstrap.scrollspy'),
tabs = Joomla.getOptions('bootstrap.tabs'),
tooltip = Joomla.getOptions('bootstrap.tooltip');
Joomla.Bootstrap.initModal = function(element) {
var $self = $(element);
// Comply with the Joomla API
// Bound element.open()
if (element) {
element.open = function () {
return $self.modal('show');
};
// Bound element.close()
element.close = function () {
return $self.modal('hide');
};
}
$self.on('show.bs.modal', function() {
// Comply with the Joomla API
// Set the current Modal ID
Joomla.Modal.setCurrent(element);
// @TODO throw the standard Joomla event
if ($self.data('url')) {
var modalBody = $self.find('.modal-body');
var el;
modalBody.find('iframe').remove();
// Hacks because com_associations and field modals use pure javascript in the url!
if ($self.data('iframe').indexOf("document.getElementById") > 0){
var iframeTextArr = $self.data('iframe').split('+');
var idFieldArr = iframeTextArr[1].split('"');
idFieldArr[0] = idFieldArr[0].replace(/&quot;/g,'"');
if (!document.getElementById(idFieldArr[1])) {
el = eval(idFieldArr[0]);
} else {
el = document.getElementById(idFieldArr[1]).value;
}
var data_iframe = iframeTextArr[0] + el + iframeTextArr[2];
modalBody.prepend(data_iframe);
} else {
modalBody.prepend($self.data('iframe'));
}
}
}).on('shown.bs.modal', function() {
var modalHeight = $('div.modal:visible').outerHeight(true),
modalHeaderHeight = $('div.modal-header:visible').outerHeight(true),
modalBodyHeightOuter = $('div.modal-body:visible').outerHeight(true),
modalBodyHeight = $('div.modal-body:visible').height(),
modalFooterHeight = $('div.modal-footer:visible').outerHeight(true),
padding = $self.offsetTop,
maxModalHeight = ($(window).height()-(padding*2)),
modalBodyPadding = (modalBodyHeightOuter-modalBodyHeight),
maxModalBodyHeight = maxModalHeight-(modalHeaderHeight+modalFooterHeight+modalBodyPadding);
if ($self.data('url')) {
var iframeHeight = $('.iframe').height();
if (iframeHeight > maxModalBodyHeight){
$('.modal-body').css({'max-height': maxModalBodyHeight, 'overflow-y': 'auto'});
$('.iframe').css('max-height', maxModalBodyHeight-modalBodyPadding);
}
}
// @TODO throw the standard Joomla event
}).on('hide.bs.modal', function() {
$('.modal-body').css({'max-height': 'initial'});
$('.modalTooltip').tooltip('dispose');
// @TODO throw the standard Joomla event
}).on('hidden.bs.modal', function() {
// Comply with the Joomla API
// Remove the current Modal ID
Joomla.Modal.setCurrent('');
// @TODO throw the standard Joomla event
});
};
/** Accordion **/
if (accordion) {
$.each(accordion, function(index, value) {
$('#' + index).collapse(
{
parent:value.parent,
toggle:value.toggle
}
).on("show", new Function(value.onShow)())
.on("shown", new Function(value.onShown)())
.on("hideme", new Function(value.onHide)())
.on("hidden", new Function(value.onHidden)());
});
}
/** Alert **/
if (alert) {
$.each(alert, function(index, value) {
$('#' + index).alert();
});
}
/** Button **/
if (button) {
$.each(button, function(index, value) {
$('#' + index).button();
});
}
/** Carousel **/
if (carousel) {
$.each(carousel, function(index, value) {
$('#' + index).carousel(
{
interval: value.interval ? value.interval : 5000,
pause: value.pause ? value.pause : 'hover'
}
);
});
}
/** Dropdown menu **/
if (dropdown) {
$.each(dropdown, function(index, value) {
$('#' + index).dropdown();
});
}
/** Modals **/
if (modals.length) {
modals.forEach(function(modal){ Joomla.Bootstrap.initModal(modal); });
}
/** Popover **/
if (popover) {
$.each(popover, function(index, value) {
value.constraints = [value.constraints];
$(index).popover(value);
});
}
/** Scrollspy **/
if (scrollspy) {
$.each(scrollspy, function(index, value) {
$('#' + index).scrollspy(value);
});
}
/** Tabs **/
if (tabs) {
$.each(tabs, function(index, value) {
$.each($('#' + index + 'Content').children('.tab-pane'), function(i, v) {
var titleAttribute = $(v).data('title');
var activeAttribute = $(v).data('active');
var idAttribute = $(v).data('id');
if (titleAttribute) {
var classLink = (activeAttribute != '') ? 'class="nav-link ' + activeAttribute + '"' : 'class="nav-link"';
$('#' + index + 'Tabs').append('<li class="nav-item"><a ' + classLink + ' href="#' + idAttribute + '" data-bs-toggle="tab">' + titleAttribute + '</a></li>');
}
});
});
}
/** Tooltip **/
if (tooltip) {
$.each(tooltip, function(index, value) {
value.constraints = [value.constraints];
$(index).tooltip(value)
.on("show.bs.tooltip", new Function(value.onShow)())
.on("shown.bs.tooltip", new Function(value.onShown)())
.on("hide.bs.tooltip", new Function(value.onHide)())
.on("hidden.bs.tooltip", new Function(value.onHidden)());
});
}
});
})(Joomla);

View File

@ -19,6 +19,9 @@
// Append clone before closing body tag
document.body.appendChild(clone);
// Modal was moved so it needs to be re initialised
Joomla.Bootstrap.Initialise.Modal(clone);
}
});
})();

View File

@ -102,7 +102,7 @@
&& Joomla.Bootstrap.Instances
&& Joomla.Bootstrap.Instances.Modal
&& Joomla.Bootstrap.Instances.Modal.get(this.modalElement) === undefined) {
Joomla.Bootstrap.Methods.initModal(this.modalElement);
Joomla.Bootstrap.Initialise.Modal(this.modalElement, { isJoomla: true });
}
if (this.buttonClearEl) {

View File

@ -59,7 +59,7 @@
&& Joomla.Bootstrap.Instances
&& Joomla.Bootstrap.Instances.Modal
&& Joomla.Bootstrap.Instances.Modal.get(this.modal) === undefined) {
Joomla.Bootstrap.Methods.initModal(this.modal);
Joomla.Bootstrap.Initialise.Modal(this.modal, { isJoomla: true });
}
if (this.buttonSelect) {

View File

@ -0,0 +1,39 @@
import Alert from '../../../../../node_modules/bootstrap/js/src/alert';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Alert = new WeakMap();
/**
* Initialise the Alert iteractivity
*
* @param {HTMLElement} el The element that will become an Alert
*/
Joomla.Bootstrap.Initialise.Alert = (el) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Alert.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Alert.set(el, new Alert(el));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const alerts = Joomla.getOptions('bootstrap.alert');
// Initialise the elements
if (alerts && alerts.length) {
alerts.forEach((selector) => {
Array.from(document.querySelectorAll(selector))
.map((el) => Joomla.Bootstrap.Initialise.Alert(el));
});
}
export default Alert;

View File

@ -0,0 +1,39 @@
import Button from '../../../../../node_modules/bootstrap/js/src/button';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Button = new WeakMap();
/**
* Initialise the Button iteractivity
*
* @param {HTMLElement} el The element that will become an Button
*/
Joomla.Bootstrap.Initialise.Button = (el) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Button.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Button.set(el, new Button(el));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const buttons = Joomla.getOptions('bootstrap.button');
// Initialise the elements
if (buttons && buttons.length) {
buttons.forEach((selector) => {
Array.from(document.querySelectorAll(selector))
.map((el) => Joomla.Bootstrap.Initialise.Button(el));
});
}
export default Button;

View File

@ -0,0 +1,52 @@
import Carousel from '../../../../../node_modules/bootstrap/js/src/carousel';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Carousel = new WeakMap();
/**
* Initialise the Carousel iteractivity
*
* @param {HTMLElement} el The element that will become an Carousel
* @param {object} options The options for this carousel
*/
Joomla.Bootstrap.Initialise.Carousel = (el, options) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Carousel.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Carousel.set(el, new Carousel(el, options));
};
// Get the elements/configurations from the PHP
const carousels = Joomla.getOptions('bootstrap.carousel');
// Force Vanilla mode!
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Initialise the elements
if (typeof carousels === 'object' && carousels !== null) {
Object.keys(carousels).forEach((carousel) => {
const opt = carousels[carousel];
const options = {
interval: opt.interval ? opt.interval : 5000,
keyboard: opt.keyboard ? opt.keyboard : true,
pause: opt.pause ? opt.pause : 'hover',
slide: opt.slide ? opt.slide : false,
wrap: opt.wrap ? opt.wrap : true,
touch: opt.touch ? opt.touch : true,
};
const elements = Array.from(document.querySelectorAll(carousel));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Carousel(el, options));
}
});
}
export default Carousel;

View File

@ -0,0 +1,51 @@
import Collapse from '../../../../../node_modules/bootstrap/js/src/collapse';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Collapse = new WeakMap();
/**
* Initialise the Collapse iteractivity
*
* @param {HTMLElement} el The element that will become an collapse
* @param {object} options The options for this collapse
*/
Joomla.Bootstrap.Initialise.Collapse = (el, options) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Collapse.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Collapse.set(el, new Collapse(el, options));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const collapses = { ...Joomla.getOptions('bootstrap.collapse'), ...Joomla.getOptions('bootstrap.accordion') };
// Initialise the elements
if (typeof collapses === 'object' && collapses !== null) {
Object.keys(collapses).forEach((collapse) => {
const opt = collapses[collapse];
const options = {
toggle: opt.toggle ? opt.toggle : true,
};
if (opt.parent) {
options.parent = opt.parent;
}
const elements = Array.from(document.querySelectorAll(collapse));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Collapse(el, options));
}
});
}
export default Collapse;

View File

@ -0,0 +1,48 @@
import Dropdown from '../../../../../node_modules/bootstrap/js/src/dropdown';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Dropdown = new WeakMap();
/**
* Initialise the iteractivity
*
* @param {HTMLElement} el The element that will become an dropdown
* @param {object} options The options for this dropdown
*/
Joomla.Bootstrap.Initialise.Dropdown = (el, options) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Dropdown.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Dropdown.set(el, new Dropdown(el, options));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const dropdowns = Joomla.getOptions('bootstrap.dropdown');
// Initialise the elements
if (typeof dropdowns === 'object' && dropdowns !== null) {
Object.keys(dropdowns).forEach((dropdown) => {
const opt = dropdowns[dropdown];
const options = {
interval: opt.interval ? opt.interval : 5000,
pause: opt.pause ? opt.pause : 'hover',
};
const elements = Array.from(document.querySelectorAll(dropdown));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Dropdown(el, options));
}
});
}
export default Dropdown;

View File

@ -0,0 +1,23 @@
import Alert from './alert.es6';
import Button from './button.es6';
import Collapse from './collapse.es6';
import Carousel from './carousel.es6';
import Dropdown from './dropdown.es6';
import Modal from './modal.es6';
import { Popover } from './popover.es6';
import Scrollspy from './scrollspy.es6';
import Tab from './tab.es6';
import Toast from './toast.es6';
export {
Alert,
Button,
Collapse,
Carousel,
Dropdown,
Modal,
Popover,
Scrollspy,
Tab,
Toast,
};

View File

@ -0,0 +1,162 @@
import Modal from '../../../../../node_modules/bootstrap/js/src/modal';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Modal = new WeakMap();
Joomla.Bootstrap.Initialise.Modal = (modal, options) => {
if (!(modal instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Modal.get(modal) && modal.dispose) {
modal.dispose();
}
Joomla.Bootstrap.Instances.Modal.set(modal, new Modal(modal, options));
// Comply with the Joomla API - Bound element.open/close
modal.open = () => { Joomla.Bootstrap.Instances.Modal.get(modal).show(modal); };
modal.close = () => { Joomla.Bootstrap.Instances.Modal.get(modal).hide(); };
// Do some Joomla specific changes
modal.addEventListener('show.bs.modal', () => {
// Comply with the Joomla API - Set the current Modal ID
Joomla.Modal.setCurrent(modal);
if (modal.dataset.url) {
const modalBody = modal.querySelector('.modal-body');
const iframe = modalBody.querySelector('iframe');
if (iframe) {
const addData = modal.querySelector('joomla-field-mediamore');
if (addData) {
addData.parentNode.removeChild(addData);
}
iframe.parentNode.removeChild(iframe);
}
// @todo merge https://github.com/joomla/joomla-cms/pull/20788
// Hacks because com_associations and field modals use pure javascript in the url!
if (modal.dataset.iframe.indexOf('document.getElementById') > 0) {
const iframeTextArr = modal.dataset.iframe.split('+');
const idFieldArr = iframeTextArr[1].split('"');
let el;
idFieldArr[0] = idFieldArr[0].replace(/&quot;/g, '"');
if (!document.getElementById(idFieldArr[1])) {
// eslint-disable-next-line no-new-func
el = new Function(idFieldArr[0]); // This is UNSAFE!!!!
} else {
el = document.getElementById(idFieldArr[1]).value;
}
modalBody.insertAdjacentHTML('afterbegin', `${iframeTextArr[0]}${el}${iframeTextArr[2]}`);
} else {
modalBody.insertAdjacentHTML('afterbegin', modal.dataset.iframe);
}
}
});
modal.addEventListener('shown.bs.modal', () => {
const modalBody = modal.querySelector('.modal-body');
const modalHeader = modal.querySelector('.modal-header');
const modalFooter = modal.querySelector('.modal-footer');
let modalHeaderHeight = 0;
let modalFooterHeight = 0;
let maxModalBodyHeight = 0;
let modalBodyPadding = 0;
let modalBodyHeightOuter = 0;
if (modalBody) {
if (modalHeader) {
const modalHeaderRects = modalHeader.getBoundingClientRect();
modalHeaderHeight = modalHeaderRects.height;
modalBodyHeightOuter = modalBody.offsetHeight;
}
if (modalFooter) {
modalFooterHeight = parseFloat(getComputedStyle(modalFooter, null).height.replace('px', ''));
}
const modalBodyHeight = parseFloat(getComputedStyle(modalBody, null).height.replace('px', ''));
const padding = modalBody.offsetTop;
const maxModalHeight = parseFloat(getComputedStyle(document.body, null).height.replace('px', '')) - (padding * 2);
modalBodyPadding = modalBodyHeightOuter - modalBodyHeight;
// eslint-disable-next-line max-len
maxModalBodyHeight = maxModalHeight - (modalHeaderHeight + modalFooterHeight + modalBodyPadding);
}
if (modal.dataset.url) {
const iframeEl = modal.querySelector('iframe');
const iframeHeight = parseFloat(getComputedStyle(iframeEl, null).height.replace('px', ''));
if (iframeHeight > maxModalBodyHeight) {
modalBody.style.maxHeight = maxModalBodyHeight;
modalBody.style.overflowY = 'auto';
iframeEl.style.maxHeight = maxModalBodyHeight - modalBodyPadding;
}
}
});
modal.addEventListener('hide.bs.modal', () => {
const modalBody = modal.querySelector('.modal-body');
modalBody.style.maxHeight = 'initial';
});
modal.addEventListener('hidden.bs.modal', () => {
// Comply with the Joomla API - Remove the current Modal ID
Joomla.Modal.setCurrent('');
});
};
/**
* Method to invoke a click on button inside an iframe
*
* @param {object} options Object with the css selector for the parent element of an iframe
* and the selector of the button in the iframe that will be clicked
* { iframeSelector: '', buttonSelector: '' }
* @returns {boolean}
*
* @since 4.0
*/
Joomla.iframeButtonClick = (options) => {
if (!options.iframeSelector || !options.buttonSelector) {
throw new Error('Selector is missing');
}
const iframe = document.querySelector(`${options.iframeSelector} iframe`);
if (iframe) {
const button = iframe.contentWindow.document.querySelector(options.buttonSelector);
if (button) {
button.click();
}
}
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const modals = Joomla.getOptions('bootstrap.modal');
// Initialise the elements
if (typeof modals === 'object' && modals !== null) {
Object.keys(modals).forEach((modal) => {
const modalEl = document.querySelector(modal);
const opt = modals[modal];
const options = {
backdrop: opt.backdrop ? opt.backdrop : true,
keyboard: opt.keyboard ? opt.keyboard : true,
focus: opt.focus ? opt.focus : true,
};
if (modalEl) {
Joomla.Bootstrap.Initialise.Modal(modalEl, options);
}
});
}
export default Modal;

View File

@ -0,0 +1,121 @@
import Popover from '../../../../../node_modules/bootstrap/js/src/popover';
import Tooltip from '../../../../../node_modules/bootstrap/js/src/tooltip';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Popover = new WeakMap();
Joomla.Bootstrap.Instances.Tooltip = new WeakMap();
/**
* Initialise the Popover iteractivity
*
* @param {HTMLElement} el The element that will become an popover
* @param {object} options The options for this popover
*/
Joomla.Bootstrap.Initialise.Popover = (el, options) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Popover.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Popover.set(el, new Popover(el, options));
};
/**
* Initialise the Tooltip iteractivity
*
* @param {HTMLElement} el The element that will become an tooltip
* @param {object} options The options for this tooltip
*/
Joomla.Bootstrap.Initialise.Tooltip = (el, options) => {
if (Joomla.Bootstrap.Instances.Tooltip.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Tooltip.set(el, new Tooltip(el, options));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const tooltips = Joomla.getOptions('bootstrap.tooltip');
const popovers = Joomla.getOptions('bootstrap.popover');
// Initialise the elements
if (typeof popovers === 'object' && popovers !== null) {
Object.keys(popovers).forEach((popover) => {
const opt = popovers[popover];
const options = {
animation: opt.animation ? opt.animation : true,
container: opt.container ? opt.container : false,
content: opt.content ? opt.content : '',
delay: opt.delay ? opt.delay : 0,
html: opt.html ? opt.html : false,
placement: opt.placement ? opt.placement : 'top',
selector: opt.selector ? opt.selector : false,
title: opt.title ? opt.title : '',
trigger: opt.trigger ? opt.trigger : 'click',
offset: opt.offset ? opt.offset : 0,
fallbackPlacement: opt.fallbackPlacement ? opt.fallbackPlacement : 'flip',
boundary: opt.boundary ? opt.boundary : 'scrollParent',
customClass: opt.customClass ? opt.customClass : '',
sanitize: opt.sanitize ? opt.sanitize : true,
sanitizeFn: opt.sanitizeFn ? opt.sanitizeFn : null,
popperConfig: opt.popperConfig ? opt.popperConfig : null,
};
if (opt.template) {
options.template = opt.template;
}
if (opt.allowList) {
options.allowList = opt.allowList;
}
const elements = Array.from(document.querySelectorAll(popover));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Popover(el, options));
}
});
}
// Initialise the elements
if (typeof tooltips === 'object' && tooltips !== null) {
Object.keys(tooltips).forEach((tooltip) => {
const opt = tooltips[tooltip];
const options = {
animation: opt.animation ? opt.animation : true,
container: opt.container ? opt.container : false,
delay: opt.delay ? opt.delay : 0,
html: opt.html ? opt.html : false,
selector: opt.selector ? opt.selector : false,
trigger: opt.trigger ? opt.trigger : 'hover focus',
fallbackPlacement: opt.fallbackPlacement ? opt.fallbackPlacement : null,
boundary: opt.boundary ? opt.boundary : 'clippingParents',
title: opt.title ? opt.title : '',
customClass: opt.customClass ? opt.customClass : '',
sanitize: opt.sanitize ? opt.sanitize : true,
sanitizeFn: opt.sanitizeFn ? opt.sanitizeFn : null,
popperConfig: opt.popperConfig ? opt.popperConfig : null,
};
if (opt.placement) {
options.placement = opt.placement;
}
if (opt.template) {
options.template = opt.template;
}
if (opt.allowList) {
options.allowList = opt.allowList;
}
const elements = Array.from(document.querySelectorAll(tooltip));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Tooltip(el, options));
}
});
}
export { Tooltip, Popover };

View File

@ -0,0 +1,52 @@
import Scrollspy from '../../../../../node_modules/bootstrap/js/src/scrollspy';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Scrollspy = new WeakMap();
/**
* Initialise the Scrollspy iteractivity
*
* @param {HTMLElement} el The element that will become a scrollspy
* @param {object} options The options for this scrollspy
*/
Joomla.Bootstrap.Initialise.Scrollspy = (el, options) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Scrollspy.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Scrollspy.set(el, new Scrollspy(el, options));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const scrollspys = Joomla.getOptions('bootstrap.scrollspy');
// Initialise the elements
if (typeof scrollspys === 'object' && scrollspys !== null) {
Object.keys(scrollspys).forEach((scrollspy) => {
const opt = scrollspys[scrollspy];
const options = {
offset: opt.offset ? opt.offset : 10,
method: opt.method ? opt.method : 'auto',
};
if (opt.target) {
options.target = opt.target;
}
const elements = Array.from(document.querySelectorAll(scrollspy));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Scrollspy(el, options));
}
});
}
export default Scrollspy;

View File

@ -0,0 +1,96 @@
import Tab from '../../../../../node_modules/bootstrap/js/src/tab';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Tab = new WeakMap();
/**
* Initialise the Tabs interactivity
*
* @param {HTMLElement} el The element that will become an collapse
* @param {object} options The options for this collapse
*/
Joomla.Bootstrap.Initialise.Tab = (el, options) => {
if (!(el instanceof Element) && options.isJoomla) {
const nSelector = el.split('.')[1];
if (!nSelector) {
throw new Error('The selector is invalid, check your PHP side');
}
const tab = document.querySelector(`#${nSelector}Content`);
if (tab) {
const related = Array.from(tab.children);
// Build the navigation
if (related.length) {
related.forEach((element) => {
if (!element.classList.contains('tab-pane')) {
return;
}
const isActive = element.dataset.active !== '';
const ul = document.querySelector(`#${nSelector}Tabs`);
if (ul) {
const link = document.createElement('a');
link.href = `#${element.dataset.id}`;
link.classList.add('nav-link');
if (isActive) {
link.classList.add('active');
}
link.dataset.bsToggle = 'tab';
link.setAttribute('role', 'tab');
link.setAttribute('aria-controls', element.dataset.id);
link.setAttribute('aria-selected', element.dataset.id);
/**
* As we are re-rendering text already displayed on the page we judge that there isn't
* a risk of XSS attacks
*/
link.innerHTML = element.dataset.title;
const li = document.createElement('li');
li.classList.add('nav-item');
li.setAttribute('role', 'presentation');
li.appendChild(link);
ul.appendChild(li);
}
});
}
if (Joomla.Bootstrap.Instances.Tab.get(tab) && tab.dispose) {
tab.dispose();
}
Joomla.Bootstrap.Instances.Tab.set(tab, new Tab(tab));
}
} else {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Tab.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Tab.set(el, new Tab(el, options));
}
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const tabs = Joomla.getOptions('bootstrap.tabs');
// Initialise the elements
if (typeof tabs === 'object' && tabs !== null) {
Object.keys(tabs).forEach((tab) => {
Joomla.Bootstrap.Initialise.Tab(tab, tabs[tab]);
});
}
export default Tab;

View File

@ -0,0 +1,49 @@
import Toast from '../../../../../node_modules/bootstrap/js/src/toast';
Joomla = Joomla || {};
Joomla.Bootstrap = Joomla.Bootstrap || {};
Joomla.Bootstrap.Initialise = Joomla.Bootstrap.Initialise || {};
Joomla.Bootstrap.Instances = Joomla.Bootstrap.Instances || {};
Joomla.Bootstrap.Instances.Toast = new WeakMap();
/**
* Initialise the iteractivity
*
* @param {HTMLElement} el The element that will become an toast
* @param {object} options The options for this toast
*/
Joomla.Bootstrap.Initialise.Toast = (el, options) => {
if (!(el instanceof Element)) {
return;
}
if (Joomla.Bootstrap.Instances.Toast.get(el) && el.dispose) {
el.dispose();
}
Joomla.Bootstrap.Instances.Toast.set(el, new Toast(el, options));
};
// Ensure vanilla mode, for consistency of the events
if (!Object.prototype.hasOwnProperty.call(document.body.dataset, 'bsNoJquery')) {
document.body.dataset.bsNoJquery = '';
}
// Get the elements/configurations from the PHP
const toasts = Joomla.getOptions('bootstrap.toast');
// Initialise the elements
if (typeof toasts === 'object' && toasts !== null) {
Object.keys(toasts).forEach((toast) => {
const opt = toasts[toast];
const options = {
animation: opt.animation ? opt.animation : true,
autohide: opt.autohide ? opt.autohide : true,
delay: opt.delay ? opt.delay : 5000,
};
const elements = Array.from(document.querySelectorAll(toast));
if (elements.length) {
elements.map((el) => Joomla.Bootstrap.Initialise.Toast(el, options));
}
});
}
export default Toast;

View File

@ -41,7 +41,7 @@ if ($this->params->get('show_autosuggest', 1))
<?php echo Text::_('JSEARCH_FILTER_SUBMIT'); ?>
</button>
<?php if ($this->params->get('show_advanced', 1)) : ?>
<?php HTMLHelper::_('bootstrap.framework'); ?>
<?php HTMLHelper::_('bootstrap.collapse'); ?>
<a href="#advancedSearch" data-bs-toggle="collapse" class="btn btn-secondary">
<span class="icon-search-plus" aria-hidden="true"></span>
<?php echo Text::_('COM_FINDER_ADVANCED_SEARCH_TOGGLE'); ?></a>

View File

@ -68,9 +68,10 @@ echo HTMLHelper::_(
?>
<button
type="button"
onclick="document.getElementById('versionsModal').open()"
class="btn btn-secondary"
data-bs-toggle="modal"<?php echo $dataAttribute; ?>>
data-bs-toggle="modal"
data-bs-target="#versionsModal"
<?php echo $dataAttribute; ?>>
<span class="icon-code-branch" aria-hidden="true"></span>
<?php echo $label; ?>
</button>

View File

@ -39,16 +39,18 @@ $direction = Factory::getLanguage()->isRtl() ? 'dropdown-menu-end' : '';
<?php if ($hasButtons && trim($button) !== ''): ?>
<?php // If there is a toggle split then render the items. Else render the parent button which has the items in the custom element. ?>
<?php if ($toggleSplit ?? true): ?>
<?php HTMLHelper::_('bootstrap.dropdown', '.dropdown-' . ($name ?? '')); ?>
<div id="<?php echo $id; ?>" class="btn-group dropdown-<?php echo $name ?? ''; ?>" role="group">
<?php echo $button; ?>
<?php HTMLHelper::_('bootstrap.dropdown', '.dropdown'); ?>
<?php // @todo use a class intead of the inline style.
// Reverse order solves a console err for dropdown ?>
<div id="<?php echo $id; ?>" class="btn-group dropdown-<?php echo $name ?? ''; ?>" role="group" style="flex-direction: row-reverse;">
<button type="button" class="<?php echo $caretClass ?? ''; ?> dropdown-toggle-split"
data-bs-toggle="dropdown" data-bs-target="#<?php echo $id; ?>" data-bs-display="static" aria-haspopup="true" aria-expanded="false">
data-bs-toggle="dropdown" data-bs-target=".dropdown-menu" data-bs-display="static" aria-haspopup="true" aria-expanded="false">
<span class="visually-hidden"><?php echo Text::_('JGLOBAL_TOGGLE_DROPDOWN'); ?></span>
<span class="icon-chevron-down" aria-hidden="true"></span>
</button>
<?php echo $button; ?>
<?php if (trim($dropdownItems) !== ''): ?>
<div class="dropdown-menu <?php echo $direction; ?>">
<?php echo $dropdownItems; ?>

View File

@ -67,7 +67,7 @@ if (isset($params['keyboard']))
if (isset($params['url']))
{
$url = 'data-url="' . $params['url'] . '"';
$iframeHtml = htmlspecialchars(LayoutHelper::render('joomla.modal.iframe', $displayData), ENT_COMPAT, 'UTF-8');
$iframeHtml = htmlspecialchars(LayoutHelper::render('libraries.html.bootstrap.modal.iframe', $displayData), ENT_COMPAT, 'UTF-8');
}
?>
<div id="<?php echo $selector; ?>" role="dialog" <?php echo ArrayHelper::toString($modalAttributes); ?> <?php echo $url ?? ''; ?> <?php echo isset($url) ? 'data-iframe="'.trim($iframeHtml).'"' : ''; ?>>
@ -77,16 +77,16 @@ if (isset($params['url']))
// Header
if (!isset($params['closeButton']) || isset($params['title']) || $params['closeButton'])
{
echo LayoutHelper::render('joomla.modal.header', $displayData);
echo LayoutHelper::render('libraries.html.bootstrap.modal.header', $displayData);
}
// Body
echo LayoutHelper::render('joomla.modal.body', $displayData);
echo LayoutHelper::render('libraries.html.bootstrap.modal.body', $displayData);
// Footer
if (isset($params['footer']))
{
echo LayoutHelper::render('joomla.modal.footer', $displayData);
echo LayoutHelper::render('libraries.html.bootstrap.modal.footer', $displayData);
}
?>
</div>

View File

@ -14,6 +14,7 @@ use Joomla\CMS\Factory;
use Joomla\CMS\HTML\HTMLHelper;
use Joomla\CMS\Layout\FileLayout;
use Joomla\CMS\Layout\LayoutHelper;
use Joomla\CMS\Log\Log;
/**
* Utility class for Bootstrap elements.
@ -26,7 +27,19 @@ abstract class Bootstrap
* @var array Array containing information for loaded files
* @since 3.0
*/
protected static $loaded = array();
protected static $loaded = [];
/**
* @var array Array containing the available components
* @since __DEPLOY_VERSION__
*/
protected static $scripts = ['alert', 'button', 'carousel', 'collapse', 'dropdown', 'modal', 'popover', 'scrollspy', 'tab', 'toast', 'tooltip'];
/**
* @var array Array containing the components loaded
* @since __DEPLOY_VERSION__
*/
protected static $loadedScripts = [];
/**
* Add javascript support for Bootstrap alerts
@ -35,9 +48,11 @@ abstract class Bootstrap
*
* @return void
*
* @throws \Exception
*
* @since 3.0
*/
public static function alert($selector = 'alert')
public static function alert($selector = '.alert') :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
@ -45,10 +60,19 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'alert');
Factory::getDocument()->addScriptOptions('bootstrap.alert', array($selector => ''));
$doc = Factory::getDocument();
$scriptOptions = $doc->getScriptOptions('bootstrap.alert');
$options = [$selector];
if (is_array($scriptOptions))
{
$options = array_merge($scriptOptions, $options);
}
$doc->addScriptOptions('bootstrap.alert', $options, false);
static::$loaded[__METHOD__][$selector] = true;
}
@ -60,9 +84,11 @@ abstract class Bootstrap
*
* @return void
*
* @throws \Exception
*
* @since 3.1
*/
public static function button($selector = 'button')
public static function button($selector = '.button') :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
@ -70,10 +96,19 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'button');
Factory::getDocument()->addScriptOptions('bootstrap.button', array($selector));
$doc = Factory::getDocument();
$scriptOptions = $doc->getScriptOptions('bootstrap.button');
$options = [$selector];
if (is_array($scriptOptions))
{
$options = array_merge($scriptOptions, $options);
}
$doc->addScriptOptions('bootstrap.button', $options, false);
static::$loaded[__METHOD__][$selector] = true;
}
@ -83,17 +118,23 @@ abstract class Bootstrap
*
* @param string $selector Common class for the carousels.
* @param array $params An array of options for the carousel.
* Options for the carousel can be:
* - interval number The amount of time to delay between automatically cycling an item.
* If false, carousel will not automatically cycle.
* - pause string Pauses the cycling of the carousel on mouseenter and resumes the cycling
* of the carousel on mouseleave.
*
* @return void
*
* @throws \Exception
*
* @since 3.0
*
* Options for the carousel can be:
* - interval number 5000 The amount of time to delay between automatically cycling an item.
* If false, carousel will not automatically cycle.
* - keyboard boolean true Whether the carousel should react to keyboard events.
* - pause string| hover Pauses the cycling of the carousel on mouseenter and resumes the cycling
* boolean of the carousel on mouseleave.
* - slide string| false Autoplays the carousel after the user manually cycles the first item.
* boolean If "carousel", autoplays the carousel on load.
*/
public static function carousel($selector = '.carousel', $params = array())
public static function carousel($selector = '.carousel', $params = []) :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
@ -101,14 +142,54 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Include Bootstrap Carousel
HTMLHelper::_('bootstrap.loadComponent', 'carousel');
// Setup options object
$opt['interval'] = isset($params['interval']) ? (int) $params['interval'] : 5000;
$opt['keyboard'] = isset($params['keyboard']) ? (bool) $params['keyboard'] : true;
$opt['pause'] = isset($params['pause']) ? $params['pause'] : 'hover';
$opt['slide'] = isset($params['slide']) ? (bool) $params['slide'] : false;
$opt['wrap'] = isset($params['wrap']) ? (bool) $params['wrap'] : true;
$opt['touch'] = isset($params['touch']) ? (bool) $params['touch'] : true;
Factory::getDocument()->addScriptOptions('bootstrap.carousel', array($selector => $opt));
Factory::getDocument()->addScriptOptions('bootstrap.carousel', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Add javascript support for Bootstrap collapse
*
* @param string $selector Common class for the collapse
*
* @return void
*
* @throws \Exception
*
* @since __DEPLOY_VERSION__
*
* Options for the collapse can be:
* - parent string false If parent is provided, then all collapsible elements under the specified parent will
* be closed when this collapsible item is shown.
* - toggle boolean true Toggles the collapsible element on invocation
*/
public static function collapse($selector = '.collapse') :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'collapse');
// Setup options object
$opt['parent'] = isset($params['parent']) ? $params['parent'] : false;
$opt['toggle'] = isset($params['toggle']) ? (bool) $params['toggle'] : true;
Factory::getDocument()->addScriptOptions('bootstrap.collapse', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
}
@ -117,12 +198,19 @@ abstract class Bootstrap
* Add javascript support for Bootstrap dropdowns
*
* @param string $selector Common class for the dropdowns
* @param string $options The options for the dropdowns
*
* @return void
*
* @since 3.0
* @since __DEPLOY_VERSION__
*
* Options for the collapse can be:
* - flip boolean true Allow Dropdown to flip in case of an overlapping on the reference element
* - boundary string scrollParent Overflow constraint boundary of the dropdown menu
* - reference string toggle Reference element of the dropdown menu. Accepts 'toggle' or 'parent'
* - display string dynamic By default, we use Popper for dynamic positioning. Disable this with static
*/
public static function dropdown($selector = '.dropdown-toggle')
public static function dropdown($selector = '.dropdown-toggle', $options = []) :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
@ -130,90 +218,56 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'dropdown');
Factory::getDocument()->addScriptOptions('bootstrap.dropdown', array($selector));
// Setup options object
$opt['flip'] = isset($params['flip']) ? $params['flip'] : true;
$opt['boundary'] = isset($params['boundary']) ? $params['boundary'] : 'scrollParent';
$opt['reference'] = isset($params['reference']) ? $params['reference'] : 'toggle';
$opt['display'] = isset($params['display']) ? $params['display'] : 'dynamic';
$opt['popperConfig'] = isset($params['popperConfig']) ? (bool) $params['popperConfig'] : true;
Factory::getDocument()->addScriptOptions('bootstrap.dropdown', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Method to load the Bootstrap JavaScript framework into the document head
*
* If debugging mode is on an uncompressed version of Bootstrap is included for easier debugging.
*
* @param mixed $debug Is debugging mode on? [optional]
*
* @return void
*
* @since 3.0
*/
public static function framework($debug = null)
{
/** @var \Joomla\CMS\WebAsset\WebAssetManager $wa */
$wa = Factory::getApplication()->getDocument()->getWebAssetManager();
if ($wa->assetExists('script', 'bootstrap.init.legacy') && $wa->isAssetActive('script', 'bootstrap.init.legacy'))
{
return;
}
// Only load once
if (!empty(static::$loaded[__METHOD__]))
{
return;
}
$wa
->registerScript('bootstrap.init.legacy', 'legacy/bootstrap-init.min.js', ['dependencies' => ['core', 'bootstrap.js.bundle']])
->useScript('bootstrap.init.legacy');
static::$loaded[__METHOD__] = true;
}
/**
* Method to render a Bootstrap modal
*
* @param string $selector The ID selector for the modal.
* @param array $params An array of options for the modal.
* Options for the modal can be:
* - title string The modal title
* - backdrop mixed A boolean select if a modal-backdrop element should be included (default = true)
* The string 'static' includes a backdrop which doesn't close the modal on click.
* - keyboard boolean Closes the modal when escape key is pressed (default = true)
* - closeButton boolean Display modal close button (default = true)
* - animation boolean Fade in from the top of the page (default = true)
* - footer string Optional markup for the modal footer
* - url string URL of a resource to be inserted as an `<iframe>` inside the modal body
* - height string height of the `<iframe>` containing the remote resource
* - width string width of the `<iframe>` containing the remote resource
* @param string $body Markup for the modal body. Appended after the `<iframe>` if the URL option is set
* @param array $options An array of options for the modal.
*
* @return string HTML markup for a modal
* @return void
*
* @since 3.0
* @since __DEPLOY_VERSION__
*
* Options for the modal can be:
* - backdrop string| true Includes a modal-backdrop element. Alternatively, specify static
* boolean for a backdrop which doesn't close the modal on click.
* - keyboard boolean true Closes the modal when escape key is pressed
* - focus boolean true Closes the modal when escape key is pressed
*/
public static function renderModal($selector = 'modal', $params = array(), $body = '')
public static function modal($selector = '.modal', $options = []) :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
{
return '';
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Setup options object
$opt['backdrop'] = isset($options['backdrop']) ? (bool) $options['backdrop'] : true;
$opt['keyboard'] = isset($options['keyboard']) ? (bool) $options['keyboard'] : true;
$opt['focus'] = isset($options['focus']) ? (bool) $options['focus'] : true;
$layoutData = array(
'selector' => $selector,
'params' => $params,
'body' => $body,
);
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'modal');
Factory::getDocument()->addScriptOptions('bootstrap.modal', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
return LayoutHelper::render('joomla.modal.main', $layoutData);
}
/**
@ -222,29 +276,31 @@ abstract class Bootstrap
* Use element's Title as popover content
*
* @param string $selector Selector for the popover
* @param array $params An array of options for the popover.
* Options for the popover can be:
* animation boolean apply a css fade transition to the popover
* container string|boolean Appends the popover to a specific element: { container: 'body' }
* content string|function default content value if `data-bs-content` attribute isn't present
* delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type
* If a number is supplied, delay is applied to both hide/show
* Object structure is: delay: { show: 500, hide: 100 }
* html boolean Insert HTML into the popover. If false, jQuery's text method will be used to insert
* content into the dom.
* placement string|function how to position the popover - top | bottom | left | right
* selector string If a selector is provided, popover objects will be delegated to the specified targets.
* template string Base HTML to use when creating the popover.
* title string|function default title value if `title` tag isn't present
* trigger string how popover is triggered - hover | focus | manual
* constraints array An array of constraints - passed through to Popper.
* offset string Offset of the popover relative to its target.
* @param array $options The options for the popover
*
* @return void
*
* @since 3.0
*
* - Options for the popover can be:
* - animation boolean true Apply a CSS fade transition to the popover
* - container string| false Appends the popover to a specific element. Eg.: 'body'
* boolean
* - content string null Default content value if data-bs-content attribute isn't present
* - delay number 0 Delay showing and hiding the popover (ms)
* does not apply to manual trigger type
* - html boolean true Insert HTML into the popover. If false, innerText property will be used
* to insert content into the DOM.
* - placement string right How to position the popover - auto | top | bottom | left | right.
* When auto is specified, it will dynamically reorient the popover
* - selector string false If a selector is provided, popover objects will be delegated to the
* specified targets.
* - template string null Base HTML to use when creating the popover.
* - title string null Default title value if `title` tag isn't present
* - trigger string click How popover is triggered - click | hover | focus | manual
* - offset integer 0 Offset of the popover relative to its target.
*/
public static function popover($selector = '.hasPopover', $params = array())
public static function popover($selector = '[data-bs-toggle="popover"]', $options = []) :void
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
@ -252,42 +308,47 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Setup options object
$opt['animation'] = isset($options['animation']) ? (bool) $options['animation'] : true;
$opt['container'] = isset($options['container']) ? $options['container'] : 'body';
$opt['content'] = isset($options['content']) ? $options['content'] : null;
$opt['delay'] = isset($options['delay']) ? (int) $options['delay'] : 0;
$opt['html'] = isset($options['html']) ? (bool) $options['html'] : true;
$opt['placement'] = isset($options['placement']) ? $options['placement'] : null;
$opt['selector'] = isset($options['selector']) ? $options['selector'] : false;
$opt['template'] = isset($options['template']) ? $options['template'] : null;
$opt['title'] = isset($options['title']) ? $options['title'] : null;
$opt['trigger'] = isset($options['trigger']) ? $options['trigger'] : 'click';
$opt['fallbackPlacement'] = isset($options['fallbackPlacement']) ? $options['fallbackPlacement'] : null;
$opt['boundary'] = isset($options['boundary']) ? $options['boundary'] : 'scrollParent';
$opt['customClass'] = isset($options['customClass']) ? $options['customClass'] : null;
$opt['sanitize'] = isset($options['sanitize']) ? (bool) $options['sanitize'] : null;
$opt['allowList'] = isset($options['allowList']) ? $options['allowList'] : null;
$opt['animation'] = isset($params['animation']) ? $params['animation'] : null;
$opt['container'] = isset($params['container']) ? $params['container'] : 'body';
$opt['content'] = isset($params['content']) ? $params['content'] : null;
$opt['delay'] = isset($params['delay']) ? $params['delay'] : null;
$opt['html'] = isset($params['html']) ? $params['html'] : true;
$opt['placement'] = isset($params['placement']) ? $params['placement'] : null;
$opt['selector'] = isset($params['selector']) ? $params['selector'] : null;
$opt['template'] = isset($params['template']) ? $params['template'] : null;
$opt['title'] = isset($params['title']) ? $params['title'] : null;
$opt['trigger'] = isset($params['trigger']) ? $params['trigger'] : 'hover focus';
$opt['constraints'] = isset($params['constraints']) ? $params['constraints'] : ['to' => 'scrollParent', 'attachment' => 'together', 'pin' => true];
$opt['offset'] = isset($params['offset']) ? $params['offset'] : '0,0';
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'popover');
$opt = (object) array_filter((array) $opt);
Factory::getDocument()->addScriptOptions('bootstrap.popover', array($selector => $opt));
Factory::getDocument()->addScriptOptions('bootstrap.popover', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Add javascript support for Bootstrap ScrollSpy
* Add javascript support for Bootstrap Scrollspy
*
* @param string $selector The ID selector for the ScrollSpy element.
* @param array $params An array of options for the ScrollSpy.
* Options for the ScrollSpy can be:
* - offset number Pixels to offset from top when calculating position of scroll.
* @param array $options An array of options for the ScrollSpy.
*
* @return void
*
* @since 3.0
*
* Options for the Scrollspy can be:
* - offset number Pixels to offset from top when calculating position of scroll.
* - method string Finds which section the spied element is in.
* - target string Specifies element to apply Scrollspy plugin.
*/
public static function scrollspy($selector = 'navbar', $params = array())
public static function scrollspy($selector = '[data-bs-spy="scroll"]', $options = []) :void
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
@ -295,10 +356,43 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Setup options object
$opt['offset'] = isset($options['offset']) ? (int) $options['offset'] : 10;
$opt['method'] = isset($options['method']) ? $options['method'] : 'auto';
$opt['target'] = isset($options['target']) ? $options['target'] : null;
Factory::getDocument()->addScriptOptions('bootstrap.scrollspy', array($selector => $params));
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'scrollspy');
Factory::getDocument()->addScriptOptions('bootstrap.scrollspy', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Add javascript support for Bootstrap tab
*
* @param string $selector Common class for the tabs
* @param array $options Options for the tabs
*
* @return void
*
* @throws \Exception
*
* @since __DEPLOY_VERSION__
*/
public static function tab($selector = '.myTab', $options = []) :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include the Bootstrap component Tab
HTMLHelper::_('bootstrap.loadComponent', 'tab');
Factory::getDocument()->addScriptOptions('bootstrap.tabs', [$selector => (object) $options]);
static::$loaded[__METHOD__][$selector] = true;
}
@ -310,29 +404,30 @@ abstract class Bootstrap
* title="title::text"
*
* @param string $selector The ID selector for the tooltip.
* @param array $params An array of options for the tooltip.
* Options for the tooltip can be:
* animation boolean apply a css fade transition to the popover
* container string|boolean Appends the popover to a specific element: { container: 'body' }
* delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type
* If a number is supplied, delay is applied to both hide/show
* Object structure is: delay: { show: 500, hide: 100 }
* html boolean Insert HTML into the popover. If false, jQuery's text method will be used to insert
* content into the dom.
* placement string|function how to position the popover - top | bottom | left | right
* selector string If a selector is provided, popover objects will be
* delegated to the specified targets.
* template string Base HTML to use when creating the popover.
* title string|function default title value if `title` tag isn't present
* trigger string how popover is triggered - hover | focus | manual
* constraints array An array of constraints - passed through to Popper.
* offset string Offset of the popover relative to its target.
* @param array $options An array of options for the tooltip.
*
* @return void
*
* @since 3.0
*
* Options for the tooltip can be:
* - animation boolean apply a css fade transition to the popover
* - container string|boolean Appends the popover to a specific element: { container: 'body' }
* - delay number|object delay showing and hiding the popover (ms) - does not apply to manual trigger type
* If a number is supplied, delay is applied to both hide/show
* Object structure is: delay: { show: 500, hide: 100 }
* - html boolean Insert HTML into the popover. If false, jQuery's text method will be used to
* insert content into the dom.
* - placement string|function how to position the popover - top | bottom | left | right
* - selector string If a selector is provided, popover objects will be
* delegated to the specified targets.
* - template string Base HTML to use when creating the popover.
* - title string|function default title value if `title` tag isn't present
* - trigger string how popover is triggered - hover | focus | manual
* - constraints array An array of constraints - passed through to Popper.
* - offset string Offset of the popover relative to its target.
*/
public static function tooltip($selector = '.hasTooltip', $params = array())
public static function tooltip($selector = '[data-bs-toggle=tooltip]', $options = []) :void
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
@ -340,57 +435,174 @@ abstract class Bootstrap
return;
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'tooltip');
// Setup options object
$opt['animation'] = isset($params['animation']) ? $params['animation'] : null;
$opt['container'] = isset($params['container']) ? $params['container'] : 'body';
$opt['delay'] = isset($params['delay']) ? $params['delay'] : null;
$opt['html'] = isset($params['html']) ? $params['html'] : true;
$opt['placement'] = isset($params['placement']) ? $params['placement'] : null;
$opt['selector'] = isset($params['selector']) ? $params['selector'] : null;
$opt['template'] = isset($params['template']) ? $params['template'] : null;
$opt['title'] = isset($params['title']) ? $params['title'] : null;
$opt['trigger'] = isset($params['trigger']) ? $params['trigger'] : 'hover focus';
$opt['constraints'] = isset($params['constraints']) ? $params['constraints'] : ['to' => 'scrollParent', 'attachment' => 'together', 'pin' => true];
$opt['offset'] = isset($params['offset']) ? $params['offset'] : '0,0';
$onShow = isset($params['onShow']) ? (string) $params['onShow'] : null;
$onShown = isset($params['onShown']) ? (string) $params['onShown'] : null;
$onHide = isset($params['onHide']) ? (string) $params['onHide'] : null;
$onHidden = isset($params['onHidden']) ? (string) $params['onHidden'] : null;
$opt['animation'] = isset($options['animation']) ? (bool) $options['animation'] : true;
$opt['container'] = isset($options['container']) ? $options['container'] : 'body';
$opt['delay'] = isset($options['delay']) ? (int) $options['delay'] : 0;
$opt['html'] = isset($options['html']) ? (bool) $options['html'] : true;
$opt['placement'] = isset($options['placement']) ? $options['placement'] : null;
$opt['selector'] = isset($options['selector']) ? $options['selector'] : false;
$opt['template'] = isset($options['template']) ? $options['template'] : null;
$opt['title'] = isset($options['title']) ? $options['title'] : null;
$opt['trigger'] = isset($options['trigger']) ? $options['trigger'] : 'hover focus';
$opt['fallbackPlacement'] = isset($options['fallbackPlacement']) ? $options['fallbackPlacement'] : null;
$opt['boundary'] = isset($options['boundary']) ? $options['boundary'] : 'clippingParents';
$opt['customClass'] = isset($options['customClass']) ? $options['customClass'] : null;
$opt['sanitize'] = isset($options['sanitize']) ? (bool) $options['sanitize'] : true;
$opt['allowList'] = isset($options['allowList']) ? $options['allowList'] : null;
$opt = (object) array_filter((array) $opt);
Factory::getDocument()->addScriptOptions('bootstrap.tooltip', array($selector => $opt));
Factory::getDocument()->addScriptOptions('bootstrap.tooltip', [$selector => (object) array_filter((array) $opt)]);
// Set static array
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Add javascript support for Bootstrap toasts
*
* @param string $selector Common class for the toasts
* @param array $options Options for the toasts
*
* @return void
*
* @throws \Exception
*
* @since __DEPLOY_VERSION__
*/
public static function toast($selector = '.toast', $options = []) :void
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
{
return;
}
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'toast');
// Setup options object
$opt['animation'] = isset($options['animation']) ? (string) $options['animation'] : null;
$opt['autohide'] = isset($options['autohide']) ? (boolean) $options['autohide'] : true;
$opt['delay'] = isset($options['delay']) ? (int) $options['delay'] : 5000;
Factory::getDocument()->addScriptOptions('bootstrap.toast', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = true;
}
/**
* Method to load the static assets for a given component
*
* @param string $script The component name
*
* @throws \Exception
*
* @return void
*/
public static function loadComponent(string $script) :void
{
if (!in_array($script, static::$loadedScripts)
&& in_array($script, static::$scripts))
{
// Tooltip + popover are combined
$script = $script === 'tooltip' ? 'popover' : $script;
// Register the ES2017+ script with an attribute type="module"
Factory::getApplication()
->getDocument()
->getWebAssetManager()
->registerScript(
'bootstrap.' . $script . '.ES6',
'vendor/bootstrap/' . $script . '.es6.min.js',
[
'dependencies' => [],
'attributes' => [
'type' => 'module',
]
]
)
->useScript('bootstrap.' . $script . '.ES6')
// Register the ES5 script with attributes: nomodule, defer
->registerScript(
'bootstrap.legacy',
'vendor/bootstrap/bootstrap.es5.min.js',
[
'dependencies' => [],
'attributes' => [
'nomodule' => '',
'defer' => 'defer',
]
]
)
->useScript('bootstrap.legacy');
array_push(static::$loadedScripts, $script);
}
}
/**
* Method to load the ALL the Bootstrap Components
*
* If debugging mode is on an uncompressed version of Bootstrap is included for easier debugging.
*
* @param mixed $debug Is debugging mode on? [optional]
*
* @return void
*
* @since 3.0
* @deprecated 5.0
*/
public static function framework($debug = null) :void
{
array_map(
function ($script) {
HTMLHelper::_('bootstrap.' . $script);
},
static::$scripts
);
}
/**
* Loads CSS files needed by Bootstrap
*
* @param boolean $includeMainCss If true, main bootstrap.css files are loaded
* @param string $direction rtl or ltr direction. If empty, ltr is assumed
* @param array $attribs Optional array of attributes to be passed to HTMLHelper::_('stylesheet')
*
* @return void
*
* @since 3.0
*/
public static function loadCss($includeMainCss = true, $direction = 'ltr', $attribs = []) :void
{
// Load Bootstrap main CSS
if ($includeMainCss)
{
Factory::getDocument()->getWebAssetManager()->useStyle('bootstrap.css');
}
}
/**
* Add javascript support for Bootstrap accordions and insert the accordion
*
* @param string $selector The ID selector for the tooltip.
* @param array $params An array of options for the tooltip.
* @param array $options An array of options for the tooltip.
*
* @return string HTML for the accordion
*
* @since 3.0
*
* Options for the tooltip can be:
* - parent selector If selector then all collapsible elements under the specified parent will be closed when this
* collapsible item is shown. (similar to traditional accordion behavior)
* - toggle boolean Toggles the collapsible element on invocation
* - active string Sets the active slide during load
*
* - onShow function This event fires immediately when the show instance method is called.
* - onShown function This event is fired when a collapse element has been made visible to the user
* (will wait for css transitions to complete).
* - onHide function This event is fired immediately when the hide method has been called.
* - onHidden function This event is fired when a collapse element has been hidden from the user
* (will wait for css transitions to complete).
*
* @return string HTML for the accordion
*
* @since 3.0
*/
public static function startAccordion($selector = 'myAccordian', $params = array())
public static function startAccordion($selector = '.myAccordian', $options = []) :string
{
// Only load once
if (isset(static::$loaded[__METHOD__][$selector]))
@ -398,19 +610,19 @@ abstract class Bootstrap
return '';
}
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Include Bootstrap component
HTMLHelper::_('bootstrap.loadComponent', 'collapse');
// Setup options object
$opt['parent'] = isset($params['parent']) ? ($params['parent'] == true ? '#' . $selector : $params['parent']) : '';
$opt['toggle'] = isset($params['toggle']) ? (boolean) $params['toggle'] : !($opt['parent'] === false || isset($params['active']));
$opt['onShow'] = isset($params['onShow']) ? (string) $params['onShow'] : null;
$opt['onShown'] = isset($params['onShown']) ? (string) $params['onShown'] : null;
$opt['onHide'] = isset($params['onHide']) ? (string) $params['onHide'] : null;
$opt['onHidden'] = isset($params['onHidden']) ? (string) $params['onHidden'] : null;
$opt['active'] = isset($params['active']) ? (string) $params['active'] : '';
$opt['parent'] = isset($options['parent']) ?
($options['parent'] == true ? '#' . preg_replace('/^\.?#/', '', $selector) : $options['parent']) : '';
$opt['toggle'] = isset($options['toggle']) ? (boolean) $options['toggle'] : !($opt['parent'] === false || isset($options['active']));
$opt['active'] = isset($options['active']) ? (string) $options['active'] : '';
Factory::getDocument()->addScriptOptions('bootstrap.accordion', array($selector => $opt));
// Initialise with the Joomla specifics
$opt['isJoomla'] = true;
Factory::getDocument()->addScriptOptions('bootstrap.accordion', [$selector => (object) array_filter((array) $opt)]);
static::$loaded[__METHOD__][$selector] = $opt;
@ -424,7 +636,7 @@ abstract class Bootstrap
*
* @since 3.0
*/
public static function endAccordion()
public static function endAccordion() :string
{
return '</div>';
}
@ -441,27 +653,26 @@ abstract class Bootstrap
*
* @since 3.0
*/
public static function addSlide($selector, $text, $id, $class = '')
public static function addSlide($selector, $text, $id, $class = '') :string
{
$in = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['active'] === $id ? ' show' : '';
$collapsed = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['active'] === $id ? '' : ' collapsed';
$parent = static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] ?
' data-parent="' . static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] . '"' : '';
'data-bs-parent="' . static::$loaded[__CLASS__ . '::startAccordion'][$selector]['parent'] . '"' : '';
$class = (!empty($class)) ? ' ' . $class : '';
$ariaExpanded = $in === 'show' ? true : false;
return <<<HTMLSTR
<div class="accordion-item $class">
<h2 class="accordion-header" id="headingOne">
<h2 class="accordion-header" id="$id-heading">
<button class="accordion-button $collapsed" type="button" data-bs-toggle="collapse" data-bs-target="#$id" aria-expanded="$ariaExpanded" aria-controls="$id" role="tab">
$text
</button>
</h2>
<div id="$id" class="accordion-collapse collapse $in" aria-labelledby="headingOne" $parent role="tabpanel">
<div id="$id" class="accordion-collapse collapse $in" aria-labelledby="$id-heading" $parent role="tabpanel">
<div class="accordion-body">
HTMLSTR;
return $html;
}
/**
@ -471,7 +682,7 @@ HTMLSTR;
*
* @since 3.0
*/
public static function endSlide()
public static function endSlide() :string
{
return <<<HTMLSTR
</div>
@ -480,6 +691,55 @@ HTMLSTR;
HTMLSTR;
}
/**
* Method to render a Bootstrap modal
*
* @param string $selector The ID selector for the modal.
* @param array $options An array of options for the modal.
* @param string $body Markup for the modal body. Appended after the `<iframe>` if the URL option is set
*
* @return string HTML markup for a modal
*
* @since 3.0
*
* Options for the modal can be:
* - backdrop string| true Includes a modal-backdrop element. Alternatively, specify static
* boolean for a backdrop which doesn't close the modal on click.
* - keyboard boolean true Closes the modal when escape key is pressed
* - focus boolean true Closes the modal when escape key is pressed
* - title string null The modal title
* - closeButton boolean true Display modal close button (default = true)
* - footer string null Optional markup for the modal footer
* - url string null URL of a resource to be inserted as an `<iframe>` inside the modal body
* - height string null Height of the `<iframe>` containing the remote resource
* - width string null Width of the `<iframe>` containing the remote resource
*/
public static function renderModal($selector = '.modal', $options = [], $body = '') :string
{
// Only load once
if (!empty(static::$loaded[__METHOD__][$selector]))
{
return '';
}
// Initialise with the Joomla specifics
$options['isJoomla'] = true;
// Include Basic Bootstrap component
HTMLHelper::_('bootstrap.modal', '#' . preg_replace('/^\.?#/', '', $selector), $options);
$layoutData = [
'selector' => $selector,
'params' => $options,
'body' => $body,
];
static::$loaded[__METHOD__][$selector] = true;
return LayoutHelper::render('libraries.html.bootstrap.modal.main', $layoutData);
}
/**
* Creates a tab pane
*
@ -490,26 +750,27 @@ HTMLSTR;
*
* @since 3.1
*/
public static function startTabSet($selector = 'myTab', $params = array())
public static function startTabSet($selector = '.myTab', $params = []) :string
{
$sig = md5(serialize(array($selector, $params)));
$sig = md5(serialize([$selector, $params]));
if (!isset(static::$loaded[__METHOD__][$sig]))
{
// Include Bootstrap framework
HTMLHelper::_('bootstrap.framework');
// Setup options object
$opt['active'] = (isset($params['active']) && ($params['active'])) ? (string) $params['active'] : '';
Factory::getDocument()->addScriptOptions('bootstrap.tabs', array($selector => $opt));
// Initialise with the Joomla specifics
$opt['isJoomla'] = true;
// Include the Bootstrap Tab Component
HTMLHelper::_('bootstrap.tab', $selector, $opt);
// Set static array
static::$loaded[__METHOD__][$sig] = true;
static::$loaded[__METHOD__][$selector]['active'] = $opt['active'];
}
return LayoutHelper::render('libraries.cms.html.bootstrap.starttabset', array('selector' => $selector));
return LayoutHelper::render('libraries.html.bootstrap.tab.starttabset', ['selector' => $selector]);
}
}
/**
@ -519,9 +780,9 @@ HTMLSTR;
*
* @since 3.1
*/
public static function endTabSet()
public static function endTabSet() :string
{
return LayoutHelper::render('libraries.cms.html.bootstrap.endtabset');
return LayoutHelper::render('libraries.html.bootstrap.tab.endtabset');
}
/**
@ -535,21 +796,14 @@ HTMLSTR;
*
* @since 3.1
*/
public static function addTab($selector, $id, $title)
public static function addTab($selector, $id, $title) :string
{
static $tabScriptLayout = null;
static $tabLayout = null;
$tabScriptLayout = $tabScriptLayout === null ? new FileLayout('libraries.cms.html.bootstrap.addtabscript') : $tabScriptLayout;
$tabLayout = $tabLayout === null ? new FileLayout('libraries.cms.html.bootstrap.addtab') : $tabLayout;
$tabLayout = $tabLayout === null ? new FileLayout('libraries.html.bootstrap.tab.addtab') : $tabLayout;
$active = (static::$loaded[__CLASS__ . '::startTabSet'][$selector]['active'] == $id) ? ' active' : '';
// Inject tab into UL
Factory::getDocument()
->addScriptDeclaration($tabScriptLayout->render(array('selector' => $selector, 'id' => $id, 'active' => $active, 'title' => $title)));
return $tabLayout->render(array('id' => $id, 'active' => $active, 'title' => $title));
return $tabLayout->render(['id' => str_replace('.', '', $id), 'active' => $active, 'title' => $title]);
}
/**
@ -559,38 +813,8 @@ HTMLSTR;
*
* @since 3.1
*/
public static function endTab()
public static function endTab() :string
{
return LayoutHelper::render('libraries.cms.html.bootstrap.endtab');
}
/**
* Loads CSS files needed by Bootstrap
*
* @param boolean $includeMainCss If true, main bootstrap.css files are loaded
* @param string $direction rtl or ltr direction. If empty, ltr is assumed
* @param array $attribs Optional array of attributes to be passed to HTMLHelper::_('stylesheet')
*
* @return void
*
* @since 3.0
*/
public static function loadCss($includeMainCss = true, $direction = 'ltr', $attribs = array())
{
// Load Bootstrap main CSS
if ($includeMainCss)
{
Factory::getDocument()->getWebAssetManager()->useStyle('bootstrap.css');
}
/**
* BOOTSTRAP RTL - WILL SORT OUT LATER DOWN THE LINE
* Load Bootstrap RTL CSS
* if ($direction === 'rtl')
* {
* HTMLHelper::_('stylesheet', 'jui/bootstrap-rtl.css', array('version' => 'auto', 'relative' => true), $attribs);
* }
*/
return LayoutHelper::render('libraries.html.bootstrap.tab.endtab');
}
}

View File

@ -158,8 +158,12 @@ class PopupButton extends ToolbarButton
// TODO: There should be a better workaround than this
Factory::getDocument()->addScriptDeclaration(
<<<JS
window.addEventListener('DOMContentLoaded', function() {
document.body.appendChild(document.getElementById('{$options['selector']}'));
document.addEventListener('DOMContentLoaded', function() {
var modal =document.getElementById('{$options['selector']}');
document.body.appendChild(modal);
if (Joomla && Joomla.Bootstrap && Joomla.Bootstrap.Methods && Joomla.Bootstrap.Methods.Modal) {
Joomla.Bootstrap.Methods.Initialise.Modal(modal);
}
});
JS
);
@ -170,9 +174,9 @@ JS
{
Factory::getDocument()->addScriptDeclaration(
<<<JS
window.addEventListener('DOMContentLoaded', function() {
document.addEventListener('DOMContentLoaded', function() {
document.querySelector('#{$options['selector']}').addEventListener('hide.bs.modal', function() {
new Function({$options['onclose']});
{$options['onclose']}
});
});
JS

View File

@ -32,6 +32,7 @@ $wa->registerAndUseStyle('mod_languages', 'mod_languages/template.css');
</select>
</form>
<?php elseif ($params->get('dropdown', 0) && $params->get('dropdownimage', 1)) : ?>
<?php HTMLHelper::_('bootstrap.dropdown'); ?>
<div class="mod-languages__select btn-group">
<?php foreach ($list as $language) : ?>
<?php if ($language->active) : ?>

423
package-lock.json generated
View File

@ -19,130 +19,27 @@
"dev": true
},
"@babel/core": {
"version": "7.12.10",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.10.tgz",
"integrity": "sha512-eTAlQKq65zHfkHZV0sIVODCPGVgoo1HdBlbSLi9CqOzuZanMv2ihzY+4paiKr1mH+XmYESMAmJ/dpZ68eN6d8w==",
"version": "7.12.3",
"resolved": "https://registry.npmjs.org/@babel/core/-/core-7.12.3.tgz",
"integrity": "sha512-0qXcZYKZp3/6N2jKYVxZv0aNCsxTSVCiK72DTiTYZAu7sjg73W0/aynWjMbiGd87EQL4WyA8reiJVh92AVla9g==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/generator": "^7.12.10",
"@babel/generator": "^7.12.1",
"@babel/helper-module-transforms": "^7.12.1",
"@babel/helpers": "^7.12.5",
"@babel/parser": "^7.12.10",
"@babel/template": "^7.12.7",
"@babel/traverse": "^7.12.10",
"@babel/types": "^7.12.10",
"@babel/helpers": "^7.12.1",
"@babel/parser": "^7.12.3",
"@babel/template": "^7.10.4",
"@babel/traverse": "^7.12.1",
"@babel/types": "^7.12.1",
"convert-source-map": "^1.7.0",
"debug": "^4.1.0",
"gensync": "^1.0.0-beta.1",
"json5": "^2.1.2",
"lodash": "^4.17.19",
"resolve": "^1.3.2",
"semver": "^5.4.1",
"source-map": "^0.5.0"
},
"dependencies": {
"@babel/generator": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz",
"integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==",
"dev": true,
"requires": {
"@babel/types": "^7.12.11",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
}
},
"@babel/helper-function-name": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz",
"integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==",
"dev": true,
"requires": {
"@babel/helper-get-function-arity": "^7.12.10",
"@babel/template": "^7.12.7",
"@babel/types": "^7.12.11"
}
},
"@babel/helper-get-function-arity": {
"version": "7.12.10",
"resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz",
"integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==",
"dev": true,
"requires": {
"@babel/types": "^7.12.10"
}
},
"@babel/helper-split-export-declaration": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz",
"integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==",
"dev": true,
"requires": {
"@babel/types": "^7.12.11"
}
},
"@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
"dev": true
},
"@babel/parser": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz",
"integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==",
"dev": true
},
"@babel/template": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz",
"integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/parser": "^7.12.7",
"@babel/types": "^7.12.7"
}
},
"@babel/traverse": {
"version": "7.12.12",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz",
"integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.12.11",
"@babel/generator": "^7.12.11",
"@babel/helper-function-name": "^7.12.11",
"@babel/helper-split-export-declaration": "^7.12.11",
"@babel/parser": "^7.12.11",
"@babel/types": "^7.12.12",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.19"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
"integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
}
}
},
"@babel/types": {
"version": "7.12.12",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz",
"integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.12.11",
"lodash": "^4.17.19",
"to-fast-properties": "^2.0.0"
}
}
}
},
"@babel/generator": {
@ -223,9 +120,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.643",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.643.tgz",
"integrity": "sha512-TGomM4gj8adt/uqRgPbu9F0yhUVAR1deww5X0fvbQgpGr9suSMjLgc4IwQ9YKGkp1t03cDbZum20OfAkiTYjAg==",
"version": "1.3.644",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.644.tgz",
"integrity": "sha512-N7FLvjDPADxad+OXXBuYfcvDvCBG0aW8ZZGr7G91sZMviYbnQJFxdSvUus4SJ0K7Q8dzMxE+Wx1d/CrJIIJ0sw==",
"dev": true
},
"escalade": {
@ -502,118 +399,14 @@
}
},
"@babel/helpers": {
"version": "7.12.5",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.5.tgz",
"integrity": "sha512-lgKGMQlKqA8meJqKsW6rUnc4MdUk35Ln0ATDqdM1a/UpARODdI4j5Y5lVfUScnSNkJcdCRAaWkspykNoFg9sJA==",
"version": "7.12.1",
"resolved": "https://registry.npmjs.org/@babel/helpers/-/helpers-7.12.1.tgz",
"integrity": "sha512-9JoDSBGoWtmbay98efmT2+mySkwjzeFeAL9BuWNoVQpkPFQF8SIIFUfY5os9u8wVzglzoiPRSW7cuJmBDUt43g==",
"dev": true,
"requires": {
"@babel/template": "^7.10.4",
"@babel/traverse": "^7.12.5",
"@babel/types": "^7.12.5"
},
"dependencies": {
"@babel/code-frame": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/code-frame/-/code-frame-7.12.11.tgz",
"integrity": "sha512-Zt1yodBx1UcyiePMSkWnU4hPqhwq7hGi2nFL1LeA3EUl+q2LQx16MISgJ0+z7dnmgvP9QtIleuETGOiOH1RcIw==",
"dev": true,
"requires": {
"@babel/highlight": "^7.10.4"
}
},
"@babel/generator": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/generator/-/generator-7.12.11.tgz",
"integrity": "sha512-Ggg6WPOJtSi8yYQvLVjG8F/TlpWDlKx0OpS4Kt+xMQPs5OaGYWy+v1A+1TvxI6sAMGZpKWWoAQ1DaeQbImlItA==",
"dev": true,
"requires": {
"@babel/types": "^7.12.11",
"jsesc": "^2.5.1",
"source-map": "^0.5.0"
}
},
"@babel/helper-function-name": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-function-name/-/helper-function-name-7.12.11.tgz",
"integrity": "sha512-AtQKjtYNolKNi6nNNVLQ27CP6D9oFR6bq/HPYSizlzbp7uC1M59XJe8L+0uXjbIaZaUJF99ruHqVGiKXU/7ybA==",
"dev": true,
"requires": {
"@babel/helper-get-function-arity": "^7.12.10",
"@babel/template": "^7.12.7",
"@babel/types": "^7.12.11"
},
"dependencies": {
"@babel/template": {
"version": "7.12.7",
"resolved": "https://registry.npmjs.org/@babel/template/-/template-7.12.7.tgz",
"integrity": "sha512-GkDzmHS6GV7ZeXfJZ0tLRBhZcMcY0/Lnb+eEbXDBfCAcZCjrZKe6p3J4we/D24O9Y8enxWAg1cWwof59yLh2ow==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.10.4",
"@babel/parser": "^7.12.7",
"@babel/types": "^7.12.7"
}
}
}
},
"@babel/helper-get-function-arity": {
"version": "7.12.10",
"resolved": "https://registry.npmjs.org/@babel/helper-get-function-arity/-/helper-get-function-arity-7.12.10.tgz",
"integrity": "sha512-mm0n5BPjR06wh9mPQaDdXWDoll/j5UpCAPl1x8fS71GHm7HA6Ua2V4ylG1Ju8lvcTOietbPNNPaSilKj+pj+Ag==",
"dev": true,
"requires": {
"@babel/types": "^7.12.10"
}
},
"@babel/helper-split-export-declaration": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-split-export-declaration/-/helper-split-export-declaration-7.12.11.tgz",
"integrity": "sha512-LsIVN8j48gHgwzfocYUSkO/hjYAOJqlpJEc7tGXcIm4cubjVUf8LGW6eWRyxEu7gA25q02p0rQUWoCI33HNS5g==",
"dev": true,
"requires": {
"@babel/types": "^7.12.11"
}
},
"@babel/helper-validator-identifier": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/helper-validator-identifier/-/helper-validator-identifier-7.12.11.tgz",
"integrity": "sha512-np/lG3uARFybkoHokJUmf1QfEvRVCPbmQeUQpKow5cQ3xWrV9i3rUHodKDJPQfTVX61qKi+UdYk8kik84n7XOw==",
"dev": true
},
"@babel/parser": {
"version": "7.12.11",
"resolved": "https://registry.npmjs.org/@babel/parser/-/parser-7.12.11.tgz",
"integrity": "sha512-N3UxG+uuF4CMYoNj8AhnbAcJF0PiuJ9KHuy1lQmkYsxTer/MAH9UBNHsBoAX/4s6NvlDD047No8mYVGGzLL4hg==",
"dev": true
},
"@babel/traverse": {
"version": "7.12.12",
"resolved": "https://registry.npmjs.org/@babel/traverse/-/traverse-7.12.12.tgz",
"integrity": "sha512-s88i0X0lPy45RrLM8b9mz8RPH5FqO9G9p7ti59cToE44xFm1Q+Pjh5Gq4SXBbtb88X7Uy7pexeqRIQDDMNkL0w==",
"dev": true,
"requires": {
"@babel/code-frame": "^7.12.11",
"@babel/generator": "^7.12.11",
"@babel/helper-function-name": "^7.12.11",
"@babel/helper-split-export-declaration": "^7.12.11",
"@babel/parser": "^7.12.11",
"@babel/types": "^7.12.12",
"debug": "^4.1.0",
"globals": "^11.1.0",
"lodash": "^4.17.19"
}
},
"@babel/types": {
"version": "7.12.12",
"resolved": "https://registry.npmjs.org/@babel/types/-/types-7.12.12.tgz",
"integrity": "sha512-lnIX7piTxOH22xE7fDXDbSHg9MM1/6ORnafpJmov5rs0kX5g4BZxeXNJLXsMRiO0U5Rb8/FvMS6xlTnTHvxonQ==",
"dev": true,
"requires": {
"@babel/helper-validator-identifier": "^7.12.11",
"lodash": "^4.17.19",
"to-fast-properties": "^2.0.0"
}
}
"@babel/traverse": "^7.12.1",
"@babel/types": "^7.12.1"
}
},
"@babel/highlight": {
@ -1568,6 +1361,77 @@
"fastq": "^1.6.0"
}
},
"@popperjs/core": {
"version": "2.6.0",
"resolved": "https://registry.npmjs.org/@popperjs/core/-/core-2.6.0.tgz",
"integrity": "sha512-cPqjjzuFWNK3BSKLm0abspP0sp/IGOli4p5I5fKFAzdS8fvjdOwDCfZqAaIiXd9lPkOWi3SUUfZof3hEb7J/uw=="
},
"@rollup/plugin-babel": {
"version": "5.2.2",
"resolved": "https://registry.npmjs.org/@rollup/plugin-babel/-/plugin-babel-5.2.2.tgz",
"integrity": "sha512-MjmH7GvFT4TW8xFdIeFS3wqIX646y5tACdxkTO+khbHvS3ZcVJL6vkAHLw2wqPmkhwCfWHoNsp15VYNwW6JEJA==",
"dev": true,
"requires": {
"@babel/helper-module-imports": "^7.10.4",
"@rollup/pluginutils": "^3.1.0"
}
},
"@rollup/plugin-node-resolve": {
"version": "11.1.0",
"resolved": "https://registry.npmjs.org/@rollup/plugin-node-resolve/-/plugin-node-resolve-11.1.0.tgz",
"integrity": "sha512-ouBBppRdWJKCllDXGzJ7ZIkYbaq+5TmyP0smt1vdJCFfoZhLi31vhpmjLhyo8lreHf4RoeSNllaWrvSqHpHRog==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.1.0",
"@types/resolve": "1.17.1",
"builtin-modules": "^3.1.0",
"deepmerge": "^4.2.2",
"is-module": "^1.0.0",
"resolve": "^1.19.0"
},
"dependencies": {
"is-core-module": {
"version": "2.2.0",
"resolved": "https://registry.npmjs.org/is-core-module/-/is-core-module-2.2.0.tgz",
"integrity": "sha512-XRAfAdyyY5F5cOXn7hYQDqh2Xmii+DEfIcQGxK/uNwMHhIkPWO0g8msXcbzLe+MpGoR951MlqM/2iIlU4vKDdQ==",
"dev": true,
"requires": {
"has": "^1.0.3"
}
},
"resolve": {
"version": "1.19.0",
"resolved": "https://registry.npmjs.org/resolve/-/resolve-1.19.0.tgz",
"integrity": "sha512-rArEXAgsBG4UgRGcynxWIWKFvh/XZCcS8UJdHhwy91zwAvCZIbcs+vAbflgBnNjYMs/i/i+/Ux6IZhML1yPvxg==",
"dev": true,
"requires": {
"is-core-module": "^2.1.0",
"path-parse": "^1.0.6"
}
}
}
},
"@rollup/plugin-replace": {
"version": "2.3.4",
"resolved": "https://registry.npmjs.org/@rollup/plugin-replace/-/plugin-replace-2.3.4.tgz",
"integrity": "sha512-waBhMzyAtjCL1GwZes2jaE9MjuQ/DQF2BatH3fRivUF3z0JBFrU0U6iBNC/4WR+2rLKhaAhPWDNPYp4mI6RqdQ==",
"dev": true,
"requires": {
"@rollup/pluginutils": "^3.1.0",
"magic-string": "^0.25.7"
}
},
"@rollup/pluginutils": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/@rollup/pluginutils/-/pluginutils-3.1.0.tgz",
"integrity": "sha512-GksZ6pr6TpIjHm8h9lSQ8pi8BE9VeubNT0OMJ3B5uZJ8pz73NPiqOtCog/x2/QzM1ENChPKxMDhiQuRHsqc+lg==",
"dev": true,
"requires": {
"@types/estree": "0.0.39",
"estree-walker": "^1.0.1",
"picomatch": "^2.2.2"
}
},
"@sindresorhus/is": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@sindresorhus/is/-/is-4.0.0.tgz",
@ -1614,6 +1478,12 @@
"@types/responselike": "*"
}
},
"@types/estree": {
"version": "0.0.39",
"resolved": "https://registry.npmjs.org/@types/estree/-/estree-0.0.39.tgz",
"integrity": "sha512-EYNwp3bU+98cpU4lAWYYL7Zz+2gryWH1qbdDTidVd6hkiR6weksdbMadyXKXNPEkQFhXM+hVO9ZygomHXp+AIw==",
"dev": true
},
"@types/http-cache-semantics": {
"version": "4.0.0",
"resolved": "https://registry.npmjs.org/@types/http-cache-semantics/-/http-cache-semantics-4.0.0.tgz",
@ -1686,6 +1556,15 @@
"integrity": "sha512-1HcDas8SEj4z1Wc696tH56G8OlRaH/sqZOynNNB+HF0WOeXPaxTtbYzJY2oEfiUxjSKjhCKr+MvR7dCHcEelug==",
"dev": true
},
"@types/resolve": {
"version": "1.17.1",
"resolved": "https://registry.npmjs.org/@types/resolve/-/resolve-1.17.1.tgz",
"integrity": "sha512-yy7HuzQhj0dhGpD8RLXSZWEkLsV9ibvxvi6EiJ3bkqLAO1RGo0WbkWQiwpRlSFymTJRz0d3k5LM3kkx8ArDbLw==",
"dev": true,
"requires": {
"@types/node": "*"
}
},
"@types/responselike": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/@types/responselike/-/responselike-1.0.0.tgz",
@ -3254,6 +3133,12 @@
"integrity": "sha1-JuYe0UIvtw3ULm42cp7VHYVf6Nk=",
"dev": true
},
"builtin-modules": {
"version": "3.2.0",
"resolved": "https://registry.npmjs.org/builtin-modules/-/builtin-modules-3.2.0.tgz",
"integrity": "sha512-lGzLKcioL90C7wMczpkY0n/oART3MbBa8R9OFGE1rJxoVI86u4WAGfEk8Wjv10eKSyTHVGkSo3bvBylCEtk7LA==",
"dev": true
},
"builtin-status-codes": {
"version": "3.0.0",
"resolved": "https://registry.npmjs.org/builtin-status-codes/-/builtin-status-codes-3.0.0.tgz",
@ -3906,6 +3791,12 @@
"integrity": "sha512-Q4+qBFnN4bwGwvtXXzbp4P/4iNk0MaiGAzvQ8OiMtlLjkIKjmNN689uVzShSM0908q7GoFHXIPx4zi75ocoaHw==",
"dev": true
},
"core-js": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/core-js/-/core-js-3.8.3.tgz",
"integrity": "sha512-KPYXeVZYemC2TkNEkX/01I+7yd+nX3KddKwZ1Ww7SKWdI2wQprSgLmrTddT8nw92AjEklTsPBoSdQBhbI1bQ6Q==",
"dev": true
},
"core-js-compat": {
"version": "3.8.3",
"resolved": "https://registry.npmjs.org/core-js-compat/-/core-js-compat-3.8.3.tgz",
@ -3936,9 +3827,9 @@
"dev": true
},
"electron-to-chromium": {
"version": "1.3.643",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.643.tgz",
"integrity": "sha512-TGomM4gj8adt/uqRgPbu9F0yhUVAR1deww5X0fvbQgpGr9suSMjLgc4IwQ9YKGkp1t03cDbZum20OfAkiTYjAg==",
"version": "1.3.644",
"resolved": "https://registry.npmjs.org/electron-to-chromium/-/electron-to-chromium-1.3.644.tgz",
"integrity": "sha512-N7FLvjDPADxad+OXXBuYfcvDvCBG0aW8ZZGr7G91sZMviYbnQJFxdSvUus4SJ0K7Q8dzMxE+Wx1d/CrJIIJ0sw==",
"dev": true
},
"escalade": {
@ -5483,6 +5374,12 @@
"integrity": "sha512-39nnKffWz8xN1BU/2c79n9nB9HDzo0niYUqx6xyqUnyoAnQyyWpOTdZEeiCch8BBu515t4wp9ZmgVfVhn9EBpw==",
"dev": true
},
"estree-walker": {
"version": "1.0.1",
"resolved": "https://registry.npmjs.org/estree-walker/-/estree-walker-1.0.1.tgz",
"integrity": "sha512-1fMXF3YP4pZZVozF8j/ZLfvnR8NSIljt56UhbZ5PeeDmmGHpgpdwQt7ITlGvYaQukCvuBRMLEiKiYC+oeIg4cg==",
"dev": true
},
"esutils": {
"version": "2.0.2",
"resolved": "https://registry.npmjs.org/esutils/-/esutils-2.0.2.tgz",
@ -6945,6 +6842,12 @@
"integrity": "sha512-gyPJuv83bHMpocVYoqof5VDiZveEoGoFL8m3BXNb2VW8Xs+rz9kqO8LOQ5DH6EsuvilT1ApazU0pyl+ytbPtlw==",
"dev": true
},
"is-module": {
"version": "1.0.0",
"resolved": "https://registry.npmjs.org/is-module/-/is-module-1.0.0.tgz",
"integrity": "sha1-Mlj7afeMFNW4FdZkM2tM/7ZEFZE=",
"dev": true
},
"is-negative-zero": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/is-negative-zero/-/is-negative-zero-2.0.1.tgz",
@ -6959,7 +6862,7 @@
},
"is-obj": {
"version": "1.0.1",
"resolved": "http://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"resolved": "https://registry.npmjs.org/is-obj/-/is-obj-1.0.1.tgz",
"integrity": "sha1-PkcprB9f3gJc19g6iW2rn09n2w8=",
"dev": true
},
@ -7930,6 +7833,15 @@
"yallist": "^4.0.0"
}
},
"magic-string": {
"version": "0.25.7",
"resolved": "https://registry.npmjs.org/magic-string/-/magic-string-0.25.7.tgz",
"integrity": "sha512-4CrMT5DOHTDk4HYDlzmwu4FVCcIYI8gauveasrdCu2IKIFOJ3f0v/8MDGJCDL9oD2ppz/Av1b0Nj345H9M+XIA==",
"dev": true,
"requires": {
"sourcemap-codec": "^1.4.4"
}
},
"make-dir": {
"version": "3.1.0",
"resolved": "https://registry.npmjs.org/make-dir/-/make-dir-3.1.0.tgz",
@ -9478,11 +9390,6 @@
"find-up": "^2.1.0"
}
},
"popper.js": {
"version": "1.16.1",
"resolved": "https://registry.npmjs.org/popper.js/-/popper.js-1.16.1.tgz",
"integrity": "sha512-Wb4p1J4zyFTbM+u6WuO4XstYx4Ky9Cewe4DWrel7B0w6VVICvPwdOpotjzcf6eD8TsckVnIMNONQyPIUFOUbCQ=="
},
"posix-character-classes": {
"version": "0.1.1",
"resolved": "https://registry.npmjs.org/posix-character-classes/-/posix-character-classes-0.1.1.tgz",
@ -10593,6 +10500,15 @@
"resolved": "https://registry.npmjs.org/roboto-fontface/-/roboto-fontface-0.10.0.tgz",
"integrity": "sha512-OlwfYEgA2RdboZohpldlvJ1xngOins5d7ejqnIBWr9KaMxsnBqotpptRXTyfNRLnFpqzX6sTDt+X+a+6udnU8g=="
},
"rollup": {
"version": "2.38.0",
"resolved": "https://registry.npmjs.org/rollup/-/rollup-2.38.0.tgz",
"integrity": "sha512-ay9zDiNitZK/LNE/EM2+v5CZ7drkB2xyDljvb1fQJCGnq43ZWRkhxN145oV8GmoW1YNi4sA/1Jdkr2LfawJoXw==",
"dev": true,
"requires": {
"fsevents": "~2.1.2"
}
},
"run-parallel": {
"version": "1.1.10",
"resolved": "https://registry.npmjs.org/run-parallel/-/run-parallel-1.1.10.tgz",
@ -11250,6 +11166,12 @@
"integrity": "sha1-PpNdfd1zYxuXZZlW1VEo6HtQhKM=",
"dev": true
},
"sourcemap-codec": {
"version": "1.4.8",
"resolved": "https://registry.npmjs.org/sourcemap-codec/-/sourcemap-codec-1.4.8.tgz",
"integrity": "sha512-9NykojV5Uih4lgo5So5dtw+f0JgJX30KCNI8gwhz2J9A15wD0Ml6tjHKwf6fTSa6fAdVBdZeNOs9eJ71qCk8vA==",
"dev": true
},
"spdx-correct": {
"version": "3.1.1",
"resolved": "https://registry.npmjs.org/spdx-correct/-/spdx-correct-3.1.1.tgz",
@ -12138,14 +12060,14 @@
}
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"version": "5.5.1",
"resolved": "https://registry.npmjs.org/terser/-/terser-5.5.1.tgz",
"integrity": "sha512-6VGWZNVP2KTUcltUQJ25TtNjx/XgdDsBDKGt8nN0MpydU36LmbPPcMBd2kmtZNNGVVDLg44k7GKeHHj+4zPIBQ==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
"source-map": "~0.7.2",
"source-map-support": "~0.5.19"
},
"dependencies": {
"commander": {
@ -12155,9 +12077,9 @@
"dev": true
},
"source-map": {
"version": "0.6.1",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"version": "0.7.3",
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.7.3.tgz",
"integrity": "sha512-CkCj6giN3S+n9qrYiBTX5gystlENnRW5jZeNLHpe6aue+SrHcG5VYwujhW9s4dY31mEGsxBDrHR6oI69fTXsaQ==",
"dev": true
}
}
@ -12179,6 +12101,12 @@
"worker-farm": "^1.7.0"
},
"dependencies": {
"commander": {
"version": "2.20.3",
"resolved": "https://registry.npmjs.org/commander/-/commander-2.20.3.tgz",
"integrity": "sha512-GpVkmM8vF2vQUkj2LvZmD35JxeJOLCwJ9cUkugyk2nuhbv3+mJvpLYYt+0+USMxE+oj+ey/lJEnhZw75x/OMcQ==",
"dev": true
},
"find-cache-dir": {
"version": "2.1.0",
"resolved": "https://registry.npmjs.org/find-cache-dir/-/find-cache-dir-2.1.0.tgz",
@ -12217,6 +12145,17 @@
"resolved": "https://registry.npmjs.org/source-map/-/source-map-0.6.1.tgz",
"integrity": "sha512-UjgapumWlbMhkBgzT7Ykc5YXUT46F0iKu8SGXq0bcwP5dz/h0Plj6enJqjz1Zbq2l5WaqYnrVbwWOWMyF3F47g==",
"dev": true
},
"terser": {
"version": "4.8.0",
"resolved": "https://registry.npmjs.org/terser/-/terser-4.8.0.tgz",
"integrity": "sha512-EAPipTNeWsb/3wLPeup1tVPaXfIaU68xMnVdPafIL1TV05OhASArYyIfFvnvJCNrR2NIOvDVNNTFRa+Re2MWyw==",
"dev": true,
"requires": {
"commander": "^2.20.0",
"source-map": "~0.6.1",
"source-map-support": "~0.5.12"
}
}
}
},

View File

@ -14,11 +14,12 @@
"scripts": {
"build:js": "node build/build.js --compile-js",
"build:css": "node build/build.js --compile-css",
"build:bs5": "node build/build-modules-js/build-bootstrap-js.es6.js",
"watch": "node build/build.js --watch",
"lint:js": "eslint --config build/.eslintrc --ignore-pattern '/media/' --ext .es6.js,.es6,.vue .",
"lint:css": "stylelint --config build/.stylelintrc.json -s scss \"administrator/components/com_media/resources/**/*.scss\" \"administrator/templates/**/*.scss\" \"build/media_source/**/*.scss\" \"templates/**/*.scss\" \"installation/template/**/*.scss\"",
"test": "karma start tests/javascript/karma.conf.js --single-run",
"install": "node build/build.js --copy-assets && node build/build.js --build-pages",
"install": "node build/build.js --copy-assets && node build/build.js --build-pages && npm run build:bs5",
"postinstall": "node build/build.js --compile-js && node build/build.js --compile-css && npm run build:com_media",
"update": "node build/build.js --copy-assets && node build/build.js --build-pages && node build/build.js --compile-js && node build/build.js --compile-css",
"gzip": "node build/build.js --gzip",
@ -32,6 +33,7 @@
],
"dependencies": {
"@claviska/jquery-minicolors": "^2.3.5",
"@fortawesome/fontawesome-free": "^5.15.2",
"@webcomponents/webcomponentsjs": "^2.5.0",
"accessibility": "^3.0.10",
"awesomplete": "1.1.5",
@ -44,13 +46,12 @@
"dragula": "3.7.2",
"file-saver": "^2.0.5",
"focus-visible": "^5.2.0",
"@fortawesome/fontawesome-free": "^5.15.2",
"joomla-ui-custom-elements": "0.0.40",
"jquery": "^3.5",
"jquery-migrate": "^3.3.2",
"mediaelement": "^4.2.16",
"metismenujs": "^1.2.1",
"popper.js": "^1.16.1",
"@popperjs/core": "^2.6.0",
"punycode": "1.4.1",
"qrcode-generator": "^1.4.4",
"roboto-fontface": "^0.10.0",
@ -63,11 +64,14 @@
"vuex-persistedstate": "^3.2.0"
},
"devDependencies": {
"@babel/core": "^7.12.10",
"@babel/core": "^7.12.3",
"@babel/plugin-transform-classes": "^7.12.1",
"@babel/plugin-transform-runtime": "^7.12.10",
"@babel/preset-env": "^7.12.11",
"@gfx/zopfli": "^1.0.15",
"@rollup/plugin-babel": "^5.2.2",
"@rollup/plugin-node-resolve": "^11.1.0",
"@rollup/plugin-replace": "^2.3.4",
"autoprefixer": "^9.8.6",
"babel-loader": "^8.2.2",
"babel-plugin-add-header-comment": "^1.0.3",
@ -76,6 +80,7 @@
"browserify": "^16.5.2",
"commander": "^5.1.0",
"copy-dir": "^1.3.0",
"core-js": "^3.8.3",
"cross-env": "^7.0.3",
"css-loader": "^3.6.0",
"cssnano": "^4.1.10",
@ -100,6 +105,8 @@
"path": "^0.12.7",
"postcss": "^7.0.35",
"recursive-readdir": "^2.2.2",
"rimraf": "^3.0.2",
"rollup": "^2.38.0",
"sass": "^1.32.5",
"sass-loader": "^8.0.2",
"selenium-standalone": "^6.23.0",
@ -107,6 +114,7 @@
"stylelint-config-standard": "^20.0.0",
"stylelint-order": "^4.1.0",
"stylelint-scss": "^3.18.0",
"terser": "^5.5.1",
"uglify-es": "^3.3.9",
"uglifycss": "^0.0.29",
"vue-loader": "^15.9.6",