PageSourceSearch

https://www.lesmachines-nantes.fr/wp-content/themes/lesmachines_2018/assets/js/dialog.js

js lesmachines-nantes.fr collected 2026-09-24 20:48:36 UTC 13,856 bytes, 464 lines download raw bytes

1/*
2 *   This content is licensed according to the W3C Software License at
3 *   https://www.w3.org/Consortium/Legal/2015/copyright-software-and-document
4 */
5
6"use strict";
7
8var aria = aria || {};
9
10aria.Utils = aria.Utils || {};
11
12(function () {
13  /*
14   * When util functions move focus around, set this true so the focus listener
15   * can ignore the events.
16   */
17  aria.Utils.IgnoreUtilFocusChanges = false;
18
19  aria.Utils.dialogOpenClass = "has-dialog";
20
21  /**
22   * @description Set focus on descendant nodes until the first focusable element is
23   *       found.
24   * @param element
25   *          DOM node for which to find the first focusable descendant.
26   * @returns {boolean}
27   *  true if a focusable element is found and focus is set.
28   */
29  aria.Utils.focusFirstDescendant = function (element) {
30    for (var i = 0; i < element.childNodes.length; i++) {
31      var child = element.childNodes[i];
32      if (
33        aria.Utils.attemptFocus(child) ||
34        aria.Utils.focusFirstDescendant(child)
35      ) {
36        return true;
37      }
38    }
39    return false;
40  }; // end focusFirstDescendant
41
42  /**
43   * @description Find the last descendant node that is focusable.
44   * @param element
45   *          DOM node for which to find the last focusable descendant.
46   * @returns {boolean}
47   *  true if a focusable element is found and focus is set.
48   */
49  aria.Utils.focusLastDescendant = function (element) {
50    for (var i = element.childNodes.length - 1; i >= 0; i--) {
51      var child = element.childNodes[i];
52      if (
53        aria.Utils.attemptFocus(child) ||
54        aria.Utils.focusLastDescendant(child)
55      ) {
56        return true;
57      }
58    }
59    return false;
60  }; // end focusLastDescendant
61
62  /**
63   * @description Set Attempt to set focus on the current node.
64   * @param element
65   *          The node to attempt to focus on.
66   * @returns {boolean}
67   *  true if element is focused.
68   */
69  aria.Utils.attemptFocus = function (element) {
70    if (!aria.Utils.isFocusable(element)) {
71      return false;
72    }
73
74    aria.Utils.IgnoreUtilFocusChanges = true;
75    try {
76      element.focus();
77    } catch (e) {
78      // continue regardless of error
79    }
80    aria.Utils.IgnoreUtilFocusChanges = false;
81    return document.activeElement === element;
82  }; // end attemptFocus
83
84  /* Modals can open modals. Keep track of them with this array. */
85  aria.OpenDialogList = aria.OpenDialogList || new Array(0);
86
87  /**
88   * @returns {object} the last opened dialog (the current dialog)
89   */
90  aria.getCurrentDialog = function () {
91    if (aria.OpenDialogList && aria.OpenDialogList.length) {
92      return aria.OpenDialogList[aria.OpenDialogList.length - 1];
93    }
94  };
95
96  aria.closeCurrentDialog = function () {
97    var currentDialog = aria.getCurrentDialog();
98    if (currentDialog) {
99      currentDialog.close();
100      return true;
101    }
102
103    return false;
104  };
105
106  aria.handleEscape = function (event) {
107    var key = event.which || event.keyCode;
108
109    if (key === aria.KeyCode.ESC && aria.closeCurrentDialog()) {
110      event.stopPropagation();
111    }
112  };
113
114  document.addEventListener("keyup", aria.handleEscape);
115
116  /**
117   * @class
118   * @description Dialog object providing modal focus management.
119   *
120   * Assumptions: The element serving as the dialog container is present in the
121   * DOM and hidden. The dialog container has role='dialog'.
122   * @param dialogId
123   *          The ID of the element serving as the dialog container.
124   * @param focusAfterClosed
125   *          Either the DOM node or the ID of the DOM node to focus when the
126   *          dialog closes.
127   * @param focusFirst
128   *          Optional parameter containing either the DOM node or the ID of the
129   *          DOM node to focus when the dialog opens. If not specified, the
130   *          first focusable element in the dialog will receive focus.
131   */
132  aria.Dialog = function (dialogId, focusAfterClosed, focusFirst) {
133    this.dialogNode = document.getElementById(dialogId);
134    if (this.dialogNode === null) {
135      throw new Error('No element found with id="' + dialogId + '".');
136    }
137
138    var validRoles = ["dialog", "alertdialog"];
139    var isDialog = (this.dialogNode.getAttribute("role") || "")
140      .trim()
141      .split(/\s+/g)
142      .some(function (token) {
143        return validRoles.some(function (role) {
144          return token === role;
145        });
146      });
147    if (!isDialog) {
148      throw new Error(
149        "Dialog() requires a DOM element with ARIA role of dialog or alertdialog."
150      );
151    }
152
153    // Wrap in an individual backdrop element if one doesn't exist
154    // Native <dialog> elements use the ::backdrop pseudo-element, which
155    // works similarly.
156    var backdropClass = "dialog-backdrop";
157    if (this.dialogNode.parentNode.classList.contains(backdropClass)) {
158      this.backdropNode = this.dialogNode.parentNode;
159    } else {
160      this.backdropNode = document.createElement("div");
161      this.backdropNode.className = backdropClass;
162      this.dialogNode.parentNode.insertBefore(
163        this.backdropNode,
164        this.dialogNode
165      );
166      this.backdropNode.appendChild(this.dialogNode);
167    }
168    this.backdropNode.classList.add("active");
169
170    // Disable scroll on the body element
171    document.body.classList.add(aria.Utils.dialogOpenClass);
172
173    if (typeof focusAfterClosed === "string") {
174      this.focusAfterClosed = document.getElementById(focusAfterClosed);
175    } else if (typeof focusAfterClosed === "object") {
176      this.focusAfterClosed = focusAfterClosed;
177    } else {
178      throw new Error(
179        "the focusAfterClosed parameter is required for the aria.Dialog constructor."
180      );
181    }
182
183    if (typeof focusFirst === "string") {
184      this.focusFirst = document.getElementById(focusFirst);
185    } else if (typeof focusFirst === "object") {
186      this.focusFirst = focusFirst;
187    } else {
188      this.focusFirst = null;
189    }
190
191    // Bracket the dialog node with two invisible, focusable nodes.
192    // While this dialog is open, we use these to make sure that focus never
193    // leaves the document even if dialogNode is the first or last node.
194    var preDiv = document.createElement("div");
195    this.preNode = this.dialogNode.parentNode.insertBefore(
196      preDiv,
197      this.dialogNode
198    );
199    this.preNode.tabIndex = 0;
200    var postDiv = document.createElement("div");
201    this.postNode = this.dialogNode.parentNode.insertBefore(
202      postDiv,
203      this.dialogNode.nextSibling
204    );
205    this.postNode.tabIndex = 0;
206
207    // If this modal is opening on top of one that is already open,
208    // get rid of the document focus listener of the open dialog.
209    if (aria.OpenDialogList.length > 0) {
210      aria.getCurrentDialog().removeListeners();
211    }
212
213    this.addListeners();
214    aria.OpenDialogList.push(this);
215    this.clearDialog();
216    this.dialogNode.className = "default_dialog"; // make visible
217
218    if (this.focusFirst) {
219      this.focusFirst.focus();
220    } else {
221      aria.Utils.focusFirstDescendant(this.dialogNode);
222    }
223
224    this.lastFocus = document.activeElement;
225  }; // end Dialog constructor
226
227  aria.Dialog.prototype.clearDialog = function () {
228    Array.prototype.map.call(
229      this.dialogNode.querySelectorAll("input"),
230      function (input) {
231        input.value = "";
232      }
233    );
234  };
235
236  /**
237   * @description
238   *  Hides the current top dialog,
239   *  removes listeners of the top dialog,
240   *  restore listeners of a parent dialog if one was open under the one that just closed,
241   *  and sets focus on the element specified for focusAfterClosed.
242   */
243  aria.Dialog.prototype.close = function () {
244    aria.OpenDialogList.pop();
245    this.removeListeners();
246    aria.Utils.remove(this.preNode);
247    aria.Utils.remove(this.postNode);
248    this.dialogNode.className = "hidden";
249    this.backdropNode.classList.remove("active");
250    this.focusAfterClosed.focus();
251
252    // If a dialog was open underneath this one, restore its listeners.
253    if (aria.OpenDialogList.length > 0) {
254      aria.getCurrentDialog().addListeners();
255    } else {
256      document.body.classList.remove(aria.Utils.dialogOpenClass);
257    }
258  }; // end close
259
260  /**
261   * @description
262   *  Hides the current dialog and replaces it with another.
263   * @param newDialogId
264   *  ID of the dialog that will replace the currently open top dialog.
265   * @param newFocusAfterClosed
266   *  Optional ID or DOM node specifying where to place focus when the new dialog closes.
267   *  If not specified, focus will be placed on the element specified by the dialog being replaced.
268   * @param newFocusFirst
269   *  Optional ID or DOM node specifying where to place focus in the new dialog when it opens.
270   *  If not specified, the first focusable element will receive focus.
271   */
272  aria.Dialog.prototype.replace = function (
273    newDialogId,
274    newFocusAfterClosed,
275    newFocusFirst
276  ) {
277    aria.OpenDialogList.pop();
278    this.removeListeners();
279    aria.Utils.remove(this.preNode);
280    aria.Utils.remove(this.postNode);
281    this.dialogNode.className = "hidden";
282    this.backdropNode.classList.remove("active");
283
284    var focusAfterClosed = newFocusAfterClosed || this.focusAfterClosed;
285    new aria.Dialog(newDialogId, focusAfterClosed, newFocusFirst);
286  }; // end replace
287
288  aria.Dialog.prototype.addListeners = function () {
289    document.addEventListener("focus", this.trapFocus, true);
290  }; // end addListeners
291
292  aria.Dialog.prototype.removeListeners = function () {
293    document.removeEventListener("focus", this.trapFocus, true);
294  }; // end removeListeners
295
296  aria.Dialog.prototype.trapFocus = function (event) {
297    if (aria.Utils.IgnoreUtilFocusChanges) {
298      return;
299    }
300    var currentDialog = aria.getCurrentDialog();
301    if (currentDialog.dialogNode.contains(event.target)) {
302      currentDialog.lastFocus = event.target;
303    } else {
304      aria.Utils.focusFirstDescendant(currentDialog.dialogNode);
305      if (currentDialog.lastFocus == document.activeElement) {
306        aria.Utils.focusLastDescendant(currentDialog.dialogNode);
307      }
308      currentDialog.lastFocus = document.activeElement;
309    }
310  }; // end trapFocus
311
312  window.openDialog = function (dialogId, focusAfterClosed, focusFirst) {
313    new aria.Dialog(dialogId, focusAfterClosed, focusFirst);
314  };
315
316  window.closeDialog = function (closeButton) {
317    var topDialog = aria.getCurrentDialog();
318    if (topDialog.dialogNode.contains(closeButton)) {
319      topDialog.close();
320    }
321  }; // end closeDialog
322
323  window.replaceDialog = function (
324    newDialogId,
325    newFocusAfterClosed,
326    newFocusFirst
327  ) {
328    var topDialog = aria.getCurrentDialog();
329    if (topDialog.dialogNode.contains(document.activeElement)) {
330      topDialog.replace(newDialogId, newFocusAfterClosed, newFocusFirst);
331    }
332  }; // end replaceDialog
333})();
334("use strict");
335/**
336 * @namespace aria
337 */
338
339var aria = aria || {};
340
341/**
342 * @description
343 *  Key code constants
344 */
345aria.KeyCode = {
346  BACKSPACE: 8,
347  TAB: 9,
348  RETURN: 13,
349  SHIFT: 16,
350  ESC: 27,
351  SPACE: 32,
352  PAGE_UP: 33,
353  PAGE_DOWN: 34,
354  END: 35,
355  HOME: 36,
356  LEFT: 37,
357  UP: 38,
358  RIGHT: 39,
359  DOWN: 40,
360  DELETE: 46,
361};
362
363aria.Utils = aria.Utils || {};
364
365// Polyfill src https://developer.mozilla.org/en-US/docs/Web/API/Element/matches
366aria.Utils.matches = function (element, selector) {
367  if (!Element.prototype.matches) {
368    Element.prototype.matches =
369      Element.prototype.matchesSelector ||
370      Element.prototype.mozMatchesSelector ||
371      Element.prototype.msMatchesSelector ||
372      Element.prototype.oMatchesSelector ||
373      Element.prototype.webkitMatchesSelector ||
374      function (s) {
375        var matches = element.parentNode.querySelectorAll(s);
376        var i = matches.length;
377        while (--i >= 0 && matches.item(i) !== this) {
378          // empty
379        }
380        return i > -1;
381      };
382  }
383
384  return element.matches(selector);
385};
386
387aria.Utils.remove = function (item) {
388  if (item.remove && typeof item.remove === "function") {
389    return item.remove();
390  }
391  if (
392    item.parentNode &&
393    item.parentNode.removeChild &&
394    typeof item.parentNode.removeChild === "function"
395  ) {
396    return item.parentNode.removeChild(item);
397  }
398  return false;
399};
400
401aria.Utils.isFocusable = function (element) {
402  if (element.tabIndex < 0) {
403    return false;
404  }
405
406  if (element.disabled) {
407    return false;
408  }
409
410  switch (element.nodeName) {
411    case "A":
412      return !!element.href && element.rel != "ignore";
413    case "INPUT":
414      return element.type != "hidden";
415    case "BUTTON":
416    case "SELECT":
417    case "TEXTAREA":
418      return true;
419    default:
420      return false;
421  }
422};
423
424aria.Utils.getAncestorBySelector = function (element, selector) {
425  if (!aria.Utils.matches(element, selector + " " + element.tagName)) {
426    // Element is not inside an element that matches selector
427    return null;
428  }
429
430  // Move up the DOM tree until a parent matching the selector is found
431  var currentNode = element;
432  var ancestor = null;
433  while (ancestor === null) {
434    if (aria.Utils.matches(currentNode.parentNode, selector)) {
435      ancestor = currentNode.parentNode;
436    } else {
437      currentNode = currentNode.parentNode;
438    }
439  }
440
441  return ancestor;
442};
443
444aria.Utils.hasClass = function (element, className) {
445  return new RegExp("(\\s|^)" + className + "(\\s|$)").test(element.className);
446};
447
448aria.Utils.addClass = function (element, className) {
449  if (!aria.Utils.hasClass(element, className)) {
450    element.className += " " + className;
451  }
452};
453
454aria.Utils.removeClass = function (element, className) {
455  var classRegex = new RegExp("(\\s|^)" + className + "(\\s|$)");
456  element.className = element.className.replace(classRegex, " ").trim();
457};
458
459aria.Utils.bindMethods = function (object /* , ...methodNames */) {
460  var methodNames = Array.prototype.slice.call(arguments, 1);
461  methodNames.forEach(function (method) {
462    object[method] = object[method].bind(object);
463  });
464};

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.