PageSourceSearch

https://pokrova.pl.ua/media/system/js/showon.js?e51227

js pokrova.pl.ua collected 2026-09-24 14:28:31 UTC 10,147 bytes, 291 lines download raw bytes

1/**
2 * @copyright  (C) 2018 Open Source Matters, Inc. <https://www.joomla.org>
3 * @license    GNU General Public License version 2 or later; see LICENSE.txt
4 */
5
6/**
7 * JField 'showon' class
8 */
9class Showon {
10  /**
11   * Constructor
12   *
13   * @param {HTMLElement} cont Container element
14   */
15  constructor(cont) {
16    const self = this;
17    this.container = cont || document;
18    this.fields = {
19      // origin-field-name: {
20      //   origin:  ['collection of all the trigger nodes'],
21      //   targets: ['collection of nodes to be controlled control']
22      // }
23    };
24    this.showonFields = this.container.querySelectorAll('[data-showon]');
25    // Populate the fields data
26    if (this.showonFields.length) {
27      // @todo refactor this, dry
28      this.showonFields.forEach(field => {
29        // Set up only once
30        if (field.hasAttribute('data-showon-initialised')) {
31          return;
32        }
33        field.setAttribute('data-showon-initialised', '');
34        const jsondata = field.getAttribute('data-showon') || '';
35        const showonData = JSON.parse(jsondata);
36        let localFields;
37        if (showonData.length) {
38          localFields = self.container.querySelectorAll(`[name="${showonData[0].field}"], [name="${showonData[0].field}[]"]`);
39          if (!this.fields[showonData[0].field]) {
40            this.fields[showonData[0].field] = {
41              origin: [],
42              targets: []
43            };
44          }
45
46          // Add trigger elements
47          localFields.forEach(cField => {
48            if (this.fields[showonData[0].field].origin.indexOf(cField) === -1) {
49              this.fields[showonData[0].field].origin.push(cField);
50            }
51          });
52
53          // Add target elements
54          this.fields[showonData[0].field].targets.push(field);
55
56          // Data showon can have multiple values
57          if (showonData.length > 1) {
58            showonData.forEach((value, index) => {
59              if (index === 0) {
60                return;
61              }
62              localFields = self.container.querySelectorAll(`[name="${value.field}"], [name="${value.field}[]"]`);
63              if (!this.fields[showonData[0].field]) {
64                this.fields[showonData[0].field] = {
65                  origin: [],
66                  targets: []
67                };
68              }
69
70              // Add trigger elements
71              localFields.forEach(cField => {
72                if (this.fields[showonData[0].field].origin.indexOf(cField) === -1) {
73                  this.fields[showonData[0].field].origin.push(cField);
74                }
75              });
76
77              // Add target elements
78              if (this.fields[showonData[0].field].targets.indexOf(field) === -1) {
79                this.fields[showonData[0].field].targets.push(field);
80              }
81            });
82          }
83        }
84      });
85
86      // Do some binding
87      this.linkedOptions = this.linkedOptions.bind(this);
88
89      // Attach events to referenced element, to check condition on change and keyup
90      Object.keys(this.fields).forEach(key => {
91        if (this.fields[key].origin.length) {
92          this.fields[key].origin.forEach(elem => {
93            // Initialize the showon behaviour for the given HTMLElement
94            self.linkedOptions(key);
95
96            // Setup listeners
97            elem.addEventListener('change', () => {
98              self.linkedOptions(key);
99            });
100            elem.addEventListener('keyup', () => {
101              self.linkedOptions(key);
102            });
103            elem.addEventListener('click', () => {
104              self.linkedOptions(key);
105            });
106          });
107        }
108      });
109    }
110  }
111
112  /**
113   *
114   * @param key
115   */
116  linkedOptions(key) {
117    // Loop through the elements that need to be either shown or hidden
118    this.fields[key].targets.forEach(field => {
119      const elementShowonDatas = JSON.parse(field.getAttribute('data-showon')) || [];
120      let showfield = true;
121      let itemval;
122
123      // Check if target conditions are satisfied
124      elementShowonDatas.forEach((elementShowonData, index) => {
125        const condition = elementShowonData || {};
126        condition.valid = 0;
127
128        // Test in each of the elements in the field array if condition is valid
129        this.fields[key].origin.forEach(originField => {
130          if (originField.name.replace('[]', '') !== elementShowonData.field) {
131            return;
132          }
133          const originId = originField.id;
134
135          // If checkbox or radio box the value is read from properties
136          if (originField.getAttribute('type') && ['checkbox', 'radio'].includes(originField.getAttribute('type').toLowerCase())) {
137            if (!originField.checked) {
138              // Unchecked fields will return a blank and so always match
139              // a != condition so we skip them
140              return;
141            }
142            itemval = document.getElementById(originId).value;
143          } else if (originField.nodeName === 'SELECT' && originField.hasAttribute('multiple')) {
144            itemval = Array.from(originField.querySelectorAll('option:checked')).map(el => el.value);
145          } else {
146            // Select lists, text-area etc. Note that multiple-select list returns
147            // an Array here so we can always treat 'itemval' as an array
148            itemval = document.getElementById(originId).value;
149            // Check data attribute data-global instead of value in <select> for use global
150            if (originField.tagName === 'SELECT') {
151              const selectedOption = document.getElementById(originId).selectedOptions[0];
152              if (selectedOption && 'globalValue' in selectedOption.dataset) {
153                itemval = selectedOption.dataset.globalValue;
154              }
155            }
156            // A multi-select <select> $field  will return null when no elements are
157            // selected so we need to define itemval accordingly
158            if (itemval === null && originField.tagName.toLowerCase() === 'select') {
159              itemval = [];
160            }
161          }
162
163          // Convert to array to allow multiple values in the field (e.g. type=list multiple)
164          // and normalize as string
165          if (!(typeof itemval === 'object')) {
166            itemval = JSON.parse(`["${itemval}"]`);
167          }
168
169          // Test if any of the values of the field exists in showon conditions
170          itemval.forEach(val => {
171            // ":" Equal to one or more of the values condition
172            if (condition.sign === '=' && condition.values.indexOf(val) !== -1) {
173              condition.valid = 1;
174            }
175            // "!:" Not equal to one or more of the values condition
176            if (condition.sign === '!=' && condition.values.indexOf(val) === -1) {
177              condition.valid = 1;
178            }
179          });
180        });
181
182        // Verify conditions
183        // First condition (no operator): current condition must be valid
184        if (condition.op === '') {
185          if (condition.valid === 0) {
186            showfield = false;
187          }
188        } else {
189          // Other conditions (if exists)
190          // AND operator: both the previous and current conditions must be valid
191          if (condition.op === 'AND' && condition.valid + elementShowonDatas[index - 1].valid < 2) {
192            showfield = false;
193            condition.valid = 0;
194          }
195          // OR operator: one of the previous and current conditions must be valid
196          if (condition.op === 'OR' && condition.valid + elementShowonDatas[index - 1].valid > 0) {
197            showfield = true;
198            condition.valid = 1;
199          }
200        }
201      });
202
203      // If conditions are satisfied show the target field(s), else hide
204      if (field.tagName !== 'option') {
205        if (showfield) {
206          field.classList.remove('hidden');
207          field.dispatchEvent(new CustomEvent('joomla:showon-show', {
208            bubbles: true
209          }));
210        } else {
211          field.classList.add('hidden');
212          field.dispatchEvent(new CustomEvent('joomla:showon-hide', {
213            bubbles: true
214          }));
215        }
216      } else {
217        // @todo: If chosen or choices.js is active we should update them
218        field.disabled = !showfield;
219      }
220    });
221  }
222}
223if (!window.Joomla) {
224  throw new Error('Joomla API is not properly initialized');
225}
226// Provide a public API
227if (!Joomla.Showon) {
228  Joomla.Showon = {
229    initialise: container => new Showon(container)
230  };
231}
232
233/**
234 * Initialize 'showon' feature at an initial page load
235 */
236Joomla.Showon.initialise(document);
237
238/**
239 * Search for matching parents
240 *
241 * @param {HTMLElement} $child
242 * @param {String} selector
243 * @returns {HTMLElement[]}
244 */
245const getMatchedParents = ($child, selector) => {
246  let $parent = $child;
247  let $matchingParent;
248  const parents = [];
249  while ($parent) {
250    $matchingParent = $parent.matches && $parent.matches(selector) ? $parent : null;
251    if ($matchingParent) {
252      parents.unshift($matchingParent);
253    }
254    $parent = $parent.parentNode;
255  }
256  return parents;
257};
258
259/**
260 * Initialize 'showon' feature when part of the page was updated
261 */
262document.addEventListener('joomla:updated', ({
263  target
264}) => {
265  // Check is it subform, then wee need to fix some "showon" config
266  if (target.classList.contains('subform-repeatable-group')) {
267    const elements = target.querySelectorAll('[data-showon]');
268    if (elements.length) {
269      const search = [];
270      const replace = [];
271
272      // Collect all parent groups of changed group
273      getMatchedParents(target, '.subform-repeatable-group').forEach($parent => {
274        search.push(new RegExp(`\\[${$parent.dataset.baseName}X\\]`, 'g'));
275        replace.push(`[${$parent.dataset.group}]`);
276      });
277
278      // Fix showon field names in a current group
279      elements.forEach(element => {
280        let {
281          showon
282        } = element.dataset;
283        search.forEach((pattern, i) => {
284          showon = showon.replace(pattern, replace[i]);
285        });
286        element.dataset.showon = showon;
287      });
288    }
289  }
290  Joomla.Showon.initialise(target);
291});

Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.