diff --git a/build/build-modules-js/javascript/compile-to-es2017.es6.js b/build/build-modules-js/javascript/compile-to-es2017.es6.js
index dc90baa5a58bd..70f8dc08d6f2c 100644
--- a/build/build-modules-js/javascript/compile-to-es2017.es6.js
+++ b/build/build-modules-js/javascript/compile-to-es2017.es6.js
@@ -10,6 +10,13 @@ const { renderSync } = require('sass-embedded');
const { minifyJsCode } = require('./minify.es6.js');
const { handleESMToLegacy } = require('./compile-to-es5.es6.js');
+function esmOrIife(file) {
+ if (file.endsWith('core.es6.js') || file.endsWith('validate.es6.js')) {
+ return 'iife';
+ }
+ return 'es';
+}
+
const getWcMinifiedCss = async (file) => {
let scssFileExists = false;
const scssFile = file.replace(`${sep}js${sep}`, `${sep}scss${sep}`).replace(/\.w-c\.es6\.js$/, '.scss');
@@ -89,7 +96,7 @@ module.exports.handleESMFile = async (file) => {
});
bundle.write({
- format: 'es',
+ format: esmOrIife(file),
sourcemap: false,
file: resolve(`${newPath}.js`),
})
diff --git a/build/media_source/plg_system_jooa11y/js/jooa11y.es6.js b/build/media_source/plg_system_jooa11y/js/jooa11y.es6.js
index a244ba011caa1..bfdccb3d1c70f 100644
--- a/build/media_source/plg_system_jooa11y/js/jooa11y.es6.js
+++ b/build/media_source/plg_system_jooa11y/js/jooa11y.es6.js
@@ -1,3 +1,4 @@
+// eslint-disable-next-line import/no-unresolved
import { Jooa11y, Lang } from '@joomla/joomla-a11y-checker/dist/js/joomla-a11y-checker.esm.js';
if (!Joomla) {
diff --git a/build/media_source/system/js/core.es6.js b/build/media_source/system/js/core.es6.js
index a3216b9353af0..c55187e4dea63 100644
--- a/build/media_source/system/js/core.es6.js
+++ b/build/media_source/system/js/core.es6.js
@@ -3,6 +3,8 @@
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
+'use strict';
+
import { sanitizeHtml } from 'bootstrap/js/src/util/sanitizer.js';
const ARIA_ATTRIBUTE_PATTERN = /^aria-[\w-]*$/i;
@@ -57,10 +59,10 @@ const DefaultAllowlist = {
window.Joomla = window.Joomla || {};
// Only define editors if not defined
-window.Joomla.editors = window.Joomla.editors || {};
+Joomla.editors = Joomla.editors || {};
// An object to hold each editor instance on page, only define if not defined.
-window.Joomla.editors.instances = window.Joomla.editors.instances || {
+Joomla.editors.instances = Joomla.editors.instances || {
/**
* *****************************************************************
* All Editors MUST register, per instance, the following callbacks:
@@ -101,7 +103,7 @@ window.Joomla.editors.instances = window.Joomla.editors.instances || {
*/
};
-window.Joomla.Modal = window.Joomla.Modal || {
+Joomla.Modal = Joomla.Modal || {
/**
* *****************************************************************
* Modals should implement
@@ -127,670 +129,666 @@ window.Joomla.Modal = window.Joomla.Modal || {
*/
current: '',
setCurrent: (element) => {
- window.Joomla.Modal.current = element;
+ Joomla.Modal.current = element;
},
- getCurrent: () => window.Joomla.Modal.current,
+ getCurrent: () => Joomla.Modal.current,
};
-((Joomla) => {
- 'use strict';
-
+/**
+ * Method to Extend Objects
+ *
+ * @param {Object} destination
+ * @param {Object} source
+ *
+ * @return Object
+ */
+Joomla.extend = (destination, source) => {
+ let newDestination = destination;
/**
- * Method to Extend Objects
- *
- * @param {Object} destination
- * @param {Object} source
- *
- * @return Object
+ * Technically null is an object, but trying to treat the destination as one in this
+ * context will error out.
+ * So emulate jQuery.extend(), and treat a destination null as an empty object.
*/
- Joomla.extend = (destination, source) => {
- let newDestination = destination;
- /**
- * Technically null is an object, but trying to treat the destination as one in this
- * context will error out.
- * So emulate jQuery.extend(), and treat a destination null as an empty object.
- */
- if (destination === null) {
- newDestination = {};
- }
+ if (destination === null) {
+ newDestination = {};
+ }
- [].slice.call(Object.keys(source)).forEach((key) => {
- newDestination[key] = source[key];
- });
+ [].slice.call(Object.keys(source)).forEach((key) => {
+ newDestination[key] = source[key];
+ });
- return destination;
- };
+ return destination;
+};
- /**
- * Joomla options storage
- *
- * @type {{}}
- *
- * @since 3.7.0
- */
- Joomla.optionsStorage = Joomla.optionsStorage || null;
+/**
+ * Joomla options storage
+ *
+ * @type {{}}
+ *
+ * @since 3.7.0
+ */
+Joomla.optionsStorage = Joomla.optionsStorage || null;
- /**
- * Get script(s) options
- *
- * @param {String} key Name in Storage
- * @param {mixed} def Default value if nothing found
- *
- * @return {mixed}
- *
- * @since 3.7.0
- */
- Joomla.getOptions = (key, def) => {
- // Load options if they not exists
- if (!Joomla.optionsStorage) {
- Joomla.loadOptions();
- }
+/**
+ * Get script(s) options
+ *
+ * @param {String} key Name in Storage
+ * @param {mixed} def Default value if nothing found
+ *
+ * @return {mixed}
+ *
+ * @since 3.7.0
+ */
+Joomla.getOptions = (key, def) => {
+ // Load options if they not exists
+ if (!Joomla.optionsStorage) {
+ Joomla.loadOptions();
+ }
- return Joomla.optionsStorage[key] !== undefined ? Joomla.optionsStorage[key] : def;
- };
+ return Joomla.optionsStorage[key] !== undefined ? Joomla.optionsStorage[key] : def;
+};
- /**
- * Load new options from given options object or from Element
- *
- * @param {Object|undefined} options The options object to load.
- * Eg {"com_foobar" : {"option1": 1, "option2": 2}}
- *
- * @since 3.7.0
- */
- Joomla.loadOptions = (options) => {
- // Load form the script container
- if (!options) {
- const elements = [].slice.call(document.querySelectorAll('.joomla-script-options.new'));
- let counter = 0;
-
- elements.forEach((element) => {
- const str = element.text || element.textContent;
- const option = JSON.parse(str);
-
- if (option) {
- Joomla.loadOptions(option);
- counter += 1;
- }
+/**
+ * Load new options from given options object or from Element
+ *
+ * @param {Object|undefined} options The options object to load.
+ * Eg {"com_foobar" : {"option1": 1, "option2": 2}}
+ *
+ * @since 3.7.0
+ */
+Joomla.loadOptions = (options) => {
+ // Load form the script container
+ if (!options) {
+ const elements = [].slice.call(document.querySelectorAll('.joomla-script-options.new'));
+ let counter = 0;
- element.className = element.className.replace(' new', ' loaded');
- });
+ elements.forEach((element) => {
+ const str = element.text || element.textContent;
+ const option = JSON.parse(str);
- if (counter) {
- return;
+ if (option) {
+ Joomla.loadOptions(option);
+ counter += 1;
}
- }
- // Initial loading
- if (!Joomla.optionsStorage) {
- Joomla.optionsStorage = options || {};
- } else if (options) {
- // Merge with existing
- [].slice.call(Object.keys(options)).forEach((key) => {
- /**
- * If both existing and new options are objects, merge them with Joomla.extend().
- * But test for new option being null, as null is an object, but we want to allow
- * clearing of options with ...
- *
- * Joomla.loadOptions({'joomla.jtext': null});
- */
- if (options[key] !== null && typeof Joomla.optionsStorage[key] === 'object' && typeof options[key] === 'object') {
- Joomla.optionsStorage[key] = Joomla.extend(Joomla.optionsStorage[key], options[key]);
- } else {
- Joomla.optionsStorage[key] = options[key];
- }
- });
- }
- };
+ element.className = element.className.replace(' new', ' loaded');
+ });
- /**
- * Custom behavior for JavaScript I18N in Joomla! 1.6
- *
- * @type {{}}
- *
- * Allows you to call Joomla.Text._() to get a translated JavaScript string
- * pushed in with Text::script() in Joomla.
- */
- Joomla.Text = {
- strings: {},
-
- /**
- * Translates a string into the current language.
- *
- * @param {String} key The string to translate
- * @param {String} def Default string
- *
- * @returns {String}
- */
- _: (key, def) => {
- let newKey = key;
- let newDef = def;
- // Check for new strings in the optionsStorage, and load them
- const newStrings = Joomla.getOptions('joomla.jtext');
- if (newStrings) {
- Joomla.Text.load(newStrings);
-
- // Clean up the optionsStorage from useless data
- Joomla.loadOptions({ 'joomla.jtext': null });
+ if (counter) {
+ return;
+ }
+ }
+
+ // Initial loading
+ if (!Joomla.optionsStorage) {
+ Joomla.optionsStorage = options || {};
+ } else if (options) {
+ // Merge with existing
+ [].slice.call(Object.keys(options)).forEach((key) => {
+ /**
+ * If both existing and new options are objects, merge them with Joomla.extend().
+ * But test for new option being null, as null is an object, but we want to allow
+ * clearing of options with ...
+ *
+ * Joomla.loadOptions({'joomla.jtext': null});
+ */
+ if (options[key] !== null && typeof Joomla.optionsStorage[key] === 'object' && typeof options[key] === 'object') {
+ Joomla.optionsStorage[key] = Joomla.extend(Joomla.optionsStorage[key], options[key]);
+ } else {
+ Joomla.optionsStorage[key] = options[key];
}
+ });
+ }
+};
- newDef = newDef === undefined ? newKey : newDef;
- newKey = newKey.toUpperCase();
-
- return Joomla.Text.strings[newKey] !== undefined ? Joomla.Text.strings[newKey] : newDef;
- },
-
- /**
- * Load new strings in to Joomla.Text
- *
- * @param {Object} object Object with new strings
- * @returns {Joomla.Text}
- */
- load: (object) => {
- [].slice.call(Object.keys(object)).forEach((key) => {
- Joomla.Text.strings[key.toUpperCase()] = object[key];
- });
-
- return Joomla.Text;
- },
- };
-
- /**
- * For B/C we still support Joomla.JText
- *
- * @type {{}}
- *
- * @deprecated 4.0 will be removed in 6.0
- * Example: Joomla.Text._('...');
- * Joomla.Text.load(...);
- */
- Joomla.JText = Joomla.Text;
+/**
+ * Custom behavior for JavaScript I18N in Joomla! 1.6
+ *
+ * @type {{}}
+ *
+ * Allows you to call Joomla.Text._() to get a translated JavaScript string
+ * pushed in with Text::script() in Joomla.
+ */
+Joomla.Text = {
+ strings: {},
/**
- * Generic submit form
+ * Translates a string into the current language.
*
- * @param {String} task The given task
- * @param {node} form The form element
- * @param {bool} validate The form element
+ * @param {String} key The string to translate
+ * @param {String} def Default string
*
- * @returns {void}
+ * @returns {String}
*/
- Joomla.submitform = (task, form, validate) => {
- let newForm = form;
- const newTask = task;
-
- if (!newForm) {
- newForm = document.getElementById('adminForm');
+ _: (key, def) => {
+ let newKey = key;
+ let newDef = def;
+ // Check for new strings in the optionsStorage, and load them
+ const newStrings = Joomla.getOptions('joomla.jtext');
+ if (newStrings) {
+ Joomla.Text.load(newStrings);
+
+ // Clean up the optionsStorage from useless data
+ Joomla.loadOptions({ 'joomla.jtext': null });
}
- if (newTask) {
- newForm.task.value = newTask;
- }
-
- // Toggle HTML5 validation
- newForm.noValidate = !validate;
-
- if (!validate) {
- newForm.setAttribute('novalidate', '');
- } else if (newForm.hasAttribute('novalidate')) {
- newForm.removeAttribute('novalidate');
- }
+ newDef = newDef === undefined ? newKey : newDef;
+ newKey = newKey.toUpperCase();
- // Submit the form.
- // Create the input type="submit"
- const button = document.createElement('input');
- button.classList.add('hidden');
- button.type = 'submit';
-
- // Append it and click it
- newForm.appendChild(button).click();
-
- // If "submit" was prevented, make sure we don't get a build up of buttons
- newForm.removeChild(button);
- };
+ return Joomla.Text.strings[newKey] !== undefined ? Joomla.Text.strings[newKey] : newDef;
+ },
/**
- * Default function. Can be overridden by the component to add custom logic
- *
- * @param {String} task The given task
- * @param {String} formSelector The form selector eg '#adminForm'
- * @param {bool} validate The form element
+ * Load new strings in to Joomla.Text
*
- * @returns {void}
+ * @param {Object} object Object with new strings
+ * @returns {Joomla.Text}
*/
- Joomla.submitbutton = (task, formSelector, validate) => {
- let form = document.querySelector(formSelector || 'form.form-validate');
- let newValidate = validate;
-
- if (typeof formSelector === 'string' && form === null) {
- form = document.querySelector(`#${formSelector}`);
- }
+ load: (object) => {
+ [].slice.call(Object.keys(object)).forEach((key) => {
+ Joomla.Text.strings[key.toUpperCase()] = object[key];
+ });
- if (form) {
- if (newValidate === undefined || newValidate === null) {
- const pressbutton = task.split('.');
- let cancelTask = form.getAttribute('data-cancel-task');
+ return Joomla.Text;
+ },
+};
- if (!cancelTask) {
- cancelTask = `${pressbutton[0]}.cancel`;
- }
+/**
+ * For B/C we still support Joomla.JText
+ *
+ * @type {{}}
+ *
+ * @deprecated 4.0 will be removed in 6.0
+ * Example: Joomla.Text._('...');
+ * Joomla.Text.load(...);
+ */
+Joomla.JText = Joomla.Text;
- newValidate = task !== cancelTask;
- }
+/**
+ * Generic submit form
+ *
+ * @param {String} task The given task
+ * @param {node} form The form element
+ * @param {bool} validate The form element
+ *
+ * @returns {void}
+ */
+Joomla.submitform = (task, form, validate) => {
+ let newForm = form;
+ const newTask = task;
+
+ if (!newForm) {
+ newForm = document.getElementById('adminForm');
+ }
+
+ if (newTask) {
+ newForm.task.value = newTask;
+ }
+
+ // Toggle HTML5 validation
+ newForm.noValidate = !validate;
+
+ if (!validate) {
+ newForm.setAttribute('novalidate', '');
+ } else if (newForm.hasAttribute('novalidate')) {
+ newForm.removeAttribute('novalidate');
+ }
+
+ // Submit the form.
+ // Create the input type="submit"
+ const button = document.createElement('input');
+ button.classList.add('hidden');
+ button.type = 'submit';
+
+ // Append it and click it
+ newForm.appendChild(button).click();
+
+ // If "submit" was prevented, make sure we don't get a build up of buttons
+ newForm.removeChild(button);
+};
- if (!newValidate || document.formvalidator.isValid(form)) {
- Joomla.submitform(task, form);
- }
- } else {
- Joomla.submitform(task);
- }
- };
+/**
+ * Default function. Can be overridden by the component to add custom logic
+ *
+ * @param {String} task The given task
+ * @param {String} formSelector The form selector eg '#adminForm'
+ * @param {bool} validate The form element
+ *
+ * @returns {void}
+ */
+Joomla.submitbutton = (task, formSelector, validate) => {
+ let form = document.querySelector(formSelector || 'form.form-validate');
+ let newValidate = validate;
- /**
- * USED IN: all list forms.
- *
- * Toggles the check state of a group of boxes
- *
- * Checkboxes must have an id attribute in the form cb0, cb1...
- *
- * @param {mixed} checkbox The number of box to 'check', for a checkbox element
- * @param {string} stub An alternative field name
- *
- * @return {boolean}
- */
- Joomla.checkAll = (checkbox, stub) => {
- if (!checkbox.form) {
- return false;
- }
+ if (typeof formSelector === 'string' && form === null) {
+ form = document.querySelector(`#${formSelector}`);
+ }
- const currentStab = stub || 'cb';
- const elements = [].slice.call(checkbox.form.elements);
- let state = 0;
+ if (form) {
+ if (newValidate === undefined || newValidate === null) {
+ const pressbutton = task.split('.');
+ let cancelTask = form.getAttribute('data-cancel-task');
- elements.forEach((element) => {
- if (element.type === checkbox.type && element.id.indexOf(currentStab) === 0) {
- element.checked = checkbox.checked;
- state += element.checked ? 1 : 0;
+ if (!cancelTask) {
+ cancelTask = `${pressbutton[0]}.cancel`;
}
- });
- if (checkbox.form.boxchecked) {
- checkbox.form.boxchecked.value = state;
- checkbox.form.boxchecked.dispatchEvent(new CustomEvent('change', {
- bubbles: true,
- cancelable: true,
- }));
+ newValidate = task !== cancelTask;
}
- return true;
- };
-
- /**
- * USED IN: administrator/components/com_cache/views/cache/tmpl/default.php
- * administrator/components/com_installer/views/discover/tmpl/default_item.php
- * administrator/components/com_installer/views/update/tmpl/default_item.php
- * administrator/components/com_languages/helpers/html/languages.php
- * libraries/joomla/html/html/grid.php
- *
- * @param {boolean} isitchecked Flag for checked
- * @param {node} form The form
- *
- * @return {void}
- */
- Joomla.isChecked = (isitchecked, form) => {
- let newForm = form;
- if (typeof newForm === 'undefined') {
- newForm = document.getElementById('adminForm');
- } else if (typeof form === 'string') {
- newForm = document.getElementById(form);
+ if (!newValidate || document.formvalidator.isValid(form)) {
+ Joomla.submitform(task, form);
}
+ } else {
+ Joomla.submitform(task);
+ }
+};
- newForm.boxchecked.value = isitchecked
- ? parseInt(newForm.boxchecked.value, 10) + 1
- : parseInt(newForm.boxchecked.value, 10) - 1;
+/**
+ * USED IN: all list forms.
+ *
+ * Toggles the check state of a group of boxes
+ *
+ * Checkboxes must have an id attribute in the form cb0, cb1...
+ *
+ * @param {mixed} checkbox The number of box to 'check', for a checkbox element
+ * @param {string} stub An alternative field name
+ *
+ * @return {boolean}
+ */
+Joomla.checkAll = (checkbox, stub) => {
+ if (!checkbox.form) {
+ return false;
+ }
- newForm.boxchecked.dispatchEvent(new CustomEvent('change', {
- bubbles: true,
- cancelable: true,
- }));
+ const currentStab = stub || 'cb';
+ const elements = [].slice.call(checkbox.form.elements);
+ let state = 0;
- // If we don't have a checkall-toggle, done.
- if (!newForm.elements['checkall-toggle']) {
- return;
+ elements.forEach((element) => {
+ if (element.type === checkbox.type && element.id.indexOf(currentStab) === 0) {
+ element.checked = checkbox.checked;
+ state += element.checked ? 1 : 0;
}
+ });
- // Toggle main toggle checkbox depending on checkbox selection
- let c = true;
- let i;
- let e;
- let n;
+ if (checkbox.form.boxchecked) {
+ checkbox.form.boxchecked.value = state;
+ checkbox.form.boxchecked.dispatchEvent(new CustomEvent('change', {
+ bubbles: true,
+ cancelable: true,
+ }));
+ }
- // eslint-disable-next-line no-plusplus
- for (i = 0, n = newForm.elements.length; i < n; i++) {
- e = newForm.elements[i];
+ return true;
+};
- if (e.type === 'checkbox' && e.name !== 'checkall-toggle' && !e.checked) {
- c = false;
- break;
- }
+/**
+ * USED IN: administrator/components/com_cache/views/cache/tmpl/default.php
+ * administrator/components/com_installer/views/discover/tmpl/default_item.php
+ * administrator/components/com_installer/views/update/tmpl/default_item.php
+ * administrator/components/com_languages/helpers/html/languages.php
+ * libraries/joomla/html/html/grid.php
+ *
+ * @param {boolean} isitchecked Flag for checked
+ * @param {node} form The form
+ *
+ * @return {void}
+ */
+Joomla.isChecked = (isitchecked, form) => {
+ let newForm = form;
+ if (typeof newForm === 'undefined') {
+ newForm = document.getElementById('adminForm');
+ } else if (typeof form === 'string') {
+ newForm = document.getElementById(form);
+ }
+
+ newForm.boxchecked.value = isitchecked
+ ? parseInt(newForm.boxchecked.value, 10) + 1
+ : parseInt(newForm.boxchecked.value, 10) - 1;
+
+ newForm.boxchecked.dispatchEvent(new CustomEvent('change', {
+ bubbles: true,
+ cancelable: true,
+ }));
+
+ // If we don't have a checkall-toggle, done.
+ if (!newForm.elements['checkall-toggle']) {
+ return;
+ }
+
+ // Toggle main toggle checkbox depending on checkbox selection
+ let c = true;
+ let i;
+ let e;
+ let n;
+
+ // eslint-disable-next-line no-plusplus
+ for (i = 0, n = newForm.elements.length; i < n; i++) {
+ e = newForm.elements[i];
+
+ if (e.type === 'checkbox' && e.name !== 'checkall-toggle' && !e.checked) {
+ c = false;
+ break;
}
+ }
- newForm.elements['checkall-toggle'].checked = c;
- };
-
- /**
- * USED IN: libraries/joomla/html/html/grid.php
- * In other words, on any reorderable table
- *
- * @param {string} order The order value
- * @param {string} dir The direction
- * @param {string} task The task
- * @param {node} form The form
- *
- * return {void}
- */
- Joomla.tableOrdering = (order, dir, task, form) => {
- let newForm = form;
- if (typeof newForm === 'undefined') {
- newForm = document.getElementById('adminForm');
- } else if (typeof form === 'string') {
- newForm = document.getElementById(form);
- }
+ newForm.elements['checkall-toggle'].checked = c;
+};
- newForm.filter_order.value = order;
- newForm.filter_order_Dir.value = dir;
- Joomla.submitform(task, newForm);
- };
+/**
+ * USED IN: libraries/joomla/html/html/grid.php
+ * In other words, on any reorderable table
+ *
+ * @param {string} order The order value
+ * @param {string} dir The direction
+ * @param {string} task The task
+ * @param {node} form The form
+ *
+ * return {void}
+ */
+Joomla.tableOrdering = (order, dir, task, form) => {
+ let newForm = form;
+ if (typeof newForm === 'undefined') {
+ newForm = document.getElementById('adminForm');
+ } else if (typeof form === 'string') {
+ newForm = document.getElementById(form);
+ }
+
+ newForm.filter_order.value = order;
+ newForm.filter_order_Dir.value = dir;
+ Joomla.submitform(task, newForm);
+};
- /**
- * USED IN: all over :)
- *
- * @param {string} id The id
- * @param {string} task The task
- * @param {string} form The optional form
- *
- * @return {boolean}
- */
- Joomla.listItemTask = (id, task, form = null) => {
- let newForm = form;
- if (form !== null) {
- newForm = document.getElementById(form);
- } else {
- newForm = document.adminForm;
- }
+/**
+ * USED IN: all over :)
+ *
+ * @param {string} id The id
+ * @param {string} task The task
+ * @param {string} form The optional form
+ *
+ * @return {boolean}
+ */
+Joomla.listItemTask = (id, task, form = null) => {
+ let newForm = form;
+ if (form !== null) {
+ newForm = document.getElementById(form);
+ } else {
+ newForm = document.adminForm;
+ }
+
+ const cb = newForm[id];
+ let i = 0;
+ let cbx;
+
+ if (!cb) {
+ return false;
+ }
- const cb = newForm[id];
- let i = 0;
- let cbx;
+ // eslint-disable-next-line no-constant-condition
+ while (true) {
+ cbx = newForm[`cb${i}`];
- if (!cb) {
- return false;
+ if (!cbx) {
+ break;
}
- // eslint-disable-next-line no-constant-condition
- while (true) {
- cbx = newForm[`cb${i}`];
+ cbx.checked = false;
- if (!cbx) {
- break;
- }
+ i += 1;
+ }
- cbx.checked = false;
+ cb.checked = true;
+ newForm.boxchecked.value = 1;
+ Joomla.submitform(task, newForm);
- i += 1;
- }
+ return false;
+};
- cb.checked = true;
- newForm.boxchecked.value = 1;
- Joomla.submitform(task, newForm);
+/**
+ * Method to replace all request tokens on the page with a new one.
+ *
+ * @param {String} newToken The token
+ *
+ * Used in Joomla Installation
+ */
+Joomla.replaceTokens = (newToken) => {
+ if (!/^[0-9A-F]{32}$/i.test(newToken)) {
+ return;
+ }
- return false;
- };
+ const elements = [].slice.call(document.getElementsByTagName('input'));
- /**
- * Method to replace all request tokens on the page with a new one.
- *
- * @param {String} newToken The token
- *
- * Used in Joomla Installation
- */
- Joomla.replaceTokens = (newToken) => {
- if (!/^[0-9A-F]{32}$/i.test(newToken)) {
- return;
+ elements.forEach((element) => {
+ if (element.type === 'hidden' && element.value === '1' && element.name.length === 32) {
+ element.name = newToken;
}
+ });
+};
- const elements = [].slice.call(document.getElementsByTagName('input'));
+/**
+ * Method to perform AJAX request
+ *
+ * @param {Object} options Request options:
+ * {
+ * url: 'index.php', Request URL
+ * method: 'GET', Request method GET (default), POST
+ * data: null, Data to be sent, see
+ * https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/send
+ * perform: true, Perform the request immediately
+ * or return XMLHttpRequest instance and perform it later
+ * headers: null, Object of custom headers, eg {'X-Foo': 'Bar', 'X-Bar': 'Foo'}
+ * promise: false Whether return a Promise instance.
+ * When true then next options is ignored: perform, onSuccess, onError, onComplete
+ *
+ * onBefore: (xhr) => {} // Callback on before the request
+ * onSuccess: (response, xhr) => {}, // Callback on the request success
+ * onError: (xhr) => {}, // Callback on the request error
+ * onComplete: (xhr) => {}, // Callback on the request completed, with/without error
+ * }
+ *
+ * @return XMLHttpRequest|Boolean
+ *
+ * @example
+ *
+ * Joomla.request({
+ * url: 'index.php?option=com_example&view=example',
+ * onSuccess: (response, xhr) => {
+ * JSON.parse(response);
+ * }
+ * })
+ *
+ * @see https://developer.mozilla.org/docs/Web/API/XMLHttpRequest
+ */
+Joomla.request = (options) => {
+ // Prepare the options
+ const newOptions = Joomla.extend({
+ url: '',
+ method: 'GET',
+ data: null,
+ perform: true,
+ promise: false,
+ }, options);
+
+ // Setup XMLHttpRequest instance
+ const createRequest = (onSuccess, onError) => {
+ const xhr = new XMLHttpRequest();
+
+ xhr.open(newOptions.method, newOptions.url, true);
+
+ // Set the headers
+ xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
+ xhr.setRequestHeader('X-Ajax-Engine', 'Joomla!');
+
+ if (newOptions.method !== 'GET') {
+ const token = Joomla.getOptions('csrf.token', '');
+
+ // Use the CSRF only on the site's domain
+ if (
+ token && (
+ (!newOptions.url.startsWith('http:') && !newOptions.url.startsWith('https:'))
+ || newOptions.url.startsWith(window.location.origin)
+ )
+ ) {
+ xhr.setRequestHeader('X-CSRF-Token', token);
+ }
- elements.forEach((element) => {
- if (element.type === 'hidden' && element.value === '1' && element.name.length === 32) {
- element.name = newToken;
+ if (typeof newOptions.data === 'string' && (!newOptions.headers || !newOptions.headers['Content-Type'])) {
+ xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
}
- });
- };
+ }
- /**
- * Method to perform AJAX request
- *
- * @param {Object} options Request options:
- * {
- * url: 'index.php', Request URL
- * method: 'GET', Request method GET (default), POST
- * data: null, Data to be sent, see
- * https://developer.mozilla.org/docs/Web/API/XMLHttpRequest/send
- * perform: true, Perform the request immediately
- * or return XMLHttpRequest instance and perform it later
- * headers: null, Object of custom headers, eg {'X-Foo': 'Bar', 'X-Bar': 'Foo'}
- * promise: false Whether return a Promise instance.
- * When true then next options is ignored: perform, onSuccess, onError, onComplete
- *
- * onBefore: (xhr) => {} // Callback on before the request
- * onSuccess: (response, xhr) => {}, // Callback on the request success
- * onError: (xhr) => {}, // Callback on the request error
- * onComplete: (xhr) => {}, // Callback on the request completed, with/without error
- * }
- *
- * @return XMLHttpRequest|Boolean
- *
- * @example
- *
- * Joomla.request({
- * url: 'index.php?option=com_example&view=example',
- * onSuccess: (response, xhr) => {
- * JSON.parse(response);
- * }
- * })
- *
- * @see https://developer.mozilla.org/docs/Web/API/XMLHttpRequest
- */
- Joomla.request = (options) => {
- // Prepare the options
- const newOptions = Joomla.extend({
- url: '',
- method: 'GET',
- data: null,
- perform: true,
- promise: false,
- }, options);
-
- // Setup XMLHttpRequest instance
- const createRequest = (onSuccess, onError) => {
- const xhr = new XMLHttpRequest();
-
- xhr.open(newOptions.method, newOptions.url, true);
-
- // Set the headers
- xhr.setRequestHeader('X-Requested-With', 'XMLHttpRequest');
- xhr.setRequestHeader('X-Ajax-Engine', 'Joomla!');
-
- if (newOptions.method !== 'GET') {
- const token = Joomla.getOptions('csrf.token', '');
-
- // Use the CSRF only on the site's domain
- if (
- token && (
- (!newOptions.url.startsWith('http:') && !newOptions.url.startsWith('https:'))
- || newOptions.url.startsWith(window.location.origin)
- )
- ) {
- xhr.setRequestHeader('X-CSRF-Token', token);
- }
+ // Custom headers
+ if (newOptions.headers) {
+ [].slice.call(Object.keys(newOptions.headers)).forEach((key) => {
+ // Allow request without Content-Type
+ // eslint-disable-next-line no-empty
+ if (key === 'Content-Type' && newOptions.headers['Content-Type'] === 'false') {
- if (typeof newOptions.data === 'string' && (!newOptions.headers || !newOptions.headers['Content-Type'])) {
- xhr.setRequestHeader('Content-Type', 'application/x-www-form-urlencoded');
+ } else {
+ xhr.setRequestHeader(key, newOptions.headers[key]);
}
- }
+ });
+ }
- // Custom headers
- if (newOptions.headers) {
- [].slice.call(Object.keys(newOptions.headers)).forEach((key) => {
- // Allow request without Content-Type
- // eslint-disable-next-line no-empty
- if (key === 'Content-Type' && newOptions.headers['Content-Type'] === 'false') {
-
- } else {
- xhr.setRequestHeader(key, newOptions.headers[key]);
- }
- });
+ xhr.onreadystatechange = () => {
+ // Request not finished
+ if (xhr.readyState !== 4) {
+ return;
}
- xhr.onreadystatechange = () => {
- // Request not finished
- if (xhr.readyState !== 4) {
- return;
- }
-
- // Request finished and response is ready
- if (xhr.status === 200) {
- if (newOptions.promise) {
- // A Promise accepts only one argument
- onSuccess.call(window, xhr);
- } else {
- onSuccess.call(window, xhr.responseText, xhr);
- }
+ // Request finished and response is ready
+ if (xhr.status === 200) {
+ if (newOptions.promise) {
+ // A Promise accepts only one argument
+ onSuccess.call(window, xhr);
} else {
- onError.call(window, xhr);
- }
-
- if (newOptions.onComplete && !newOptions.promise) {
- newOptions.onComplete.call(window, xhr);
+ onSuccess.call(window, xhr.responseText, xhr);
}
- };
-
- // Do request
- if (newOptions.perform) {
- if (newOptions.onBefore && newOptions.onBefore.call(window, xhr) === false) {
- // Request interrupted
- if (newOptions.promise) {
- onSuccess.call(window, xhr);
- }
- return xhr;
- }
-
- xhr.send(newOptions.data);
+ } else {
+ onError.call(window, xhr);
}
- return xhr;
+ if (newOptions.onComplete && !newOptions.promise) {
+ newOptions.onComplete.call(window, xhr);
+ }
};
- // Return a Promise
- if (newOptions.promise) {
- return new Promise((resolve, reject) => {
- newOptions.perform = true;
- createRequest(resolve, reject);
- });
- }
+ // Do request
+ if (newOptions.perform) {
+ if (newOptions.onBefore && newOptions.onBefore.call(window, xhr) === false) {
+ // Request interrupted
+ if (newOptions.promise) {
+ onSuccess.call(window, xhr);
+ }
+ return xhr;
+ }
- // Return a Request
- try {
- return createRequest(newOptions.onSuccess || (() => {}), newOptions.onError || (() => {}));
- } catch (error) {
- // eslint-disable-next-line no-unused-expressions,no-console
- console.error(error);
- return false;
+ xhr.send(newOptions.data);
}
+
+ return xhr;
};
- let lastRequestPromise;
+ // Return a Promise
+ if (newOptions.promise) {
+ return new Promise((resolve, reject) => {
+ newOptions.perform = true;
+ createRequest(resolve, reject);
+ });
+ }
+
+ // Return a Request
+ try {
+ return createRequest(newOptions.onSuccess || (() => {}), newOptions.onError || (() => {}));
+ } catch (error) {
+ // eslint-disable-next-line no-unused-expressions,no-console
+ console.error(error);
+ return false;
+ }
+};
- /**
- * Joomla Request queue.
- *
- * A FIFO queue of requests to execute serially. Used to prevent simultaneous execution of
- * multiple requests against the server which could trigger its Denial of Service protection.
- *
- * @param {object} options Options for Joomla.request()
- * @returns {Promise}
- */
- Joomla.enqueueRequest = (options) => {
- if (!options.promise) {
- throw new Error('Joomla.enqueueRequest supports only Joomla.request as Promise');
- }
- if (!lastRequestPromise) {
- lastRequestPromise = Joomla.request(options);
- } else {
- lastRequestPromise = lastRequestPromise.then(() => Joomla.request(options));
- }
- return lastRequestPromise;
- };
+let lastRequestPromise;
- /**
- *
- * @param {string} unsafeHtml The html for sanitization
- * @param {object} allowList The list of HTMLElements with an array of allowed attributes
- * @param {function} sanitizeFn A custom sanitization function
- *
- * @return string
- */
- Joomla.sanitizeHtml = (unsafeHtml, allowList, sanitizeFn) => {
- const allowed = (allowList === undefined || allowList === null)
- ? DefaultAllowlist : { ...DefaultAllowlist, ...allowList };
- return sanitizeHtml(unsafeHtml, allowed, sanitizeFn);
- };
+/**
+ * Joomla Request queue.
+ *
+ * A FIFO queue of requests to execute serially. Used to prevent simultaneous execution of
+ * multiple requests against the server which could trigger its Denial of Service protection.
+ *
+ * @param {object} options Options for Joomla.request()
+ * @returns {Promise}
+ */
+Joomla.enqueueRequest = (options) => {
+ if (!options.promise) {
+ throw new Error('Joomla.enqueueRequest supports only Joomla.request as Promise');
+ }
+ if (!lastRequestPromise) {
+ lastRequestPromise = Joomla.request(options);
+ } else {
+ lastRequestPromise = lastRequestPromise.then(() => Joomla.request(options));
+ }
+ return lastRequestPromise;
+};
- /**
- * Treat AJAX errors.
- * Used by some javascripts such as sendtestmail.js and permissions.js
- *
- * @param {object} xhr XHR object.
- * @param {string} textStatus Type of error that occurred.
- * @param {string} error Textual portion of the HTTP status.
- *
- * @return {object} JavaScript object containing the system error message.
- *
- * @since 3.6.0
- */
- Joomla.ajaxErrorsMessages = (xhr, textStatus) => {
- const msg = {};
+/**
+ *
+ * @param {string} unsafeHtml The html for sanitization
+ * @param {object} allowList The list of HTMLElements with an array of allowed attributes
+ * @param {function} sanitizeFn A custom sanitization function
+ *
+ * @return string
+ */
+Joomla.sanitizeHtml = (unsafeHtml, allowList, sanitizeFn) => {
+ const allowed = (allowList === undefined || allowList === null)
+ ? DefaultAllowlist : { ...DefaultAllowlist, ...allowList };
+ return sanitizeHtml(unsafeHtml, allowed, sanitizeFn);
+};
- if (textStatus === 'parsererror') {
- // For jQuery jqXHR
- const buf = [];
+/**
+ * Treat AJAX errors.
+ * Used by some javascripts such as sendtestmail.js and permissions.js
+ *
+ * @param {object} xhr XHR object.
+ * @param {string} textStatus Type of error that occurred.
+ * @param {string} error Textual portion of the HTTP status.
+ *
+ * @return {object} JavaScript object containing the system error message.
+ *
+ * @since 3.6.0
+ */
+Joomla.ajaxErrorsMessages = (xhr, textStatus) => {
+ const msg = {};
- // Html entity encode.
- let encodedJson = xhr.responseText.trim();
+ if (textStatus === 'parsererror') {
+ // For jQuery jqXHR
+ const buf = [];
- // eslint-disable-next-line no-plusplus
- for (let i = encodedJson.length - 1; i >= 0; i--) {
- buf.unshift(['', encodedJson[i].charCodeAt(), ';'].join(''));
- }
+ // Html entity encode.
+ let encodedJson = xhr.responseText.trim();
- encodedJson = buf.join('');
-
- msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_PARSE').replace('%s', encodedJson)];
- } else if (textStatus === 'nocontent') {
- msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_NO_CONTENT')];
- } else if (textStatus === 'timeout') {
- msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_TIMEOUT')];
- } else if (textStatus === 'abort') {
- msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_CONNECTION_ABORT')];
- } else if (xhr.responseJSON && xhr.responseJSON.message) {
- // For vanilla XHR
- msg.error = [`${Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)} ${xhr.responseJSON.message}`];
- } else if (xhr.statusText) {
- msg.error = [`${Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)} ${xhr.statusText}`];
- } else {
- msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)];
+ // eslint-disable-next-line no-plusplus
+ for (let i = encodedJson.length - 1; i >= 0; i--) {
+ buf.unshift(['', encodedJson[i].charCodeAt(), ';'].join(''));
}
- return msg;
- };
-})(Joomla);
+ encodedJson = buf.join('');
+
+ msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_PARSE').replace('%s', encodedJson)];
+ } else if (textStatus === 'nocontent') {
+ msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_NO_CONTENT')];
+ } else if (textStatus === 'timeout') {
+ msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_TIMEOUT')];
+ } else if (textStatus === 'abort') {
+ msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_CONNECTION_ABORT')];
+ } else if (xhr.responseJSON && xhr.responseJSON.message) {
+ // For vanilla XHR
+ msg.error = [`${Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)} ${xhr.responseJSON.message}`];
+ } else if (xhr.statusText) {
+ msg.error = [`${Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)} ${xhr.statusText}`];
+ } else {
+ msg.error = [Joomla.Text._('JLIB_JS_AJAX_ERROR_OTHER').replace('%s', xhr.status)];
+ }
+
+ return msg;
+};
diff --git a/build/media_source/system/js/fields/validate.es6.js b/build/media_source/system/js/fields/validate.es6.js
index 5412dc52bd15f..7a6dc220b071b 100644
--- a/build/media_source/system/js/fields/validate.es6.js
+++ b/build/media_source/system/js/fields/validate.es6.js
@@ -3,6 +3,8 @@
* @license GNU General Public License version 2 or later; see LICENSE.txt
*/
+'use strict';
+
import punycode from 'punycode';
class JFormValidator {
@@ -333,3 +335,10 @@ const initialize = () => {
};
document.addEventListener('DOMContentLoaded', initialize);
+
+/**
+ * Expose the classes to the global scope
+ * These will be removed in Joomla! 6.0
+ */
+window.JFormValidator = JFormValidator;
+window.punycode = punycode;
diff --git a/package-lock.json b/package-lock.json
index b9589d7db4a78..778a69b09d25c 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -1,12 +1,12 @@
{
"name": "joomla",
- "version": "4.4.4",
+ "version": "4.4.6",
"lockfileVersion": 3,
"requires": true,
"packages": {
"": {
"name": "joomla",
- "version": "4.4.4",
+ "version": "4.4.6",
"hasInstallScript": true,
"license": "GPL-2.0-or-later",
"dependencies": {
@@ -33,7 +33,7 @@
"mark.js": "^8.11.1",
"mediaelement": "^5.1.1",
"metismenujs": "^1.4.0",
- "punycode": "^2.3.0",
+ "punycode": "^2.3.1",
"qrcode-generator": "^1.4.4",
"roboto-fontface": "^0.10.0",
"shepherd.js": "^11.2.0",