# JavaScript Integration Reference

## Module List

All modules export a default init function. Import and call them:

```js
import initModule from '@Ui/js/module/{name}';
initModule(); // or initModule(config)
```

### Feature Modules

| Module | Import path | Config |
|--------|-------------|--------|
| Header | `@Ui/js/module/header/index` | none |
| Header min | `@Ui/js/module/header/headerMin` | none |
| Bottom bar | `@Ui/js/module/bottomBar/index` | `{ items, scrollThreshold, alwaysVisible, showOnLoad }` |
| Roller | `@Ui/js/module/roller/index` | none |
| Overlay | `@Ui/js/module/overlay/index` | none |
| Search | `@Ui/js/module/search/index` | none |
| Share | `@Ui/js/module/share/share` | none |
| Dark mode | `@Ui/js/module/darkMode/index` | none |
| Fullscreen | `@Ui/js/module/fullScreen/index` | none |
| Video card | `@Ui/js/module/videoCard/index` | none |
| Player | `@Ui/js/module/player/index` | none |
| Player YT | `@Ui/js/module/player/yt` | none |
| Player PiP | `@Ui/js/module/player/pip` | none |
| Audion reader | `@Ui/js/module/playerAudion/audionReader` | none |
| Tracking | `@Ui/js/module/tracking/index` | none |
| Load Outbrain | `@Ui/js/module/sponsoredLink/loadOutbrain` | none |

### Native Placements Modules
| Module | Import path |
|--------|-------------|
| Banner countdown | `@Ui/js/module/nativePlacements/bannerCountdown` |
| Banner showtimes | `@Ui/js/module/nativePlacements/bannerShowtimes` |
| Tracking element | `@Ui/js/module/nativePlacements/trackingElement` |
| Tracking player | `@Ui/js/module/nativePlacements/trackingPlayer` |

### Consent Modules
| Module | Import path |
|--------|-------------|
| Cookie banner | `@Ui/js/consent/cookieBanner` |
| Didomi | `@Ui/js/consent/didomi` |
| TCF API | `@Ui/js/consent/tcfApi` |

---

## Module Init Pattern

```js
import initHeader from '@Ui/js/module/header/index';
import initRoller from '@Ui/js/module/roller/index';

// Simple init
initRoller();

// Async init
await initHeader();

// Init with config
import initBottomBar from '@Ui/js/module/bottomBar/index';
initBottomBar({
  items: [
    { name: "home", iconName: "home", label: "Accueil", link: "/" },
    { name: "menu", iconName: "menu", label: "Menu" },
  ],
  scrollThreshold: 500,
  alwaysVisible: false,
});
```

Modules guard against double init with internal `isInitialized` flag. Some export a state getter:

```js
import { getBottomBarState } from '@Ui/js/module/bottomBar/index';
getBottomBarState(); // { is_open: false }
```

---

## EventEmitter

```js
import eventEmitter, { EventsTypes } from '@Ui/js/services/events/eventEmitter';
```

### Event Types

| Constant | Value | Emitted by |
|----------|-------|------------|
| `DESOBFUSCATION_DONE` | `custom-events/desobfuscation-done` | SEO service |
| `MQ_STATE` | `custom-events/mq-change` | mqState utility |
| `ADSERVER_LOADED` | `custom-events/adserver-loaded` | Ad tools |
| `MENU_OPEN` | `custom-events/menu-open` | Header / bottom bar |
| `MENU_CLOSE` | `custom-events/menu-close` | Header / bottom bar |
| `SEARCH_OPEN` | `custom-events/search-open` | Header search |
| `SEARCH_CLOSE` | `custom-events/search-close` | Header search |

```js
eventEmitter.on(EventsTypes.MENU_OPEN, () => { /* ... */ });
eventEmitter.once(EventsTypes.ADSERVER_LOADED, () => { /* ... */ });
eventEmitter.emit(EventsTypes.MENU_CLOSE);
```

---

## Tools (`tools/`)

| File | Purpose |
|------|---------|
| `debounce` | Debounce function calls |
| `lazyload` | Lazy loading via IntersectionObserver |
| `cookie` | Get/set/remove cookies |
| `request` | Fetch wrapper |
| `loadAssets` | Dynamic asset loading |
| `loadFonts` | Font loading |
| `loadGtm` | Google Tag Manager loader |
| `intersectionObserverItems` | IntersectionObserver helpers |
| `idleRequestCb` | requestIdleCallback wrapper |
| `ads` | Ad blocker detection |
| `string` | String manipulation |
| `yScroll` | Vertical scroll utilities |
| `requestAnimationFrame` | rAF wrapper |
| `noop` | No-op function |

---

## DOM Utilities (`dom/`)

| File | Exports |
|------|---------|
| `events` | `on`, `off`, `once`, `deferToLoad` |
| `classes` | `addClass`, `removeClass`, `toggleClass`, `hasClass`, `replaceClass` |
| `anchor` | Anchor element utilities |
| `scrollTo` | Smooth scroll (default export) |
| `mqState` | Media query state management |
| `setTouchEvent` | Touch event utilities |

---

## Constants (`constant/dom`)

DOM element refs: `BODY`, `HEADER_MAIN`

CSS classes: `CLASS_BODY_OVERLAY_OPEN`, `CLASS_BODY_HAS_BOTTOM_BAR`, `CLASS_BODY_BOTTOM_BAR_ON`, `CLASS_BODY_HEADER_MAIN_OFF`, `CLASS_MOBILE_SUBNAV_IS_OPEN`, `CLASS_MOBILE_SEARCH_IS_OPEN`, `HIDDEN_AD_CLASS`

---

## SEO Service

```js
import { runAfterDeobfuscation } from '@Ui/js/services/seo/deobfuscation';
runAfterDeobfuscation(() => { /* runs after link deobfuscation */ });
```

---

## Twig-to-JS Mapping

Templates that need JS initialization:

| Twig template | JS module |
|---------------|-----------|
| `module/header/header_main_v2` | `module/header/index` |
| `module/roller/roller` | `module/roller/index` |
| `module/search/search` | `module/search/index` |
| `component/share` | `module/share/share` |
| `module/video/video_card` | `module/videoCard/index` |
| `module/player/player_internal` | `module/player/index` |
| `module/player/player_internal_yt` | `module/player/yt` |
| `module/player_audion/audion_reader` | `module/playerAudion/audionReader` |
| `module/anchor/overlay_anchor` | `module/overlay/index` |
| `module/consent/cookie_banner` | `consent/cookieBanner` |
