PageSourceSearch

https://sp13starachowice.pl/js/modal.js

js sp13starachowice.pl collected 2026-10-03 02:02:46 UTC 13,153 bytes, 394 lines download raw bytes

1(function (global, factory) {
2    typeof exports === 'object' && typeof module !== 'undefined' ? module.exports = factory() :
3    typeof define === 'function' && define.amd ? define(factory) :
4    (global = typeof globalThis !== 'undefined' ? globalThis : global || self, global.A11yDialog = factory());
5  }(this, (function () { 'use strict';
6  
7    var focusableSelectors = [
8      'a[href]:not([tabindex^="-"])',
9      'area[href]:not([tabindex^="-"])',
10      'input:not([type="hidden"]):not([type="radio"]):not([disabled]):not([tabindex^="-"])',
11      'input[type="radio"]:not([disabled]):not([tabindex^="-"])',
12      'select:not([disabled]):not([tabindex^="-"])',
13      'textarea:not([disabled]):not([tabindex^="-"])',
14      'button:not([disabled]):not([tabindex^="-"])',
15      'iframe:not([tabindex^="-"])',
16      'audio[controls]:not([tabindex^="-"])',
17      'video[controls]:not([tabindex^="-"])',
18      '[contenteditable]:not([tabindex^="-"])',
19      '[tabindex]:not([tabindex^="-"])',
20    ];
21  
22    var TAB_KEY = 9;
23    var ESCAPE_KEY = 27;
24  
25    /**
26     * Define the constructor to instantiate a dialog
27     *
28     * @constructor
29     * @param {Element} element
30     */
31    function A11yDialog(element) {
32      // Prebind the functions that will be bound in addEventListener and
33      // removeEventListener to avoid losing references
34      this._show = this.show.bind(this);
35      this._hide = this.hide.bind(this);
36      this._maintainFocus = this._maintainFocus.bind(this);
37      this._bindKeypress = this._bindKeypress.bind(this);
38  
39      this.$el = element;
40      this.shown = false;
41      this._id = this.$el.getAttribute('data-a11y-dialog') || this.$el.id;
42      this._previouslyFocused = null;
43      this._listeners = {};
44  
45      // Initialise everything needed for the dialog to work properly
46      this.create();
47    }
48  
49    /**
50     * Set up everything necessary for the dialog to be functioning
51     *
52     * @param {(NodeList | Element | string)} targets
53     * @return {this}
54     */
55    A11yDialog.prototype.create = function () {
56      this.$el.setAttribute('aria-hidden', true);
57      this.$el.setAttribute('aria-modal', true);
58      this.$el.setAttribute('tabindex', -1);
59  
60      if (!this.$el.hasAttribute('role')) {
61        this.$el.setAttribute('role', 'dialog');
62      }
63  
64      // Keep a collection of dialog openers, each of which will be bound a click
65      // event listener to open the dialog
66      this._openers = $$('[data-a11y-dialog-show="' + this._id + '"]');
67      this._openers.forEach(
68        function (opener) {
69          opener.addEventListener('click', this._show);
70        }.bind(this)
71      );
72  
73      // Keep a collection of dialog closers, each of which will be bound a click
74      // event listener to close the dialog
75      this._closers = $$('[data-a11y-dialog-hide]', this.$el).concat(
76        $$('[data-a11y-dialog-hide="' + this._id + '"]')
77      );
78      this._closers.forEach(
79        function (closer) {
80          closer.addEventListener('click', this._hide);
81        }.bind(this)
82      );
83  
84      // Execute all callbacks registered for the `create` event
85      this._fire('create');
86  
87      return this
88    };
89  
90    /**
91     * Show the dialog element, disable all the targets (siblings), trap the
92     * current focus within it, listen for some specific key presses and fire all
93     * registered callbacks for `show` event
94     *
95     * @param {Event} event
96     * @return {this}
97     */
98    A11yDialog.prototype.show = function (event) {
99      // If the dialog is already open, abort
100      if (this.shown) {
101        return this
102      }
103  
104      // Keep a reference to the currently focused element to be able to restore
105      // it later
106      this._previouslyFocused = document.activeElement;
107      this.$el.removeAttribute('aria-hidden');
108      this.shown = true;
109  
110      // Set the focus to the dialog element
111      moveFocusToDialog(this.$el);
112  
113      // Bind a focus event listener to the body element to make sure the focus
114      // stays trapped inside the dialog while open, and start listening for some
115      // specific key presses (TAB and ESC)
116      document.body.addEventListener('focus', this._maintainFocus, true);
117      document.addEventListener('keydown', this._bindKeypress);
118  
119      // Execute all callbacks registered for the `show` event
120      this._fire('show', event);
121  
122      return this
123    };
124  
125    /**
126     * Hide the dialog element, enable all the targets (siblings), restore the
127     * focus to the previously active element, stop listening for some specific
128     * key presses and fire all registered callbacks for `hide` event
129     *
130     * @param {Event} event
131     * @return {this}
132     */
133    A11yDialog.prototype.hide = function (event) {
134      // If the dialog is already closed, abort
135      if (!this.shown) {
136        return this
137      }
138  
139      this.shown = false;
140      this.$el.setAttribute('aria-hidden', 'true');
141  
142      // If there was a focused element before the dialog was opened (and it has a
143      // `focus` method), restore the focus back to it
144      // See: https://github.com/KittyGiraudel/a11y-dialog/issues/108
145      if (this._previouslyFocused && this._previouslyFocused.focus) {
146        this._previouslyFocused.focus();
147      }
148  
149      // Remove the focus event listener to the body element and stop listening
150      // for specific key presses
151      document.body.removeEventListener('focus', this._maintainFocus, true);
152      document.removeEventListener('keydown', this._bindKeypress);
153  
154      // Execute all callbacks registered for the `hide` event
155      this._fire('hide', event);
156  
157      return this
158    };
159  
160    /**
161     * Destroy the current instance (after making sure the dialog has been hidden)
162     * and remove all associated listeners from dialog openers and closers
163     *
164     * @return {this}
165     */
166    A11yDialog.prototype.destroy = function () {
167      // Hide the dialog to avoid destroying an open instance
168      this.hide();
169  
170      // Remove the click event listener from all dialog openers
vendor: 6,923 bytes, lines 171-394
171      this._openers.forEach(
172        function (opener) {
173          opener.removeEventListener('click', this._show);
174        }.bind(this)
175      );
176  
177      // Remove the click event listener from all dialog closers
178      this._closers.forEach(
179        function (closer) {
180          closer.removeEventListener('click', this._hide);
181        }.bind(this)
182      );
183  
184      // Execute all callbacks registered for the `destroy` event
185      this._fire('destroy');
186  
187      // Keep an object of listener types mapped to callback functions
188      this._listeners = {};
189  
190      return this
191    };
192  
193    /**
194     * Register a new callback for the given event type
195     *
196     * @param {string} type
197     * @param {Function} handler
198     */
199    A11yDialog.prototype.on = function (type, handler) {
200      if (typeof this._listeners[type] === 'undefined') {
201        this._listeners[type] = [];
202      }
203  
204      this._listeners[type].push(handler);
205  
206      return this
207    };
208  
209    /**
210     * Unregister an existing callback for the given event type
211     *
212     * @param {string} type
213     * @param {Function} handler
214     */
215    A11yDialog.prototype.off = function (type, handler) {
216      var index = (this._listeners[type] || []).indexOf(handler);
217  
218      if (index > -1) {
219        this._listeners[type].splice(index, 1);
220      }
221  
222      return this
223    };
224  
225    /**
226     * Iterate over all registered handlers for given type and call them all with
227     * the dialog element as first argument, event as second argument (if any).
228     *
229     * @access private
230     * @param {string} type
231     * @param {Event} event
232     */
233    A11yDialog.prototype._fire = function (type, event) {
234      var listeners = this._listeners[type] || [];
235  
236      listeners.forEach(
237        function (listener) {
238          listener(this.$el, event);
239        }.bind(this)
240      );
241    };
242  
243    /**
244     * Private event handler used when listening to some specific key presses
245     * (namely ESCAPE and TAB)
246     *
247     * @access private
248     * @param {Event} event
249     */
250    A11yDialog.prototype._bindKeypress = function (event) {
251      // This is an escape hatch in case there are nested dialogs, so the keypresses
252      // are only reacted to for the most recent one
253      if (!this.$el.contains(document.activeElement)) return
254  
255      // If the dialog is shown and the ESCAPE key is being pressed, prevent any
256      // further effects from the ESCAPE key and hide the dialog, unless its role
257      // is 'alertdialog', which should be modal
258      if (
259        this.shown &&
260        event.which === ESCAPE_KEY &&
261        this.$el.getAttribute('role') !== 'alertdialog'
262      ) {
263        event.preventDefault();
264        this.hide(event);
265      }
266  
267      // If the dialog is shown and the TAB key is being pressed, make sure the
268      // focus stays trapped within the dialog element
269      if (this.shown && event.which === TAB_KEY) {
270        trapTabKey(this.$el, event);
271      }
272    };
273  
274    /**
275     * Private event handler used when making sure the focus stays within the
276     * currently open dialog
277     *
278     * @access private
279     * @param {Event} event
280     */
281    A11yDialog.prototype._maintainFocus = function (event) {
282      // If the dialog is shown and the focus is not within a dialog element (either
283      // this one or another one in case of nested dialogs) or within an element
284      // with the `data-a11y-dialog-focus-trap-ignore` attribute, move it back to
285      // its first focusable child.
286      // See: https://github.com/KittyGiraudel/a11y-dialog/issues/177
287      if (
288        this.shown &&
289        !event.target.closest('[aria-modal="true"]') &&
290        !event.target.closest('[data-a11y-dialog-ignore-focus-trap]')
291      ) {
292        moveFocusToDialog(this.$el);
293      }
294    };
295  
296    /**
297     * Convert a NodeList into an array
298     *
299     * @param {NodeList} collection
300     * @return {Array<Element>}
301     */
302    function toArray(collection) {
303      return Array.prototype.slice.call(collection)
304    }
305  
306    /**
307     * Query the DOM for nodes matching the given selector, scoped to context (or
308     * the whole document)
309     *
310     * @param {String} selector
311     * @param {Element} [context = document]
312     * @return {Array<Element>}
313     */
314    function $$(selector, context) {
315      return toArray((context || document).querySelectorAll(selector))
316    }
317  
318    /**
319     * Set the focus to the first element with `autofocus` with the element or the
320     * element itself
321     *
322     * @param {Element} node
323     */
324    function moveFocusToDialog(node) {
325      var focused = node.querySelector('[autofocus]') || node;
326  
327      focused.focus();
328    }
329  
330    /**
331     * Get the focusable children of the given element
332     *
333     * @param {Element} node
334     * @return {Array<Element>}
335     */
336    function getFocusableChildren(node) {
337      return $$(focusableSelectors.join(','), node).filter(function (child) {
338        return !!(
339          child.offsetWidth ||
340          child.offsetHeight ||
341          child.getClientRects().length
342        )
343      })
344    }
345  
346    /**
347     * Trap the focus inside the given element
348     *
349     * @param {Element} node
350     * @param {Event} event
351     */
352    function trapTabKey(node, event) {
353      var focusableChildren = getFocusableChildren(node);
354      var focusedItemIndex = focusableChildren.indexOf(document.activeElement);
355  
356      // If the SHIFT key is being pressed while tabbing (moving backwards) and
357      // the currently focused item is the first one, move the focus to the last
358      // focusable item from the dialog element
359      if (event.shiftKey && focusedItemIndex === 0) {
360        focusableChildren[focusableChildren.length - 1].focus();
361        event.preventDefault();
362        // If the SHIFT key is not being pressed (moving forwards) and the currently
363        // focused item is the last one, move the focus to the first focusable item
364        // from the dialog element
365      } else if (
366        !event.shiftKey &&
367        focusedItemIndex === focusableChildren.length - 1
368      ) {
369        focusableChildren[0].focus();
370        event.preventDefault();
371      }
372    }
373  
374    function instantiateDialogs() {
375      $$('[data-a11y-dialog]').forEach(function (node) {
376        new A11yDialog(node);
377      });
378    }
379  
380    if (typeof document !== 'undefined') {
381      if (document.readyState === 'loading') {
382        document.addEventListener('DOMContentLoaded', instantiateDialogs);
383      } else {
384        if (window.requestAnimationFrame) {
385          window.requestAnimationFrame(instantiateDialogs);
386        } else {
387          window.setTimeout(instantiateDialogs, 16);
388        }
389      }
390    }
391  
392    return A11yDialog;
393  
394  })));

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.