PageSourceSearch

https://www.stanleytools.ca/sites/g/files/xnuzat1481/files/cohesion/scripts/modal/aria.modal.js?tl1p70

js stanleytools.ca collected 2026-09-28 06:04:42 UTC 14,506 bytes, 379 lines download raw bytes

1// https://github.com/cohesiondx8/accessible_modal_window - forked to here
2// @sitestudioexcludesonar
3;(function (w, doc, Drupal) {
4  'use strict';
5  Drupal.behaviors.ARIAmodal = {
6    attach: function (context, settings) {
7
8      var ARIAmodal = {};
9      w.ARIAmodal = ARIAmodal;
10      ARIAmodal.NS = 'ARIAmodal';
11      ARIAmodal.AUTHOR = 'Scott O\'Hara';
12      ARIAmodal.VERSION = '3.4.0';
13      ARIAmodal.LICENSE = 'https://github.com/scottaohara/accessible_modal_window/blob/master/LICENSE';
14      var activeClass = 'modal-open';
15      var body = doc.body;
16      var main = doc.getElementsByTagName('main')[0] || body;
17      var modal = doc.querySelectorAll('[data-modal]');
18      var children = doc.querySelectorAll('body > *:not([data-modal])');
19      var initialTrigger;
20      var activeModal;
21      var useAriaModal = false;
22      var returnToBody = false;
23      var firstClass = 'js-first-focus';
24      var lastClass = 'js-last-focus';
25      var tabFocusElements = 'button:not([hidden]):not([disabled]), [href]:not([hidden]), input:not([hidden]):not([type="hidden"]):not([disabled]), select:not([hidden]):not([disabled]), textarea:not([hidden]):not([disabled]), [tabindex="0"]:not([hidden]):not([disabled]), summary:not([hidden]), [contenteditable]:not([hidden]), audio[controls]:not([hidden]), video[controls]:not([hidden])';
26
27      ARIAmodal.getOnce = function(element, namespace) {
28        return element.dataset[namespace] || false;
29      };
30      ARIAmodal.unsetOnce = function(element, namespace) {
31        element.dataset[namespace] = undefined;
32      };
33      ARIAmodal.setOnce = function(element, namespace) {
34        element.dataset[namespace] = true;
35      };
36      ARIAmodal.organizeDOM = function () {
37        var refEl = body.firstElementChild || null;
38        var i;
39        var self;
40        for (i = 0; i < modal.length; i += 1) {
41
42          self = modal[i];
43
44          if(ARIAmodal.getOnce(self, 'modal')) {
45            continue;
46          }
47          ARIAmodal.setOnce(self, 'modal');
48
49          body.insertBefore(modal[i], refEl)
50        }
51      };
52      ARIAmodal.setupTrigger = function () {
53        var trigger = context.querySelectorAll('[data-modal-open]');
54        var self;
55        var i;
56        for (i = 0; i < trigger.length; i += 1) {
57          self = trigger[i];
58
59          // Skip if already set
60          if(ARIAmodal.getOnce(self, 'trigger'))  {
61            continue;
62          }
63
64          ARIAmodal.setOnce(self, 'trigger');
65
66          var getOpenTarget = self.getAttribute('data-modal-open');
67          var hasHref = self.getAttribute('href');
68          if (self.nodeName !== 'BUTTON') {
69            self.setAttribute('role', 'button');
70            self.tabIndex = 0
71          }
72          if (getOpenTarget === '' && hasHref) {
73            self.setAttribute('data-modal-open', hasHref.split('#')[1]);
74            getOpenTarget = hasHref.split('#')[1]
75          }
76          self.removeAttribute('href');
77          if (getOpenTarget) {
78            if (self.hasAttribute('disabled') && !self.hasAttribute('data-modal-disabled')) {
79              self.removeAttribute('disabled')
80            }
81            self.removeAttribute('hidden');
82            self.addEventListener('click', ARIAmodal.openModal);
83            self.addEventListener('keydown', ARIAmodal.keyEvents, false)
84          } else {
85            console.warn('Missing target modal dialog - [data-modal-open="IDREF"]')
86          }
87        }
88      };
89      ARIAmodal.setupModal = function () {
90        var self;
91        var i;
92        for (i = 0; i < modal.length; i += 1) {
93          self = modal[i];
94          // Skip if already set
95          if(ARIAmodal.getOnce(self, 'setupModal'))  {
96            continue;
97          }
98          ARIAmodal.setOnce(self, 'setupModal');
99
100          var modalType = self.getAttribute('data-modal');
101          var getClass = self.getAttribute('data-modal-class') || 'a11y-modal';
102          var heading = self.querySelector('h1, h2, h3, h4, h5, h6');
103          var modalLabel = self.getAttribute('data-modal-label');
104          var hideHeading = self.hasAttribute('data-modal-hide-heading');
105          var modalDesc = self.querySelector('[data-modal-description]');
106          var modalDoc = self.querySelector('[data-modal-document]');
107          if (modalType === 'alert') {
108            self.setAttribute('role', 'alertdialog')
109          } else {
110            self.setAttribute('role', 'dialog')
111          }
112          self.classList.add(getClass);
113          self.hidden = true;
114          self.tabIndex = '-1';
115          if (modalDoc) {
116            modalDoc.setAttribute('role', 'document')
117          }
118          ARIAmodal.setupModalCloseBtn(self, getClass, modalType);
119          if (self.hasAttribute('data-aria-modal')) {
120            self.setAttribute('aria-modal', 'true')
121          }
122          if (modalDesc) {
123            modalDesc.id = modalDesc.id || 'md_desc_' + Math.floor(Math.random() * 999) + 1;
124            self.setAttribute('aria-describedby', modalDesc.id)
125          }
126          if (modalLabel) {
127            self.setAttribute('aria-label', modalLabel)
128          } else {
129            if (heading) {
130              var makeHeading = self.id + '_heading';
131              heading.classList.add(getClass + '__heading');
132              heading.id = makeHeading;
133              self.setAttribute('aria-labelledby', makeHeading);
134              if (heading.hasAttribute('data-autofocus')) {
135                heading.tabIndex = '-1'
136              }
137            } else {
138              console.warn('Dialogs should have their purpose conveyed by a heading element (h1).')
139            }
140          }
141          if (hideHeading) {
142            self.querySelector('#' + heading.id).classList.add('at-only')
143          }
144          var focusable = self.querySelectorAll(tabFocusElements);
145          focusable[0].classList.add(firstClass);
146          focusable[focusable.length - 1].classList.add(lastClass)
147
148
149        }
150      };
151      ARIAmodal.setupModalCloseBtn = function (self, modalClass, modalType) {
152        var doNotGenerate = self.hasAttribute('data-modal-manual-close');
153        var manualClose = self.querySelectorAll('[data-modal-close-btn]');
154        var modalClose = self.getAttribute('data-modal-close');
155        var modalCloseClass = self.getAttribute('data-modal-close-class');
156        var closeIcon = '<span data-modal-x></span>';
157        var btnClass = modalClass;
158        var i;
159        if (!doNotGenerate) {
160          if (manualClose.length < 2) {
161            var closeBtn = doc.createElement('button');
162            closeBtn.type = 'button';
163            self.classList.add(modalClass);
164            closeBtn.classList.add(modalClass + '__close-btn');
165            if (!modalClose && modalType !== 'alert') {
166              closeBtn.innerHTML = closeIcon;
167              closeBtn.setAttribute('aria-label', 'Close');
168              closeBtn.classList.add('is-icon-btn')
169            } else {
170              closeBtn.innerHTML = modalClose;
171              if (modalCloseClass) {
172                closeBtn.classList.add(modalCloseClass)
173              }
174            }
175            if (modalType !== 'alert') {
176              if (self.querySelector('[role="document"]')) {
177                self.querySelector('[role="document"]').appendChild(closeBtn)
178              } else {
179                self.appendChild(closeBtn)
180              }
181            }
182            closeBtn.addEventListener('click', ARIAmodal.closeModal)
183          }
184        }
185        for (i = 0; i < manualClose.length; i += 1) {
186          manualClose[i].addEventListener('click', ARIAmodal.closeModal)
187        }
188        doc.addEventListener('keydown', ARIAmodal.keyEvents, false)
189      };
190      ARIAmodal.openModal = function (e, autoOpen) {
191        var i;
192        var getTargetModal = autoOpen || this.getAttribute('data-modal-open');
193        activeModal = doc.getElementById(getTargetModal);
194        var focusTarget = activeModal;
195        var getAutofocus = activeModal.querySelector('[autofocus]') || activeModal.querySelector('[data-autofocus]');
196        var setInert = activeModal.hasAttribute('data-coh-modal-overlay');
197        useAriaModal = activeModal.hasAttribute('aria-modal');
198        if (autoOpen) {
199          returnToBody = true
200        }
201        if (!autoOpen) {
202          e.preventDefault();
203          initialTrigger = this.id
204        }
205        if (getAutofocus) {
206          focusTarget = getAutofocus
207        } else if (activeModal.hasAttribute('data-modal-close-focus')) {
208          focusTarget = activeModal.querySelector('[class*="close-btn"]')
209        }
210        if (!body.classList.contains(activeClass)) {
211          body.classList.add(activeClass);
212
213          if(setInert) {
214            for (i = 0; i < children.length; i += 1) {
215              if (!useAriaModal) {
216                if (children[i].hasAttribute('aria-hidden')) {
217                  children[i].setAttribute('data-keep-hidden', children[i].getAttribute('aria-hidden'))
218                }
219                children[i].setAttribute('aria-hidden', 'true')
220              }
221              if (children[i].getAttribute('inert')) {
222                children[i].setAttribute('data-keep-inert', '')
223              } else {
224                children[i].setAttribute('inert', 'true')
225              }
226            }
227          }
228        } else {
229          console.warn('It is not advised to open dialogs from within other dialogs. Instead consider replacing the contents of this dialog with new content. Or providing a stepped, or tabbed interface within this dialog.')
230        }
231        activeModal.removeAttribute('hidden');
232        requestAnimationFrame(function () {
233          focusTarget.focus()
234        });
235
236        return [initialTrigger, activeModal, returnToBody]
237      };
238      ARIAmodal.closeModal = function (e) {
239        var trigger = doc.getElementById(initialTrigger) || null;
240        var i;
241        var m;
242        for (i = 0; i < children.length; i += 1) {
243          if (!children[i].hasAttribute('data-keep-inert')) {
244            children[i].removeAttribute('inert')
245          }
246          children[i].removeAttribute('data-keep-inert');
247          if (children[i].getAttribute('data-keep-hidden')) {
248            children[i].setAttribute('aria-hidden', children[i].getAttribute('data-keep-hidden'))
249          } else {
250            children[i].removeAttribute('aria-hidden')
251          }
252          children[i].removeAttribute('data-keep-hidden')
253        }
254        body.classList.remove(activeClass);
255        for (m = 0; m < modal.length; m += 1) {
256          if (!modal[m].hasAttribute('hidden')) {
257            modal[m].hidden = true
258          }
259        }
260        if (trigger !== null) {
261          trigger.focus()
262        } else {
263          if (main && !returnToBody) {
264            main.tabIndex = -1;
265            main.focus()
266          } else {
267            body.tabIndex = -1;
268            body.focus()
269          }
270        }
271        initialTrigger = undefined;
272        activeModal = undefined;
273        returnToBody = false;
274        return [initialTrigger, activeModal, returnToBody]
275      };
276      ARIAmodal.keyEvents = function (e) {
277        var keyCode = e.keyCode || e.which;
278        var escKey = 27;
279        var enterKey = 13;
280        var spaceKey = 32;
281        var tabKey = 9;
282        if (e.target.hasAttribute('data-modal-open')) {
283          switch (keyCode) {
284            case enterKey:
285            case spaceKey:
286              e.preventDefault();
287              e.target.click();
288              break
289          }
290        }
291        if (body.classList.contains(activeClass)) {
292          switch (keyCode) {
293            case escKey:
294              ARIAmodal.closeModal();
295              break;
296            default:
297              break
298          }
299          if (body.classList.contains(activeClass)) {
300            var firstFocus = activeModal.querySelector('.' + firstClass);
301            var lastFocus = activeModal.querySelector('.' + lastClass)
302          }
303          if (doc.activeElement.classList.contains(lastClass)) {
304            if (keyCode === tabKey && !e.shiftKey) {
305              e.preventDefault();
306              firstFocus.focus()
307            }
308          }
309          if (doc.activeElement.classList.contains(firstClass)) {
310            if (keyCode === tabKey && e.shiftKey) {
311              e.preventDefault();
312              lastFocus.focus()
313            }
314          }
315        }
316      };
317
318      ARIAmodal.autoLoad = function () {
319        var getAuto = context.querySelectorAll('[data-modal-auto]');
320
321        // Skip if already set
322        if(getAuto.length <= 0 || ARIAmodal.getOnce(getAuto[0], 'autoload'))  {
323          return;
324        }
325
326        ARIAmodal.setOnce(getAuto[0], 'autoload');
327
328        var hashValue = w.location.hash || null;
329        var autoOpen;
330        var useHash = false;
331        var e = null;
332        if (hashValue !== null) {
333          autoOpen = hashValue.split('#')[1];
334          if (autoOpen === '') {
335            return false
336          } else if (autoOpen === '!null') {
337            return false
338          } else {
339            var checkforDialog = context.getElementById(autoOpen) || null;
340            if (checkforDialog !== null) {
341              if (checkforDialog.getAttribute('role') === 'dialog' || checkforDialog.getAttribute('role') === 'alertdialog') {
342                useHash = true
343              }
344            }
345          }
346        }
347        if (useHash) {
348          ARIAmodal.openModal(e, autoOpen);
349          if (getAuto.length > 1) {
350            console.warn('Only the modal indicated by the hash value will load.')
351          }
352        } else if (getAuto.length !== 0) {
353          if (getAuto[0].getAttribute('role') === 'dialog' || getAuto[0].getAttribute('role') === 'alertdialog') {
354            autoOpen = getAuto[0].id;
355            ARIAmodal.openModal(e, autoOpen);
356            if (getAuto.length > 1) {
357              console.warn('Multiple modal dialogs can not auto load.')
358            }
359          } else if (getAuto[0].getAttribute('role') === 'button' || getAuto[0].tagName === 'BUTTON') {
360            autoOpen = getAuto[0].id;
361            getAuto[0].click()
362          }
363        }
364        if (getAuto.length !== 0 && !doc.getElementById(autoOpen).hasAttribute('data-modal-auto-persist')) {
365          w.location.replace("#!null")
366        }
367      };
368      ARIAmodal.init = function () {
369        ARIAmodal.organizeDOM();
370        ARIAmodal.setupTrigger();
371        ARIAmodal.setupModal();
372        ARIAmodal.autoLoad()
373      };
374
375
376      ARIAmodal.init()
377    }
378  }
379})(window, document, Drupal);

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.