(function () { 'use strict'; /** * @copyright (C) 2018 Open Source Matters, Inc. <https://www.joomla.org> * @license GNU General Public License version 2 or later; see LICENSE.txt */ /** * JField 'showon' class */ var Showon = /*#__PURE__*/function () { /** * Constructor * * @param {HTMLElement} cont Container element */ function Showon(cont) { var _this = this; var self = this; this.container = cont || document; this.fields = {// origin-field-name: { // origin: ['collection of all the trigger nodes'], // targets: ['collection of nodes to be controlled control'] // } }; this.showonFields = [].slice.call(this.container.querySelectorAll('[data-showon]')); // Populate the fields data if (this.showonFields.length) { // @todo refactor this, dry this.showonFields.forEach(function (field) { // Set up only once if (field.hasAttribute('data-showon-initialised')) { return; } field.setAttribute('data-showon-initialised', ''); var jsondata = field.getAttribute('data-showon') || ''; var showonData = JSON.parse(jsondata); var localFields; if (showonData.length) { localFields = [].slice.call(self.container.querySelectorAll("[name=\"" + showonData[0].field + "\"], [name=\"" + showonData[0].field + "[]\"]")); if (!_this.fields[showonData[0].field]) { _this.fields[showonData[0].field] = { origin: [], targets: [] }; } // Add trigger elements localFields.forEach(function (cField) { if (_this.fields[showonData[0].field].origin.indexOf(cField) === -1) { _this.fields[showonData[0].field].origin.push(cField); } }); // Add target elements _this.fields[showonData[0].field].targets.push(field); // Data showon can have multiple values if (showonData.length > 1) { showonData.forEach(function (value, index) { if (index === 0) { return; } localFields = [].slice.call(self.container.querySelectorAll("[name=\"" + value.field + "\"], [name=\"" + value.field + "[]\"]")); if (!_this.fields[showonData[0].field]) { _this.fields[showonData[0].field] = { origin: [], targets: [] }; } // Add trigger elements localFields.forEach(function (cField) { if (_this.fields[showonData[0].field].origin.indexOf(cField) === -1) { _this.fields[showonData[0].field].origin.push(cField); } }); // Add target elements if (_this.fields[showonData[0].field].targets.indexOf(field) === -1) { _this.fields[showonData[0].field].targets.push(field); } }); } } }); // Do some binding this.linkedOptions = this.linkedOptions.bind(this); // Attach events to referenced element, to check condition on change and keyup Object.keys(this.fields).forEach(function (key) { if (_this.fields[key].origin.length) { _this.fields[key].origin.forEach(function (elem) { // Initialize the showon behaviour for the given HTMLElement self.linkedOptions(key); // Setup listeners elem.addEventListener('change', function () { self.linkedOptions(key); }); elem.addEventListener('keyup', function () { self.linkedOptions(key); }); elem.addEventListener('click', function () { self.linkedOptions(key); }); }); } }); } } /** * * @param key */ var _proto = Showon.prototype; _proto.linkedOptions = function linkedOptions(key) { var _this2 = this; // Loop through the elements that need to be either shown or hidden this.fields[key].targets.forEach(function (field) { var elementShowonDatas = JSON.parse(field.getAttribute('data-showon')) || []; var showfield = true; var itemval; // Check if target conditions are satisfied elementShowonDatas.forEach(function (elementShowonData, index) { var condition = elementShowonData || {}; condition.valid = 0; // Test in each of the elements in the field array if condition is valid _this2.fields[key].origin.forEach(function (originField) { if (originField.name.replace('[]', '') !== elementShowonData.field) { return; } var originId = originField.id; // If checkbox or radio box the value is read from properties if (originField.getAttribute('type') && ['checkbox', 'radio'].includes(originField.getAttribute('type').toLowerCase())) { if (!originField.checked) { // Unchecked fields will return a blank and so always match // a != condition so we skip them return; } itemval = document.getElementById(originId).value; } else if (originField.nodeName === 'SELECT' && originField.hasAttribute('multiple')) { itemval = Array.from(originField.querySelectorAll('option:checked')).map(function (el) { return el.value; }); } else { // Select lists, text-area etc. Note that multiple-select list returns // an Array here s0 we can always treat 'itemval' as an array itemval = document.getElementById(originId).value; // A multi-select <select> $field will return null when no elements are // selected so we need to define itemval accordingly if (itemval === null && originField.tagName.toLowerCase() === 'select') { itemval = []; } } // Convert to array to allow multiple values in the field (e.g. type=list multiple) // and normalize as string if (!(typeof itemval === 'object')) { itemval = JSON.parse("[\"" + itemval + "\"]"); } // Test if any of the values of the field exists in showon conditions itemval.forEach(function (val) { // ":" Equal to one or more of the values condition if (condition.sign === '=' && condition.values.indexOf(val) !== -1) { condition.valid = 1; } // "!:" Not equal to one or more of the values condition if (condition.sign === '!=' && condition.values.indexOf(val) === -1) { condition.valid = 1; } }); }); // Verify conditions // First condition (no operator): current condition must be valid if (condition.op === '') { if (condition.valid === 0) { showfield = false; } } else { // Other conditions (if exists) // AND operator: both the previous and current conditions must be valid if (condition.op === 'AND' && condition.valid + elementShowonDatas[index - 1].valid < 2) { showfield = false; condition.valid = 0; } // OR operator: one of the previous and current conditions must be valid if (condition.op === 'OR' && condition.valid + elementShowonDatas[index - 1].valid > 0) { showfield = true; condition.valid = 1; } } }); // If conditions are satisfied show the target field(s), else hide if (field.tagName !== 'option') { if (showfield) { field.classList.remove('hidden'); field.dispatchEvent(new CustomEvent('joomla:showon-show', { bubbles: true })); } else { field.classList.add('hidden'); field.dispatchEvent(new CustomEvent('joomla:showon-hide', { bubbles: true })); } } else { // @todo: If chosen or choices.js is active we should update them field.disabled = !showfield; } }); }; return Showon; }(); if (!window.Joomla) { throw new Error('Joomla API is not properly initialized'); } // Provide a public API if (!Joomla.Showon) { Joomla.Showon = { initialise: function initialise(container) { return new Showon(container); } }; } /** * Initialize 'showon' feature at an initial page load */ Joomla.Showon.initialise(document); /** * Search for matching parents * * @param {HTMLElement} $child * @param {String} selector * @returns {HTMLElement[]} */ var getMatchedParents = function getMatchedParents($child, selector) { var $parent = $child; var $matchingParent; var parents = []; while ($parent) { $matchingParent = $parent.matches && $parent.matches(selector) ? $parent : null; if ($matchingParent) { parents.unshift($matchingParent); } $parent = $parent.parentNode; } return parents; }; /** * Initialize 'showon' feature when part of the page was updated */ document.addEventListener('joomla:updated', function (_ref) { var target = _ref.target; // Check is it subform, then wee need to fix some "showon" config if (target.classList.contains('subform-repeatable-group')) { var elements = [].slice.call(target.querySelectorAll('[data-showon]')); if (elements.length) { var search = []; var replace = []; // Collect all parent groups of changed group getMatchedParents(target, '.subform-repeatable-group').forEach(function ($parent) { search.push(new RegExp("\\[" + $parent.dataset.baseName + "X\\]", 'g')); replace.push("[" + $parent.dataset.group + "]"); }); // Fix showon field names in a current group elements.forEach(function (element) { var showon = element.dataset.showon; search.forEach(function (pattern, i) { showon = showon.replace(pattern, replace[i]); }); element.dataset.showon = showon; }); } } Joomla.Showon.initialise(target); }); })();