PageSourceSearch

https://www.impots.gouv.fr/libraries/dsfr/dist/dsfr.module.js?tlussq

js impots.gouv.fr collected 2026-09-24 07:35:01 UTC 186,222 bytes, 7,039 lines download raw bytes

vendor: 1,201 bytes, lines 1-62
1/*! DSFR v1.15.2 | SPDX-License-Identifier: Etalab-2.0 | License-Filename: LICENSE.md | restricted use (see terms and conditions) */
2
3class State {
4  constructor () {
5    this.modules = {};
6  }
7
8  create (ModuleClass) {
9    const module = new ModuleClass();
10    this.modules[module.type] = module;
11  }
12
13  getModule (type) {
14    return this.modules[type];
15  }
16
17  add (type, item) {
18    this.modules[type].add(item);
19  }
20
21  remove (type, item) {
22    this.modules[type].remove(item);
23  }
24
25  get isActive () {
26    return this._isActive;
27  }
28
29  set isActive (value) {
30    if (value === this._isActive) return;
31    this._isActive = value;
32    const values = Object.keys(this.modules).map((e) => {
33      return this.modules[e];
34    });
35    if (value) {
36      for (const module of values) {
37        module.activate();
38      }
39    } else {
40      for (const module of values) {
41        module.deactivate();
42      }
43    }
44  }
45
46  get isLegacy () {
47    return this._isLegacy;
48  }
49
50  set isLegacy (value) {
51    if (value === this._isLegacy) return;
52    this._isLegacy = value;
53  }
54}
55
56const state = new State();
57
58const config = {
59  prefix: 'fr',
60  namespace: 'dsfr',
61  organisation: '@gouvfr',
62  version: '1.15.2
vendor: 8,193 bytes, lines 62-409
62'
63};
64
65class LogLevel {
66  constructor (level, light, dark, logger) {
67    this.level = level;
68    this.light = light;
69    this.dark = dark;
70
71    switch (logger) {
72      case 'warn':
73        this.logger = console.warn;
74        break;
75
76      case 'error':
77        this.logger = console.error;
78        break;
79
80      default:
81        this.logger = console.log;
82    }
83  }
84
85  log (...values) {
86    const message = new Message(config.namespace);
87    for (const value of values) message.add(value);
88    this.print(message);
89  }
90
91  print (message) {
92    message.setColor(this.color);
93    this.logger.apply(console, message.getMessage());
94  }
95
96  get color () {
97    return window.matchMedia('(prefers-color-scheme: dark)').matches ? this.dark : this.light;
98  }
99}
100
101class Message {
102  constructor (domain) {
103    this.inputs = ['%c'];
104    this.styles = ['font-family:Marianne', 'line-height: 1.5'];
105    this.objects = [];
106
107    if (domain) this.add(`${domain} :`);
108  }
109
110  add (value) {
111    switch (typeof value) {
112      case 'object':
113      case 'function':
114        this.inputs.push('%o ');
115        this.objects.push(value);
116        break;
117
118      default:
119        this.inputs.push(`${value} `);
120    }
121  }
122
123  setColor (color) {
124    this.styles.push(`color:${color}`);
125  }
126
127  getMessage () {
128    return [this.inputs.join(''), this.styles.join(';'), ...this.objects];
129  }
130}
131
132const LEVELS = {
133  log: new LogLevel(0, '#616161', '#989898'),
134  debug: new LogLevel(1, '#000091', '#8B8BFF'),
135  info: new LogLevel(2, '#007c3b', '#00ed70'),
136  warn: new LogLevel(3, '#ba4500', '#fa5c00', 'warn'),
137  error: new LogLevel(4, '#D80600', '#FF4641', 'error')
138};
139
140class Inspector {
141  constructor () {
142    this.level = 2;
143
144    for (const id in LEVELS) {
145      const level = LEVELS[id];
146      this[id] = (...msgs) => {
147        if (this.level <= level.level) level.log.apply(level, msgs);
148      };
149      this[id].print = level.print.bind(level);
150    }
151  }
152
153  state () {
154    const message = new Message();
155    message.add(state);
156    this.log.print(message);
157  }
158
159  tree () {
160    const stage = state.getModule('stage');
161    if (!stage) return;
162    const message = new Message();
163    this._branch(stage.root, 0, message);
164    this.log.print(message);
165  }
166
167  _branch (element, space, message) {
168    let branch = '';
169    if (space > 0) {
170      let indent = '';
171      for (let i = 0; i < space; i++) indent += '    ';
172      // branch += indent + '|\n';
173      branch += indent + '└─ ';
174    }
175    branch += `[${element.id}] ${element.html}`;
176    message.add(branch);
177    message.add({ '@': element });
178    message.add('\n');
179    for (const child of element.children) branch += this._branch(child, space + 1, message);
180  }
181}
182
183const inspector = new Inspector();
184
185const startAtDomContentLoaded = (callback) => {
186  if (document.readyState !== 'loading') window.requestAnimationFrame(callback);
187  else document.addEventListener('DOMContentLoaded', callback);
188};
189
190const startAuto = (callback) => {
191  // detect
192  startAtDomContentLoaded(callback);
193};
194
195const Modes = {
196  AUTO: 'auto',
197  MANUAL: 'manual',
198  RUNTIME: 'runtime',
199  LOADED: 'loaded',
200  VUE: 'vue',
201  ANGULAR: 'angular',
202  REACT: 'react'
203};
204
205const dispatch = (node, type, detail = null, bubbles = true, cancelable = false) => {
206  const options = { bubbles: bubbles === true, cancelable: cancelable === true };
207  if (detail) options.detail = detail;
208  const event = new CustomEvent(type, options);
209  node.dispatchEvent(event);
210};
211
212const rootDispatch = (type, detail = null, bubbles = false, cancelable = false) => dispatch(document.documentElement, type, detail, bubbles, cancelable);
213
214const ns = name => `${config.prefix}-${name}`;
215
216ns.selector = (name, notation) => {
217  if (notation === undefined) notation = '.';
218  return `${notation}${ns(name)}`;
219};
220
221ns.attr = (name) => `data-${ns(name)}`;
222
223ns.attr.selector = (name, value) => {
224  let result = ns.attr(name);
225  if (value !== undefined) result += `="${value}"`;
226  return `[${result}]`;
227};
228
229ns.event = (type) => `${config.namespace}.${type}`;
230
231ns.emission = (domain, type) => `emission:${domain}.${type}`;
232
233const RootEvent = {
234  READY: ns.event('ready'),
235  START: ns.event('start'),
236  STOP: ns.event('stop'),
237  RENDER: ns.event('render'),
238  RESIZE: ns.event('resize'),
239  BREAKPOINT: ns.event('breakpoint'),
240  SCROLL_LOCK: ns.event('scroll-lock'),
241  SCROLL_UNLOCK: ns.event('scroll-unlock')
242};
243
244class Options {
245  constructor () {
246    this._mode = Modes.AUTO;
247    this.isStarted = false;
248    this.starting = this.start.bind(this);
249    this.preventManipulation = false;
250  }
251
252  configure (settings = {}, start, query) {
253    this.startCallback = start;
254    const isProduction = settings.production && (!query || query.production !== 'false');
255    switch (true) {
256      case query && !isNaN(query.level):
257        inspector.level = Number(query.level);
258        break;
259
260      case query && query.verbose && (query.verbose === 'true' || query.verbose === 1):
261        inspector.level = 0;
262        break;
263
264      case isProduction:
265        inspector.level = 999;
266        break;
267
268      case settings.verbose:
269        inspector.level = 0;
270        break;
271    }
272    inspector.info(`version ${config.version}`);
273    rootDispatch(RootEvent.READY);
274    this.mode = settings.mode || Modes.AUTO;
275  }
276
277  set mode (value) {
278    switch (value) {
279      case Modes.AUTO:
280        this.preventManipulation = false;
281        startAuto(this.starting);
282        break;
283
284      case Modes.LOADED:
285        this.preventManipulation = false;
286        startAtDomContentLoaded(this.starting);
287        break;
288
289      case Modes.RUNTIME:
290        this.preventManipulation = false;
291        this.start();
292        break;
293
294      case Modes.MANUAL:
295        this.preventManipulation = false;
296        break;
297
298      case Modes.VUE:
299        this.preventManipulation = true;
300        break;
301
302      case Modes.ANGULAR:
303        this.preventManipulation = true;
304        break;
305
306      case Modes.REACT:
307        this.preventManipulation = true;
308        break;
309
310      default:
311        inspector.error('Illegal mode');
312        return;
313    }
314
315    this._mode = value;
316    inspector.info(`mode set to ${value}`);
317  }
318
319  get mode () {
320    return this._mode;
321  }
322
323  start () {
324    inspector.info('start');
325    this.startCallback();
326  }
327}
328
329const options = new Options();
330
331class Collection {
332  constructor () {
333    this._collection = [];
334  }
335
336  forEach (callback) {
337    this._collection.forEach(callback);
338  }
339
340  map (callback) {
341    return this._collection.map(callback);
342  }
343
344  get length () {
345    return this._collection.length;
346  }
347
348  add (collectable) {
349    if (this._collection.indexOf(collectable) > -1) return false;
350    this._collection.push(collectable);
351    if (this.onAdd) this.onAdd();
352    if (this.onPopulate && this._collection.length === 1) this.onPopulate();
353    return true;
354  }
355
356  remove (collectable) {
357    const index = this._collection.indexOf(collectable);
358    if (index === -1) return false;
359    this._collection.splice(index, 1);
360    if (this.onRemove) this.onRemove();
361    if (this.onEmpty && this._collection.length === 0) this.onEmpty();
362  }
363
364  execute (...args) {
365    for (const collectable of this._collection) if (collectable) collectable.apply(null, args);
366  }
367
368  clear () {
369    this._collection.length = 0;
370  }
371
372  clone () {
373    const clone = new Collection();
374    clone._collection = this._collection.slice();
375    return clone;
376  }
377
378  get collection () {
379    return this._collection;
380  }
381}
382
383class Module extends Collection {
384  constructor (type) {
385    super();
386    this.type = type;
387    this.isActive = false;
388  }
389
390  activate () {}
391  deactivate () {}
392}
393
394const querySelectorAllArray = (element, selectors) => Array.prototype.slice.call(element.querySelectorAll(selectors));
395
396const queryParentSelector = (element, selectors) => {
397  const parent = element.parentElement;
398  if (parent.matches(selectors)) return parent;
399  if (parent === document.documentElement) return null;
400  return queryParentSelector(parent, selectors);
401};
402
403class Registration {
404  constructor (selector, InstanceClass, creator) {
405    this.selector = selector;
406    this.InstanceClass = InstanceClass;
407    this.creator = creator;
408    this.instances = new Collection();
409    this.isIntroduced = false;
vendor: 4,897 bytes, lines 410-584
410    this._instanceClassName = this.InstanceClass.instanceClassName;
411    this._instanceClassNames = this.getInstanceClassNames(this.InstanceClass);
412    this._property = this._instanceClassName.substring(0, 1).toLowerCase() + this._instanceClassName.substring(1);
413    const dashed = this._instanceClassName
414      .replace(/[^a-zA-Z0-9]+/g, '-')
415      .replace(/([A-Z]+)([A-Z][a-z])/g, '$1-$2')
416      .replace(/([a-z])([A-Z])/g, '$1-$2')
417      .replace(/([0-9])([^0-9])/g, '$1-$2')
418      .replace(/([^0-9])([0-9])/g, '$1-$2')
419      .toLowerCase();
420    this._attribute = ns.attr(`js-${dashed}`);
421  }
422
423  getInstanceClassNames (InstanceClass) {
424    const prototype = Object.getPrototypeOf(InstanceClass);
425    if (!prototype || prototype.instanceClassName === 'Instance') return [InstanceClass.instanceClassName];
426    return [...this.getInstanceClassNames(prototype), InstanceClass.instanceClassName];
427  }
428
429  hasInstanceClassName (instanceClassName) {
430    return this._instanceClassNames.indexOf(instanceClassName) > -1;
431  }
432
433  introduce () {
434    if (this.isIntroduced) return;
435    this.isIntroduced = true;
436    state.getModule('stage').parse(document.documentElement, this);
437  }
438
439  parse (node, nonRecursive) {
440    const nodes = [];
441    if (node.matches && node.matches(this.selector)) nodes.push(node);
442    // eslint-disable-next-line no-useless-call
443    if (!nonRecursive && node.querySelectorAll && node.querySelector(this.selector)) nodes.push.apply(nodes, querySelectorAllArray(node, this.selector));
444    return nodes;
445  }
446
447  create (element) {
448    if (!element.node.matches(this.selector)) return;
449    const instance = new this.InstanceClass();
450    this.instances.add(instance);
451    return instance;
452  }
453
454  remove (instance) {
455    this.instances.remove(instance);
456  }
457
458  dispose () {
459    const instances = this.instances.collection;
460    for (let i = instances.length - 1; i > -1; i--) instances[i]._dispose();
461    this.creator = null;
462  }
463
464  get instanceClassName () {
465    return this._instanceClassName;
466  }
467
468  get instanceClassNames () {
469    return this._instanceClassNames;
470  }
471
472  get property () {
473    return this._property;
474  }
475
476  get attribute () {
477    return this._attribute;
478  }
479}
480
481class Register extends Module {
482  constructor () {
483    super('register');
484  }
485
486  register (selector, InstanceClass, creator) {
487    const registration = new Registration(selector, InstanceClass, creator);
488    this.add(registration);
489    if (state.isActive) registration.introduce();
490    return registration;
491  }
492
493  activate () {
494    for (const registration of this.collection) registration.introduce();
495  }
496
497  remove (registration) {
498    registration.dispose();
499    super.remove(registration);
500  }
501}
502
503let count = 0;
504
505class Element$1 {
506  constructor (node, id) {
507    if (!id) {
508      count++;
509      this.id = count;
510    } else this.id = id;
511    this.node = node;
512    this.attributeNames = [];
513    this.instances = [];
514    this._children = [];
515    this._parent = null;
516    this._projects = [];
517  }
518
519  get proxy () {
520    const scope = this;
521    if (!this._proxy) {
522      this._proxy = {
523        id: this.id,
524        get parent () {
525          return scope.parent ? scope.parent.proxy : null;
526        },
527        get children () {
528          return scope.children.map((child) => child.proxy);
529        }
530      };
531
532      for (const instance of this.instances) this._proxy[instance.registration.property] = instance.proxy;
533    }
534    return this._proxy;
535  }
536
537  get html () {
538    if (!this.node || !this.node.outerHTML) return '';
539    const end = this.node.outerHTML.indexOf('>');
540    return this.node.outerHTML.substring(0, end + 1);
541  }
542
543  project (registration) {
544    if (this._projects.indexOf(registration) === -1) this._projects.push(registration);
545  }
546
547  populate () {
548    const projects = this._projects.slice();
549    this._projects.length = 0;
550    for (const registration of projects) this.create(registration);
551  }
552
553  create (registration) {
554    if (this.hasInstance(registration.instanceClassName)) {
555      // inspector.debug(`failed creation, instance of ${registration.instanceClassName} already exists on element [${this.id}]`);
556      return;
557    }
558    inspector.debug(`create instance of ${registration.instanceClassName} on element [${this.id}]`);
559    const instance = registration.create(this);
560    this.instances.push(instance);
561    instance._config(this, registration);
562    if (this._proxy) this._proxy[registration.property] = instance.proxy;
563  }
564
565  remove (instance) {
566    const index = this.instances.indexOf(instance);
567    if (index > -1) this.instances.splice(index, 1);
568    if (this._proxy) delete this._proxy[instance.registration.property];
569  }
570
571  get parent () {
572    return this._parent;
573  }
574
575  get ascendants () {
576    return [this.parent, ...this.parent.ascendants];
577  }
578
579  get children () {
580    return this._children;
581  }
582
583  get descendants () {
584    const descendants = [...this._children];
vendor: 4,513 bytes, lines 585-741
585    this._children.forEach(child => descendants.push(...child.descendants));
586    return descendants;
587  }
588
589  // TODO : emit ascendant et descendant de changement ?
590
591  addChild (child, index) {
592    if (this._children.indexOf(child) > -1) return null;
593    child._parent = this;
594    if (!isNaN(index) && index > -1 && index < this._children.length) this._children.splice(index, 0, child);
595    else this._children.push(child);
596    return child;
597  }
598
599  removeChild (child) {
600    const index = this._children.indexOf(child);
601    if (index === -1) return null;
602    child._parent = null;
603    this._children.splice(index, 1);
604  }
605
606  emit (type, data) {
607    const elements = state.getModule('stage').collection;
608    const response = [];
609    for (const element of elements) response.push(...element._emit(type, data));
610    return response;
611  }
612
613  _emit (type, data) {
614    const response = [];
615    for (const instance of this.instances) response.push(...instance._emitter.emit(type, data));
616    return response;
617  }
618
619  ascend (type, data) {
620    if (this._parent) return this._parent._ascend(type, data);
621    return [];
622  }
623
624  _ascend (type, data) {
625    const response = [];
626    for (const instance of this.instances) response.push(...instance._ascent.emit(type, data));
627    if (this._parent) response.push(...this._parent._ascend(type, data));
628    return response;
629  }
630
631  descend (type, data) {
632    const response = [];
633    for (const child of this._children) response.push(...child._descend(type, data));
634    return response;
635  }
636
637  _descend (type, data) {
638    const response = [];
639    for (const instance of this.instances) response.push(...instance._descent.emit(type, data));
640    for (const child of this._children) response.push(...child._descend(type, data));
641    return response;
642  }
643
644  getInstance (instanceClassName) {
645    for (const instance of this.instances) if (instance.registration.hasInstanceClassName(instanceClassName)) return instance;
646    return null;
647  }
648
649  hasInstance (instanceClassName) {
650    return this.getInstance(instanceClassName) !== null;
651  }
652
653  getDescendantInstances (instanceClassName, stopAtInstanceClassName, stopAtFirstInstance) {
654    if (!instanceClassName) return [];
655    const instances = [];
656    for (const child of this._children) {
657      const instance = child.getInstance(instanceClassName);
658      if (instance) {
659        instances.push(instance);
660        if (stopAtFirstInstance) continue;
661      }
662      if ((!stopAtInstanceClassName || !child.hasInstance(stopAtInstanceClassName)) && child.children.length) instances.push.apply(instances, child.getDescendantInstances(instanceClassName, stopAtInstanceClassName, stopAtFirstInstance));
663    }
664    return instances;
665  }
666
667  getAscendantInstance (instanceClassName, stopAtInstanceClassName) {
668    if (!instanceClassName || !this._parent) return null;
669    const instance = this._parent.getInstance(instanceClassName);
670    if (instance) return instance;
671    if (stopAtInstanceClassName && this._parent.hasInstance(stopAtInstanceClassName)) return null;
672    return this._parent.getAscendantInstance(instanceClassName, stopAtInstanceClassName);
673  }
674
675  dispose () {
676    for (let i = this.instances.length - 1; i >= 0; i--) {
677      const instance = this.instances[i];
678      if (instance) instance._dispose();
679    }
680    this.instances.length = 0;
681    state.remove('stage', this);
682    this.parent.removeChild(this);
683    this._children.length = 0;
684    inspector.debug(`remove element [${this.id}] ${this.html}`);
685  }
686
687  prepare (attributeName) {
688    if (this.attributeNames.indexOf(attributeName) === -1) this.attributeNames.push(attributeName);
689  }
690
691  examine () {
692    const attributeNames = this.attributeNames.slice();
693    this.attributeNames.length = 0;
694    for (let i = this.instances.length - 1; i > -1; i--) this.instances[i].examine(attributeNames);
695  }
696}
697
698const RootEmission = {
699  CLICK: ns.emission('root', 'click'),
700  INTERACTION: ns.emission('root', 'interaction'),
701  KEYDOWN: ns.emission('root', 'keydown'),
702  KEYUP: ns.emission('root', 'keyup')
703};
704
705const KeyCodes = {
706  TAB: {
707    id: 'tab',
708    value: 9
709  },
710  ESCAPE: {
711    id: 'escape',
712    value: 27
713  },
714  END: {
715    id: 'end',
716    value: 35
717  },
718  HOME: {
719    id: 'home',
720    value: 36
721  },
722  LEFT: {
723    id: 'left',
724    value: 37
725  },
726  UP: {
727    id: 'up',
728    value: 38
729  },
730  RIGHT: {
731    id: 'right',
732    value: 39
733  },
734  DOWN: {
735    id: 'down',
736    value: 40
737  }
738};
739
740const getKeyCode = (keyCode) => Object.values(KeyCodes).filter(entry => entry.value === keyCode)[0];
741
vendor: 5,399 bytes, lines 742-923
742class Root extends Element$1 {
743  constructor () {
744    super(document.documentElement, 'root');
745    this.node.setAttribute(ns.attr('js'), true);
746    this.listen();
747  }
748
749  listen () {
750    // TODO v2 => listener au niveau des éléments qui redistribuent aux instances.
751    document.documentElement.addEventListener('click', this.click.bind(this), { capture: true });
752    document.documentElement.addEventListener(window.PointerEvent ? 'pointerdown' : 'touchstart', this.interaction.bind(this), { capture: true, passive: true }); // IOS
753    document.documentElement.addEventListener('keydown', this.keydown.bind(this), { capture: true });
754    document.documentElement.addEventListener('keyup', this.keyup.bind(this), { capture: true });
755  }
756
757  click (e) {
758    this.emit(RootEmission.CLICK, e.target);
759  }
760
761  interaction (e) {
762    this.emit(RootEmission.INTERACTION, e.target);
763  }
764
765  keydown (e) {
766    this.emit(RootEmission.KEYDOWN, getKeyCode(e.keyCode));
767  }
768
769  keyup (e) {
770    this.emit(RootEmission.KEYUP, getKeyCode(e.keyCode));
771  }
772}
773
774class Stage extends Module {
775  constructor () {
776    super('stage');
777    this.root = new Root();
778    super.add(this.root);
779    this.observer = new MutationObserver(this.mutate.bind(this));
780    this.modifications = [];
781    this.willModify = false;
782    this.modifying = this.modify.bind(this);
783  }
784
785  hasElement (node) {
786    for (const element of this.collection) if (element.node === node) return true;
787    return false;
788  }
789
790  getElement (node) {
791    for (const element of this.collection) if (element.node === node) return element;
792    const element = new Element$1(node);
793    this.add(element);
794    inspector.debug(`add element [${element.id}] ${element.html}`);
795    return element;
796  }
797
798  getProxy (node) {
799    if (!this.hasElement(node)) return null;
800    const element = this.getElement(node);
801    return element.proxy;
802  }
803
804  add (element) {
805    super.add(element);
806    this.put(element, this.root);
807  }
808
809  put (element, branch) {
810    let index = 0;
811    for (let i = branch.children.length - 1; i > -1; i--) {
812      const child = branch.children[i];
813      const position = element.node.compareDocumentPosition(child.node);
814      if (position & Node.DOCUMENT_POSITION_CONTAINS) {
815        this.put(element, child);
816        return;
817      } else if (position & Node.DOCUMENT_POSITION_CONTAINED_BY) {
818        branch.removeChild(child);
819        element.addChild(child, 0);
820      } else if (position & Node.DOCUMENT_POSITION_PRECEDING) {
821        index = i + 1;
822        break;
823      }
824    }
825
826    branch.addChild(element, index);
827  }
828
829  activate () {
830    this.observer.observe(document.documentElement, { childList: true, subtree: true, attributes: true });
831  }
832
833  deactivate () {
834    this.observer.disconnect();
835  }
836
837  mutate (mutations) {
838    const examinations = [];
839    mutations.forEach((mutation) => {
840      switch (mutation.type) {
841        case 'childList':
842          mutation.removedNodes.forEach((node) => this.dispose(node));
843          mutation.addedNodes.forEach((node) => this.parse(node));
844          break;
845
846        case 'attributes':
847          if (this.hasElement(mutation.target)) {
848            const element = this.getElement(mutation.target);
849            element.prepare(mutation.attributeName);
850            if (examinations.indexOf(element) === -1) examinations.push(element);
851            for (const descendant of element.descendants) if (examinations.indexOf(descendant) === -1) examinations.push(descendant);
852          }
853          if (this.modifications.indexOf(mutation.target) === -1) this.modifications.push(mutation.target);
854          break;
855      }
856    });
857
858    examinations.forEach(element => element.examine());
859    if (this.modifications.length && !this.willModify) {
860      this.willModify = true;
861      window.requestAnimationFrame(this.modifying);
862    }
863  }
864
865  modify () {
866    this.willModify = false;
867    const targets = this.modifications.slice();
868    this.modifications.length = 0;
869    for (const target of targets) if (document.documentElement.contains(target)) this.parse(target);
870  }
871
872  dispose (node) {
873    const disposables = [];
874    this.forEach((element) => {
875      if (node.contains(element.node)) disposables.push(element);
876    });
877
878    for (const disposable of disposables) {
879      disposable.dispose();
880      this.remove(disposable);
881    }
882  }
883
884  parse (node, registration, nonRecursive) {
885    const registrations = registration ? [registration] : state.getModule('register').collection;
886    const creations = [];
887
888    for (const registration of registrations) {
889      const nodes = registration.parse(node, nonRecursive);
890
891      for (const n of nodes) {
892        const element = this.getElement(n);
893        element.project(registration);
894        if (creations.indexOf(element) === -1) creations.push(element);
895      }
896    }
897
898    for (const element of creations) element.populate();
899  }
900}
901
902class Renderer extends Module {
903  constructor () {
904    super('render');
905    this.rendering = this.render.bind(this);
906    this.nexts = new Collection();
907  }
908
909  activate () {
910    window.requestAnimationFrame(this.rendering);
911  }
912
913  request (instance) {
914    this.nexts.add(instance);
915  }
916
917  render () {
918    if (!state.isActive) return;
919    window.requestAnimationFrame(this.rendering);
920    this.forEach((instance) => instance.render());
921    if (!this.nexts.length) return;
922    const nexts = this.nexts.clone();
923    this.nexts.clear();
vendor: 72,403 bytes, lines 924-3813
924    nexts.forEach((instance) => instance.next());
925    rootDispatch(RootEvent.RENDER);
926  }
927}
928
929class Resizer extends Module {
930  constructor () {
931    super('resize');
932    this.requireResize = false;
933    this.resizing = this.resize.bind(this);
934    const requesting = this.request.bind(this);
935    if (document.fonts) {
936      document.fonts.ready.then(requesting);
937    }
938    window.addEventListener('resize', requesting);
939    window.addEventListener('orientationchange', requesting);
940  }
941
942  activate () {
943    this.request();
944  }
945
946  request () {
947    if (this.requireResize) return;
948    this.requireResize = true;
949    window.requestAnimationFrame(this.resizing);
950  }
951
952  resize () {
953    if (!this.requireResize) return;
954    this.forEach((instance) => instance.resize());
955    this.requireResize = false;
956    rootDispatch(RootEvent.RESIZE);
957  }
958}
959
960class ScrollLocker extends Module {
961  constructor () {
962    super('lock');
963    this._isLocked = false;
964    this._scrollY = 0;
965    this.onPopulate = this.lock.bind(this);
966    this.onEmpty = this.unlock.bind(this);
967  }
968
969  get isLocked () {
970    return this._isLocked;
971  }
972
973  lock () {
974    if (!this._isLocked) {
975      this._isLocked = true;
976      this._scrollY = window.scrollY;
977      const scrollBarGap = window.innerWidth - document.documentElement.clientWidth;
978      document.documentElement.setAttribute(ns.attr('scrolling'), 'false');
979      document.body.style.top = `${-this._scrollY}px`;
980      this.behavior = getComputedStyle(document.documentElement).getPropertyValue('scroll-behavior');
981      if (this.behavior === 'smooth') document.documentElement.style.scrollBehavior = 'auto';
982      if (scrollBarGap > 0) {
983        document.documentElement.style.setProperty('--scrollbar-width', `${scrollBarGap}px`);
984      }
985      rootDispatch(RootEvent.SCROLL_LOCK);
986    }
987  }
988
989  unlock () {
990    if (this._isLocked) {
991      this._isLocked = false;
992      document.documentElement.removeAttribute(ns.attr('scrolling'));
993      document.body.style.top = '';
994      window.scrollTo(0, this._scrollY);
995      if (this.behavior === 'smooth') document.documentElement.style.removeProperty('scroll-behavior');
996      document.documentElement.style.removeProperty('--scrollbar-width');
997      rootDispatch(RootEvent.SCROLL_UNLOCK);
998    }
999  }
1000
1001  move (value) {
1002    if (this._isLocked) {
1003      this._scrollY += value;
1004      document.body.style.top = `${-this._scrollY}px`;
1005    } else {
1006      window.scrollTo(0, window.scrollY + value);
1007    }
1008  }
1009}
1010
1011class Load extends Module {
1012  constructor () {
1013    super('load');
1014    this.loading = this.load.bind(this);
1015  }
1016
1017  activate () {
1018    window.addEventListener('load', this.loading);
1019  }
1020
1021  load () {
1022    this.forEach((instance) => instance.load());
1023  }
1024}
1025
1026const FONT_FAMILIES = ['Marianne', 'Spectral'];
1027
1028class FontSwap extends Module {
1029  constructor () {
1030    super('font-swap');
1031    this.swapping = this.swap.bind(this);
1032  }
1033
1034  activate () {
1035    if (document.fonts) {
1036      document.fonts.addEventListener('loadingdone', this.swapping);
1037    }
1038  }
1039
1040  swap () {
1041    const families = FONT_FAMILIES.filter(family => document.fonts.check(`16px ${family}`));
1042
1043    this.forEach((instance) => instance.swapFont(families));
1044  }
1045}
1046
1047class MouseMove extends Module {
1048  constructor () {
1049    super('mouse-move');
1050    this.requireMove = false;
1051    this._isMoving = false;
1052    this.moving = this.move.bind(this);
1053    this.requesting = this.request.bind(this);
1054    this.onPopulate = this.listen.bind(this);
1055    this.onEmpty = this.unlisten.bind(this);
1056  }
1057
1058  listen () {
1059    if (this._isMoving) return;
1060    this._isMoving = true;
1061    this.requireMove = false;
1062    document.documentElement.addEventListener('mousemove', this.requesting);
1063  }
1064
1065  unlisten () {
1066    if (!this._isMoving) return;
1067    this._isMoving = false;
1068    this.requireMove = false;
1069    document.documentElement.removeEventListener('mousemove', this.requesting);
1070  }
1071
1072  request (e) {
1073    if (!this._isMoving) return;
1074    this.point = { x: e.clientX, y: e.clientY };
1075    if (this.requireMove) return;
1076    this.requireMove = true;
1077    window.requestAnimationFrame(this.moving);
1078  }
1079
1080  move () {
1081    if (!this.requireMove) return;
1082    this.forEach((instance) => instance.mouseMove(this.point));
1083    this.requireMove = false;
1084  }
1085}
1086
1087class Hash extends Module {
1088  constructor () {
1089    super('hash');
1090    this.handling = this.handle.bind(this);
1091    this.getLocationHash();
1092  }
1093
1094  activate () {
1095    window.addEventListener('hashchange', this.handling);
1096  }
1097
1098  deactivate () {
1099    window.removeEventListener('hashchange', this.handling);
1100  }
1101
1102  _sanitize (hash) {
1103    if (hash.charAt(0) === '#') return hash.substring(1);
1104    return hash;
1105  }
1106
1107  set hash (value) {
1108    const hash = this._sanitize(value);
1109    if (this._hash !== hash) window.location.hash = hash;
1110  }
1111
1112  get hash () {
1113    return this._hash;
1114  }
1115
1116  getLocationHash () {
1117    const hash = window.location.hash;
1118    this._hash = this._sanitize(hash);
1119  }
1120
1121  handle (e) {
1122    this.getLocationHash();
1123    this.forEach((instance) => instance.handleHash(this._hash, e));
1124  }
1125}
1126
1127class Engine {
1128  constructor () {
1129    state.create(Register);
1130    state.create(Stage);
1131    state.create(Renderer);
1132    state.create(Resizer);
1133    state.create(ScrollLocker);
1134    state.create(Load);
1135    state.create(FontSwap);
1136    state.create(MouseMove);
1137    state.create(Hash);
1138
1139    const registerModule = state.getModule('register');
1140    this.register = registerModule.register.bind(registerModule);
1141  }
1142
1143  get isActive () {
1144    return state.isActive;
1145  }
1146
1147  start () {
1148    inspector.debug('START');
1149    state.isActive = true;
1150    rootDispatch(RootEvent.START);
1151  }
1152
1153  stop () {
1154    inspector.debug('STOP');
1155    state.isActive = false;
1156    rootDispatch(RootEvent.STOP);
1157  }
1158}
1159
1160const engine = new Engine();
1161
1162class Colors {
1163  getColor (context, use, tint, options = {}) {
1164    const option = getOption(options);
1165    const decision = `--${context}-${use}-${tint}${option}`;
1166    return getComputedStyle(document.documentElement).getPropertyValue(decision).trim() || null;
1167  }
1168}
1169
1170const getOption = (options) => {
1171  switch (true) {
1172    case options.hover:
1173      return '-hover';
1174    case options.active:
1175      return '-active';
1176    default:
1177      return '';
1178  }
1179};
1180
1181const colors = new Colors();
1182
1183const sanitize = (className) => className.charAt(0) === '.' ? className.substr(1) : className;
1184
1185const getClassNames = (element) => {
1186  switch (true) {
1187    case !element.className:
1188      return [];
1189
1190    case typeof element.className === 'string':
1191      return element.className.split(' ');
1192
1193    case typeof element.className.baseVal === 'string':
1194      return element.className.baseVal.split(' ');
1195  }
1196
1197  return [];
1198};
1199
1200const modifyClass = (element, className, remove) => {
1201  className = sanitize(className);
1202  const classNames = getClassNames(element);
1203  const index = classNames.indexOf(className);
1204  if (remove === true) {
1205    if (index > -1) classNames.splice(index, 1);
1206  } else if (index === -1) classNames.push(className);
1207  element.className = classNames.join(' ');
1208};
1209
1210const addClass = (element, className) => modifyClass(element, className);
1211
1212const removeClass = (element, className) => modifyClass(element, className, true);
1213
1214const hasClass = (element, className) => getClassNames(element).indexOf(sanitize(className)) > -1;
1215
1216const ACTIONS = [
1217  '[tabindex]:not([tabindex="-1"])',
1218  'a[href]',
1219  'button:not([disabled])',
1220  'input:not([disabled])',
1221  'select:not([disabled])',
1222  'textarea:not([disabled])',
1223  'audio[controls]',
1224  'video[controls]',
1225  '[contenteditable]:not([contenteditable="false"])',
1226  'details>summary:first-of-type',
1227  'details',
1228  'iframe'
1229];
1230
1231const ACTIONS_SELECTOR = ACTIONS.join();
1232
1233const queryActions = (element) => {
1234  return element.querySelectorAll(ACTIONS_SELECTOR);
1235};
1236
1237let counter = 0;
1238
1239const uniqueId = (id) => {
1240  if (!document.getElementById(id)) return id;
1241  let element = true;
1242  const base = id;
1243  while (element) {
1244    counter++;
1245    id = `${base}-${counter}`;
1246    element = document.getElementById(id);
1247  }
1248  return id;
1249};
1250
1251const dom = {
1252  addClass: addClass,
1253  hasClass: hasClass,
1254  removeClass: removeClass,
1255  queryParentSelector: queryParentSelector,
1256  querySelectorAllArray: querySelectorAllArray,
1257  queryActions: queryActions,
1258  uniqueId: uniqueId,
1259  dispatch: dispatch
1260};
1261
1262class DataURISVG {
1263  constructor (width = 0, height = 0) {
1264    this._width = width;
1265    this._height = height;
1266    this._content = '';
1267  }
1268
1269  get width () {
1270    return this._width;
1271  }
1272
1273  set width (value) {
1274    this._width = value;
1275  }
1276
1277  get height () {
1278    return this._height;
1279  }
1280
1281  set height (value) {
1282    this._height = value;
1283  }
1284
1285  get content () {
1286    return this._content;
1287  }
1288
1289  set content (value) {
1290    this._content = value;
1291  }
1292
1293  getDataURI (isLegacy = false) {
1294    let svg = `<svg xmlns='http://www.w3.org/2000/svg' viewbox='0 0 ${this._width} ${this._height}' width='${this._width}px' height='${this._height}px'>${this._content}</svg>`;
1295
1296    svg = svg.replace(/#/gi, '%23');
1297    if (isLegacy) {
1298      svg = svg.replace(/</gi, '%3C');
1299      svg = svg.replace(/>/gi, '%3E');
1300      svg = svg.replace(/"/gi, '\'');
1301      svg = svg.replace(/{/gi, '%7B');
1302      svg = svg.replace(/}/gi, '%7D');
1303    }
1304    return `data:image/svg+xml;charset=utf8,${svg}`;
1305  }
1306}
1307
1308const image = {
1309  DataURISVG: DataURISVG
1310};
1311
1312const supportLocalStorage = () => {
1313  try {
1314    return 'localStorage' in window && window.localStorage !== null;
1315  } catch (e) {
1316    return false;
1317  }
1318};
1319
1320const supportAspectRatio = () => {
1321  if (!window.CSS) return false;
1322  return CSS.supports('aspect-ratio: 16 / 9');
1323};
1324
1325const support = {
1326  supportLocalStorage: supportLocalStorage,
1327  supportAspectRatio: supportAspectRatio
1328};
1329
1330const TransitionSelector = {
1331  NONE: ns.selector('transition-none')
1332};
1333
1334const selector = {
1335  TransitionSelector: TransitionSelector
1336};
1337
1338/**
1339 * Copy properties from multiple sources including accessors.
1340 * source : https://developer.mozilla.org/fr/docs/Web/JavaScript/Reference/Global_Objects/Object/assign#copier_des_accesseurs
1341 *
1342 * @param {object} [target] - Target object to copy into
1343 * @param {...objects} [sources] - Multiple objects
1344 * @return {object} A new object
1345 *
1346 * @example
1347 *
1348 *     const obj1 = {
1349 *        key: 'value'
1350 *     };
1351 *     const obj2 = {
1352 *        get function01 () {
1353 *          return a-value;
1354 *        }
1355 *        set function01 () {
1356 *          return a-value;
1357 *        }
1358 *     };
1359 *     completeAssign(obj1, obj2)
1360 */
1361const completeAssign = (target, ...sources) => {
1362  sources.forEach(source => {
1363    const descriptors = Object.keys(source).reduce((descriptors, key) => {
1364      descriptors[key] = Object.getOwnPropertyDescriptor(source, key);
1365      return descriptors;
1366    }, {});
1367
1368    Object.getOwnPropertySymbols(source).forEach(sym => {
1369      const descriptor = Object.getOwnPropertyDescriptor(source, sym);
1370      if (descriptor.enumerable) {
1371        descriptors[sym] = descriptor;
1372      }
1373    });
1374    Object.defineProperties(target, descriptors);
1375  });
1376  return target;
1377};
1378
1379const property = {
1380  completeAssign: completeAssign
1381};
1382
1383/**
1384 * Return an object of query params or null
1385 *
1386 * @method
1387 * @name searchParams
1388 * @param {string} url - an url
1389 * @returns {Object} object of query params or null
1390 */
1391
1392const searchParams = (url) => {
1393  if (url && url.search) {
1394    const params = new URLSearchParams(window.location.search);
1395    const entries = params.entries();
1396    return Object.fromEntries(entries);
1397  }
1398  return null;
1399};
1400
1401const internals = {};
1402const legacy = {};
1403
1404Object.defineProperty(legacy, 'isLegacy', {
1405  get: () => state.isLegacy
1406});
1407
1408legacy.setLegacy = () => {
1409  state.isLegacy = true;
1410};
1411
1412internals.legacy = legacy;
1413internals.dom = dom;
1414internals.image = image;
1415internals.support = support;
1416internals.motion = selector;
1417internals.property = property;
1418internals.ns = ns;
1419internals.register = engine.register;
1420internals.state = state;
1421internals.query = searchParams(window.location);
1422
1423Object.defineProperty(internals, 'preventManipulation', {
1424  get: () => options.preventManipulation
1425});
1426Object.defineProperty(internals, 'stage', {
1427  get: () => state.getModule('stage')
1428});
1429
1430const api$1 = (node) => {
1431  const stage = state.getModule('stage');
1432  return stage.getProxy(node);
1433};
1434
1435api$1.version = config.version;
1436api$1.prefix = config.prefix;
1437api$1.organisation = config.organisation;
1438api$1.Modes = Modes;
1439
1440Object.defineProperty(api$1, 'mode', {
1441  set: (value) => { options.mode = value; },
1442  get: () => options.mode
1443});
1444
1445api$1.internals = internals;
1446api$1.version = config.version;
1447
1448api$1.start = engine.start;
1449api$1.stop = engine.stop;
1450
1451api$1.inspector = inspector;
1452api$1.colors = colors;
1453
1454const configuration = window[config.namespace];
1455api$1.internals.configuration = configuration;
1456
1457options.configure(configuration, api$1.start, api$1.internals.query);
1458
1459window[config.namespace] = api$1;
1460
1461class Emitter {
1462  constructor () {
1463    this.emissions = {};
1464  }
1465
1466  add (type, closure) {
1467    if (typeof closure !== 'function') throw new Error('closure must be a function');
1468    if (!this.emissions[type]) this.emissions[type] = [];
1469    this.emissions[type].push(closure);
1470  }
1471
1472  remove (type, closure) {
1473    if (!this.emissions[type]) return;
1474    if (!closure) delete this.emissions[type];
1475    else {
1476      const index = this.emissions[type].indexOf(closure);
1477      if (index > -1) this.emissions[type].splice(index);
1478    }
1479  }
1480
1481  emit (type, data) {
1482    if (!this.emissions[type]) return [];
1483    const response = [];
1484    for (const closure of this.emissions[type]) if (closure) response.push(closure(data));
1485    return response;
1486  }
1487
1488  dispose () {
1489    this.emissions = null;
1490  }
1491}
1492
1493class FocusManager {
1494  constructor () {
1495    this._activeElements = [];
1496    window.document.addEventListener('focusin', this._onFocusIn.bind(this));
1497  }
1498
1499  _onFocusIn (e) {
1500    this._activeElements.push(e.target);
1501  }
1502
1503  get index () {
1504    return this._activeElements.length - 1;
1505  }
1506
1507  focus (index) {
1508    const element = this._activeElements[index];
1509    switch (true) {
1510      case index < 0:
1511      case this._activeElements.length === 0:
1512        this.focusOnLogo();
1513        return;
1514      case !element:
1515      case !document.documentElement.contains(element):
1516      case !this._isDisplayed(element):
1517        this.focus(index - 1);
1518        return;
1519    }
1520
1521    element.focus();
1522  }
1523
1524  focusOnLogo () {
1525    const logo = document.querySelector(api$1.header.HeaderSelector.BRAND_LINK);
1526    if (logo) logo.focus();
1527  }
1528
1529  _isDisplayed (element) {
1530    while (element && element !== document.documentElement) {
1531      if (element === document.body) return true;
1532      const style = window.getComputedStyle(element);
1533      if (style.display === 'none' || style.visibility === 'hidden') return false;
1534      element = element.parentElement;
1535    }
1536    return true;
1537  }
1538}
1539
1540const focusManager = new FocusManager();
1541
1542class Breakpoint {
1543  constructor (id, minWidth) {
1544    this.id = id;
1545    this.minWidth = minWidth;
1546  }
1547
1548  test () {
1549    return window.matchMedia(`(min-width: ${this.minWidth}em)`).matches;
1550  }
1551}
1552
1553const Breakpoints = {
1554  XS: new Breakpoint('xs', 0),
1555  SM: new Breakpoint('sm', 36),
1556  MD: new Breakpoint('md', 48),
1557  LG: new Breakpoint('lg', 62),
1558  XL: new Breakpoint('xl', 78)
1559};
1560
1561const InstanceEvent = {
1562  CLICK: ns.event('click')
1563};
1564
1565class Instance {
1566  constructor (jsAttribute = true) {
1567    this.jsAttribute = jsAttribute;
1568    this._isRendering = false;
1569    this._isResizing = false;
1570    this._isScrollLocked = false;
1571    this._isLoading = false;
1572    this._isSwappingFont = false;
1573    this._isEnabled = true;
1574    this._isDisposed = false;
1575    this._listeners = {};
1576    this._handlingClick = this._handleClick.bind(this);
1577    this._hashes = [];
1578    this._hash = '';
1579    this._keyListenerTypes = [];
1580    this._keys = [];
1581    this.handlingKey = this.handleKey.bind(this);
1582    this._emitter = new Emitter();
1583    this._ascent = new Emitter();
1584    this._descent = new Emitter();
1585    this._registrations = [];
1586    this._nexts = [];
1587  }
1588
1589  static get instanceClassName () {
1590    return 'Instance';
1591  }
1592
1593  _config (element, registration) {
1594    this.element = element;
1595    this.registration = registration;
1596    this.node = element.node;
1597    this.id = element.node.id;
1598    if (this.jsAttribute) this.setAttribute(registration.attribute, true);
1599    this.init();
1600  }
1601
1602  init () {}
1603
1604  get proxy () {
1605    const scope = this;
1606    const proxy = {
1607      render: () => scope.render(),
1608      resize: () => scope.resize()
1609    };
1610
1611    const proxyAccessors = {
1612      get node () {
1613        return scope.node;
1614      },
1615      get isEnabled () {
1616        return scope.isEnabled;
1617      },
1618      set isEnabled (value) {
1619        scope.isEnabled = value;
1620      }
1621    };
1622
1623    return completeAssign(proxy, proxyAccessors);
1624  }
1625
1626  log (...values) {
1627    values.unshift(`${this.registration.instanceClassName} #${this.id} - `);
1628    inspector.log.apply(inspector, values);
1629  }
1630
1631  debug (...values) {
1632    values.unshift(`${this.registration.instanceClassName} #${this.id} - `);
1633    inspector.debug.apply(inspector, values);
1634  }
1635
1636  info (...values) {
1637    values.unshift(`${this.registration.instanceClassName} #${this.id} - `);
1638    inspector.info.apply(inspector, values);
1639  }
1640
1641  warn (...values) {
1642    values.unshift(`${this.registration.instanceClassName} #${this.id} - `);
1643    inspector.warn.apply(inspector, values);
1644  }
1645
1646  error (...values) {
1647    values.unshift(`${this.registration.instanceClassName} #${this.id} - `);
1648    inspector.error.apply(inspector, values);
1649  }
1650
1651  register (selector, InstanceClass) {
1652    const registration = state.getModule('register').register(selector, InstanceClass, this);
1653    this._registrations.push(registration);
1654  }
1655
1656  getRegisteredInstances (instanceClassName) {
1657    for (const registration of this._registrations) if (registration.hasInstanceClassName(instanceClassName)) return registration.instances.collection;
1658    return [];
1659  }
1660
1661  dispatch (type, detail = null, bubbles = true, cancelable = false) {
1662    dispatch(this.node, type, detail, bubbles, cancelable);
1663  }
1664
1665  // TODO v2 => listener au niveau des éléments qui redistribuent aux instances.
1666  listen (type, closure, options) {
1667    if (!this._listeners[type]) this._listeners[type] = [];
1668    const listeners = this._listeners[type];
1669    // if (listeners.some(listener => listener.closure === closure)) return;
1670    const listener = new Listener(this.node, type, closure, options);
1671    listeners.push(listener);
1672    listener.listen();
1673  }
1674
1675  unlisten (type, closure, options) {
1676    if (!type) {
1677      for (const type in this._listeners) this.unlisten(type);
1678      return;
1679    }
1680
1681    const listeners = this._listeners[type];
1682
1683    if (!listeners) return;
1684
1685    if (!closure) {
1686      listeners.forEach(listener => this.unlisten(type, listener.closure));
1687      return;
1688    }
1689
1690    const removal = listeners.filter(listener => listener.closure === closure && listener.matchOptions(options));
1691    removal.forEach(listener => listener.unlisten());
1692    this._listeners[type] = listeners.filter(listener => removal.indexOf(listener) === -1);
1693  }
1694
1695  listenClick (options) {
1696    this.listen('click', this._handlingClick, options);
1697  }
1698
1699  unlistenClick (options) {
1700    this.unlisten('click', this._handlingClick, options);
1701  }
1702
1703  _handleClick (e) {
1704    this.handleClick(e);
1705    this.dispatch(InstanceEvent.CLICK, this);
1706  }
1707
1708  handleClick (e) {}
1709
1710  set hash (value) {
1711    state.getModule('hash').hash = value;
1712  }
1713
1714  get hash () {
1715    return state.getModule('hash').hash;
1716  }
1717
1718  listenHash (hash, add) {
1719    if (!this._hashes) return;
1720    if (this._hashes.length === 0) state.add('hash', this);
1721    const action = new HashAction(hash, add);
1722    this._hashes = this._hashes.filter(action => action.hash !== hash);
1723    this._hashes.push(action);
1724  }
1725
1726  unlistenHash (hash) {
1727    if (!this._hashes) return;
1728    this._hashes = this._hashes.filter(action => action.hash !== hash);
1729    if (this._hashes.length === 0) state.remove('hash', this);
1730  }
1731
1732  handleHash (hash, e) {
1733    if (!this._hashes) return;
1734    for (const action of this._hashes) action.handle(hash, e);
1735  }
1736
1737  listenKey (keyCode, closure, preventDefault = false, stopPropagation = false, type = 'down') {
1738    if (this._keyListenerTypes.indexOf(type) === -1) {
1739      this.listen(`key${type}`, this.handlingKey);
1740      this._keyListenerTypes.push(type);
1741    }
1742
1743    this._keys.push(new KeyAction(type, keyCode, closure, preventDefault, stopPropagation));
1744  }
1745
1746  unlistenKey (code, closure) {
1747    this._keys = this._keys.filter((key) => key.code !== code || key.closure !== closure);
1748
1749    this._keyListenerTypes.forEach(type => {
1750      if (!this._keys.some(key => key.type === type)) this.unlisten(`key${type}`, this.handlingKey);
1751    });
1752  }
1753
1754  handleKey (e) {
1755    for (const key of this._keys) key.handle(e);
1756  }
1757
1758  get isEnabled () { return this._isEnabled; }
1759
1760  set isEnabled (value) {
1761    this._isEnabled = value;
1762  }
1763
1764  get isRendering () { return this._isRendering; }
1765
1766  set isRendering (value) {
1767    if (this._isRendering === value) return;
1768    if (value) state.add('render', this);
1769    else state.remove('render', this);
1770    this._isRendering = value;
1771  }
1772
1773  render () {}
1774
1775  request (closure) {
1776    this._nexts.push(closure);
1777    state.getModule('render').request(this);
1778  }
1779
1780  next () {
1781    const nexts = this._nexts.slice();
1782    this._nexts.length = 0;
1783    for (const closure of nexts) if (closure) closure();
1784  }
1785
1786  get isResizing () { return this._isResizing; }
1787
1788  set isResizing (value) {
1789    if (this._isResizing === value) return;
1790    if (value) {
1791      state.add('resize', this);
1792      this.resize();
1793    } else state.remove('resize', this);
1794    this._isResizing = value;
1795  }
1796
1797  resize () {}
1798
1799  isBreakpoint (breakpoint) {
1800    switch (true) {
1801      case typeof breakpoint === 'string':
1802        return Breakpoints[breakpoint.toUpperCase()].test();
1803
1804      default:
1805        return breakpoint.test();
1806    }
1807  }
1808
1809  get isScrollLocked () {
1810    return this._isScrollLocked;
1811  }
1812
1813  set isScrollLocked (value) {
1814    if (this._isScrollLocked === value) return;
1815    if (value) state.add('lock', this);
1816    else state.remove('lock', this);
1817    this._isScrollLocked = value;
1818  }
1819
1820  get isLoading () {
1821    return this._isLoading;
1822  }
1823
1824  set isLoading (value) {
1825    if (this._isLoading === value) return;
1826    if (value) state.add('load', this);
1827    else state.remove('load', this);
1828    this._isLoading = value;
1829  }
1830
1831  load () {}
1832
1833  get isSwappingFont () {
1834    return this._isSwappingFont;
1835  }
1836
1837  set isSwappingFont (value) {
1838    if (this._isSwappingFont === value) return;
1839    if (value) state.add('font-swap', this);
1840    else state.remove('font-swap', this);
1841    this._isSwappingFont = value;
1842  }
1843
1844  swapFont () {}
1845
1846  get isMouseMoving () { return this._isMouseMoving; }
1847
1848  set isMouseMoving (value) {
1849    if (this._isMouseMoving === value) return;
1850    if (value) {
1851      state.add('mouse-move', this);
1852    } else {
1853      state.remove('mouse-move', this);
1854    }
1855    this._isMouseMoving = value;
1856  }
1857
1858  mouseMove (point) {}
1859
1860  examine (attributeNames) {
1861    if (!this.node.matches(this.registration.selector)) {
1862      this._dispose();
1863      return;
1864    }
1865
1866    this.mutate(attributeNames);
1867  }
1868
1869  mutate (attributeNames) {}
1870
1871  retrieveNodeId (node, append) {
1872    if (node.id) return node.id;
1873    const id = uniqueId(`${this.id}-${append}`);
1874    this.warn(`add id '${id}' to ${append}`);
1875    node.setAttribute('id', id);
1876    return id;
1877  }
1878
1879  get isDisposed () {
1880    return this._isDisposed;
1881  }
1882
1883  _dispose () {
1884    this.debug(`dispose instance of ${this.registration.instanceClassName} on element [${this.element.id}]`);
1885    this.removeAttribute(this.registration.attribute);
1886    this.unlisten();
1887    state.remove('hash', this);
1888    this._hashes = null;
1889    this._keys = null;
1890    this.isRendering = false;
1891    this.isResizing = false;
1892    this._nexts = null;
1893    state.getModule('render').nexts.remove(this);
1894    this.isScrollLocked = false;
1895    this.isLoading = false;
1896    this.isSwappingFont = false;
1897    this.isMouseMoving = false;
1898    this._emitter.dispose();
1899    this._emitter = null;
1900    this._ascent.dispose();
1901    this._ascent = null;
1902    this._descent.dispose();
1903    this._descent = null;
1904    this.element.remove(this);
1905    for (const registration of this._registrations) state.remove('register', registration);
1906    this._registrations = null;
1907    this.registration.remove(this);
1908    this._isDisposed = true;
1909    this.dispose();
1910  }
1911
1912  dispose () {}
1913
1914  emit (type, data) {
1915    return this.element.emit(type, data);
1916  }
1917
1918  addEmission (type, closure) {
1919    this._emitter.add(type, closure);
1920  }
1921
1922  removeEmission (type, closure) {
1923    this._emitter.remove(type, closure);
1924  }
1925
1926  ascend (type, data) {
1927    return this.element.ascend(type, data);
1928  }
1929
1930  addAscent (type, closure) {
1931    this._ascent.add(type, closure);
1932  }
1933
1934  removeAscent (type, closure) {
1935    this._ascent.remove(type, closure);
1936  }
1937
1938  descend (type, data) {
1939    return this.element.descend(type, data);
1940  }
1941
1942  addDescent (type, closure) {
1943    this._descent.add(type, closure);
1944  }
1945
1946  removeDescent (type, closure) {
1947    this._descent.remove(type, closure);
1948  }
1949
1950  get style () {
1951    return this.node.style;
1952  }
1953
1954  addClass (className) {
1955    addClass(this.node, className);
1956  }
1957
1958  removeClass (className) {
1959    removeClass(this.node, className);
1960  }
1961
1962  hasClass (className) {
1963    return hasClass(this.node, className);
1964  }
1965
1966  get classNames () {
1967    return getClassNames(this.node);
1968  }
1969
1970  remove () {
1971    this.node.parentNode.removeChild(this.node);
1972  }
1973
1974  setAttribute (attributeName, value) {
1975    this.node.setAttribute(attributeName, value);
1976  }
1977
1978  getAttribute (attributeName) {
1979    return this.node.getAttribute(attributeName);
1980  }
1981
1982  hasAttribute (attributeName) {
1983    return this.node.hasAttribute(attributeName);
1984  }
1985
1986  removeAttribute (attributeName) {
1987    this.node.removeAttribute(attributeName);
1988  }
1989
1990  setProperty (propertyName, value) {
1991    this.node.style.setProperty(propertyName, value);
1992  }
1993
1994  removeProperty (propertyName) {
1995    this.node.style.removeProperty(propertyName);
1996  }
1997
1998  focus () {
1999    this.node.focus();
2000  }
2001
2002  blur () {
2003    this.node.blur();
2004  }
2005
2006  retainFocus () {
2007    this._focusIndex = focusManager.index;
2008  }
2009
2010  focusBack () {
2011    focusManager.focus(this._focusIndex);
2012  }
2013
2014  focusClosest () {
2015    const closest = this._focusClosest(this.node.parentNode);
2016    if (closest) closest.focus();
2017  }
2018
2019  _focusClosest (parent) {
2020    if (!parent) return null;
2021    const actions = [...queryActions(parent)];
2022    if (actions.length <= 1) {
2023      return this._focusClosest(parent.parentNode);
2024    } else {
2025      const index = actions.indexOf(this.node);
2026      return actions[index + (index < actions.length - 1 ? 1 : -1)];
2027    }
2028  }
2029
2030  get hasFocus () {
2031    return this.node === document.activeElement;
2032  }
2033
2034  scrollIntoView () {
2035    const rect = this.getRect();
2036
2037    const scroll = state.getModule('lock');
2038
2039    if (rect.top < 0) {
2040      scroll.move(rect.top - 50);
2041    }
2042
2043    if (rect.bottom > window.innerHeight) {
2044      scroll.move(rect.bottom - window.innerHeight + 50);
2045    }
2046  }
2047
2048  matches (selectors) {
2049    return this.node.matches(selectors);
2050  }
2051
2052  querySelector (selectors) {
2053    return this.node.querySelector(selectors);
2054  }
2055
2056  querySelectorAll (selectors) {
2057    return querySelectorAllArray(this.node, selectors);
2058  }
2059
2060  queryParentSelector (selectors) {
2061    return queryParentSelector(this.node, selectors);
2062  }
2063
2064  getRect () {
2065    const rect = this.node.getBoundingClientRect();
2066    rect.center = rect.left + rect.width * 0.5;
2067    rect.middle = rect.top + rect.height * 0.5;
2068    return rect;
2069  }
2070
2071  get isLegacy () {
2072    return state.isLegacy;
2073  }
2074}
2075
2076class KeyAction {
2077  constructor (type, keyCode, closure, preventDefault, stopPropagation) {
2078    this.type = type;
2079    this.eventType = `key${type}`;
2080    this.keyCode = keyCode;
2081    this.closure = closure;
2082    this.preventDefault = preventDefault === true;
2083    this.stopPropagation = stopPropagation === true;
2084  }
2085
2086  handle (e) {
2087    if (e.type !== this.eventType) return;
2088    if (e.keyCode === this.keyCode.value) {
2089      this.closure(e);
2090      if (this.preventDefault) {
2091        e.preventDefault();
2092      }
2093      if (this.stopPropagation) {
2094        e.stopPropagation();
2095      }
2096    }
2097  }
2098}
2099
2100class Listener {
2101  constructor (node, type, closure, options) {
2102    this._node = node;
2103    this._type = type;
2104    this._closure = closure;
2105    this._options = options;
2106  }
2107
2108  get closure () {
2109    return this._closure;
2110  }
2111
2112  listen () {
2113    this._node.addEventListener(this._type, this._closure, this._options);
2114  }
2115
2116  matchOptions (options = null) {
2117    switch (true) {
2118      case options === null:
2119      case typeof this._options === 'boolean' && typeof options === 'boolean' && this._options === options:
2120        return true;
2121
2122      case Object.keys(this._options).length !== Object.keys(options).length:
2123        return false;
2124
2125      case Object.keys(options).every(key => this._options[key] === options[key]):
2126        return true;
2127    }
2128
2129    return false;
2130  }
2131
2132  unlisten () {
2133    this._node.removeEventListener(this._type, this._closure, this._options);
2134  }
2135}
2136
2137class HashAction {
2138  constructor (hash, add) {
2139    this.hash = hash;
2140    this.add = add;
2141  }
2142
2143  handle (hash, e) {
2144    if (this.hash === hash) this.add(e);
2145  }
2146}
2147
2148const DisclosureEvent = {
2149  DISCLOSE: ns.event('disclose'),
2150  CONCEAL: ns.event('conceal'),
2151  CURRENT: ns.event('current')
2152};
2153
2154const DisclosureEmission = {
2155  RESET: ns.emission('disclosure', 'reset'),
2156  ADDED: ns.emission('disclosure', 'added'),
2157  RETRIEVE: ns.emission('disclosure', 'retrieve'),
2158  REMOVED: ns.emission('disclosure', 'removed'),
2159  GROUP: ns.emission('disclosure', 'group'),
2160  UNGROUP: ns.emission('disclosure', 'ungroup'),
2161  SPOTLIGHT: ns.emission('disclosure', 'spotlight')
2162};
2163
2164class Disclosure extends Instance {
2165  constructor (type, selector, DisclosureButtonInstanceClass, disclosuresGroupInstanceClassName) {
2166    super();
2167    this.type = type;
2168    this._selector = selector;
2169    this.DisclosureButtonInstanceClass = DisclosureButtonInstanceClass;
2170    this.disclosuresGroupInstanceClassName = disclosuresGroupInstanceClassName;
2171    this.modifier = this._selector + '--' + this.type.id;
2172    this._isPristine = true;
2173    this._isActive = true;
2174    this._isRetrievingPrimaries = false;
2175    this._hasRetrieved = false;
2176    this._primaryButtons = [];
2177  }
2178
2179  static get instanceClassName () {
2180    return 'Disclosure';
2181  }
2182
2183  init () {
2184    this.addDescent(DisclosureEmission.RESET, this.reset.bind(this));
2185    this.addDescent(DisclosureEmission.GROUP, this.update.bind(this));
2186    this.addDescent(DisclosureEmission.UNGROUP, this.update.bind(this));
2187    this.addAscent(DisclosureEmission.SPOTLIGHT, this.disclose.bind(this));
2188    this.register(`[aria-controls="${this.id}"]`, this.DisclosureButtonInstanceClass);
2189    this.ascend(DisclosureEmission.ADDED);
2190    this.listenHash(this.id, this._spotlight.bind(this));
2191    this.update();
2192  }
2193
2194  get isEnabled () { return super.isEnabled; }
2195
2196  set isEnabled (value) {
2197    if (this.isEnabled === value) return;
2198    super.isEnabled = value;
2199    if (value) this.ascend(DisclosureEmission.ADDED);
2200    else this.ascend(DisclosureEmission.REMOVED);
2201  }
2202
2203  get isActive () {
2204    return this._isActive;
2205  }
2206
2207  set isActive (value) {
2208    if (this._isActive === value) return;
2209    this._isActive = value;
2210    if (value) this.ascend(DisclosureEmission.ADDED);
2211    else {
2212      this.ascend(DisclosureEmission.REMOVED);
2213      if (this.isDisclosed) this.conceal();
2214    }
2215  }
2216
2217  get isPristine () {
2218    return this._isPristine;
2219  }
2220
2221  get proxy () {
2222    const scope = this;
2223    const proxy = Object.assign(super.proxy, {
2224      disclose: scope.disclose.bind(scope),
2225      focus: scope.focus.bind(scope)
2226    });
2227
2228    if (this.type.canConceal) proxy.conceal = scope.conceal.bind(scope);
2229
2230    const proxyAccessors = {
2231      get buttons () {
2232        return scope.buttons.map((button) => button.proxy);
2233      },
2234      get group () {
2235        const group = scope.group;
2236        return group ? group.proxy : null;
2237      },
2238      get isDisclosed () {
2239        return scope.isDisclosed;
2240      },
2241      get isEnabled () {
2242        return scope.isEnabled;
2243      },
2244      get isActive () {
2245        return scope.isActive;
2246      }
2247    };
2248
2249    return completeAssign(proxy, proxyAccessors);
2250  }
2251
2252  get buttons () {
2253    return this.getRegisteredInstances(this.DisclosureButtonInstanceClass.instanceClassName);
2254  }
2255
2256  update () {
2257    this.getGroup();
2258    this.retrievePrimaries();
2259  }
2260
2261  getGroup () {
2262    if (!this.disclosuresGroupInstanceClassName) {
2263      this._group = null;
2264      return;
2265    }
2266
2267    const group = this.element.getAscendantInstance(this.disclosuresGroupInstanceClassName, this.constructor.instanceClassName);
2268    if (!group || !group.validate(this)) {
2269      this._group = null;
2270      return;
2271    }
2272
2273    this._group = group;
2274  }
2275
2276  get group () {
2277    return this._group;
2278  }
2279
2280  disclose (withhold) {
2281    if (this.isDisclosed === true || !this.isEnabled || !this._isActive) return false;
2282    this.retainFocus();
2283    this._isPristine = false;
2284    this.isDisclosed = true;
2285    if (!withhold && this.group) this.group.current = this;
2286    return true;
2287  }
2288
2289  conceal (withhold, preventFocus = true) {
2290    if (this.isDisclosed === false) return false;
2291    if (!this.type.canConceal && this.group && this.group.current === this) return false;
2292    this.isDisclosed = false;
2293    if (!withhold && this.group && this.group.current === this) this.group.current = null;
2294    if (!preventFocus) this.focus();
2295    if (!this._isPristine) this.descend(DisclosureEmission.RESET);
2296    return true;
2297  }
2298
2299  get isDisclosed () {
2300    return this._isDisclosed;
2301  }
2302
2303  set isDisclosed (value) {
2304    if (this._isDisclosed === value || (!this.isEnabled && value === true)) return;
2305    if (!this._isPristine) this.dispatch(value ? DisclosureEvent.DISCLOSE : DisclosureEvent.CONCEAL, this);
2306    this._isDisclosed = value;
2307    if (value) this.addClass(this.modifier);
2308    else this.removeClass(this.modifier);
2309    for (let i = 0; i < this.buttons.length; i++) this.buttons[i].apply(value);
2310  }
2311
2312  get isInitiallyDisclosed () {
2313    return this.primaryButtons.some(button => button.isInitiallyDisclosed);
2314  }
2315
2316  hasRetrieved () {
2317    return this._hasRetrieved;
2318  }
2319
2320  reset () {}
2321
2322  toggle (canDisclose) {
2323    if (!this.type.canConceal) this.disclose();
2324    else {
2325      switch (true) {
2326        case !canDisclose:
2327        case this.isDisclosed:
2328          this.conceal(false, false);
2329          break;
2330
2331        default:
2332          this.disclose();
2333      }
2334    }
2335  }
2336
2337  get buttonHasFocus () {
2338    return this.buttons.some((button) => { return button.hasFocus; });
2339  }
2340
2341  get hasFocus () {
2342    if (super.hasFocus) return true;
2343    if (this.buttonHasFocus) return true;
2344    return this.querySelectorAll(':focus').length > 0;
2345  }
2346
2347  focus () {
2348    this.focusBack();
2349  }
2350
2351  get primaryButtons () {
2352    return this._primaryButtons;
2353  }
2354
2355  retrievePrimaries () {
2356    if (this._isRetrievingPrimaries) return;
2357    this._isRetrievingPrimaries = true;
2358    this.request(this._retrievePrimaries.bind(this));
2359  }
2360
2361  _retrievePrimaries () {
2362    this._isRetrievingPrimaries = false;
2363    this._primaryButtons = this._electPrimaries(this.buttons);
2364
2365    if (this._hasRetrieved || (this._primaryButtons.length === 0 && this.type.requirePrimary)) return;
2366    this.retrieved();
2367    this._hasRetrieved = true;
2368
2369    this.applyAbility(true);
2370
2371    if (this.group) {
2372      this.group.retrieve();
2373      return;
2374    }
2375
2376    if (this._isPristine && this.isEnabled && this.isActive && !this.group) {
2377      switch (true) {
2378        case this.hash === this.id:
2379          this._spotlight();
2380          break;
2381
2382        case this.isInitiallyDisclosed:
2383          this.disclose();
2384          break;
2385      }
2386    }
2387  }
2388
2389  retrieved () {}
2390
2391  _spotlight () {
2392    this.disclose();
2393    this.request(() => { this.ascend(DisclosureEmission.SPOTLIGHT); });
2394  }
2395
2396  _electPrimaries (candidates) {
2397    return candidates.filter(button => button.canDisclose && !this.node.contains(button.node));
2398  }
2399
2400  applyAbility (withhold = false) {
2401    const isEnabled = !this.type.requirePrimary || this._primaryButtons.some(button => !button.isDisabled);
2402
2403    if (this.isEnabled === isEnabled) return;
2404
2405    this.isEnabled = isEnabled;
2406
2407    if (withhold) return;
2408
2409    if (!this.isEnabled && this.isDisclosed) {
2410      if (this.group) this.ascend(DisclosureEmission.REMOVED);
2411      else if (this.type.canConceal) this.conceal();
2412    }
2413
2414    if (this.isEnabled) {
2415      if (this.group) this.ascend(DisclosureEmission.ADDED);
2416      if (this.hash === this.id) {
2417        this._spotlight();
2418      }
2419    }
2420  }
2421
2422  dispose () {
2423    this._group = null;
2424    this._primaryButtons = null;
2425    super.dispose();
2426    this.ascend(DisclosureEmission.REMOVED);
2427  }
2428}
2429
2430class DisclosureButton extends Instance {
2431  constructor (type) {
2432    super();
2433    this.type = type;
2434    this.attributeName = type.ariaState ? 'aria-' + type.id : ns.attr(type.id);
2435    this._canDisclose = false;
2436  }
2437
2438  static get instanceClassName () {
2439    return 'DisclosureButton';
2440  }
2441
2442  get isPrimary () {
2443    return this.registration.creator.primaryButtons.includes(this);
2444  }
2445
2446  get canDisclose () {
2447    return this._canDisclose;
2448  }
2449
2450  get isDisabled () {
2451    return this.type.canDisable && this.hasAttribute('disabled');
2452  }
2453
2454  init () {
2455    this._canDisclose = this.hasAttribute(this.attributeName);
2456    this._isInitiallyDisclosed = this.isDisclosed;
2457    this._isContained = this.registration.creator.node.contains(this.node);
2458    this.controlsId = this.getAttribute('aria-controls');
2459    this.registration.creator.retrievePrimaries();
2460    this.listenClick();
2461  }
2462
2463  get proxy () {
2464    const scope = this;
2465    return Object.assign(super.proxy, {
2466      focus: scope.focus.bind(scope)
2467    });
2468  }
2469
2470  handleClick (e) {
2471    if (this.registration.creator) this.registration.creator.toggle(this.canDisclose);
2472  }
2473
2474  mutate (attributeNames) {
2475    this._canDisclose = this.hasAttribute(this.attributeName);
2476    this.registration.creator.applyAbility();
2477    if (!this._isApplying && this.isPrimary && attributeNames.indexOf(this.attributeName) > -1 && this.registration.creator) {
2478      if (this.isDisclosed) this.registration.creator.disclose();
2479      else if (this.type.canConceal) this.registration.creator.conceal();
2480    }
2481  }
2482
2483  apply (value) {
2484    if (!this.canDisclose) return;
2485    this._isApplying = true;
2486    this.setAttribute(this.attributeName, value);
2487    this.request(() => { this._isApplying = false; });
2488  }
2489
2490  get isDisclosed () {
2491    return this.getAttribute(this.attributeName) === 'true';
2492  }
2493
2494  get isInitiallyDisclosed () {
2495    return this._isInitiallyDisclosed;
2496  }
2497
2498  focus () {
2499    super.focus();
2500    this.scrollIntoView();
2501  }
2502
2503  measure (rect) {
2504    const buttonRect = this.rect;
2505    this._dx = rect.x - buttonRect.x;
2506    this._dy = rect.y - buttonRect.y;
2507  }
2508
2509  get dx () {
2510    return this._dx;
2511  }
2512
2513  get dy () {
2514    return this._dy;
2515  }
2516}
2517
2518const DisclosureSelector = {
2519  PREVENT_CONCEAL: ns.attr.selector('prevent-conceal'),
2520  GROUP: ns.attr('group')
2521};
2522
2523class DisclosuresGroup extends Instance {
2524  constructor (disclosureInstanceClassName, jsAttribute) {
2525    super(jsAttribute);
2526    this.disclosureInstanceClassName = disclosureInstanceClassName;
2527    this._members = [];
2528    this._index = -1;
2529    this._isRetrieving = false;
2530    this._hasRetrieved = false;
2531    this._isGrouped = true;
2532  }
2533
2534  static get instanceClassName () {
2535    return 'DisclosuresGroup';
2536  }
2537
2538  init () {
2539    this.addAscent(DisclosureEmission.ADDED, this.update.bind(this));
2540    this.addAscent(DisclosureEmission.RETRIEVE, this.retrieve.bind(this));
2541    this.addAscent(DisclosureEmission.REMOVED, this.update.bind(this));
2542    this.descend(DisclosureEmission.GROUP);
2543    this._isGrouped = this.getAttribute(DisclosureSelector.GROUP) !== 'false';
2544    this.update();
2545  }
2546
2547  get proxy () {
2548    const scope = this;
2549
2550    const proxyAccessors = {
2551      set index (value) {
2552        scope.index = value;
2553      },
2554      get index () {
2555        return scope.index;
2556      },
2557      get length () {
2558        return scope.length;
2559      },
2560      get current () {
2561        const current = scope.current;
2562        return current ? current.proxy : null;
2563      },
2564      get members () {
2565        return scope.members.map((member) => member.proxy);
2566      },
2567      get hasFocus () {
2568        return scope.hasFocus;
2569      },
2570      set isGrouped (value) {
2571        scope.isGrouped = value;
2572      },
2573      get isGrouped () {
2574        return scope.isGrouped;
2575      }
2576    };
2577
2578    return completeAssign(super.proxy, proxyAccessors);
2579  }
2580
2581  validate (member) {
2582    return true;
2583  }
2584
2585  getMembers () {
2586    const members = this.element.getDescendantInstances(this.disclosureInstanceClassName, this.constructor.instanceClassName, true);
2587    this._members = members.filter(this.validate.bind(this)).filter(member => member.isEnabled && member.isActive);
2588    const invalids = members.filter(member => !this._members.includes(member));
2589    invalids.forEach(invalid => invalid.conceal());
2590  }
2591
2592  retrieve (bypassPrevention = false) {
2593    if (this._isRetrieving || (this._hasRetrieved && !bypassPrevention)) return;
2594    this._isRetrieving = true;
2595    this.request(this._retrieve.bind(this));
2596  }
2597
2598  _retrieve () {
2599    this.getMembers();
2600    this._isRetrieving = false;
2601    this._hasRetrieved = true;
2602
2603    if (!this.isGrouped) {
2604      for (let i = 0; i < this.length; i++) {
2605        const member = this.members[i];
2606        if (member.isInitiallyDisclosed) {
2607          member.disclose(true);
2608        }
2609      }
2610      return;
2611    }
2612
2613    if (this.hash) {
2614      for (let i = 0; i < this.length; i++) {
2615        const member = this.members[i];
2616        if (this.hash === member.id) {
2617          this.index = i;
2618          this.request(() => { this.ascend(DisclosureEmission.SPOTLIGHT); });
2619          return i;
2620        }
2621      }
2622    }
2623
2624    for (let i = 0; i < this.length; i++) {
2625      const member = this.members[i];
2626      if (member.isInitiallyDisclosed) {
2627        this.index = i;
2628        return i;
2629      }
2630    }
2631
2632    return this.getIndex();
2633  }
2634
2635  update () {
2636    this.getMembers();
2637    if (this._hasRetrieved) {
2638      if (this.isGrouped) this.getIndex();
2639    }
2640  }
2641
2642  get members () {
2643    return this._members;
2644  }
2645
2646  get length () {
2647    return this.members ? this.members.length : 0;
2648  }
2649
2650  getIndex (defaultIndex = -1) {
2651    this._index = undefined;
2652    let index = defaultIndex;
2653    for (let i = 0; i < this.length; i++) {
2654      if (this.members[i].isDisclosed) {
2655        index = i;
2656        break;
2657      }
2658    }
2659
2660    this.index = index;
2661    return index;
2662  }
2663
2664  get index () {
2665    return this._index;
2666  }
2667
2668  set index (value) {
2669    if (value < -1 || value >= this.length || value === this._index) return;
2670    this._index = value;
2671    for (let i = 0; i < this.length; i++) {
2672      const member = this.members[i];
2673      if (value === i) {
2674        if (!member.isDisclosed) member.disclose(true);
2675      } else {
2676        if ((this.isGrouped || !this.canUngroup) && member.isDisclosed !== false) member.conceal(true);
2677      }
2678    }
2679    this.apply();
2680    this.dispatch(DisclosureEvent.CURRENT, this.current);
2681  }
2682
2683  get current () {
2684    if (this._index === -1 || isNaN(this._index)) return null;
2685    return this._members[this._index] || null;
2686  }
2687
2688  set current (member) {
2689    this.index = this.members.indexOf(member);
2690  }
2691
2692  get hasFocus () {
2693    const current = this.current;
2694    if (current) return current.hasFocus;
2695    return false;
2696  }
2697
2698  set isGrouped (value) {
2699    const isGrouped = !!value;
2700    if (this._isGrouped === isGrouped) return;
2701    this._isGrouped = isGrouped;
2702    this.setAttribute(DisclosureSelector.GROUP, !!value);
2703    this.update();
2704  }
2705
2706  get isGrouped () {
2707    return this._isGrouped;
2708  }
2709
2710  get canUngroup () {
2711    return false;
2712  }
2713
2714  mutate (attributesNames) {
2715    if (attributesNames.includes(DisclosureSelector.GROUP)) {
2716      this.isGrouped = this.getAttribute(DisclosureSelector.GROUP) !== 'false';
2717    }
2718  }
2719
2720  apply () {}
2721
2722  dispose () {
2723    super.dispose();
2724    this.descend(DisclosureEmission.UNGROUP);
2725    this._members = null;
2726  }
2727}
2728
2729const DisclosureType = {
2730  EXPAND: {
2731    id: 'expanded',
2732    ariaState: true,
2733    ariaControls: true,
2734    canConceal: true,
2735    canDisable: true,
2736    requirePrimary: true
2737  },
2738  SELECT: {
2739    id: 'selected',
2740    ariaState: true,
2741    ariaControls: true,
2742    canConceal: false,
2743    canDisable: true,
2744    requirePrimary: true
2745  },
2746  OPENED: {
2747    id: 'opened',
2748    ariaState: false,
2749    ariaControls: true,
2750    canConceal: true,
2751    canDisable: false,
2752    requirePrimary: false
2753  }
2754};
2755
2756class CollapseButton extends DisclosureButton {
2757  constructor () {
2758    super(DisclosureType.EXPAND);
2759  }
2760
2761  static get instanceClassName () {
2762    return 'CollapseButton';
2763  }
2764}
2765
2766const CollapseSelector = {
2767  COLLAPSE: ns.selector('collapse'),
2768  COLLAPSING: ns.selector('collapsing')
2769};
2770
2771/**
2772 * Tab coorespond au panel d'un élement Tabs (tab panel)
2773 * Tab étend disclosure qui ajoute/enleve le modifier --selected,
2774 * et ajoute/eleve l'attribut hidden, sur le panel
2775 */
2776class Collapse extends Disclosure {
2777  constructor () {
2778    super(DisclosureType.EXPAND, CollapseSelector.COLLAPSE, CollapseButton, 'CollapsesGroup');
2779  }
2780
2781  static get instanceClassName () {
2782    return 'Collapse';
2783  }
2784
2785  init () {
2786    super.init();
2787    this.listen('transitionend', this.endCollapsing.bind(this));
2788  }
2789
2790  endCollapsing (e) {
2791    if (!this._isCollpasing) return;
2792    if (this._timeout) clearTimeout(this._timeout);
2793    this._timeout = null;
2794    this._isCollpasing = false;
2795    this.removeClass(CollapseSelector.COLLAPSING);
2796    if (!this.isDisclosed) {
2797      if (this.isLegacy) this.style.maxHeight = '';
2798    }
2799  }
2800
2801  unbound () {
2802    if (this.isLegacy) this.style.maxHeight = 'none';
2803  }
2804
2805  disclose (withhold) {
2806    if (this.isDisclosed === true || !this.isEnabled) return false;
2807    this.unbound();
2808    this.collapsing(() => super.disclose(withhold));
2809  }
2810
2811  conceal (withhold, preventFocus) {
2812    if (this.isDisclosed === false) return false;
2813    this.collapsing(() => super.conceal(withhold, preventFocus));
2814  }
2815
2816  collapsing (request) {
2817    this._isCollpasing = true;
2818    if (this._timeout) clearTimeout(this._timeout);
2819    this.addClass(CollapseSelector.COLLAPSING);
2820    this.adjust();
2821    this.request(() => {
2822      request();
2823      this._timeout = setTimeout(this.endCollapsing.bind(this), 500);
2824    });
2825  }
2826
2827  adjust () {
2828    this.setProperty('--collapser', 'none');
2829    const height = this.node.offsetHeight;
2830    this.setProperty('--collapse', -height + 'px');
2831    this.setProperty('--collapser', '');
2832  }
2833
2834  reset () {
2835    if (!this.isPristine) this.isDisclosed = false;
2836  }
2837
2838  _electPrimaries (candidates) {
2839    const primary = this.element.parent.instances.map(instance => instance.collapsePrimary).filter(button => button !== undefined && candidates.indexOf(button) > -1);
2840    if (primary.length === 1) return primary;
2841    candidates = super._electPrimaries(candidates);
2842    if (candidates.length === 1) return candidates;
2843    const before = candidates.filter(candidate => candidate.dy >= 0);
2844    if (before.length > 0) candidates = before;
2845    if (candidates.length === 1) return candidates;
2846    const min = Math.min(...candidates.map(candidate => candidate.dy));
2847    const mins = candidates.filter(candidate => candidate.dy === min);
2848    if (mins.length > 0) candidates = mins;
2849    if (candidates.length === 1) return candidates;
2850    candidates.sort((a, b) => Math.abs(b.dx) - Math.abs(a.dx));
2851    return candidates;
2852  }
2853}
2854
2855class CollapsesGroup extends DisclosuresGroup {
2856  constructor () {
2857    super('Collapse');
2858  }
2859
2860  static get instanceClassName () {
2861    return 'CollapsesGroup';
2862  }
2863
2864  get canUngroup () {
2865    return true;
2866  }
2867}
2868
2869const EquisizedEmission = {
2870  CHANGE: ns('equisized')
2871};
2872
2873class Equisized extends Instance {
2874  static get instanceClassName () {
2875    return 'Equisized';
2876  }
2877
2878  init () {
2879    this.ascend(EquisizedEmission.CHANGE);
2880  }
2881
2882  measure () {
2883    if (this.isLegacy) this.style.width = 'auto';
2884    return this.getRect().width;
2885  }
2886
2887  adjust (width) {
2888    if (this.isLegacy) this.style.width = `${width}px`;
2889  }
2890
2891  dispose () {
2892    this.ascend(EquisizedEmission.CHANGE);
2893  }
2894}
2895
2896class EquisizedsGroup extends Instance {
2897  static get instanceClassName () {
2898    return 'EquisizedsGroup';
2899  }
2900
2901  init () {
2902    this.isResizing = true;
2903    this.isLoading = true;
2904    this.addAscent(EquisizedEmission.CHANGE, this.resize.bind(this));
2905  }
2906
2907  load () {
2908    this.resize();
2909  }
2910
2911  resize () {
2912    const equisizeds = this.element.getDescendantInstances('Equisized');
2913    if (!this.isLegacy) this.style.setProperty('--equisized-width', 'auto');
2914
2915    const width = Math.max(...equisizeds.map(equisized => equisized.measure()));
2916    if (this.isLegacy) equisizeds.forEach(equisized => equisized.adjust(width));
2917    else this.style.setProperty('--equisized-width', `${width}px`);
2918  }
2919}
2920
2921class Toggle extends Instance {
2922  static get instanceClassName () {
2923    return 'Toggle';
2924  }
2925
2926  init () {
2927    this.pressed = this.pressed === 'true';
2928    this.listenClick();
2929  }
2930
2931  handleClick () {
2932    this.toggle();
2933  }
2934
2935  toggle () {
2936    this.pressed = this.pressed !== 'true';
2937  }
2938
2939  get pressed () {
2940    return this.getAttribute('aria-pressed');
2941  }
2942
2943  set pressed (value) {
2944    this.setAttribute('aria-pressed', value ? 'true' : 'false');
2945  }
2946
2947  get proxy () {
2948    const scope = this;
2949    const proxy = Object.assign(super.proxy, {
2950      toggle: scope.toggle.bind(scope)
2951    });
2952
2953    const proxyAccessors = {
2954      get pressed () {
2955        return scope.pressed;
2956      },
2957      set pressed (value) {
2958        scope.pressed = value;
2959      }
2960    };
2961
2962    return completeAssign(proxy, proxyAccessors);
2963  }
2964}
2965
2966const RootSelector = {
2967  ROOT: ':root'
2968};
2969
2970const setAttributes = (el, attrs) => {
2971  Object.keys(attrs).forEach(key => el.setAttribute(key, attrs[key]));
2972};
2973
2974/**
2975 * Allowed SVG/HTML elements for inline SVG content.
2976 * Only standard SVG presentational and structural elements are permitted.
2977 */
2978const ALLOWED_TAGS = new Set([
2979  'svg', 'g', 'defs', 'symbol', 'use', 'title', 'desc', 'style',
2980  'path', 'rect', 'circle', 'ellipse', 'line', 'polyline', 'polygon',
2981  'text', 'tspan', 'textpath', 'image', 'clippath', 'mask',
2982  'lineargradient', 'radialgradient', 'stop', 'pattern', 'marker',
2983  'filter', 'fegaussianblur', 'fecolormatrix', 'feblend', 'fecomposite',
2984  'feflood', 'femerge', 'femergenode', 'feoffset', 'view', 'switch'
2985]);
2986
2987/**
2988 * Attributes that must not start with "javascript:" or "data:" (URL-type attributes).
2989 */
2990const URL_ATTRIBUTES = new Set([
2991  'href', 'xlink:href', 'src', 'action', 'formaction'
2992]);
2993
2994const DANGEROUS_PROTOCOL = /^\s*(?:javascript|data|vbscript)\s*:/i;
2995const DANGEROUS_CSS = /@import|expression\s*\(|url\s*\(\s*['"]?\s*(?:javascript|data|vbscript):|behavior\s*:|-moz-binding/i;
2996
2997/**
2998 * Recursively sanitize an SVG element in-place:
2999 * - removes elements not in ALLOWED_TAGS
3000 * - removes all event-handler attributes (on*)
3001 * - removes dangerous protocol values from URL attributes
3002 *
3003 * @param {Element} element
3004 */
3005function sanitizeElement (element) {
3006  for (const child of Array.from(element.childNodes)) {
3007    if (child.nodeType === Node.ELEMENT_NODE) {
3008      const tag = child.tagName.toLowerCase();
3009      if (!ALLOWED_TAGS.has(tag)) {
3010        element.removeChild(child);
3011        continue;
3012      }
3013      // Remove dangerous attributes
3014      for (const attr of Array.from(child.attributes)) {
3015        const name = attr.name.toLowerCase();
3016        if (name.startsWith('on')) {
3017          child.removeAttribute(attr.name);
3018          continue;
3019        }
3020        if (URL_ATTRIBUTES.has(name) && DANGEROUS_PROTOCOL.test(attr.value)) {
3021          child.removeAttribute(attr.name);
3022          continue;
3023        }
3024        if (URL_ATTRIBUTES.has(name) && !isAllowedUrlAttributeValue(attr.value)) {
3025          child.removeAttribute(attr.name);
3026          continue;
3027        }
3028        if (name === 'style') {
3029          const sanitizedStyle = sanitizeCssContent(attr.value);
3030          if (sanitizedStyle) child.setAttribute(attr.name, sanitizedStyle);
3031          else child.removeAttribute(attr.name);
3032        }
3033      }
3034
3035      if (tag === 'style') {
3036        const sanitizedStyleContent = sanitizeCssContent(child.textContent);
3037        if (!sanitizedStyleContent) {
3038          element.removeChild(child);
3039          continue;
3040        }
3041        child.textContent = sanitizedStyleContent;
3042      }
3043
3044      sanitizeElement(child);
3045    }
3046  }
3047}
3048
3049/**
3050 * Remove CSS payloads commonly used for script execution.
3051 * Returns an empty string when the CSS contains dangerous patterns.
3052 *
3053 * @param {string} css
3054 * @returns {string}
3055 */
3056function sanitizeCssContent (css) {
3057  const content = String(css || '').trim();
3058  if (!content) return '';
3059  if (DANGEROUS_CSS.test(content)) return '';
3060  return content;
3061}
3062
3063/**
3064 * Sanitize an SVG DOM element and return it.
3065 * Mutates the element in-place.
3066 *
3067 * @param {SVGElement} svg
3068 * @returns {SVGElement}
3069 */
3070function sanitizeSvg (svg) {
3071  // Remove dangerous attributes on the root <svg> element itself
3072  for (const attr of Array.from(svg.attributes)) {
3073    const name = attr.name.toLowerCase();
3074    if (name.startsWith('on')) {
3075      svg.removeAttribute(attr.name);
3076      continue;
3077    }
3078    if (URL_ATTRIBUTES.has(name) && DANGEROUS_PROTOCOL.test(attr.value)) {
3079      svg.removeAttribute(attr.name);
3080      continue;
3081    }
3082    if (URL_ATTRIBUTES.has(name) && !isAllowedUrlAttributeValue(attr.value)) {
3083      svg.removeAttribute(attr.name);
3084    }
3085  }
3086  sanitizeElement(svg);
3087  return svg;
3088}
3089
3090/**
3091 * Validate URL-valued attributes for inline SVG usage.
3092 * Allows same-document fragments (#id) and same-origin URLs only.
3093 *
3094 * @param {string} value
3095 * @returns {boolean}
3096 */
3097function isAllowedUrlAttributeValue (value) {
3098  const url = String(value || '').trim();
3099  if (!url) return true;
3100  if (url.charAt(0) === '#') return true;
3101  return isSameOrigin(url);
3102}
3103
3104/**
3105 * Check whether a URL is same-origin as the current page.
3106 * Non-empty relative URLs are considered same-origin.
3107 *
3108 * @param {string} url
3109 * @returns {boolean}
3110 */
3111function isSameOrigin (url) {
3112  if (!url) return false;
3113  try {
3114    const link = document.createElement('a');
3115    link.href = url;
3116
3117    const protocol = (link.protocol || window.location.protocol).toLowerCase();
3118    if (protocol !== 'http:' && protocol !== 'https:') return false;
3119
3120    const hostname = link.hostname || window.location.hostname;
3121    const port = link.port || (protocol === 'https:' ? '443' : '80');
3122    const currentPort = window.location.port || (window.location.protocol === 'https:' ? '443' : '80');
3123
3124    return protocol === window.location.protocol && hostname === window.location.hostname && port === currentPort;
3125  } catch (e) {
3126    return false;
3127  }
3128}
3129
3130class InjectSvg extends Instance {
3131  static get instanceClassName () {
3132    return 'InjectSvg';
3133  }
3134
3135  init () {
3136    if (this.node) {
3137      this.img = this.node.querySelector('img');
3138    }
3139
3140    if (!this.isLegacy) {
3141      this.replace();
3142    }
3143  }
3144
3145  get proxy () {
3146    const scope = this;
3147    return Object.assign(super.proxy, {
3148      replace: scope.replace.bind(scope),
3149      restore: scope.restore.bind(scope)
3150    });
3151  }
3152
3153  fetch () {
3154    if (this.img) {
3155      this.imgID = this.img.getAttribute('id');
3156      this.imgClass = this.img.getAttribute('class');
3157      this.imgURL = this.img.getAttribute('src');
3158
3159      if (!isSameOrigin(this.imgURL)) return;
3160
3161      fetch(this.imgURL)
3162        .then(data => data.text())
3163        .then(response => {
3164          const parser = new DOMParser();
3165          const xmlDoc = parser.parseFromString(response, 'text/html');
3166          const rawSvg = xmlDoc.querySelector('svg');
3167
3168          if (rawSvg) {
3169            this.svg = sanitizeSvg(rawSvg);
3170            this.replace();
3171          }
3172        });
3173    }
3174  }
3175
3176  replace () {
3177    if (!this.svg) {
3178      this.fetch();
3179      return;
3180    }
3181
3182    if (this.imgID && typeof this.imgID !== 'undefined') {
3183      this.svg.setAttribute('id', this.imgID);
3184    }
3185
3186    // gestion de la dépréciation
3187    let name = this.imgURL.match(/[ \w-]+\./)[0];
3188    if (name) {
3189      name = name.slice(0, -1);
3190
3191      if (['dark', 'light', 'system'].includes(name)) {
3192        this.svg.innerHTML = this.svg.innerHTML.replaceAll('id="artwork-', `id="${name}-artwork-`);
3193        this.svg.innerHTML = this.svg.innerHTML.replaceAll('"#artwork-', `"#${name}-artwork-`);
3194      }
3195    }
3196
3197    if (this.imgClass && typeof this.imgClass !== 'undefined') {
3198      this.svg.setAttribute('class', this.imgClass);
3199    }
3200
3201    if (this.svg.hasAttribute('xmlns:a')) {
3202      this.svg.removeAttribute('xmlns:a');
3203    }
3204
3205    this.node.setAttribute('data-fr-inject-svg', true);
3206    const svgAttributes = {
3207      'aria-hidden': true,
3208      focusable: false
3209    };
3210    setAttributes(this.svg, svgAttributes);
3211    this.node.replaceChild(this.svg, this.img);
3212  }
3213
3214  restore () {
3215    if (this.img && this.svg) {
3216      this.node.setAttribute('data-fr-inject-svg', false);
3217      this.node.replaceChild(this.img, this.svg);
3218    }
3219  }
3220}
3221
3222const InjectSvgSelector = {
3223  INJECT_SVG: `[${ns.attr('inject-svg')}]`
3224};
3225
3226class Artwork extends Instance {
3227  static get instanceClassName () {
3228    return 'Artwork';
3229  }
3230
3231  init () {
3232    if (this.isLegacy) {
3233      this.replace();
3234    }
3235  }
3236
3237  get proxy () {
3238    const scope = this;
3239    return Object.assign(super.proxy, {
3240      replace: scope.replace.bind(scope)
3241    });
3242  }
3243
3244  fetch () {
3245    this.xlink = this.node.getAttribute('href');
3246    const splitUrl = this.xlink.split('#');
3247    this.svgUrl = splitUrl[0];
3248    this.svgName = splitUrl[1];
3249
3250    if (!isSameOrigin(this.svgUrl)) return;
3251
3252    const xhr = new XMLHttpRequest();
3253    xhr.onload = () => {
3254      const parser = new DOMParser();
3255      const xmlDoc = parser.parseFromString(xhr.responseText, 'text/html');
3256      this.realSvgContent = xmlDoc.getElementById(this.svgName);
3257      if (this.realSvgContent) {
3258        sanitizeSvg(this.realSvgContent);
3259        if (this.realSvgContent.tagName === 'symbol') {
3260          const expectedHref = `#${this.svgName}`;
3261          const uses = xmlDoc.getElementsByTagName('use');
3262          this.use = null;
3263          for (let i = 0; i < uses.length; i++) {
3264            const use = uses[i];
3265            const href = use.getAttribute('href') || use.getAttribute('xlink:href');
3266            if (href === expectedHref) {
3267              this.use = use;
3268              break;
3269            }
3270          }
3271          if (this.use) {
3272            sanitizeSvg(this.use);
3273            this.node.parentNode.insertBefore(this.use, this.node);
3274          }
3275        } else {
3276          // deprecated svg structure
3277          this.realSvgContent.classList.add(this.node.classList);
3278        }
3279
3280        this.replace();
3281      }
3282    };
3283    xhr.open('GET', this.svgUrl);
3284    xhr.send();
3285  }
3286
3287  replace () {
3288    if (!this.realSvgContent) {
3289      this.fetch();
3290      return;
3291    }
3292
3293    this.node.parentNode.replaceChild(this.realSvgContent, this.node);
3294  }
3295}
3296
3297const ArtworkSelector = {
3298  ARTWORK_USE: `${ns.selector('artwork')} use`
3299};
3300
3301const AssessSelector = {
3302  ASSESS_FILE: `${ns.attr.selector('assess-file')}`,
3303  DETAIL: `${ns.attr.selector('assess-file')} [class$="__detail"], ${ns.attr.selector('assess-file')} [class*="__detail "]`
3304};
3305
3306const AssessEmission = {
3307  UPDATE: ns.emission('assess', 'update'),
3308  ADDED: ns.emission('assess', 'added')
3309};
3310
3311class AssessFile extends Instance {
3312  static get instanceClassName () {
3313    return 'AssessFile';
3314  }
3315
3316  init () {
3317    this.lang = this.getLang(this.node);
3318    this.href = this.getAttribute('href');
3319    this.hreflang = this.getAttribute('hreflang');
3320    this.file = {};
3321    this.gather();
3322    this.addAscent(AssessEmission.ADDED, this.update.bind(this));
3323    this.addDescent(AssessEmission.ADDED, this.update.bind(this));
3324  }
3325
3326  getFileLength () {
3327    if (this.href === undefined) {
3328      this.length = -1;
3329      return;
3330    }
3331
3332    fetch(this.href, { method: 'HEAD', mode: 'cors' }).then(response => {
3333      if (response.ok) {
3334        this.length = response.headers.get('content-length') || -1;
3335        if (this.length && this.length === -1) {
3336          throw new Error('File size unknown' + '\n Unable to get HTTP header: "content-length"');
3337        }
3338        this.gather();
3339      } else {
3340        throw new Error('Unable to access the resource : Status ' + response.status);
3341      }
3342    }).catch((error) => {
3343      this.length = -1;
3344      this.gather();
3345      inspector.warn('Fetch error on assess file : ' + this.href + '\n ' + error);
3346    });
3347  }
3348
3349  mutate (attributeNames) {
3350    if (attributeNames.indexOf('href') !== -1) {
3351      this.href = this.getAttribute('href');
3352      this.getFileLength();
3353    }
3354
3355    if (attributeNames.indexOf('hreflang') !== -1) {
3356      this.hreflang = this.getAttribute('hreflang');
3357      this.gather();
3358    }
3359  }
3360
3361  gather () {
3362    // TODO V2: implémenter async
3363    if (this.isLegacy) this.length = -1;
3364
3365    if (!this.length) {
3366      this.getFileLength();
3367    }
3368
3369    this.details = [];
3370
3371    if (this.href) {
3372      const extension = this.parseExtension(this.href);
3373      if (extension) this.details.push(extension.toUpperCase());
3374    }
3375
3376    if (this.length && this.length !== -1) {
3377      this.details.push(this.bytesToSize(this.length));
3378    }
3379
3380    if (this.hreflang) {
3381      this.details.push(this.getLangDisplayName(this.hreflang));
3382    }
3383
3384    this.update();
3385  }
3386
3387  update () {
3388    if (!this.details) return;
3389    this.descend(AssessEmission.UPDATE, this.details);
3390    this.ascend(AssessEmission.UPDATE, this.details);
3391  }
3392
3393  getLang (elem) {
3394    // todo: ajouter un listener global de changement de langue
3395    if (elem.lang) return elem.lang;
3396    if (document.documentElement === elem) return window.navigator.language;
3397    return this.getLang(elem.parentElement);
3398  }
3399
3400  parseExtension (url) {
3401    const regexExtension = /\.(\w{1,9})(?:$|[?#])/;
3402    return url.match(regexExtension)[0].replace('.', '');
3403  }
3404
3405  getLangDisplayName (locale) {
3406    if (this.isLegacy) return locale;
3407    const displayNames = new Intl.DisplayNames([this.lang], { type: 'language' });
3408    const name = displayNames.of(locale);
3409    return name.charAt(0).toUpperCase() + name.slice(1);
3410  }
3411
3412  bytesToSize (bytes) {
3413    if (bytes === -1) return null;
3414
3415    let sizeUnits = ['octets', 'ko', 'Mo', 'Go', 'To'];
3416    if (this.getAttribute(ns.attr('assess-file')) === 'bytes') {
3417      sizeUnits = ['bytes', 'KB', 'MB', 'GB', 'TB'];
3418    }
3419
3420    const i = parseInt(Math.floor(Math.log(bytes) / Math.log(1000)), 10);
3421    if (i === 0) return `${bytes} ${sizeUnits[i]}`;
3422
3423    const size = bytes / (1000 ** i);
3424    const roundedSize = Math.round((size + Number.EPSILON) * 100) / 100; // arrondi a 2 décimal
3425    const stringSize = String(roundedSize).replace('.', ',');
3426
3427    return `${stringSize} ${sizeUnits[i]}`;
3428  }
3429}
3430
3431class AssessDetail extends Instance {
3432  static get instanceClassName () {
3433    return 'AssessDetail';
3434  }
3435
3436  init () {
3437    this.addDescent(AssessEmission.UPDATE, this.update.bind(this));
3438    this.ascend(AssessEmission.ADDED);
3439  }
3440
3441  update (details) {
3442    this.node.innerHTML = details.join(' - ');
3443  }
3444}
3445
3446const ratiosImg = ['32x9', '16x9', '3x2', '4x3', '1x1', '3x4', '2x3'];
3447const ratiosVid = ['16x9', '4x3', '1x1'];
3448
3449const ratioSelector = (name, modifiers) => {
3450  return modifiers.map(modifier => ns.selector(`${name}--${modifier}`)).join(',');
3451};
3452
3453const deprecatedRatioSelector = `${ns.selector('responsive-img')}, ${ratioSelector('responsive-img', ratiosImg)}, ${ns.selector('responsive-vid')}, ${ratioSelector('responsive-vid', ratiosVid)}`;
3454
3455const RatioSelector = {
3456  RATIO: `${ns.selector('ratio')}, ${ratioSelector('ratio', ratiosImg)}, ${deprecatedRatioSelector}`
3457};
3458
3459const api = window[config.namespace];
3460
3461class Ratio extends Instance {
3462  static get instanceClassName () {
3463    return 'Ratio';
3464  }
3465
3466  init () {
3467    if (!api.internals.support.supportAspectRatio()) {
3468      this.ratio = 16 / 9;
3469      for (const className in this.classNames) {
3470        if (this.registration.selector.indexOf(this.classNames[className]) > 0) {
3471          const ratio = this.classNames[className].split('ratio-');
3472          if (ratio[1]) {
3473            this.ratio = ratio[1].split('x')[0] / ratio[1].split('x')[1];
3474          }
3475        }
3476      }
3477      this.isRendering = true;
3478      this.update();
3479    }
3480  }
3481
3482  render () {
3483    const width = this.getRect().width;
3484    if (width !== this.currentWidth) this.update();
3485  }
3486
3487  update () {
3488    this.currentWidth = this.getRect().width;
3489    this.style.height = this.currentWidth / this.ratio + 'px';
3490  }
3491}
3492
3493const PlaceSelector = {
3494  TOP: ns.selector('placement--top'),
3495  RIGHT: ns.selector('placement--right'),
3496  BOTTOM: ns.selector('placement--bottom'),
3497  LEFT: ns.selector('placement--left')
3498};
3499
3500const AlignSelector = {
3501  START: ns.selector('placement--start'),
3502  CENTER: ns.selector('placement--center'),
3503  END: ns.selector('placement--end')
3504};
3505
3506const PlacementPosition = {
3507  TOP: 'place_top',
3508  RIGHT: 'place_right',
3509  BOTTOM: 'place_bottom',
3510  LEFT: 'place_left'
3511};
3512
3513const PlacementAlign = {
3514  START: 'align_start',
3515  CENTER: 'align_center',
3516  END: 'align_end'
3517};
3518
3519const PlacementMode = {
3520  AUTO: 'placement_auto',
3521  MANUAL: 'placement_manual'
3522};
3523
3524class Placement extends Instance {
3525  constructor (mode = PlacementMode.AUTO, places = [PlacementPosition.BOTTOM, PlacementPosition.TOP, PlacementPosition.LEFT, PlacementPosition.RIGHT], aligns = [PlacementAlign.CENTER, PlacementAlign.START, PlacementAlign.END], safeAreaMargin = 16) {
3526    super();
3527    this._mode = mode;
3528    this._places = places;
3529    this._aligns = aligns;
3530    this._safeAreaMargin = safeAreaMargin;
3531    this._isShown = false;
3532    this._x = this._y = 0;
3533  }
3534
3535  static get instanceClassName () {
3536    return 'Placement';
3537  }
3538
3539  init () {
3540    this.isResizing = true;
3541  }
3542
3543  get proxy () {
3544    const scope = this;
3545    const proxy = Object.assign(super.proxy, {
3546      show: scope.show.bind(scope),
3547      hide: scope.hide.bind(scope)
3548    });
3549
3550    const proxyAccessors = {
3551      get mode () {
3552        return scope.mode;
3553      },
3554      set mode (value) {
3555        scope.mode = value;
3556      },
3557      get place () {
3558        return scope.place;
3559      },
3560      set place (value) {
3561        scope.place = value;
3562      },
3563      get align () {
3564        return scope.align;
3565      },
3566      set align (value) {
3567        scope.align = value;
3568      },
3569      get isShown () {
3570        return scope.isShown;
3571      },
3572      set isShown (value) {
3573        scope.isShown = value;
3574      }
3575    };
3576
3577    return completeAssign(proxy, proxyAccessors);
3578  }
3579
3580  get mode () {
3581    return this._mode;
3582  }
3583
3584  set mode (value) {
3585    this._mode = value;
3586  }
3587
3588  get place () {
3589    return this._place;
3590  }
3591
3592  set place (value) {
3593    if (this._place === value) return;
3594    switch (this._place) {
3595      case PlacementPosition.TOP:
3596        this.removeClass(PlaceSelector.TOP);
3597        break;
3598
3599      case PlacementPosition.RIGHT:
3600        this.removeClass(PlaceSelector.RIGHT);
3601        break;
3602
3603      case PlacementPosition.BOTTOM:
3604        this.removeClass(PlaceSelector.BOTTOM);
3605        break;
3606
3607      case PlacementPosition.LEFT:
3608        this.removeClass(PlaceSelector.LEFT);
3609        break;
3610    }
3611    this._place = value;
3612    switch (this._place) {
3613      case PlacementPosition.TOP:
3614        this.addClass(PlaceSelector.TOP);
3615        break;
3616
3617      case PlacementPosition.RIGHT:
3618        this.addClass(PlaceSelector.RIGHT);
3619        break;
3620
3621      case PlacementPosition.BOTTOM:
3622        this.addClass(PlaceSelector.BOTTOM);
3623        break;
3624
3625      case PlacementPosition.LEFT:
3626        this.addClass(PlaceSelector.LEFT);
3627        break;
3628    }
3629  }
3630
3631  get align () {
3632    return this._align;
3633  }
3634
3635  set align (value) {
3636    if (this._align === value) return;
3637    switch (this._align) {
3638      case PlacementAlign.START:
3639        this.removeClass(AlignSelector.START);
3640        break;
3641
3642      case PlacementAlign.CENTER:
3643        this.removeClass(AlignSelector.CENTER);
3644        break;
3645
3646      case PlacementAlign.END:
3647        this.removeClass(AlignSelector.END);
3648        break;
3649    }
3650    this._align = value;
3651    switch (this._align) {
3652      case PlacementAlign.START:
3653        this.addClass(AlignSelector.START);
3654        break;
3655
3656      case PlacementAlign.CENTER:
3657        this.addClass(AlignSelector.CENTER);
3658        break;
3659
3660      case PlacementAlign.END:
3661        this.addClass(AlignSelector.END);
3662        break;
3663    }
3664  }
3665
3666  show () {
3667    this.isShown = true;
3668  }
3669
3670  hide () {
3671    this.isShown = false;
3672  }
3673
3674  get isShown () {
3675    return this._isShown;
3676  }
3677
3678  set isShown (value) {
3679    if (this._isShown === value || !this.isEnabled) return;
3680    this.isRendering = value;
3681    this._isShown = value;
3682  }
3683
3684  setReferent (referent) {
3685    this._referent = referent;
3686  }
3687
3688  render () {
3689    if (!this._referent) return;
3690    this.referentRect = this._referent.getRect();
3691    this.rect = this.getRect();
3692    this.safeArea = this.getSafeArea();
3693
3694    if (this.mode === PlacementMode.AUTO) {
3695      this.place = this.getPlace();
3696      switch (this.place) {
3697        case PlacementPosition.TOP:
3698        case PlacementPosition.BOTTOM:
3699          this.align = this.getHorizontalAlign();
3700          break;
3701
3702        case PlacementPosition.LEFT:
3703        case PlacementPosition.RIGHT:
3704          this.align = this.getVerticalAlign();
3705      }
3706    }
3707
3708    let x, y;
3709
3710    switch (this.place) {
3711      case PlacementPosition.TOP:
3712        y = this.referentRect.top - this.rect.top - this.rect.height;
3713        break;
3714
3715      case PlacementPosition.RIGHT:
3716        x = this.referentRect.left - this.rect.left + this.referentRect.width;
3717        break;
3718
3719      case PlacementPosition.BOTTOM:
3720        y = this.referentRect.top - this.rect.top + this.referentRect.height;
3721        break;
3722
3723      case PlacementPosition.LEFT:
3724        x = this.referentRect.left - this.rect.left - this.rect.width;
3725        break;
3726    }
3727
3728    switch (this.place) {
3729      case PlacementPosition.TOP:
3730      case PlacementPosition.BOTTOM:
3731        switch (this.align) {
3732          case PlacementAlign.CENTER:
3733            x = this.referentRect.left - this.rect.left + this.referentRect.width * 0.5 - this.rect.width * 0.5;
3734            break;
3735
3736          case PlacementAlign.START:
3737            x = this.referentRect.left - this.rect.left;
3738            break;
3739
3740          case PlacementAlign.END:
3741            x = this.referentRect.left - this.rect.left + this.referentRect.width - this.rect.width;
3742            break;
3743        }
3744        break;
3745
3746      case PlacementPosition.RIGHT:
3747      case PlacementPosition.LEFT:
3748        switch (this.align) {
3749          case PlacementAlign.CENTER:
3750            y = this.referentRect.top - this.rect.top + this.referentRect.height * 0.5 - this.rect.height * 0.5;
3751            break;
3752
3753          case PlacementAlign.START:
3754            y = this.referentRect.top - this.rect.top;
3755            break;
3756
3757          case PlacementAlign.END:
3758            y = this.referentRect.top - this.rect.top - this.rect.height;
3759            break;
3760        }
3761        break;
3762    }
3763
3764    this._x += (x + 0.5) | 0;
3765    this._y += (y + 0.5) | 0;
3766    this.node.style.transform = `translate(${this._x}px,${this._y}px)`;
3767  }
3768
3769  getPlace () {
3770    for (const place of this._places) {
3771      switch (place) {
3772        case PlacementPosition.TOP:
3773          if (this.referentRect.top - this.rect.height > this.safeArea.top) return PlacementPosition.TOP;
3774          break;
3775
3776        case PlacementPosition.RIGHT:
3777          if (this.referentRect.right + this.rect.width < this.safeArea.right) return PlacementPosition.RIGHT;
3778          break;
3779
3780        case PlacementPosition.BOTTOM:
3781          if (this.referentRect.bottom + this.rect.height < this.safeArea.bottom) return PlacementPosition.BOTTOM;
3782          break;
3783
3784        case PlacementPosition.LEFT:
3785          if (this.referentRect.left - this.rect.width > this.safeArea.left) return PlacementPosition.LEFT;
3786          break;
3787      }
3788    }
3789
3790    return this._places[0];
3791  }
3792
3793  getHorizontalAlign () {
3794    for (const align of this._aligns) {
3795      switch (align) {
3796        case PlacementAlign.CENTER:
3797          if (this.referentRect.center - this.rect.width * 0.5 > this.safeArea.left && this.referentRect.center + this.rect.width * 0.5 < this.safeArea.right) return PlacementAlign.CENTER;
3798          break;
3799
3800        case PlacementAlign.START:
3801          if (this.referentRect.left + this.rect.width < this.safeArea.right) return PlacementAlign.START;
3802          break;
3803
3804        case PlacementAlign.END:
3805          if (this.referentRect.right - this.rect.width > this.safeArea.left) return PlacementAlign.END;
3806          break;
3807      }
3808    }
3809
3810    return this._aligns[0];
3811  }
3812
3813  getVerticalAlign () {
vendor: 21,959 bytes, lines 3814-4608
3814    for (const align of this._aligns) {
3815      switch (align) {
3816        case PlacementAlign.CENTER:
3817          if (this.referentRect.middle - this.rect.height * 0.5 > this.safeArea.top && this.referentRect.middle + this.rect.height * 0.5 < this.safeArea.bottom) return PlacementAlign.CENTER;
3818          break;
3819
3820        case PlacementAlign.START:
3821          if (this.referentRect.top + this.rect.height < this.safeArea.bottom) return PlacementAlign.START;
3822          break;
3823
3824        case PlacementAlign.END:
3825          if (this.referentRect.bottom - this.rect.height > this.safeArea.top) return PlacementAlign.END;
3826          break;
3827      }
3828    }
3829
3830    return this._aligns[0];
3831  }
3832
3833  getSafeArea () {
3834    let element = this.node;
3835    let isX, isY;
3836    let top = this._safeAreaMargin;
3837    let right = window.innerWidth - this._safeAreaMargin;
3838    let bottom = window.innerHeight - this._safeAreaMargin;
3839    let left = this._safeAreaMargin;
3840
3841    while (element) {
3842      if (element === document.body) break;
3843      element = element.parentElement;
3844      const style = window.getComputedStyle(element);
3845
3846      const overflow = /(visible|(\w+))(\s(visible|(\w+)))?/.exec(style.overflow);
3847      isX = overflow[2] !== undefined;
3848      isY = overflow[3] !== undefined ? overflow[5] !== undefined : overflow[2] !== undefined;
3849
3850      if (!isX && !isY) continue;
3851      const rect = element.getBoundingClientRect();
3852
3853      if (isX) {
3854        if (rect.left > left) left = rect.left;
3855        if (rect.right < right) right = rect.right;
3856      }
3857      if (isY) {
3858        if (rect.top > top) top = rect.top;
3859        if (rect.bottom < bottom) bottom = rect.bottom;
3860      }
3861    }
3862
3863    return {
3864      top: top,
3865      right: right,
3866      bottom: bottom,
3867      left: left,
3868      center: left + (right - left) * 0.5,
3869      middle: top + (bottom - top) * 0.5
3870    };
3871  }
3872
3873  dispose () {
3874    this._referent = null;
3875    super.dispose();
3876  }
3877}
3878
3879class PlacementReferent extends Instance {
3880  constructor () {
3881    super();
3882    this._isShown = false;
3883  }
3884
3885  static get instanceClassName () {
3886    return 'PlacementReferent';
3887  }
3888
3889  init () {
3890    this.registration.creator.setReferent(this);
3891    this._placement = this.registration.creator;
3892  }
3893
3894  get placement () {
3895    return this._placement;
3896  }
3897
3898  get isShown () {
3899    return this._isShown;
3900  }
3901
3902  set isShown (value) {
3903    if (this._isShown === value || !this.isEnabled) return;
3904    this._isShown = value;
3905    if (value) this.registration.creator.show();
3906    else this.registration.creator.hide();
3907  }
3908
3909  show () {
3910    this.isShown = true;
3911  }
3912
3913  hide () {
3914    this.isShown = false;
3915  }
3916}
3917
3918api$1.core = {
3919  Instance: Instance,
3920  Breakpoints: Breakpoints,
3921  KeyCodes: KeyCodes,
3922  Disclosure: Disclosure,
3923  DisclosureButton: DisclosureButton,
3924  DisclosuresGroup: DisclosuresGroup,
3925  DisclosureType: DisclosureType,
3926  DisclosureEvent: DisclosureEvent,
3927  DisclosureSelector: DisclosureSelector,
3928  DisclosureEmission: DisclosureEmission,
3929  Collapse: Collapse,
3930  CollapseButton: CollapseButton,
3931  CollapsesGroup: CollapsesGroup,
3932  CollapseSelector: CollapseSelector,
3933  RootSelector: RootSelector,
3934  RootEmission: RootEmission,
3935  Equisized: Equisized,
3936  EquisizedEmission: EquisizedEmission,
3937  Toggle: Toggle,
3938  EquisizedsGroup: EquisizedsGroup,
3939  InjectSvg: InjectSvg,
3940  InjectSvgSelector: InjectSvgSelector,
3941  Artwork: Artwork,
3942  ArtworkSelector: ArtworkSelector,
3943  AssessFile: AssessFile,
3944  AssessDetail: AssessDetail,
3945  AssessEmission: AssessEmission,
3946  AssessSelector: AssessSelector,
3947  Ratio: Ratio,
3948  RatioSelector: RatioSelector,
3949  Placement: Placement,
3950  PlacementReferent: PlacementReferent,
3951  PlacementAlign: PlacementAlign,
3952  PlacementPosition: PlacementPosition,
3953  PlacementMode: PlacementMode
3954};
3955
3956api$1.internals.register(api$1.core.CollapseSelector.COLLAPSE, api$1.core.Collapse);
3957api$1.internals.register(api$1.core.InjectSvgSelector.INJECT_SVG, api$1.core.InjectSvg);
3958api$1.internals.register(api$1.core.RatioSelector.RATIO, api$1.core.Ratio);
3959api$1.internals.register(api$1.core.AssessSelector.ASSESS_FILE, api$1.core.AssessFile);
3960api$1.internals.register(api$1.core.AssessSelector.DETAIL, api$1.core.AssessDetail);
3961
3962const SchemeValue = {
3963  SYSTEM: 'system',
3964  LIGHT: 'light',
3965  DARK: 'dark'
3966};
3967
3968const SchemeAttribute = {
3969  THEME: api.internals.ns.attr('theme'),
3970  SCHEME: api.internals.ns.attr('scheme'),
3971  TRANSITION: api.internals.ns.attr('transition')
3972};
3973
3974const SchemeTheme = {
3975  LIGHT: 'light',
3976  DARK: 'dark'
3977};
3978
3979const SchemeEmission = {
3980  SCHEME: api.internals.ns.emission('scheme', 'scheme'),
3981  THEME: api.internals.ns.emission('scheme', 'theme'),
3982  ASK: api.internals.ns.emission('scheme', 'ask')
3983};
3984
3985const SchemeEvent = {
3986  SCHEME: api.internals.ns.event('scheme'),
3987  THEME: api.internals.ns.event('theme')
3988};
3989
3990class Scheme extends api.core.Instance {
3991  constructor () {
3992    super(false);
3993  }
3994
3995  static get instanceClassName () {
3996    return 'Scheme';
3997  }
3998
3999  init () {
4000    this.changing = this.change.bind(this);
4001
4002    if (this.hasAttribute(SchemeAttribute.TRANSITION)) {
4003      this.removeAttribute(SchemeAttribute.TRANSITION);
4004      this.request(this.restoreTransition.bind(this));
4005    }
4006
4007    const scheme = api.internals.support.supportLocalStorage() ? localStorage.getItem('scheme') : '';
4008    const schemeAttr = this.getAttribute(SchemeAttribute.SCHEME);
4009
4010    switch (scheme) {
4011      case SchemeValue.DARK:
4012      case SchemeValue.LIGHT:
4013      case SchemeValue.SYSTEM:
4014        this.scheme = scheme;
4015        break;
4016
4017      default:
4018        switch (schemeAttr) {
4019          case SchemeValue.DARK:
4020            this.scheme = SchemeValue.DARK;
4021            break;
4022
4023          case SchemeValue.LIGHT:
4024            this.scheme = SchemeValue.LIGHT;
4025            break;
4026
4027          default:
4028            this.scheme = SchemeValue.SYSTEM;
4029        }
4030    }
4031
4032    this.addAscent(SchemeEmission.ASK, this.ask.bind(this));
4033    this.addAscent(SchemeEmission.SCHEME, this.apply.bind(this));
4034  }
4035
4036  get proxy () {
4037    const scope = this;
4038
4039    const proxyAccessors = {
4040      get scheme () {
4041        return scope.scheme;
4042      },
4043      set scheme (value) {
4044        scope.scheme = value;
4045      }
4046    };
4047
4048    return api.internals.property.completeAssign(super.proxy, proxyAccessors);
4049  }
4050
4051  restoreTransition () {
4052    this.setAttribute(SchemeAttribute.TRANSITION, '');
4053  }
4054
4055  ask () {
4056    this.descend(SchemeEmission.SCHEME, this.scheme);
4057  }
4058
4059  apply (value) {
4060    this.scheme = value;
4061  }
4062
4063  get scheme () {
4064    return this._scheme;
4065  }
4066
4067  set scheme (value) {
4068    if (this._scheme === value) return;
4069    this._scheme = value;
4070    switch (value) {
4071      case SchemeValue.SYSTEM:
4072        this.listenPreferences();
4073        break;
4074
4075      case SchemeValue.DARK:
4076        this.unlistenPreferences();
4077        this.theme = SchemeTheme.DARK;
4078        break;
4079
4080      case SchemeValue.LIGHT:
4081        this.unlistenPreferences();
4082        this.theme = SchemeTheme.LIGHT;
4083        break;
4084
4085      default:
4086        this.scheme = SchemeValue.SYSTEM;
4087        return;
4088    }
4089
4090    this.descend(SchemeEmission.SCHEME, value);
4091    if (api.internals.support.supportLocalStorage()) {
4092      localStorage.setItem('scheme', value);
4093    }
4094    this.setAttribute(SchemeAttribute.SCHEME, value);
4095    this.dispatch(SchemeEvent.SCHEME, { scheme: this._scheme }, false);
4096  }
4097
4098  get theme () {
4099    return this._theme;
4100  }
4101
4102  set theme (value) {
4103    if (this._theme === value) return;
4104    switch (value) {
4105      case SchemeTheme.LIGHT:
4106      case SchemeTheme.DARK:
4107        this._theme = value;
4108        this.setAttribute(SchemeAttribute.THEME, value);
4109        this.descend(SchemeEmission.THEME, value);
4110        this.dispatch(SchemeEvent.THEME, { theme: this._theme }, false);
4111        document.documentElement.style.colorScheme = value === SchemeTheme.DARK ? 'dark' : '';
4112        break;
4113    }
4114  }
4115
4116  listenPreferences () {
4117    if (this.isListening) return;
4118    this.isListening = true;
4119    this.mediaQuery = window.matchMedia('(prefers-color-scheme: dark)');
4120    if (this.mediaQuery.addEventListener) this.mediaQuery.addEventListener('change', this.changing);
4121    this.change();
4122  }
4123
4124  unlistenPreferences () {
4125    if (!this.isListening) return;
4126    this.isListening = false;
4127    this.mediaQuery.removeEventListener('change', this.changing);
4128    this.mediaQuery = null;
4129  }
4130
4131  change () {
4132    if (!this.isListening) return;
4133    this.theme = this.mediaQuery.matches ? SchemeTheme.DARK : SchemeTheme.LIGHT;
4134  }
4135
4136  mutate (attributeNames) {
4137    if (attributeNames.indexOf(SchemeAttribute.SCHEME) > -1) this.scheme = this.getAttribute(SchemeAttribute.SCHEME);
4138    if (attributeNames.indexOf(SchemeAttribute.THEME) > -1) this.theme = this.getAttribute(SchemeAttribute.THEME);
4139  }
4140
4141  dispose () {
4142    this.unlistenPreferences();
4143  }
4144}
4145
4146const SchemeSelector = {
4147  SCHEME: `:root${api.internals.ns.attr.selector('theme')}, :root${api.internals.ns.attr.selector('scheme')}`,
4148  SWITCH_THEME: api.internals.ns.selector('switch-theme'),
4149  RADIO_BUTTONS: `input[name="${api.internals.ns('radios-theme')}"]`
4150};
4151
4152api.scheme = {
4153  Scheme: Scheme,
4154  SchemeValue: SchemeValue,
4155  SchemeSelector: SchemeSelector,
4156  SchemeAttribute: SchemeAttribute,
4157  SchemeEmission: SchemeEmission,
4158  SchemeTheme: SchemeTheme,
4159  SchemeEvent: SchemeEvent
4160};
4161
4162api.internals.register(api.scheme.SchemeSelector.SCHEME, api.scheme.Scheme);
4163
4164const ACCORDION = api.internals.ns.selector('accordion');
4165const COLLAPSE$2 = api.internals.ns.selector('collapse');
4166
4167const AccordionSelector = {
4168  GROUP: api.internals.ns.selector('accordions-group'),
4169  ACCORDION: ACCORDION,
4170  COLLAPSE: `${ACCORDION} > ${COLLAPSE$2}, ${ACCORDION} > *:not(${ACCORDION}):not(${COLLAPSE$2}) > ${COLLAPSE$2}, ${ACCORDION} > *:not(${ACCORDION}):not(${COLLAPSE$2}) > *:not(${ACCORDION}):not(${COLLAPSE$2}) > ${COLLAPSE$2}`,
4171  COLLAPSE_LEGACY: `${ACCORDION} ${COLLAPSE$2}`,
4172  BUTTON: `${ACCORDION}__btn`
4173};
4174
4175class Accordion extends api.core.Instance {
4176  static get instanceClassName () {
4177    return 'Accordion';
4178  }
4179
4180  get collapsePrimary () {
4181    const buttons = this.element.children.map(child => child.getInstance('CollapseButton')).filter(button => button !== null && button.hasClass(AccordionSelector.BUTTON));
4182    return buttons[0];
4183  }
4184}
4185
4186class AccordionsGroup extends api.core.CollapsesGroup {
4187  static get instanceClassName () {
4188    return 'AccordionsGroup';
4189  }
4190
4191  validate (member) {
4192    const match = member.node.matches(api.internals.legacy.isLegacy ? AccordionSelector.COLLAPSE_LEGACY : AccordionSelector.COLLAPSE);
4193    return super.validate(member) && match;
4194  }
4195}
4196
4197api.accordion = {
4198  Accordion: Accordion,
4199  AccordionSelector: AccordionSelector,
4200  AccordionsGroup: AccordionsGroup
4201};
4202
4203api.internals.register(api.accordion.AccordionSelector.GROUP, api.accordion.AccordionsGroup);
4204api.internals.register(api.accordion.AccordionSelector.ACCORDION, api.accordion.Accordion);
4205
4206const ButtonSelector = {
4207  EQUISIZED_BUTTON: `${api.internals.ns.selector('btns-group--equisized')} ${api.internals.ns.selector('btn')}`,
4208  EQUISIZED_GROUP: api.internals.ns.selector('btns-group--equisized')
4209};
4210
4211api.button = {
4212  ButtonSelector: ButtonSelector
4213};
4214
4215api.internals.register(api.button.ButtonSelector.EQUISIZED_BUTTON, api.core.Equisized);
4216api.internals.register(api.button.ButtonSelector.EQUISIZED_GROUP, api.core.EquisizedsGroup);
4217
4218class CardDownload extends api.core.Instance {
4219  static get instanceClassName () {
4220    return 'CardDownload';
4221  }
4222
4223  init () {
4224    this.addAscent(api.core.AssessEmission.UPDATE, details => {
4225      this.descend(api.core.AssessEmission.UPDATE, details);
4226    });
4227    this.addAscent(api.core.AssessEmission.ADDED, () => {
4228      this.descend(api.core.AssessEmission.ADDED);
4229    });
4230  }
4231}
4232
4233const CardSelector = {
4234  DOWNLOAD: api.internals.ns.selector('card--download'),
4235  DOWNLOAD_DETAIL: `${api.internals.ns.selector('card--download')} ${api.internals.ns.selector('card__end')} ${api.internals.ns.selector('card__detail')}`
4236};
4237
4238api.card = {
4239  CardSelector: CardSelector,
4240  CardDownload: CardDownload
4241};
4242
4243api.internals.register(api.card.CardSelector.DOWNLOAD, api.card.CardDownload);
4244api.internals.register(api.card.CardSelector.DOWNLOAD_DETAIL, api.core.AssessDetail);
4245
4246const CheckboxSelector = {
4247  INPUT: `${api.internals.ns.selector('checkbox-group')} input[type="checkbox"]`
4248};
4249
4250const CheckboxEmission = {
4251  CHANGE: api.internals.ns.emission('checkbox', 'change'),
4252  RETRIEVE: api.internals.ns.emission('checkbox', 'retrieve')
4253};
4254
4255class CheckboxInput extends api.core.Instance {
4256  static get instanceClassName () {
4257    return 'CheckboxInput';
4258  }
4259
4260  constructor () {
4261    super();
4262    this._handlingChange = this.handleChange.bind(this);
4263  }
4264
4265  init () {
4266    this.node.addEventListener('change', this._handlingChange);
4267    this.addDescent(CheckboxEmission.RETRIEVE, this._handlingChange);
4268    this.handleChange();
4269  }
4270
4271  get isChecked () {
4272    return this.node.checked;
4273  }
4274
4275  handleChange () {
4276    this.ascend(CheckboxEmission.CHANGE, this.node);
4277  }
4278}
4279
4280api.checkbox = {
4281  CheckboxSelector: CheckboxSelector,
4282  CheckboxEmission: CheckboxEmission,
4283  CheckboxInput: CheckboxInput
4284};
4285
4286api.internals.register(api.checkbox.CheckboxSelector.INPUT, api.checkbox.CheckboxInput);
4287
4288const SegmentedSelector = {
4289  SEGMENTED: api.internals.ns.selector('segmented'),
4290  SEGMENTED_ELEMENTS: api.internals.ns.selector('segmented__elements'),
4291  SEGMENTED_ELEMENT: api.internals.ns.selector('segmented__element input'),
4292  SEGMENTED_LEGEND: api.internals.ns.selector('segmented__legend')
4293};
4294
4295const SegmentedEmission = {
4296  ADDED: api.internals.ns.emission('segmented', 'added'),
4297  REMOVED: api.internals.ns.emission('segmented', 'removed')
4298};
4299
4300class Segmented extends api.core.Instance {
4301  static get instanceClassName () {
4302    return 'Segmented';
4303  }
4304
4305  init () {
4306    this.elements = this.node.querySelector(SegmentedSelector.SEGMENTED_ELEMENTS);
4307    this.legend = this.node.querySelector(SegmentedSelector.SEGMENTED_LEGEND);
4308    this.addAscent(SegmentedEmission.ADDED, this.resize.bind(this));
4309    this.addAscent(SegmentedEmission.REMOVED, this.resize.bind(this));
4310    this._isLegendInline = this.legend && this.legend.classList.contains(`${api.prefix}-segmented__legend--inline`);
4311    this.isResizing = true;
4312  }
4313
4314  resize () {
4315    const SEGMENTED_VERTICAL = `${api.prefix}-segmented--vertical`;
4316    const LEGEND_INLINE = `${api.prefix}-segmented__legend--inline`;
4317    const gapOffset = 16;
4318
4319    this.removeClass(SEGMENTED_VERTICAL);
4320
4321    if (this._isLegendInline) {
4322      this.legend.classList.add(LEGEND_INLINE);
4323
4324      if (this.node.offsetWidth > this.node.parentNode.offsetWidth || (this.elements.scrollWidth + this.legend.offsetWidth + gapOffset) > this.node.parentNode.offsetWidth) {
4325        this.legend.classList.remove(LEGEND_INLINE);
4326      }
4327    }
4328
4329    if (this.elements.offsetWidth > this.node.parentNode.offsetWidth || this.elements.scrollWidth > this.node.parentNode.offsetWidth) {
4330      this.addClass(SEGMENTED_VERTICAL);
4331    } else {
4332      this.removeClass(SEGMENTED_VERTICAL);
4333    }
4334  }
4335}
4336
4337class SegmentedElement extends api.core.Instance {
4338  static get instanceClassName () {
4339    return 'SegmentedElement';
4340  }
4341
4342  init () {
4343    this.ascend(SegmentedEmission.ADDED);
4344  }
4345
4346  dispose () {
4347    this.ascend(SegmentedEmission.REMOVED);
4348  }
4349}
4350
4351api.segmented = {
4352  SegmentedSelector: SegmentedSelector,
4353  SegmentedEmission: SegmentedEmission,
4354  SegmentedElement: SegmentedElement,
4355  Segmented: Segmented
4356};
4357
4358api.internals.register(api.segmented.SegmentedSelector.SEGMENTED, api.segmented.Segmented);
4359api.internals.register(api.segmented.SegmentedSelector.SEGMENTED_ELEMENT, api.segmented.SegmentedElement);
4360
4361const BreadcrumbSelector = {
4362  BREADCRUMB: api.internals.ns.selector('breadcrumb'),
4363  BUTTON: api.internals.ns.selector('breadcrumb__button')
4364};
4365
4366class Breadcrumb extends api.core.Instance {
4367  constructor () {
4368    super();
4369    this.count = 0;
4370    this.focusing = this.focus.bind(this);
4371  }
4372
4373  static get instanceClassName () {
4374    return 'Breadcrumb';
4375  }
4376
4377  init () {
4378    this.getCollapse();
4379    this.isResizing = true;
4380  }
4381
4382  get proxy () {
4383    const scope = this;
4384    return Object.assign(super.proxy, {
4385      focus: scope.focus.bind(scope),
4386      disclose: scope.collapse.disclose.bind(scope.collapse)
4387    });
4388  }
4389
4390  getCollapse () {
4391    const collapse = this.collapse;
4392    if (collapse) {
4393      collapse.listen(api.core.DisclosureEvent.DISCLOSE, this.focusing);
4394    } else {
4395      this.addAscent(api.core.DisclosureEmission.ADDED, this.getCollapse.bind(this));
4396    }
4397  }
4398
4399  resize () {
4400    const collapse = this.collapse;
4401    const links = this.links;
4402    if (!collapse || !links.length) return;
4403
4404    if (this.isBreakpoint(api.core.Breakpoints.MD)) {
4405      if (collapse.buttonHasFocus) links[0].focus();
4406    } else {
4407      if (links.indexOf(document.activeElement) > -1) collapse.focus();
4408    }
4409  }
4410
4411  get links () {
4412    return [...this.querySelectorAll('a[href]')];
4413  }
4414
4415  get collapse () {
4416    return this.element.getDescendantInstances(api.core.Collapse.instanceClassName, null, true)[0];
4417  }
4418
4419  focus () {
4420    this.count = 0;
4421    this._focus();
4422  }
4423
4424  _focus () {
4425    const link = this.links[0];
4426    if (!link) return;
4427    link.focus();
4428    this.request(this.verify.bind(this));
4429  }
4430
4431  verify () {
4432    this.count++;
4433    if (this.count > 100) return;
4434    const link = this.links[0];
4435    if (!link) return;
4436    if (document.activeElement !== link) this._focus();
4437  }
4438
4439  get collapsePrimary () {
4440    const buttons = this.element.children.map(child => child.getInstance('CollapseButton')).filter(button => button !== null && button.hasClass(BreadcrumbSelector.BUTTON));
4441    return buttons[0];
4442  }
4443}
4444
4445api.breadcrumb = {
4446  BreadcrumbSelector: BreadcrumbSelector,
4447  Breadcrumb: Breadcrumb
4448};
4449
4450api.internals.register(api.breadcrumb.BreadcrumbSelector.BREADCRUMB, api.breadcrumb.Breadcrumb);
4451
4452const TooltipSelector = {
4453  TOOLTIP: api.internals.ns.selector('tooltip'),
4454  SHOWN: api.internals.ns.selector('tooltip--shown'),
4455  HIDDING: api.internals.ns.selector('tooltip--hidding'),
4456  BUTTON: api.internals.ns.selector('btn--tooltip')
4457};
4458
4459const TooltipReferentState = {
4460  FOCUS: 1 << 0,
4461  HOVER: 1 << 1
4462};
4463
4464class TooltipReferent extends api.core.PlacementReferent {
4465  constructor () {
4466    super();
4467    this._state = 0;
4468    this._isShownOnInteraction = null;
4469  }
4470
4471  static get instanceClassName () {
4472    return 'TooltipReferent';
4473  }
4474
4475  init () {
4476    super.init();
4477    this.listen('focusin', this.focusIn.bind(this));
4478    this.listen('focusout', (e) => this.focusOut(e));
4479    if (!this.matches(TooltipSelector.BUTTON)) {
4480      const mouseover = this.mouseover.bind(this);
4481      this.listen('mouseover', mouseover);
4482      this.placement.listen('mouseover', mouseover);
4483      const mouseout = this.mouseout.bind(this);
4484      this.listen('mouseout', mouseout);
4485      this.placement.listen('mouseout', mouseout);
4486    }
4487    this.addEmission(api.core.RootEmission.KEYDOWN, this._keydown.bind(this));
4488    this.listen('pointerdown', this._interactionStart.bind(this));
4489    this.listen('mousedown', this._interactionStart.bind(this));
4490    this.listen('touchstart', this._interactionStart.bind(this));
4491    this.listen('click', this._click.bind(this));
4492    this.addEmission(api.core.RootEmission.CLICK, this._clickOut.bind(this));
4493    this.addEmission(api.core.RootEmission.INTERACTION, this._clickOut.bind(this)); // IOS
4494  }
4495
4496  _interactionStart () {
4497    this._isShownOnInteraction = this.state > 0;
4498  }
4499
4500  _click () {
4501    const isButtonTooltip = this.matches(TooltipSelector.BUTTON);
4502
4503    if (isButtonTooltip && (this._isShownOnInteraction === true || (this._isShownOnInteraction === null && this.state > 0))) {
4504      this.close();
4505      this._isShownOnInteraction = null;
4506      return;
4507    }
4508
4509    if (!isButtonTooltip) {
4510      this.focusOut();
4511      this._isShownOnInteraction = null;
4512      return;
4513    }
4514
4515    this.focusIn();
4516    this._isShownOnInteraction = null;
4517  }
4518
4519  _clickOut (target) {
4520    if (this.node.contains(target) || this.placement.node.contains(target)) return;
4521    this.focusOut();
4522  }
4523
4524  _keydown (keyCode) {
4525    switch (keyCode) {
4526      case api.core.KeyCodes.ESCAPE:
4527        this.close();
4528        break;
4529
4530      case api.core.KeyCodes.TAB:
4531        this.request(this._tab.bind(this));
4532        break;
4533    }
4534  }
4535
4536  _tab () {
4537    if (this.node.contains(document.activeElement) || this.placement.node.contains(document.activeElement)) return;
4538    this.focusOut();
4539  }
4540
4541  close () {
4542    this.state = 0;
4543  }
4544
4545  get state () {
4546    return this._state;
4547  }
4548
4549  set state (value) {
4550    if (this._state === value) return;
4551    this.isShown = value > 0;
4552    this._state = value;
4553  }
4554
4555  focusIn () {
4556    this.state |= TooltipReferentState.FOCUS;
4557  }
4558
4559  focusOut (event = null) {
4560    const relatedTarget = event ? event.relatedTarget : null;
4561    if (relatedTarget && (this.node.contains(relatedTarget) || this.placement.node.contains(relatedTarget))) return;
4562    this.state &= ~TooltipReferentState.FOCUS;
4563  }
4564
4565  mouseover () {
4566    this.state |= TooltipReferentState.HOVER;
4567  }
4568
4569  mouseout () {
4570    this.state &= ~TooltipReferentState.HOVER;
4571  }
4572}
4573
4574const TooltipEvent = {
4575  SHOW: ns.event('show'),
4576  HIDE: ns.event('hide')
4577};
4578
4579const TooltipState = {
4580  HIDDEN: 'hidden',
4581  SHOWN: 'shown',
4582  HIDING: 'hiding'
4583};
4584
4585class Tooltip extends api.core.Placement {
4586  constructor () {
4587    super(api.core.PlacementMode.AUTO, [api.core.PlacementPosition.TOP, api.core.PlacementPosition.BOTTOM], [api.core.PlacementAlign.CENTER, api.core.PlacementAlign.START, api.core.PlacementAlign.END]);
4588    this.modifier = '';
4589    this._state = TooltipState.HIDDEN;
4590  }
4591
4592  static get instanceClassName () {
4593    return 'Tooltip';
4594  }
4595
4596  init () {
4597    super.init();
4598    this.register(`[aria-describedby="${this.id}"]`, TooltipReferent);
4599    this.listen('transitionend', this.transitionEnd.bind(this));
4600    this.setAttribute('tabindex', '-1');
4601  }
4602
4603  transitionEnd () {
4604    if (this._state === TooltipState.HIDING) {
4605      this.removeClass(TooltipSelector.SHOWN);
4606      this.removeClass(TooltipSelector.HIDDING);
4607      this._state = TooltipState.HIDDEN;
4608      this.isShown = false;
vendor: 48,999 bytes, lines 4609-6413
4609    }
4610  }
4611
4612  get isShown () {
4613    return super.isShown;
4614  }
4615
4616  set isShown (value) {
4617    if (!this.isEnabled) return;
4618    switch (true) {
4619      case value:
4620        this._state = TooltipState.SHOWN;
4621        this.addClass(TooltipSelector.SHOWN);
4622        this.removeClass(TooltipSelector.HIDDING);
4623        this.dispatch(TooltipEvent.SHOW);
4624        super.isShown = true;
4625        break;
4626
4627      case this.isShown && !value && this._state === TooltipState.SHOWN:
4628        this._state = TooltipState.HIDING;
4629        this.addClass(TooltipSelector.HIDDING);
4630        break;
4631
4632      case this.isShown && !value && this._state === TooltipState.HIDDEN:
4633        this.dispatch(TooltipEvent.HIDE);
4634        this.removeClass(TooltipSelector.HIDDING);
4635        super.isShown = false;
4636        break;
4637    }
4638  }
4639
4640  render () {
4641    super.render();
4642    this.rect = this.getRect();
4643    let x = this.referentRect.center - this.rect.center;
4644    const limit = this.rect.width * 0.5 - 8;
4645    if (x < -limit) x = -limit;
4646    if (x > limit) x = limit;
4647    this.setProperty('--arrow-x', `${x.toFixed(2)}px`);
4648  }
4649}
4650
4651api.tooltip = {
4652  Tooltip: Tooltip,
4653  TooltipSelector: TooltipSelector,
4654  TooltipEvent: TooltipEvent
4655};
4656
4657api.internals.register(api.tooltip.TooltipSelector.TOOLTIP, api.tooltip.Tooltip);
4658
4659class ToggleInput extends api.core.Instance {
4660  static get instanceClassName () {
4661    return 'ToggleInput';
4662  }
4663
4664  get isChecked () {
4665    return this.node.checked;
4666  }
4667}
4668
4669class ToggleStatusLabel extends api.core.Instance {
4670  static get instanceClassName () {
4671    return 'ToggleStatusLabel';
4672  }
4673
4674  init () {
4675    this.register(`input[id="${this.getAttribute('for')}"]`, ToggleInput);
4676    this.update();
4677    this.isSwappingFont = true;
4678  }
4679
4680  get proxy () {
4681    const scope = this;
4682    return Object.assign(super.proxy, {
4683      update: scope.update.bind(scope)
4684    });
4685  }
4686
4687  get input () {
4688    return this.getRegisteredInstances('ToggleInput')[0];
4689  }
4690
4691  update () {
4692    this.node.style.removeProperty('--toggle-status-width');
4693    const checked = this.input.isChecked;
4694
4695    const style = getComputedStyle(this.node, ':before');
4696    let maxWidth = parseFloat(style.width);
4697    this.input.node.checked = !checked;
4698
4699    const style2 = getComputedStyle(this.node, ':before');
4700    const width = parseFloat(style2.width);
4701    if (width > maxWidth) maxWidth = width;
4702    this.input.node.checked = checked;
4703
4704    this.node.style.setProperty('--toggle-status-width', (maxWidth / 16) + 'rem');
4705  }
4706
4707  swapFont (families) {
4708    this.update();
4709  }
4710}
4711
4712const ToggleSelector = {
4713  STATUS_LABEL: `${api.internals.ns.selector('toggle__label')}${api.internals.ns.attr.selector('checked-label')}${api.internals.ns.attr.selector('unchecked-label')}`
4714};
4715
4716// import { ToggleInput } from './script/toggle/toggle-input.js';
4717
4718api.toggle = {
4719  ToggleStatusLabel: ToggleStatusLabel,
4720  ToggleSelector: ToggleSelector
4721};
4722
4723api.internals.register(api.toggle.ToggleSelector.STATUS_LABEL, api.toggle.ToggleStatusLabel);
4724
4725const ITEM$1 = api.internals.ns.selector('sidemenu__item');
4726const COLLAPSE$1 = api.internals.ns.selector('collapse');
4727
4728const SidemenuSelector = {
4729  LIST: api.internals.ns.selector('sidemenu__list'),
4730  COLLAPSE: `${ITEM$1} > ${COLLAPSE$1}, ${ITEM$1} > *:not(${ITEM$1}):not(${COLLAPSE$1}) > ${COLLAPSE$1}, ${ITEM$1} > *:not(${ITEM$1}):not(${COLLAPSE$1}) > *:not(${ITEM$1}):not(${COLLAPSE$1}) > ${COLLAPSE$1}`,
4731  COLLAPSE_LEGACY: `${ITEM$1} ${COLLAPSE$1}`,
4732  ITEM: api.internals.ns.selector('sidemenu__item'),
4733  BUTTON: api.internals.ns.selector('sidemenu__btn')
4734};
4735
4736class SidemenuList extends api.core.CollapsesGroup {
4737  static get instanceClassName () {
4738    return 'SidemenuList';
4739  }
4740
4741  validate (member) {
4742    return super.validate(member) && member.node.matches(api.internals.legacy.isLegacy ? SidemenuSelector.COLLAPSE_LEGACY : SidemenuSelector.COLLAPSE);
4743  }
4744}
4745
4746class SidemenuItem extends api.core.Instance {
4747  static get instanceClassName () {
4748    return 'SidemenuItem';
4749  }
4750
4751  get collapsePrimary () {
4752    const buttons = this.element.children.map(child => child.getInstance('CollapseButton')).filter(button => button !== null && button.hasClass(SidemenuSelector.BUTTON));
4753    return buttons[0];
4754  }
4755}
4756
4757api.sidemenu = {
4758  SidemenuList: SidemenuList,
4759  SidemenuItem: SidemenuItem,
4760  SidemenuSelector: SidemenuSelector
4761};
4762
4763api.internals.register(api.sidemenu.SidemenuSelector.LIST, api.sidemenu.SidemenuList);
4764api.internals.register(api.sidemenu.SidemenuSelector.ITEM, api.sidemenu.SidemenuItem);
4765
4766const ModalSelector = {
4767  MODAL: api.internals.ns.selector('modal'),
4768  FOOTER: api.internals.ns.selector('modal__footer'),
4769  SCROLL_DIVIDER: api.internals.ns.selector('scroll-divider'),
4770  BODY: api.internals.ns.selector('modal__body'),
4771  TITLE: api.internals.ns.selector('modal__title')
4772};
4773
4774class ModalButton extends api.core.DisclosureButton {
4775  constructor () {
4776    super(api.core.DisclosureType.OPENED);
4777  }
4778
4779  static get instanceClassName () {
4780    return 'ModalButton';
4781  }
4782}
4783
4784const ModalAttribute = {
4785  CONCEALING_BACKDROP: api.internals.ns.attr('concealing-backdrop')
4786};
4787
4788class Modal extends api.core.Disclosure {
4789  constructor () {
4790    super(api.core.DisclosureType.OPENED, ModalSelector.MODAL, ModalButton, 'ModalsGroup');
4791    this._isDecorated = false;
4792    this.scrolling = this.resize.bind(this, false);
4793    this.resizing = this.resize.bind(this, true);
4794  }
4795
4796  static get instanceClassName () {
4797    return 'Modal';
4798  }
4799
4800  init () {
4801    super.init();
4802    this._isDialog = this.node.tagName === 'DIALOG';
4803    this.listenClick();
4804    this.addEmission(api.core.RootEmission.KEYDOWN, this._keydown.bind(this));
4805  }
4806
4807  _keydown (keyCode) {
4808    switch (keyCode) {
4809      case api.core.KeyCodes.ESCAPE:
4810        this._escape();
4811        break;
4812    }
4813  }
4814
4815  // TODO v2 : passer les tagName d'action en constante
4816  _escape () {
4817    const tagName = document.activeElement ? document.activeElement.tagName : undefined;
4818    const isTooltipReferent = document.activeElement ? document.activeElement.hasAttribute('data-fr-js-tooltip-referent') : false;
4819
4820    if (isTooltipReferent) return;
4821
4822    switch (tagName) {
4823      case 'INPUT':
4824      case 'LABEL':
4825      case 'TEXTAREA':
4826      case 'SELECT':
4827      case 'AUDIO':
4828      case 'VIDEO':
4829        break;
4830
4831      default:
4832        if (this.isDisclosed) {
4833          this.conceal();
4834          this.focus();
4835        }
4836    }
4837  }
4838
4839  retrieved () {
4840    this._ensureAccessibleName();
4841  }
4842
4843  get body () {
4844    return this.element.getDescendantInstances('ModalBody', 'Modal')[0];
4845  }
4846
4847  handleClick (e) {
4848    if (e.target === this.node && this.getAttribute(ModalAttribute.CONCEALING_BACKDROP) !== 'false') this.conceal();
4849  }
4850
4851  disclose (withhold) {
4852    if (!super.disclose(withhold)) return false;
4853    if (this.body) {
4854      this.body.isResizing = true;
4855      this.body.resize();
4856    }
4857    this.isScrollLocked = true;
4858    this.setAttribute('aria-modal', 'true');
4859    this.setAttribute('open', 'true');
4860    if (!this._isDialog) {
4861      this.decorateDialog();
4862    }
4863    return true;
4864  }
4865
4866  conceal (withhold, preventFocus) {
4867    if (!super.conceal(withhold, preventFocus)) return false;
4868    this.isScrollLocked = false;
4869    this.removeAttribute('aria-modal');
4870    this.removeAttribute('open');
4871    if (this.body) this.body.isResizing = false;
4872    if (!this._isDialog) {
4873      this.stripDialog();
4874    }
4875    return true;
4876  }
4877
4878  get isDialog () {
4879    return this._isDialog;
4880  }
4881
4882  set isDialog (value) {
4883    this._isDialog = value;
4884  }
4885
4886  get isActive () {
4887    return super.isActive;
4888  }
4889
4890  set isActive (value) {
4891    super.isActive = value;
4892    if (value) this._ensureAccessibleName();
4893  }
4894
4895  decorateDialog () {
4896    if (this._isDecorated) return;
4897    this._isDecorated = true;
4898    this._hasDialogRole = this.getAttribute('role') === 'dialog';
4899    if (!this._hasDialogRole) this.setAttribute('role', 'dialog');
4900  }
4901
4902  stripDialog () {
4903    if (!this._isDecorated) return;
4904    this._isDecorated = false;
4905    if (!this._hasDialogRole) this.removeAttribute('role');
4906  }
4907
4908  _setAccessibleName (node, append) {
4909    const id = this.retrieveNodeId(node, append);
4910    this.warn(`add reference to ${append} for accessible name (aria-labelledby)`);
4911    this.setAttribute('aria-labelledby', id);
4912  }
4913
4914  _ensureAccessibleName () {
4915    if (!this.isActive || !this.isEnabled || (this.isEnabled && (this.hasAttribute('aria-labelledby') || this.hasAttribute('aria-label')))) return;
4916    this.warn('missing accessible name');
4917    const title = this.node.querySelector(ModalSelector.TITLE);
4918    const primary = this.primaryButtons[0];
4919
4920    switch (true) {
4921      case title !== null:
4922        this._setAccessibleName(title, 'title');
4923        break;
4924
4925      case primary !== undefined:
4926        this.warn('missing required title, fallback to primary button');
4927        this._setAccessibleName(primary, 'primary');
4928        break;
4929    }
4930  }
4931}
4932
4933class ModalFooter extends api.core.Instance {
4934  static get instanceClassName () {
4935    return 'ModalFooter';
4936  }
4937
4938  init () {
4939    this.isResizing = true;
4940  }
4941
4942  resize () {
4943    this.adjust();
4944  }
4945
4946  adjust () {
4947    const height = this.getRect().height;
4948    this.node.parentNode.style.setProperty('--modal-scroll-padding-bottom', `${height}px`);
4949  }
4950}
4951
4952const unordereds = [
4953  '[tabindex="0"]',
4954  'a[href]',
4955  'button:not([disabled])',
4956  'input:not([disabled])',
4957  'select:not([disabled])',
4958  'textarea:not([disabled])',
4959  'audio[controls]',
4960  'video[controls]',
4961  '[contenteditable]:not([contenteditable="false"])',
4962  'details>summary:first-of-type',
4963  'details',
4964  'iframe'
4965];
4966
4967const UNORDEREDS = unordereds.join();
4968
4969const ordereds = [
4970  '[tabindex]:not([tabindex="-1"]):not([tabindex="0"])'
4971];
4972
4973const ORDEREDS = ordereds.join();
4974
4975const isFocusable = (element, container) => {
4976  if (!(element instanceof Element)) return false;
4977  const windowElement = window.frameElement ? window.frameElement.contentWindow : window;
4978  const style = windowElement.getComputedStyle(element);
4979  if (!style) return false;
4980  if (style.visibility === 'hidden') return false;
4981  if (container === undefined) container = element;
4982
4983  while (container.contains(element)) {
4984    if (style.display === 'none') return false;
4985    element = element.parentElement;
4986  }
4987
4988  return true;
4989};
4990
4991class FocusTrap {
4992  constructor (onTrap, onUntrap) {
4993    this.element = null;
4994    this.activeElement = null;
4995    this.onTrap = onTrap;
4996    this.onUntrap = onUntrap;
4997    this.waiting = this.wait.bind(this);
4998    this.handling = this.handle.bind(this);
4999    this.focusing = this.maintainFocus.bind(this);
5000    this.current = null;
5001    this.window = window.frameElement ? window.frameElement.contentWindow : window;
5002  }
5003
5004  get trapped () { return this.element !== null; }
5005
5006  trap (element) {
5007    if (this.trapped) this.untrap();
5008
5009    this.element = element;
5010    this.isTrapping = true;
5011    this.wait();
5012
5013    if (this.onTrap) this.onTrap();
5014  }
5015
5016  wait () {
5017    if (!isFocusable(this.element)) {
5018      this.window.requestAnimationFrame(this.waiting);
5019      return;
5020    }
5021
5022    this.trapping();
5023  }
5024
5025  trapping () {
5026    if (!this.isTrapping) return;
5027    this.isTrapping = false;
5028    const focusables = this.focusables;
5029    if (focusables.length && focusables.indexOf(this.window.document.activeElement) === -1) focusables[0].focus();
5030    this.element.setAttribute('aria-modal', true);
5031    this.window.addEventListener('keydown', this.handling);
5032    this.window.document.body.addEventListener('focus', this.focusing, true);
5033  }
5034
5035  stun (node) {
5036    for (const child of node.children) {
5037      if (child === this.element) continue;
5038      if (child.contains(this.element)) {
5039        this.stun(child);
5040        continue;
5041      }
5042      this.stunneds.push(new Stunned(child));
5043    }
5044  }
5045
5046  maintainFocus (event) {
5047    if (!this.element.contains(event.target)) {
5048      const focusables = this.focusables;
5049      if (focusables.length === 0) return;
5050      const first = focusables[0];
5051      event.preventDefault();
5052      first.focus();
5053    }
5054  }
5055
5056  handle (e) {
5057    if (e.keyCode !== 9) return;
5058
5059    const focusables = this.focusables;
5060    if (focusables.length === 0) return;
5061
5062    const first = focusables[0];
5063    const last = focusables[focusables.length - 1];
5064
5065    const index = focusables.indexOf(this.window.document.activeElement);
5066
5067    if (e.shiftKey) {
5068      if (!this.element.contains(this.window.document.activeElement) || index < 1) {
5069        e.preventDefault();
5070        last.focus();
5071      } else if (this.window.document.activeElement.tabIndex > 0 || focusables[index - 1].tabIndex > 0) {
5072        e.preventDefault();
5073        focusables[index - 1].focus();
5074      }
5075    } else {
5076      if (!this.element.contains(this.window.document.activeElement) || index === focusables.length - 1 || index === -1) {
5077        e.preventDefault();
5078        first.focus();
5079      } else if (this.window.document.activeElement.tabIndex > 0) {
5080        e.preventDefault();
5081        focusables[index + 1].focus();
5082      }
5083    }
5084  }
5085
5086  get focusables () {
5087    let unordereds = api.internals.dom.querySelectorAllArray(this.element, UNORDEREDS);
5088
5089    /**
5090     *  filtrage des radiobutttons de même name (la navigations d'un groupe de radio se fait à la flèche et non pas au tab
5091     **/
5092    const radios = api.internals.dom.querySelectorAllArray(this.window.document.documentElement, 'input[type="radio"]');
5093
5094    if (radios.length) {
5095      const groups = {};
5096
5097      for (const radio of radios) {
5098        const name = radio.getAttribute('name');
5099        if (groups[name] === undefined) groups[name] = new RadioButtonGroup(name);
5100        groups[name].push(radio);
5101      }
5102
5103      unordereds = unordereds.filter((unordered) => {
5104        if (unordered.tagName.toLowerCase() !== 'input' || (unordered.getAttribute('type') && unordered.getAttribute('type').toLowerCase() !== 'radio')) return true;
5105        const name = unordered.getAttribute('name');
5106        return groups[name].keep(unordered);
5107      });
5108    }
5109
5110    const ordereds = api.internals.dom.querySelectorAllArray(this.element, ORDEREDS);
5111
5112    ordereds.sort((a, b) => a.tabIndex - b.tabIndex);
5113
5114    const noDuplicates = unordereds.filter((element) => ordereds.indexOf(element) === -1);
5115    const concateneds = ordereds.concat(noDuplicates);
5116    return concateneds.filter((element) => element.tabIndex !== '-1' && isFocusable(element, this.element));
5117  }
5118
5119  untrap () {
5120    if (!this.trapped) return;
5121    this.isTrapping = false;
5122
5123    this.element.removeAttribute('aria-modal');
5124    this.window.removeEventListener('keydown', this.handling);
5125    this.window.document.body.removeEventListener('focus', this.focusing, true);
5126
5127    this.element = null;
5128
5129    if (this.onUntrap) this.onUntrap();
5130  }
5131
5132  dispose () {
5133    this.untrap();
5134  }
5135}
5136
5137class Stunned {
5138  constructor (element) {
5139    this.element = element;
5140    // this.hidden = element.getAttribute('aria-hidden');
5141    this.inert = element.getAttribute('inert');
5142
5143    // this.element.setAttribute('aria-hidden', true);
5144    this.element.setAttribute('inert', '');
5145  }
5146
5147  unstun () {
5148    /*
5149    if (this.hidden === null) this.element.removeAttribute('aria-hidden');
5150    else this.element.setAttribute('aria-hidden', this.hidden);
5151     */
5152
5153    if (this.inert === null) this.element.removeAttribute('inert');
5154    else this.element.setAttribute('inert', this.inert);
5155  }
5156}
5157
5158class RadioButtonGroup {
5159  constructor (name) {
5160    this.name = name;
5161    this.buttons = [];
5162  }
5163
5164  push (button) {
5165    this.buttons.push(button);
5166    const windowElement = window.frameElement ? window.frameElement.contentWindow : window;
5167    if (button === windowElement.document.activeElement || button.checked || this.selected === undefined) this.selected = button;
5168  }
5169
5170  keep (button) {
5171    return this.selected === button;
5172  }
5173}
5174
5175class ModalsGroup extends api.core.DisclosuresGroup {
5176  constructor () {
5177    super('Modal', false);
5178    this.focusTrap = new FocusTrap();
5179  }
5180
5181  static get instanceClassName () {
5182    return 'ModalsGroup';
5183  }
5184
5185  apply (value, initial) {
5186    super.apply(value, initial);
5187    if (this.current === null) this.focusTrap.untrap();
5188    else this.focusTrap.trap(this.current.node);
5189  }
5190}
5191
5192const OFFSET = 32; // 32px => 8v => 2rem
5193
5194class ModalBody extends api.core.Instance {
5195  static get instanceClassName () {
5196    return 'ModalBody';
5197  }
5198
5199  init () {
5200    this.listen('scroll', this.divide.bind(this));
5201  }
5202
5203  divide () {
5204    if (this.node.scrollHeight > this.node.clientHeight) {
5205      if (this.node.offsetHeight + this.node.scrollTop >= this.node.scrollHeight) {
5206        this.removeClass(ModalSelector.SCROLL_DIVIDER);
5207      } else {
5208        this.addClass(ModalSelector.SCROLL_DIVIDER);
5209      }
5210    } else {
5211      this.removeClass(ModalSelector.SCROLL_DIVIDER);
5212    }
5213  }
5214
5215  resize () {
5216    this.adjust();
5217    this.request(this.adjust.bind(this));
5218  }
5219
5220  adjust () {
5221    const iframe = window.frameElement;
5222    const windowElement = iframe ? iframe.contentWindow : window;
5223    const offset = OFFSET * (this.isBreakpoint(api.core.Breakpoints.MD) ? 2 : 1);
5224    if (this.isLegacy) this.style.maxHeight = `${windowElement.innerHeight - offset}px`;
5225    else this.style.setProperty('--modal-max-height', `${windowElement.innerHeight - offset}px`);
5226    this.divide();
5227  }
5228}
5229
5230api.modal = {
5231  Modal: Modal,
5232  ModalButton: ModalButton,
5233  ModalFooter: ModalFooter,
5234  ModalBody: ModalBody,
5235  ModalsGroup: ModalsGroup,
5236  ModalSelector: ModalSelector
5237};
5238
5239api.internals.register(api.modal.ModalSelector.MODAL, api.modal.Modal);
5240api.internals.register(api.modal.ModalSelector.BODY, api.modal.ModalBody);
5241api.internals.register(api.modal.ModalSelector.FOOTER, api.modal.ModalFooter);
5242api.internals.register(api.core.RootSelector.ROOT, api.modal.ModalsGroup);
5243
5244const PasswordEmission = {
5245  TOGGLE: api.internals.ns.emission('password', 'toggle'),
5246  ADJUST: api.internals.ns.emission('password', 'adjust')
5247};
5248
5249class PasswordToggle extends api.core.Instance {
5250  static get instanceClassName () {
5251    return 'PasswordToggle';
5252  }
5253
5254  init () {
5255    this.listenClick();
5256    this.ascend(PasswordEmission.ADJUST, this.width);
5257    this.isSwappingFont = true;
5258    this._isChecked = this.isChecked;
5259  }
5260
5261  get width () {
5262    const style = getComputedStyle(this.node.parentNode);
5263    return parseInt(style.width);
5264  }
5265
5266  get isChecked () {
5267    return this.node.checked;
5268  }
5269
5270  set isChecked (value) {
5271    this._isChecked = value;
5272    this.ascend(PasswordEmission.TOGGLE, value);
5273  }
5274
5275  handleClick () {
5276    this.isChecked = !this._isChecked;
5277  }
5278
5279  swapFont (families) {
5280    this.ascend(PasswordEmission.ADJUST, this.width);
5281  }
5282}
5283
5284class Password extends api.core.Instance {
5285  static get instanceClassName () {
5286    return 'Password';
5287  }
5288
5289  init () {
5290    this.addAscent(PasswordEmission.TOGGLE, this.toggle.bind(this));
5291    this.addAscent(PasswordEmission.ADJUST, this.adjust.bind(this));
5292  }
5293
5294  toggle (value) {
5295    this.descend(PasswordEmission.TOGGLE, value);
5296  }
5297
5298  adjust (value) {
5299    this.descend(PasswordEmission.ADJUST, value);
5300  }
5301}
5302
5303const PasswordSelector = {
5304  PASSWORD: api.internals.ns.selector('password'),
5305  INPUT: api.internals.ns.selector('password__input'),
5306  LABEL: api.internals.ns.selector('password__label'),
5307  TOOGLE: `${api.internals.ns.selector('password__checkbox')} input[type="checkbox"]`
5308};
5309
5310class PasswordInput extends api.core.Instance {
5311  static get instanceClassName () {
5312    return 'PasswordInput';
5313  }
5314
5315  init () {
5316    this.addDescent(PasswordEmission.TOGGLE, this.toggle.bind(this));
5317    this._isRevealed = this.hasAttribute('type') === 'password';
5318    this.listen('keydown', this.capslock.bind(this)); // for capslock enabled
5319    this.listen('keyup', this.capslock.bind(this)); // for capslock desabled
5320  }
5321
5322  toggle (value) {
5323    this.isRevealed = value;
5324    this.setAttribute('type', value ? 'text' : 'password');
5325  }
5326
5327  get isRevealed () {
5328    return this._isRevealed;
5329  }
5330
5331  capslock (event) {
5332    if (event && typeof event.getModifierState !== 'function') return;
5333    if (event.getModifierState('CapsLock')) {
5334      this.node.parentNode.setAttribute(api.internals.ns.attr('capslock'), '');
5335    } else {
5336      this.node.parentNode.removeAttribute(api.internals.ns.attr('capslock'));
5337    }
5338  }
5339
5340  set isRevealed (value) {
5341    this._isRevealed = value;
5342    this.setAttribute('type', value ? 'text' : 'password');
5343  }
5344}
5345
5346class PasswordLabel extends api.core.Instance {
5347  static get instanceClassName () {
5348    return 'PasswordLabel';
5349  }
5350
5351  init () {
5352    this.addDescent(PasswordEmission.ADJUST, this.adjust.bind(this));
5353  }
5354
5355  adjust (value) {
5356    const valueREM = Math.ceil(value / 16);
5357    this.node.style.paddingRight = valueREM + 'rem';
5358  }
5359}
5360
5361api.password = {
5362  Password: Password,
5363  PasswordToggle: PasswordToggle,
5364  PasswordSelector: PasswordSelector,
5365  PasswordInput: PasswordInput,
5366  PasswordLabel: PasswordLabel
5367};
5368
5369api.internals.register(api.password.PasswordSelector.INPUT, api.password.PasswordInput);
5370api.internals.register(api.password.PasswordSelector.PASSWORD, api.password.Password);
5371api.internals.register(api.password.PasswordSelector.TOOGLE, api.password.PasswordToggle);
5372api.internals.register(api.password.PasswordSelector.LABEL, api.password.PasswordLabel);
5373
5374const ITEM = api.internals.ns.selector('nav__item');
5375const COLLAPSE = api.internals.ns.selector('collapse');
5376
5377const NavigationSelector = {
5378  NAVIGATION: api.internals.ns.selector('nav'),
5379  COLLAPSE: `${ITEM} > ${COLLAPSE}, ${ITEM} > *:not(${ITEM}):not(${COLLAPSE}) > ${COLLAPSE}, ${ITEM} > *:not(${ITEM}):not(${COLLAPSE}) > *:not(${ITEM}):not(${COLLAPSE}) > ${COLLAPSE}`,
5380  COLLAPSE_LEGACY: `${ITEM} ${COLLAPSE}`,
5381  ITEM: ITEM,
5382  ITEM_RIGHT: `${ITEM}--align-right`,
5383  MENU: api.internals.ns.selector('menu'),
5384  BUTTON: api.internals.ns.selector('nav__btn'),
5385  TRANSLATE_BUTTON: api.internals.ns.selector('translate__btn')
5386};
5387
5388class NavigationItem extends api.core.Instance {
5389  constructor () {
5390    super();
5391    this._isRightAligned = false;
5392  }
5393
5394  static get instanceClassName () {
5395    return 'NavigationItem';
5396  }
5397
5398  init () {
5399    this.addAscent(api.core.DisclosureEmission.ADDED, this.calculate.bind(this));
5400    this.addAscent(api.core.DisclosureEmission.REMOVED, this.calculate.bind(this));
5401    this.isResizing = true;
5402    this.calculate();
5403  }
5404
5405  resize () {
5406    this.calculate();
5407  }
5408
5409  calculate () {
5410    const collapse = this.element.getDescendantInstances(api.core.Collapse.instanceClassName, null, true)[0];
5411    if (collapse && this.isBreakpoint(api.core.Breakpoints.LG) && collapse.element.node.matches(NavigationSelector.MENU)) {
5412      const right = this.element.node.parentElement.getBoundingClientRect().right; // todo: ne fonctionne que si la nav fait 100% du container
5413      const width = collapse.element.node.getBoundingClientRect().width;
5414      const left = this.element.node.getBoundingClientRect().left;
5415      this.isRightAligned = left + width > right;
5416    } else this.isRightAligned = false;
5417  }
5418
5419  get isRightAligned () {
5420    return this._isRightAligned;
5421  }
5422
5423  set isRightAligned (value) {
5424    if (this._isRightAligned === value) return;
5425    this._isRightAligned = value;
5426    if (value) api.internals.dom.addClass(this.element.node, NavigationSelector.ITEM_RIGHT);
5427    else api.internals.dom.removeClass(this.element.node, NavigationSelector.ITEM_RIGHT);
5428  }
5429
5430  get collapsePrimary () {
5431    const buttons = this.element.children.map(child => child.getInstance('CollapseButton')).filter(button => button !== null && (button.hasClass(NavigationSelector.BUTTON) || button.hasClass(NavigationSelector.TRANSLATE_BUTTON)));
5432    return buttons[0];
5433  }
5434}
5435
5436const NavigationMousePosition = {
5437  NONE: -1,
5438  INSIDE: 0,
5439  OUTSIDE: 1
5440};
5441
5442class Navigation extends api.core.CollapsesGroup {
5443  static get instanceClassName () {
5444    return 'Navigation';
5445  }
5446
5447  init () {
5448    super.init();
5449    this.clicked = false;
5450    this.out = false;
5451    this.addEmission(api.core.RootEmission.CLICK, this._handleRootClick.bind(this));
5452    this.listen('mousedown', this.handleMouseDown.bind(this));
5453    this.addEmission(api.core.RootEmission.KEYDOWN, this._keydown.bind(this));
5454    this.listenClick({ capture: true });
5455    this.isResizing = true;
5456  }
5457
5458  validate (member) {
5459    return super.validate(member) && member.element.node.matches(api.internals.legacy.isLegacy ? NavigationSelector.COLLAPSE_LEGACY : NavigationSelector.COLLAPSE);
5460  }
5461
5462  get hasOpenedMenu () {
5463    return this.isBreakpoint(api.core.Breakpoints.LG) && this.index > -1;
5464  }
5465
5466  _keydown (keyCode) {
5467    switch (keyCode) {
5468      case api.core.KeyCodes.ESCAPE:
5469        if (!this.hasOpenedMenu) return;
5470        this.index = -1;
5471        break;
5472
5473      case api.core.KeyCodes.TAB:
5474        if (!this.hasOpenedMenu) return;
5475        this.request(() => {
5476          if (this.current.node.contains(document.activeElement)) return;
5477          this.index = -1;
5478        });
5479        break;
5480    }
5481  }
5482
5483  handleMouseDown (e) {
5484    if (!this.hasOpenedMenu) return;
5485    this.position = this.current.node.contains(e.target) ? NavigationMousePosition.INSIDE : NavigationMousePosition.OUTSIDE;
5486    this.requestPosition();
5487  }
5488
5489  handleClick (e) {
5490    const linkOrButton = e.target.closest('a, button');
5491    if (linkOrButton && linkOrButton !== this.node && !linkOrButton.matches('[aria-controls]') && !linkOrButton.matches(api.core.DisclosureSelector.PREVENT_CONCEAL)) {
5492      this.index = -1;
5493    }
5494  }
5495
5496  _handleRootClick (target) {
5497    if (!this.isBreakpoint(api.core.Breakpoints.LG)) return;
5498    if (!this.node.contains(target)) {
5499      this.out = true;
5500      this.requestPosition();
5501    }
5502  }
5503
5504  requestPosition () {
5505    if (this.isRequesting) return;
5506    this.isRequesting = true;
5507    this.request(this.getPosition.bind(this));
5508  }
5509
5510  getPosition () {
5511    if (this.out) {
5512      switch (this.position) {
5513        case NavigationMousePosition.OUTSIDE:
5514          this.index = -1;
5515          break;
5516
5517        case NavigationMousePosition.INSIDE:
5518          if (this.current && !this.current.node.contains(document.activeElement)) this.current.focus();
5519          break;
5520
5521        default:
5522          if (this.index > -1 && !this.current.hasFocus) this.index = -1;
5523      }
5524    }
5525
5526    this.request(this.requested.bind(this));
5527  }
5528
5529  requested () {
5530    this.position = NavigationMousePosition.NONE;
5531    this.out = false;
5532    this.isRequesting = false;
5533  }
5534
5535  get index () { return super.index; }
5536
5537  set index (value) {
5538    if (value === -1 && this.current && this.current.hasFocus) this.current.focus();
5539    super.index = value;
5540  }
5541
5542  get canUngroup () {
5543    return !this.isBreakpoint(api.core.Breakpoints.LG);
5544  }
5545
5546  resize () {
5547    this.update();
5548  }
5549}
5550
5551api.navigation = {
5552  Navigation: Navigation,
5553  NavigationItem: NavigationItem,
5554  NavigationMousePosition: NavigationMousePosition,
5555  NavigationSelector: NavigationSelector
5556};
5557
5558api.internals.register(api.navigation.NavigationSelector.NAVIGATION, api.navigation.Navigation);
5559api.internals.register(api.navigation.NavigationSelector.ITEM, api.navigation.NavigationItem);
5560
5561/**
5562  * TabButton correspond au bouton cliquable qui change le panel
5563  * TabButton étend de DisclosureButton qui ajoute/enelve l'attribut aria-selected,
5564  * Et change l'attributte tabindex a 0 si le boutton est actif (value=true), -1 s'il n'est pas actif (value=false)
5565 */
5566class TabButton extends api.core.DisclosureButton {
5567  constructor () {
5568    super(api.core.DisclosureType.SELECT);
5569  }
5570
5571  static get instanceClassName () {
5572    return 'TabButton';
5573  }
5574
5575  handleClick (e) {
5576    super.handleClick(e);
5577    this.focus();
5578  }
5579
5580  apply (value) {
5581    super.apply(value);
5582    if (this.isPrimary) {
5583      this.setAttribute('tabindex', value ? '0' : '-1');
5584      if (value) {
5585        if (this.list) this.list.focalize(this);
5586      }
5587    }
5588  }
5589
5590  get list () {
5591    return this.element.getAscendantInstance('TabsList', 'TabsGroup');
5592  }
5593}
5594
5595const TabSelector = {
5596  TAB: api.internals.ns.selector('tabs__tab'),
5597  GROUP: api.internals.ns.selector('tabs'),
5598  PANEL: api.internals.ns.selector('tabs__panel'),
5599  LIST: api.internals.ns.selector('tabs__list'),
5600  SHADOW: api.internals.ns.selector('tabs__shadow'),
5601  SHADOW_LEFT: api.internals.ns.selector('tabs__shadow--left'),
5602  SHADOW_RIGHT: api.internals.ns.selector('tabs__shadow--right'),
5603  PANEL_START: api.internals.ns.selector('tabs__panel--direction-start'),
5604  PANEL_END: api.internals.ns.selector('tabs__panel--direction-end')
5605};
5606
5607const TabPanelDirection = {
5608  START: 'direction-start',
5609  END: 'direction-end',
5610  NONE: 'none'
5611};
5612
5613/**
5614  * Tab coorespond au panel d'un élement Tabs (tab panel)
5615  * Tab étend disclosure qui ajoute/enleve le modifier --selected,
5616  * et ajoute/eleve l'attribut hidden, sur le panel
5617  */
5618class TabPanel extends api.core.Disclosure {
5619  constructor () {
5620    super(api.core.DisclosureType.SELECT, TabSelector.PANEL, TabButton, 'TabsGroup');
5621    this._direction = TabPanelDirection.NONE;
5622    this._isPreventingTransition = false;
5623  }
5624
5625  static get instanceClassName () {
5626    return 'TabPanel';
5627  }
5628
5629  get direction () {
5630    return this._direction;
5631  }
5632
5633  set direction (value) {
5634    if (value === this._direction) return;
5635    switch (this._direction) {
5636      case TabPanelDirection.START:
5637        this.removeClass(TabSelector.PANEL_START);
5638        break;
5639
5640      case TabPanelDirection.END:
5641        this.removeClass(TabSelector.PANEL_END);
5642        break;
5643
5644      case TabPanelDirection.NONE:
5645        break;
5646
5647      default:
5648        return;
5649    }
5650
5651    this._direction = value;
5652
5653    switch (this._direction) {
5654      case TabPanelDirection.START:
5655        this.addClass(TabSelector.PANEL_START);
5656        break;
5657
5658      case TabPanelDirection.END:
5659        this.addClass(TabSelector.PANEL_END);
5660        break;
5661    }
5662  }
5663
5664  get isPreventingTransition () {
5665    return this._isPreventingTransition;
5666  }
5667
5668  set isPreventingTransition (value) {
5669    if (this._isPreventingTransition === value) return;
5670    if (value) this.addClass(api.internals.motion.TransitionSelector.NONE);
5671    else this.removeClass(api.internals.motion.TransitionSelector.NONE);
5672    this._isPreventingTransition = value === true;
5673  }
5674
5675  translate (direction, initial) {
5676    this.isPreventingTransition = initial;
5677    this.direction = direction;
5678  }
5679
5680  reset () {
5681    if (this.group) this.group.retrieve(true);
5682  }
5683
5684  _electPrimaries (candidates) {
5685    if (!this.group || !this.group.list) return [];
5686    return super._electPrimaries(candidates).filter(candidate => this.group.list.node.contains(candidate.node));
5687  }
5688}
5689
5690const TabKeys = {
5691  LEFT: 'tab_keys_left',
5692  RIGHT: 'tab_keys_right',
5693  HOME: 'tab_keys_home',
5694  END: 'tab_keys_end'
5695};
5696
5697const TabEmission = {
5698  PRESS_KEY: api.internals.ns.emission('tab', 'press_key'),
5699  LIST_HEIGHT: api.internals.ns.emission('tab', 'list_height')
5700};
5701
5702/**
5703* TabGroup est la classe étendue de DiscosuresGroup
5704* Correspond à un objet Tabs avec plusieurs tab-button & Tab (panel)
5705*/
5706class TabsGroup extends api.core.DisclosuresGroup {
5707  constructor () {
5708    super('TabPanel');
5709  }
5710
5711  static get instanceClassName () {
5712    return 'TabsGroup';
5713  }
5714
5715  init () {
5716    super.init();
5717
5718    this.listen('transitionend', this.transitionend.bind(this));
5719    this.addAscent(TabEmission.PRESS_KEY, this.pressKey.bind(this));
5720    this.addAscent(TabEmission.LIST_HEIGHT, this.setListHeight.bind(this));
5721    this.isRendering = true;
5722  }
5723
5724  getIndex (defaultIndex = 0) {
5725    super.getIndex(defaultIndex);
5726  }
5727
5728  get list () {
5729    return this.element.getDescendantInstances('TabsList', 'TabsGroup', true)[0];
5730  }
5731
5732  setListHeight (value) {
5733    this.listHeight = value;
5734  }
5735
5736  transitionend (e) {
5737    this.isPreventingTransition = true;
5738  }
5739
5740  get buttonHasFocus () {
5741    return this.members.some(member => member.buttonHasFocus);
5742  }
5743
5744  pressKey (key) {
5745    switch (key) {
5746      case TabKeys.LEFT:
5747        this.pressLeft();
5748        break;
5749
5750      case TabKeys.RIGHT:
5751        this.pressRight();
5752        break;
5753
5754      case TabKeys.HOME:
5755        this.pressHome();
5756        break;
5757
5758      case TabKeys.END:
5759        this.pressEnd();
5760        break;
5761    }
5762  }
5763
5764  /**
5765   * Selectionne l'element suivant de la liste si on est sur un bouton
5766   * Si on est à la fin on retourne au début
5767   */
5768  pressRight () {
5769    if (this.buttonHasFocus) {
5770      if (this.index < this.length - 1) {
5771        this.index++;
5772      } else {
5773        this.index = 0;
5774      }
5775
5776      this.focus();
5777    }
5778  };
5779
5780  /**
5781   * Selectionne l'element précédent de la liste si on est sur un bouton
5782   * Si on est au debut retourne a la fin
5783   */
5784  pressLeft () {
5785    if (this.buttonHasFocus) {
5786      if (this.index > 0) {
5787        this.index--;
5788      } else {
5789        this.index = this.length - 1;
5790      }
5791
5792      this.focus();
5793    }
5794  };
5795
5796  /**
5797   * Selectionne le permier element de la liste si on est sur un bouton
5798   */
5799  pressHome () {
5800    if (this.buttonHasFocus) {
5801      this.index = 0;
5802      this.focus();
5803    }
5804  };
5805
5806  /**
5807   * Selectionne le dernier element de la liste si on est sur un bouton
5808   */
5809  pressEnd () {
5810    if (this.buttonHasFocus) {
5811      this.index = this.length - 1;
5812      this.focus();
5813    }
5814  };
5815
5816  focus () {
5817    if (!this.current) return;
5818
5819    const button = this.current.primaryButtons.find(button => button.list === this.list);
5820    if (button) button.focus();
5821  }
5822
5823  apply () {
5824    for (let i = 0; i < this._index; i++) this.members[i].translate(TabPanelDirection.START);
5825    if (this.current) this.current.translate(TabPanelDirection.NONE);
5826    for (let i = this._index + 1; i < this.length; i++) this.members[i].translate(TabPanelDirection.END);
5827    this.isPreventingTransition = false;
5828  }
5829
5830  get isPreventingTransition () {
5831    return this._isPreventingTransition;
5832  }
5833
5834  set isPreventingTransition (value) {
5835    if (this._isPreventingTransition === value) return;
5836    if (value) this.addClass(api.internals.motion.TransitionSelector.NONE);
5837    else this.removeClass(api.internals.motion.TransitionSelector.NONE);
5838    this._isPreventingTransition = value === true;
5839  }
5840
5841  render () {
5842    if (this.current === null) return;
5843    this.node.scrollTop = 0;
5844    this.node.scrollLeft = 0;
5845    const paneHeight = Math.round(this.current.node.offsetHeight);
5846    if (this.panelHeight === paneHeight) return;
5847    this.panelHeight = paneHeight;
5848    const offsetNegativeMargin = 4;
5849    this.style.setProperty('--tabs-height', (this.panelHeight + this.listHeight - offsetNegativeMargin) + 'px');
5850  }
5851}
5852
5853const FOCALIZE_OFFSET = 16;
5854const SCROLL_OFFSET$1 = 16; // valeur en px du scroll avant laquelle le shadow s'active ou se desactive
5855
5856class TabsList extends api.core.Instance {
5857  static get instanceClassName () {
5858    return 'TabsList';
5859  }
5860
5861  init () {
5862    this.listen('scroll', this.scroll.bind(this));
5863    this.listenKey(api.core.KeyCodes.RIGHT, this.ascend.bind(this, TabEmission.PRESS_KEY, TabKeys.RIGHT), true, true);
5864    this.listenKey(api.core.KeyCodes.LEFT, this.ascend.bind(this, TabEmission.PRESS_KEY, TabKeys.LEFT), true, true);
5865    this.listenKey(api.core.KeyCodes.HOME, this.ascend.bind(this, TabEmission.PRESS_KEY, TabKeys.HOME), true, true);
5866    this.listenKey(api.core.KeyCodes.END, this.ascend.bind(this, TabEmission.PRESS_KEY, TabKeys.END), true, true);
5867    this.isResizing = true;
5868  }
5869
5870  focalize (btn) {
5871    const btnRect = btn.getRect();
5872    const listRect = this.getRect();
5873    const actualScroll = this.node.scrollLeft;
5874    if (btnRect.left < listRect.left) this.node.scrollTo(actualScroll - listRect.left + btnRect.left - FOCALIZE_OFFSET, 0);
5875    else if (btnRect.right > listRect.right) this.node.scrollTo(actualScroll - listRect.right + btnRect.right + FOCALIZE_OFFSET, 0);
5876  }
5877
5878  get isScrolling () {
5879    return this._isScrolling;
5880  }
5881
5882  set isScrolling (value) {
5883    if (this._isScrolling === value) return;
5884    this._isScrolling = value;
5885    this.apply();
5886  }
5887
5888  apply () {
5889    if (this._isScrolling) {
5890      this.addClass(TabSelector.SHADOW);
5891      this.scroll();
5892    } else {
5893      this.removeClass(TabSelector.SHADOW_RIGHT);
5894      this.removeClass(TabSelector.SHADOW_LEFT);
5895      this.removeClass(TabSelector.SHADOW);
5896    }
5897  }
5898
5899  /* ajoute la classe fr-table__shadow-left ou fr-table__shadow-right sur fr-table en fonction d'une valeur de scroll et du sens (right, left) */
5900  scroll () {
5901    const scrollLeft = Math.abs(this.node.scrollLeft);
5902    const isMin = scrollLeft <= SCROLL_OFFSET$1;
5903    const max = this.node.scrollWidth - this.node.clientWidth - SCROLL_OFFSET$1;
5904    const isMax = Math.abs(scrollLeft) >= max;
5905    const isRtl = getComputedStyle(this.node).direction === 'rtl';
5906    const minSelector = isRtl ? TabSelector.SHADOW_RIGHT : TabSelector.SHADOW_LEFT;
5907    const maxSelector = isRtl ? TabSelector.SHADOW_LEFT : TabSelector.SHADOW_RIGHT;
5908
5909    if (isMin) {
5910      this.removeClass(minSelector);
5911    } else {
5912      this.addClass(minSelector);
5913    }
5914
5915    if (isMax) {
5916      this.removeClass(maxSelector);
5917    } else {
5918      this.addClass(maxSelector);
5919    }
5920  }
5921
5922  resize () {
5923    this.isScrolling = this.node.scrollWidth > this.node.clientWidth + SCROLL_OFFSET$1;
5924    const height = this.getRect().height;
5925    this.setProperty('--tabs-list-height', `${height}px`);
5926    this.ascend(TabEmission.LIST_HEIGHT, height);
5927  }
5928
5929  dispose () {
5930    this.isScrolling = false;
5931  }
5932}
5933
5934api.tab = {
5935  TabPanel: TabPanel,
5936  TabButton: TabButton,
5937  TabsGroup: TabsGroup,
5938  TabsList: TabsList,
5939  TabSelector: TabSelector,
5940  TabEmission: TabEmission
5941};
5942
5943api.internals.register(api.tab.TabSelector.PANEL, api.tab.TabPanel);
5944api.internals.register(api.tab.TabSelector.GROUP, api.tab.TabsGroup);
5945api.internals.register(api.tab.TabSelector.LIST, api.tab.TabsList);
5946
5947const TagEvent = {
5948  DISMISS: api.internals.ns.event('dismiss')
5949};
5950
5951class TagDismissible extends api.core.Instance {
5952  static get instanceClassName () {
5953    return 'TagDismissible';
5954  }
5955
5956  init () {
5957    this.listenClick();
5958  }
5959
5960  handleClick () {
5961    this.focusClosest();
5962
5963    switch (api.mode) {
5964      case api.Modes.ANGULAR:
5965      case api.Modes.REACT:
5966      case api.Modes.VUE:
5967        this.request(this.verify.bind(this));
5968        break;
5969
5970      default:
5971        this.remove();
5972    }
5973
5974    this.dispatch(TagEvent.DISMISS);
5975  }
5976
5977  verify () {
5978    if (document.body.contains(this.node)) this.warn(`a TagDismissible has just been dismissed and should be removed from the dom. In ${api.mode} mode, the api doesn't handle dom modification. An event ${TagEvent.DISMISS} is dispatched by the element to trigger the removal`);
5979  }
5980}
5981
5982const TagSelector = {
5983  PRESSABLE: `${api.internals.ns.selector('tag')}[aria-pressed]`,
5984  DISMISSIBLE: `${api.internals.ns.selector('tag--dismiss')}`
5985};
5986
5987api.tag = {
5988  TagDismissible: TagDismissible,
5989  TagSelector: TagSelector,
5990  TagEvent: TagEvent
5991};
5992
5993api.internals.register(api.tag.TagSelector.PRESSABLE, api.core.Toggle);
5994api.internals.register(api.tag.TagSelector.DISMISSIBLE, api.tag.TagDismissible);
5995
5996const TRANSCRIPTION = api.internals.ns.selector('transcription');
5997
5998const TranscriptionSelector = {
5999  TRANSCRIPTION: TRANSCRIPTION,
6000  BUTTON: `${TRANSCRIPTION}__btn`
6001};
6002
6003class Transcription extends api.core.Instance {
6004  static get instanceClassName () {
6005    return 'Transcription';
6006  }
6007
6008  get collapsePrimary () {
6009    const buttons = this.element.children.map(child => child.getInstance('CollapseButton')).filter(button => button !== null && button.hasClass(TranscriptionSelector.BUTTON));
6010    return buttons[0];
6011  }
6012}
6013
6014api.transcription = {
6015  Transcription: Transcription,
6016  TranscriptionSelector: TranscriptionSelector
6017};
6018
6019api.internals.register(api.transcription.TranscriptionSelector.TRANSCRIPTION, api.transcription.Transcription);
6020
6021class TileDownload extends api.core.Instance {
6022  static get instanceClassName () {
6023    return 'TileDownload';
6024  }
6025
6026  init () {
6027    this.addAscent(api.core.AssessEmission.UPDATE, details => {
6028      this.descend(api.core.AssessEmission.UPDATE, details);
6029    });
6030    this.addAscent(api.core.AssessEmission.ADDED, () => {
6031      this.descend(api.core.AssessEmission.ADDED);
6032    });
6033  }
6034}
6035
6036const TileSelector = {
6037  DOWNLOAD: api.internals.ns.selector('tile--download'),
6038  DOWNLOAD_DETAIL: `${api.internals.ns.selector('tile--download')} ${api.internals.ns.selector('tile__detail')}`
6039};
6040
6041api.tile = {
6042  TileSelector: TileSelector,
6043  TileDownload: TileDownload
6044};
6045
6046api.internals.register(api.tile.TileSelector.DOWNLOAD, api.tile.TileDownload);
6047api.internals.register(api.tile.TileSelector.DOWNLOAD_DETAIL, api.core.AssessDetail);
6048
6049const RangeSelector = {
6050  RANGE: api.internals.ns.selector('range'),
6051  RANGE_SM: api.internals.ns.selector('range--sm'),
6052  RANGE_STEP: api.internals.ns.selector('range--step'),
6053  RANGE_DOUBLE: api.internals.ns.selector('range--double'),
6054  RANGE_DOUBLE_STEP: api.internals.ns.selector('range--double') + api.internals.ns.selector('range--step'),
6055  RANGE_INPUT: api.internals.ns.selector('range input[type=range]:nth-of-type(1)'),
6056  RANGE_INPUT2: `${api.internals.ns.selector('range--double')} input[type=range]:nth-of-type(2)`,
6057  RANGE_OUTPUT: api.internals.ns.selector('range__output'),
6058  RANGE_MIN: api.internals.ns.selector('range__min'),
6059  RANGE_MAX: api.internals.ns.selector('range__max'),
6060  RANGE_PREFIX: api.internals.ns.attr('prefix'),
6061  RANGE_SUFFIX: api.internals.ns.attr('suffix')
6062};
6063
6064const RangeEmission = {
6065  VALUE: api.internals.ns.emission('range', 'value'),
6066  VALUE2: api.internals.ns.emission('range', 'value2'),
6067  OUTPUT: api.internals.ns.emission('range', 'output'),
6068  CONSTRAINTS: api.internals.ns.emission('range', 'constraints'),
6069  MIN: api.internals.ns.emission('range', 'min'),
6070  MAX: api.internals.ns.emission('range', 'max'),
6071  STEP: api.internals.ns.emission('range', 'step'),
6072  PREFIX: api.internals.ns.emission('range', 'prefix'),
6073  SUFFIX: api.internals.ns.emission('range', 'suffix'),
6074  DISABLED: api.internals.ns.emission('range', 'disabled'),
6075  ENABLE_POINTER: api.internals.ns.emission('range', 'enable_pointer')
6076};
6077
6078class RangeModel {
6079  constructor () {
6080    this._width = 0;
6081    this._min = 0;
6082    this._max = 0;
6083    this._value = 0;
6084    this._thumbSize = 24;
6085    this._innerWidth = 0;
6086    this._prefix = '';
6087    this._suffix = '';
6088    this._background = {};
6089  }
6090
6091  configure (model) {
6092    if (!model) return;
6093    this._prefix = model._prefix;
6094    this._suffix = model._suffix;
6095    this._width = model.width;
6096    this.setConstraints(model._constraints);
6097    this.value = model.value;
6098    this.update();
6099  }
6100
6101  setPrefix (value) {
6102    this._prefix = value !== null ? value : '';
6103  }
6104
6105  setSuffix (value) {
6106    this._suffix = value !== null ? value : '';
6107  }
6108
6109  _decorate (value) {
6110    return `${this._prefix}${value}${this._suffix}`;
6111  }
6112
6113  get width () {
6114    return this._width;
6115  }
6116
6117  set width (value) {
6118    this._width = value;
6119  }
6120
6121  get isSm () {
6122    return this._isSm;
6123  }
6124
6125  set isSm (value) {
6126    if (this._isSm === value) return;
6127    this._isSm = value;
6128    this.setThumbSize(value ? 16 : 24);
6129    this.update();
6130  }
6131
6132  setThumbSize (value, mult = 1) {
6133    this._thumbSize = value;
6134    this._innerPadding = value * mult;
6135  }
6136
6137  get textValue () {
6138    return this._decorate(this._value);
6139  }
6140
6141  get value () {
6142    return this._value;
6143  }
6144
6145  set value (value) {
6146    this._value = value;
6147  }
6148
6149  get outputX () {
6150    return this._outputX;
6151  }
6152
6153  setConstraints (constraints) {
6154    this._constraints = constraints;
6155    this._min = constraints.min;
6156    this._max = constraints.max;
6157    this._step = constraints.step;
6158    this._rangeWidth = constraints.rangeWidth;
6159  }
6160
6161  get min () {
6162    return this._min;
6163  }
6164
6165  get textMin () {
6166    return this._decorate(this._min);
6167  }
6168
6169  get max () {
6170    return this._max;
6171  }
6172
6173  get textMax () {
6174    return this._decorate(this._max);
6175  }
6176
6177  get step () {
6178    return this._step;
6179  }
6180
6181  get output () {
6182    return {
6183      text: this.textValue,
6184      transform: `translateX(${this._translateX}px) translateX(-${this._centerPercent}%)`
6185    };
6186  }
6187
6188  _getRatio (value) {
6189    return (value - this._min) / this._rangeWidth;
6190  }
6191
6192  get progress () {
6193    return this._progress;
6194  }
6195
6196  update () {
6197    this._update();
6198  }
6199
6200  _update () {
6201    this._innerWidth = this._width - this._innerPadding;
6202    const ratio = this._getRatio(this._value);
6203    this._translateX = ratio * this._width;
6204    this._centerPercent = ratio * 100;
6205    this._progress = {
6206      right: `${(this._innerWidth * ratio + this._innerPadding * 0.5).toFixed(2)}px`
6207    };
6208  }
6209}
6210
6211class RangeModelStep extends RangeModel {
6212  get stepWidth () {
6213    return `${this._stepWidth.toFixed(3)}px`;
6214  }
6215
6216  _update () {
6217    super._update();
6218    const steps = this._rangeWidth / this._step;
6219    this._stepWidth = this._innerWidth / steps;
6220    if (this._stepWidth < 1 || !isFinite(this._stepWidth)) this._stepWidth = 4;
6221    while (this._stepWidth < 4) this._stepWidth *= 2;
6222  }
6223}
6224
6225class RangeModelDouble extends RangeModel {
6226  get value2 () {
6227    return this._value;
6228  }
6229
6230  set value2 (value) {
6231    if (this._value2 === value) return;
6232    this._value2 = value;
6233    this.update();
6234  }
6235
6236  get textValue () {
6237    return `${this._decorate(this._value)} - ${this._decorate(this._value2)}`;
6238  }
6239
6240  setThumbSize (value) {
6241    super.setThumbSize(value, 2);
6242  }
6243
6244  _update () {
6245    super._update();
6246    const ratio = this._getRatio((this._value + this._value2) * 0.5);
6247    this._translateX = ratio * this._width;
6248    this._centerPercent = ratio * 100;
6249    const ratio1 = this._getRatio(this._value);
6250    const ratio2 = this._getRatio(this._value2);
6251    this._progress = {
6252      left: `${(this._innerWidth * ratio1 + this._innerPadding * 0.25).toFixed(2)}px`,
6253      right: `${(this._innerWidth * ratio2 + this._innerPadding * 0.75).toFixed(2)}px`
6254    };
6255  }
6256}
6257
6258class RangeModelDoubleStep extends RangeModelDouble {
6259  get stepWidth () {
6260    return `${this._stepWidth.toFixed(3)}px`;
6261  }
6262
6263  _update () {
6264    super._update();
6265    const steps = this._rangeWidth / this._step;
6266    this._stepWidth = this._innerWidth / steps;
6267    if (this._stepWidth < 4) this._stepWidth *= Math.ceil(4 / this._stepWidth);
6268  }
6269}
6270
6271const RangeTypes = {
6272  STEP: 'step',
6273  DOUBLE: 'double',
6274  DOUBLE_STEP: 'double-step',
6275  DEFAULT: 'default'
6276};
6277
6278class Range extends api.core.Instance {
6279  static get instanceClassName () {
6280    return 'Range';
6281  }
6282
6283  init () {
6284    this._retrieveType();
6285    this._retrieveSize();
6286    if (this.isLegacy) {
6287      this.isResizing = true;
6288      this.isMouseMoving = true;
6289    } else {
6290      this._observer = new ResizeObserver(this.resize.bind(this));
6291      this._observer.observe(this.node);
6292    }
6293
6294    this.addAscent(RangeEmission.CONSTRAINTS, this.setConstraints.bind(this));
6295    this.addAscent(RangeEmission.VALUE, this.setValue.bind(this));
6296    this.addAscent(RangeEmission.VALUE2, this.setValue2.bind(this));
6297    if (this.getAttribute(RangeSelector.RANGE_PREFIX)) this.setPrefix(this.getAttribute(RangeSelector.RANGE_PREFIX));
6298    if (this.getAttribute(RangeSelector.RANGE_SUFFIX)) this.setSuffix(this.getAttribute(RangeSelector.RANGE_SUFFIX));
6299    this.update();
6300  }
6301
6302  _retrieveType () {
6303    switch (true) {
6304      case this.matches(RangeSelector.RANGE_DOUBLE_STEP):
6305        this.type = RangeTypes.DOUBLE;
6306        break;
6307
6308      case this.matches(RangeSelector.RANGE_DOUBLE):
6309        this.type = RangeTypes.DOUBLE;
6310        break;
6311
6312      case this.matches(RangeSelector.RANGE_STEP):
6313        this.type = RangeTypes.STEP;
6314        break;
6315
6316      default:
6317        this.type = RangeTypes.DEFAULT;
6318    }
6319  }
6320
6321  set type (value) {
6322    if (this._type === value) return;
6323    this._type = value;
6324
6325    const oldModel = this._model;
6326
6327    switch (this._type) {
6328      case RangeTypes.DOUBLE_STEP:
6329        this._model = new RangeModelDoubleStep();
6330        break;
6331
6332      case RangeTypes.DOUBLE:
6333        this._model = new RangeModelDouble();
6334        break;
6335
6336      case RangeTypes.STEP:
6337        this._model = new RangeModelStep();
6338        break;
6339
6340      default:
6341        this._model = new RangeModel();
6342    }
6343
6344    this._model.configure(oldModel);
6345  }
6346
6347  get type () {
6348    return this._type;
6349  }
6350
6351  _retrieveSize () {
6352    this._model.isSm = this.matches(RangeSelector.RANGE_SM);
6353  }
6354
6355  resize () {
6356    this._retrieveWidth();
6357    this.update();
6358  }
6359
6360  _retrieveWidth () {
6361    this._model.width = this.getRect().width;
6362  }
6363
6364  setValue (value) {
6365    this._model.value = value;
6366    switch (this._type) {
6367      case RangeTypes.DOUBLE_STEP:
6368      case RangeTypes.DOUBLE:
6369        this.descend(RangeEmission.VALUE, value);
6370        break;
6371    }
6372    this.update();
6373  }
6374
6375  setValue2 (value) {
6376    this._model.value2 = value;
6377    this.descend(RangeEmission.VALUE2, value);
6378    this.update();
6379  }
6380
6381  setConstraints (constraints) {
6382    this._model.setConstraints(constraints);
6383    this.update();
6384    this.descend(RangeEmission.CONSTRAINTS, constraints);
6385  }
6386
6387  setPrefix (value) {
6388    this._model.setPrefix(value);
6389    this.update();
6390  }
6391
6392  setSuffix (value) {
6393    this._model.setSuffix(value);
6394    this.update();
6395  }
6396
6397  mutate (attributesNames) {
6398    switch (true) {
6399      case attributesNames.includes('class'):
6400        this._retrieveType();
6401        this._retrieveSize();
6402        break;
6403
6404      case attributesNames.includes(RangeSelector.RANGE_PREFIX):
6405      case attributesNames.includes(RangeSelector.RANGE_SUFFIX):
6406        this._model.setPrefix(this.getAttribute(RangeSelector.RANGE_PREFIX));
6407        this._model.setSuffix(this.getAttribute(RangeSelector.RANGE_SUFFIX));
6408        this.update();
6409        break;
6410    }
6411  }
6412
6413  update () {
vendor: 6,122 bytes, lines 6414-6645
6414    this._model.update();
6415    this.descend(RangeEmission.OUTPUT, this._model.output);
6416    this.descend(RangeEmission.MIN, this._model.textMin);
6417    this.descend(RangeEmission.MAX, this._model.textMax);
6418    const progress = this._model.progress;
6419    if (progress.left) {
6420      this.style.setProperty('--progress-left', progress.left);
6421    } else {
6422      this.style.removeProperty('--progress-left');
6423    }
6424    if (progress.right) {
6425      this.style.setProperty('--progress-right', progress.right);
6426      if (this.isLegacy) {
6427        if (progress.left) {
6428          this.style.setProperty('background-position-x', progress.left);
6429          this.style.setProperty('background-size', `${parseFloat(progress.right) - parseFloat(progress.left)}px ${this._model.isSm ? '8px' : '12px'}`);
6430        }
6431      }
6432    } else {
6433      this.style.removeProperty('--progress-right');
6434      if (this.isLegacy) {
6435        this.style.removeProperty('background-size');
6436        this.style.removeProperty('background-position-x');
6437      }
6438    }
6439    if (this._model.stepWidth) this.style.setProperty('--step-width', this._model.stepWidth);
6440    else this.style.removeProperty('--step-width');
6441  }
6442
6443  mouseMove (point) {
6444    if (this._type !== RangeTypes.DOUBLE && this._type !== RangeTypes.DOUBLE_STEP) return;
6445    const x = point.x - this.getRect().left;
6446    this.descend(RangeEmission.ENABLE_POINTER, (parseFloat(this._model.progress.right) - parseFloat(this._model.progress.left)) / 2 + parseFloat(this._model.progress.left) < x ? 2 : 1);
6447  }
6448
6449  dispose () {
6450    this._observer.disconnect();
6451  }
6452}
6453
6454class RangeConstraints {
6455  constructor (node) {
6456    this._min = isNaN(node.min) ? 0 : node.min;
6457    this._max = isNaN(node.max) ? 100 : node.max;
6458    this._step = isNaN(node.step) ? 1 : node.step;
6459    this._rangeWidth = this._max - this._min;
6460  }
6461
6462  get min () {
6463    return this._min;
6464  }
6465
6466  get max () {
6467    return this._max;
6468  }
6469
6470  get step () {
6471    return this._step;
6472  }
6473
6474  get rangeWidth () {
6475    return this._rangeWidth;
6476  }
6477
6478  test (min, max, step) {
6479    return this._min === min && this._max === max && this._step === step;
6480  }
6481}
6482
6483class RangeInput extends api.core.Instance {
6484  static get instanceClassName () {
6485    return 'RangeInput';
6486  }
6487
6488  init () {
6489    this._init();
6490    this._value = parseFloat(this.node.getAttribute('value'));
6491    if (this.isLegacy) this.addDescent(RangeEmission.ENABLE_POINTER, this._enablePointer.bind(this));
6492    this.isRendering = true;
6493    this.change();
6494  }
6495
6496  _init () {
6497    this._pointerId = 1;
6498    this.request(() => {
6499      if (!this.hasAttribute('min')) this.setAttribute('min', 0);
6500      this.ascend(RangeEmission.CONSTRAINTS, new RangeConstraints(this.node));
6501      this.ascend(RangeEmission.DISABLED, this.node.disabled);
6502    });
6503
6504    this.addDescent(RangeEmission.VALUE2, this.setValue.bind(this));
6505  }
6506
6507  get proxy () {
6508    const scope = this;
6509
6510    const proxyAccessors = {
6511      get value () {
6512        return scope.value;
6513      },
6514      set value (value) {
6515        scope.value = value;
6516      }
6517    };
6518
6519    return api.internals.property.completeAssign(super.proxy, proxyAccessors);
6520  }
6521
6522  _enablePointer (pointerId) {
6523    const isEnabled = pointerId === this._pointerId;
6524    if (this._isPointerEnabled === isEnabled) return;
6525    this._isPointerEnabled = isEnabled;
6526    if (isEnabled) this.style.removeProperty('pointer-events');
6527    else this.style.setProperty('pointer-events', 'none');
6528  }
6529
6530  get value () {
6531    return parseFloat(this.node.value);
6532  }
6533
6534  set value (value) {
6535    const parsedValue = parseFloat(value);
6536    if (parsedValue === this._value) return;
6537    this._value = parsedValue;
6538    this.node.value = parsedValue;
6539    this.dispatch('change');
6540    this.change();
6541  }
6542
6543  setValue (value) {
6544    if (parseFloat(this.node.value) > value) {
6545      this.value = value;
6546    }
6547  }
6548
6549  change () {
6550    this.ascend(RangeEmission.VALUE, this._value);
6551  }
6552
6553  render () {
6554    const parsedValue = parseFloat(this.node.value);
6555    if (parsedValue !== this._value) this.value = parsedValue;
6556  }
6557
6558  mutate (attributesNames) {
6559    if (attributesNames.includes('disabled')) this.ascend(RangeEmission.DISABLED, this.node.disabled);
6560    if (attributesNames.includes('min') || attributesNames.includes('max') || attributesNames.includes('step')) {
6561      this.ascend(RangeEmission.CONSTRAINTS, new RangeConstraints(this.node));
6562      this.change();
6563    }
6564  }
6565
6566  dispose () {
6567    if (this._listenerType) this.unlisten(this._listenerType, this._changing);
6568  }
6569}
6570
6571class RangeInput2 extends RangeInput {
6572  static get instanceClassName () {
6573    return 'RangeInput2';
6574  }
6575
6576  _init () {
6577    this._pointerId = 2;
6578    this.addDescent(RangeEmission.CONSTRAINTS, this.setConstraints.bind(this));
6579    this.addDescent(RangeEmission.VALUE, this.setValue.bind(this));
6580  }
6581
6582  setValue (value) {
6583    if (parseFloat(this.node.value) < value) {
6584      this.value = value;
6585    }
6586  }
6587
6588  change () {
6589    this.ascend(RangeEmission.VALUE2, parseFloat(this.node.value));
6590  }
6591
6592  setConstraints (constraints) {
6593    this.node.min = constraints.min;
6594    this.node.max = constraints.max;
6595    this.node.step = constraints.step;
6596    this.change();
6597  }
6598
6599  mutate (attributesNames) {}
6600}
6601
6602class RangeOutput extends api.core.Instance {
6603  static get instanceClassName () {
6604    return 'RangeOutput';
6605  }
6606
6607  init () {
6608    this.addDescent(RangeEmission.OUTPUT, this.change.bind(this));
6609  }
6610
6611  change (data) {
6612    this.node.innerText = data.text;
6613    this.node.style.transform = data.transform;
6614  }
6615}
6616
6617class RangeLimit extends api.core.Instance {
6618  static get instanceClassName () {
6619    return 'RangeLimit';
6620  }
6621
6622  init () {
6623    switch (true) {
6624      case this.matches(RangeSelector.RANGE_MIN):
6625        this.addDescent(RangeEmission.MIN, this.change.bind(this));
6626        break;
6627
6628      case this.matches(RangeSelector.RANGE_MAX):
6629        this.addDescent(RangeEmission.MAX, this.change.bind(this));
6630        break;
6631    }
6632  }
6633
6634  change (text) {
6635    this.node.innerText = text;
6636  }
6637}
6638
6639api.range = {
6640  Range: Range,
6641  RangeInput: RangeInput,
6642  RangeInput2: RangeInput2,
6643  RangeOutput: RangeOutput,
6644  RangeLimit: RangeLimit,
6645  RangeEmission: RangeEmission,
vendor: 8,290 bytes, lines 6646-6895
6646  RangeSelector: RangeSelector
6647};
6648
6649api.internals.register(api.range.RangeSelector.RANGE, api.range.Range);
6650api.internals.register(api.range.RangeSelector.RANGE_INPUT, api.range.RangeInput);
6651api.internals.register(api.range.RangeSelector.RANGE_INPUT2, api.range.RangeInput2);
6652api.internals.register(api.range.RangeSelector.RANGE_OUTPUT, api.range.RangeOutput);
6653api.internals.register(api.range.RangeSelector.RANGE_MIN, api.range.RangeLimit);
6654api.internals.register(api.range.RangeSelector.RANGE_MAX, api.range.RangeLimit);
6655
6656const HeaderSelector = {
6657  HEADER: api.internals.ns.selector('header'),
6658  BRAND_LINK: api.internals.ns.selector('header__brand a'),
6659  TOOLS_LINKS: api.internals.ns.selector('header__tools-links'),
6660  MENU_LINKS: api.internals.ns.selector('header__menu-links'),
6661  BUTTONS: `${api.internals.ns.selector('header__tools-links')} ${api.internals.ns.selector('btns-group')}, ${api.internals.ns.selector('header__tools-links')} ${api.internals.ns.selector('links-group')}`,
6662  MODALS: `${api.internals.ns.selector('header__search')}${api.internals.ns.selector('modal')}, ${api.internals.ns.selector('header__menu')}${api.internals.ns.selector('modal')}`
6663};
6664
6665class HeaderLinks extends api.core.Instance {
6666  static get instanceClassName () {
6667    return 'HeaderLinks';
6668  }
6669
6670  init () {
6671    const header = this.queryParentSelector(HeaderSelector.HEADER);
6672    this.toolsLinks = header.querySelector(HeaderSelector.TOOLS_LINKS);
6673    this.menuLinks = header.querySelector(HeaderSelector.MENU_LINKS);
6674    const copySuffix = '-mobile';
6675
6676    const toolsHtml = this.toolsLinks.innerHTML.replace(/  +/g, ' ');
6677    const menuHtml = this.menuLinks.innerHTML.replace(/  +/g, ' ');
6678    // Pour éviter de dupliquer des id, on ajoute un suffixe aux id et aria-controls duppliqués.
6679    let toolsHtmlIdList = toolsHtml.match(/id="(.*?)"/gm) || [];
6680
6681    // on a besoin d'échapper les backslash dans la chaine de caractère
6682    // eslint-disable-next-line no-useless-escape
6683    toolsHtmlIdList = toolsHtmlIdList.map(element => element.replace('id=\"', '').replace('\"', ''));
6684
6685    const dupplicateAttributes = ['aria-controls', 'aria-describedby', 'aria-labelledby'];
6686
6687    let toolsHtmlDuplicateId = toolsHtml.replace(/id="(.*?)"/gm, `id="$1${copySuffix}"`);
6688
6689    for (const attribute of dupplicateAttributes) {
6690      const toolsHtmlAttributeList = toolsHtml.match(new RegExp(`${attribute}="(.*?)"`, 'gm'));
6691      if (toolsHtmlAttributeList) {
6692        for (const element of toolsHtmlAttributeList) {
6693          const attributeValue = element.replace(`${attribute}="`, '').replace('"', '');
6694          if (toolsHtmlIdList.includes(attributeValue)) {
6695            toolsHtmlDuplicateId = toolsHtmlDuplicateId.replace(`${attribute}="${attributeValue}"`, `${attribute}="${attributeValue + copySuffix}"`);
6696          }        }
6697      }
6698    }
6699
6700    if (toolsHtmlDuplicateId === menuHtml) return;
6701
6702    switch (api.mode) {
6703      case api.Modes.ANGULAR:
6704      case api.Modes.REACT:
6705      case api.Modes.VUE:
6706        this.warn(`header__tools-links content is different from header__menu-links content.
6707As you're using a dynamic framework, you should handle duplication of this content yourself, please refer to documentation:
6708${api.header.doc}`);
6709        break;
6710
6711      default:
6712        this.menuLinks.innerHTML = toolsHtmlDuplicateId;
6713    }
6714  }
6715}
6716
6717class HeaderModal extends api.core.Instance {
6718  static get instanceClassName () {
6719    return 'HeaderModal';
6720  }
6721
6722  init () {
6723    this.storeAria();
6724    this.isResizing = true;
6725  }
6726
6727  resize () {
6728    if (this.isBreakpoint(api.core.Breakpoints.LG)) this.deactivateModal();
6729    else this.activateModal();
6730  }
6731
6732  activateModal () {
6733    const modal = this.element.getInstance('Modal');
6734    if (!modal) {
6735      this.request(this.activateModal.bind(this));
6736      return;
6737    }
6738    this.restoreAria();
6739    modal.isActive = true;
6740    this.listenClick({ capture: true });
6741  }
6742
6743  deactivateModal () {
6744    const modal = this.element.getInstance('Modal');
6745    if (!modal) {
6746      this.request(this.deactivateModal.bind(this));
6747      return;
6748    }
6749    modal.conceal();
6750    modal.isActive = false;
6751    this.storeAria();
6752    this.unlistenClick({ capture: true });
6753  }
6754
6755  storeAria () {
6756    if (this.hasAttribute('aria-labelledby')) this._ariaLabelledby = this.getAttribute('aria-labelledby');
6757    if (this.hasAttribute('aria-label')) this._ariaLabel = this.getAttribute('aria-label');
6758    this.removeAttribute('aria-labelledby');
6759    this.removeAttribute('aria-label');
6760  }
6761
6762  restoreAria () {
6763    if (this._ariaLabelledby) this.setAttribute('aria-labelledby', this._ariaLabelledby);
6764    if (this._ariaLabel) this.setAttribute('aria-label', this._ariaLabel);
6765  }
6766
6767  handleClick (e) {
6768    if (e.target.matches('a, button') && !e.target.matches('[aria-controls]') && !e.target.matches(api.core.DisclosureSelector.PREVENT_CONCEAL)) {
6769      const modal = this.element.getInstance('Modal');
6770      modal.conceal();
6771    }
6772  }
6773}
6774
6775api.header = {
6776  HeaderLinks: HeaderLinks,
6777  HeaderModal: HeaderModal,
6778  HeaderSelector: HeaderSelector,
6779  doc: 'https://www.systeme-de-design.gouv.fr/version-courante/fr/composants/en-tete'
6780};
6781
6782api.internals.register(api.header.HeaderSelector.TOOLS_LINKS, api.header.HeaderLinks);
6783api.internals.register(api.header.HeaderSelector.MODALS, api.header.HeaderModal);
6784
6785const DisplaySelector = {
6786  DISPLAY: api.internals.ns.selector('display'),
6787  RADIO_BUTTONS: `input[name="${api.internals.ns('radios-theme')}"]`,
6788  FIELDSET: api.internals.ns.selector('fieldset')
6789};
6790
6791class Display extends api.core.Instance {
6792  static get instanceClassName () {
6793    return 'Display';
6794  }
6795
6796  init () {
6797    this.radios = this.querySelectorAll(DisplaySelector.RADIO_BUTTONS);
6798
6799    if (api.scheme && document.documentElement.hasAttribute(api.scheme.SchemeAttribute.SCHEME)) {
6800      this.changing = this.change.bind(this);
6801      for (const radio of this.radios) radio.addEventListener('change', this.changing);
6802      this.addDescent(api.scheme.SchemeEmission.SCHEME, this.apply.bind(this));
6803      this.ascend(api.scheme.SchemeEmission.ASK);
6804    } else {
6805      this.disableChoices();
6806    }
6807  }
6808
6809  get scheme () {
6810    return this._scheme;
6811  }
6812
6813  set scheme (value) {
6814    if (this._scheme === value || !api.scheme) return;
6815    switch (value) {
6816      case api.scheme.SchemeValue.SYSTEM:
6817      case api.scheme.SchemeValue.LIGHT:
6818      case api.scheme.SchemeValue.DARK:
6819        this._scheme = value;
6820        for (const radio of this.radios) {
6821          radio.checked = radio.value === value;
6822        }
6823        this.ascend(api.scheme.SchemeEmission.SCHEME, value);
6824        break;
6825    }
6826  }
6827
6828  change () {
6829    for (const radio of this.radios) {
6830      if (radio.checked) {
6831        this.scheme = radio.value;
6832        return;
6833      }
6834    }
6835  }
6836
6837  apply (value) {
6838    this.scheme = value;
6839  }
6840
6841  dispose () {
6842    for (const radio of this.radios) radio.removeEventListener('change', this.changing);
6843  }
6844
6845  disableChoices () {
6846    this.querySelector(DisplaySelector.FIELDSET).setAttribute('disabled', '');
6847  }
6848}
6849
6850api.display = {
6851  Display: Display,
6852  DisplaySelector: DisplaySelector
6853};
6854
6855api.internals.register(api.display.DisplaySelector.DISPLAY, api.display.Display);
6856
6857const TableEmission = {
6858  SCROLLABLE: api.internals.ns.emission('table', 'scrollable'),
6859  CHANGE: api.internals.ns.emission('table', 'change'),
6860  CAPTION_HEIGHT: api.internals.ns.emission('table', 'captionheight'),
6861  CAPTION_WIDTH: api.internals.ns.emission('table', 'captionwidth')
6862};
6863
6864class Table extends api.core.Instance {
6865  static get instanceClassName () {
6866    return 'Table';
6867  }
6868
6869  init () {
6870    this.addAscent(TableEmission.CAPTION_HEIGHT, this.setCaptionHeight.bind(this));
6871  }
6872
6873  setCaptionHeight (value) {
6874    this.setProperty('--table-offset', value);
6875  }
6876}
6877
6878class TableWrapper extends api.core.Instance {
6879  static get instanceClassName () {
6880    return 'TableWrapper';
6881  }
6882
6883  init () {
6884    this.addAscent(TableEmission.CAPTION_HEIGHT, this.setCaptionHeight.bind(this));
6885  }
6886
6887  setCaptionHeight (value) {
6888    requestAnimationFrame(() => this.ascend(TableEmission.CAPTION_HEIGHT, 0));
6889    this.setProperty('--table-offset', value);
6890  }
6891}
6892
6893const TableSelector = {
6894  TABLE: api.internals.ns.selector('table'),
6895  TABLE_WRAPPER: [`${api.internals.ns.selector('table')} ${api.internals.ns.selector('table__wrapper')}`],
vendor: 4,246 bytes, lines 6896-7039
6896  SHADOW: api.internals.ns.selector('table__shadow'),
6897  SHADOW_LEFT: api.internals.ns.selector('table__shadow--left'),
6898  SHADOW_RIGHT: api.internals.ns.selector('table__shadow--right'),
6899  ELEMENT: [`${api.internals.ns.selector('table')}:not(${api.internals.ns.selector('table--no-scroll')}) table`],
6900  CAPTION: `${api.internals.ns.selector('table')} table caption`,
6901  ROW: `${api.internals.ns.selector('table')} tbody tr`,
6902  COL: `${api.internals.ns.selector('table')} thead th`
6903};
6904
6905const SCROLL_OFFSET = 0; // valeur en px du scroll avant laquelle le shadow s'active ou se desactive
6906
6907class TableElement extends api.core.Instance {
6908  static get instanceClassName () {
6909    return 'TableElement';
6910  }
6911
6912  init () {
6913    this.listen('scroll', this.scroll.bind(this));
6914    this.content = this.querySelector('tbody');
6915    this.tableOffsetHeight = 0;
6916    this.isResizing = true;
6917  }
6918
6919  get isScrolling () {
6920    return this._isScrolling;
6921  }
6922
6923  set isScrolling (value) {
6924    if (this._isScrolling === value) return;
6925    this._isScrolling = value;
6926
6927    if (value) {
6928      this.addClass(TableSelector.SHADOW);
6929      this.scroll();
6930    } else {
6931      this.removeClass(TableSelector.SHADOW);
6932      this.removeClass(TableSelector.SHADOW_LEFT);
6933      this.removeClass(TableSelector.SHADOW_RIGHT);
6934    }
6935  }
6936
6937  /* ajoute la classe fr-table__shadow-left ou fr-table__shadow-right sur fr-table en fonction d'une valeur de scroll et du sens (right, left) */
6938  scroll () {
6939    const isMin = this.node.scrollLeft <= SCROLL_OFFSET;
6940    const max = this.content.offsetWidth - this.node.offsetWidth - SCROLL_OFFSET;
6941    const isMax = Math.abs(this.node.scrollLeft) >= max;
6942    const isRtl = document.documentElement.getAttribute('dir') === 'rtl';
6943    const minSelector = isRtl ? TableSelector.SHADOW_RIGHT : TableSelector.SHADOW_LEFT;
6944    const maxSelector = isRtl ? TableSelector.SHADOW_LEFT : TableSelector.SHADOW_RIGHT;
6945
6946    if (isMin) {
6947      this.removeClass(minSelector);
6948    } else {
6949      this.addClass(minSelector);
6950    }
6951
6952    if (isMax) {
6953      this.removeClass(maxSelector);
6954    } else {
6955      this.addClass(maxSelector);
6956    }
6957  }
6958
6959  resize () {
6960    this.isScrolling = this.content.offsetWidth > this.node.offsetWidth;
6961  }
6962
6963  dispose () {
6964    this.isScrolling = false;
6965  }
6966}
6967
6968const PADDING = '1rem'; // padding de 4v sur le caption
6969class TableCaption extends api.core.Instance {
6970  static get instanceClassName () {
6971    return 'TableCaption';
6972  }
6973
6974  init () {
6975    this.height = 0;
6976    this.isResizing = true;
6977  }
6978
6979  resize () {
6980    const height = this.getRect().height;
6981    if (this.height === height) return;
6982    this.height = height;
6983    this.ascend(TableEmission.CAPTION_HEIGHT, `calc(${height}px + ${PADDING})`);
6984  }
6985}
6986
6987class TableRow extends api.core.Instance {
6988  static get instanceClassName () {
6989    return 'TableRow';
6990  }
6991
6992  init () {
6993    if (api.checkbox) {
6994      this.addAscent(CheckboxEmission.CHANGE, this._handleCheckboxChange.bind(this));
6995      this.descend(CheckboxEmission.RETRIEVE);
6996    }
6997  }
6998
6999  _handleCheckboxChange (node) {
7000    if (node.name === 'row-select' ||
7001      node.getAttribute(api.internals.ns.attr('row-select')) === 'true') {
7002      this.isSelected = node.checked === true;
7003    }
7004  }
7005
7006  render () {
7007    const height = this.getRect().height + 2;
7008    if (this._height === height) return;
7009    this._height = height;
7010    this.setProperty('--row-height', `${this._height}px`);
7011  }
7012
7013  get isSelected () {
7014    return this._isSelected;
7015  }
7016
7017  set isSelected (value) {
7018    if (this._isSelected === value) return;
7019    this.isRendering = value;
7020    this._isSelected = value;
7021    this.setAttribute('aria-selected', value);
7022  }
7023}
7024
7025api.table = {
7026  Table: Table,
7027  TableWrapper: TableWrapper,
7028  TableElement: TableElement,
7029  TableCaption: TableCaption,
7030  TableSelector: TableSelector,
7031  TableRow: TableRow
7032};
7033
7034api.internals.register(api.table.TableSelector.TABLE, api.table.Table);
7035api.internals.register(api.table.TableSelector.TABLE_WRAPPER, api.table.TableWrapper);
7036api.internals.register(api.table.TableSelector.ELEMENT, api.table.TableElement);
7037api.internals.register(api.table.TableSelector.CAPTION, api.table.TableCaption);
7038api.internals.register(api.table.TableSelector.ROW, api.table.TableRow);
7039//# sourceMappingURL=dsfr.module.js.map

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.