/** * @copyright (C) 2018 Open Source Matters, Inc. * @license GNU General Public License version 2 or later; see LICENSE.txt */ /** * JField 'showon' class */ class Showon { /** * Constructor * * @param {HTMLElement} cont Container element */ constructor(cont) { const 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(field => { // Set up only once if (field.hasAttribute('data-showon-initialised')) { return; } field.setAttribute('data-showon-initialised', ''); const jsondata = field.getAttribute('data-showon') || ''; const showonData = JSON.parse(jsondata); let 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(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((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(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(key => { if (this.fields[key].origin.length) { this.fields[key].origin.forEach(elem => { // Initialize the showon behaviour for the given HTMLElement self.linkedOptions(key); // Setup listeners elem.addEventListener('change', () => { self.linkedOptions(key); }); elem.addEventListener('keyup', () => { self.linkedOptions(key); }); elem.addEventListener('click', () => { self.linkedOptions(key); }); }); } }); } } /** * * @param key */ linkedOptions(key) { // Loop through the elements that need to be either shown or hidden this.fields[key].targets.forEach(field => { const elementShowonDatas = JSON.parse(field.getAttribute('data-showon')) || []; let showfield = true; let itemval; // Check if target conditions are satisfied elementShowonDatas.forEach((elementShowonData, index) => { const condition = elementShowonData || {}; condition.valid = 0; // Test in each of the elements in the field array if condition is valid this.fields[key].origin.forEach(originField => { if (originField.name.replace('[]', '') !== elementShowonData.field) { return; } const 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(el => 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